1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "Screenshot.h"
10
11 #include "ServiceBroker.h"
12 #include "URL.h"
13 #include "Util.h"
14 #include "filesystem/File.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "pictures/Picture.h"
17 #include "settings/SettingPath.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "settings/windows/GUIControlSettings.h"
21 #include "utils/JobManager.h"
22 #include "utils/URIUtils.h"
23 #include "utils/log.h"
24
25 using namespace XFILE;
26
27 std::vector<std::function<std::unique_ptr<IScreenshotSurface>()>> CScreenShot::m_screenShotSurfaces;
28
Register(const std::function<std::unique_ptr<IScreenshotSurface> ()> & createFunc)29 void CScreenShot::Register(const std::function<std::unique_ptr<IScreenshotSurface>()>& createFunc)
30 {
31 m_screenShotSurfaces.emplace_back(createFunc);
32 }
33
TakeScreenshot(const std::string & filename,bool sync)34 void CScreenShot::TakeScreenshot(const std::string& filename, bool sync)
35 {
36 auto surface = m_screenShotSurfaces.back()();
37
38 if (!surface)
39 {
40 CLog::Log(LOGERROR, "failed to create screenshot surface");
41 return;
42 }
43
44 if (!surface->Capture())
45 {
46 CLog::Log(LOGERROR, "Screenshot %s failed", CURL::GetRedacted(filename).c_str());
47 return;
48 }
49
50 surface->CaptureVideo(true);
51
52 CLog::Log(LOGDEBUG, "Saving screenshot %s", CURL::GetRedacted(filename).c_str());
53
54 //set alpha byte to 0xFF
55 for (int y = 0; y < surface->GetHeight(); y++)
56 {
57 unsigned char* alphaptr = surface->GetBuffer() - 1 + y * surface->GetStride();
58 for (int x = 0; x < surface->GetWidth(); x++)
59 *(alphaptr += 4) = 0xFF;
60 }
61
62 //if sync is true, the png file needs to be completely written when this function returns
63 if (sync)
64 {
65 if (!CPicture::CreateThumbnailFromSurface(surface->GetBuffer(), surface->GetWidth(), surface->GetHeight(), surface->GetStride(), filename))
66 CLog::Log(LOGERROR, "Unable to write screenshot %s", CURL::GetRedacted(filename).c_str());
67
68 surface->ReleaseBuffer();
69 }
70 else
71 {
72 //make sure the file exists to avoid concurrency issues
73 XFILE::CFile file;
74 if (file.OpenForWrite(filename))
75 file.Close();
76 else
77 CLog::Log(LOGERROR, "Unable to create file %s", CURL::GetRedacted(filename).c_str());
78
79 //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
80 //buffer is deleted from CThumbnailWriter
81 CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(surface->GetBuffer(), surface->GetWidth(), surface->GetHeight(), surface->GetStride(), filename);
82 CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
83 }
84 }
85
TakeScreenshot()86 void CScreenShot::TakeScreenshot()
87 {
88 std::shared_ptr<CSettingPath> screenshotSetting = std::static_pointer_cast<CSettingPath>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_DEBUG_SCREENSHOTPATH));
89 if (!screenshotSetting)
90 return;
91
92 std::string strDir = screenshotSetting->GetValue();
93 if (strDir.empty())
94 {
95 if (!CGUIControlButtonSetting::GetPath(screenshotSetting, &g_localizeStrings))
96 return;
97
98 strDir = screenshotSetting->GetValue();
99 }
100
101 URIUtils::RemoveSlashAtEnd(strDir);
102
103 if (!strDir.empty())
104 {
105 std::string file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%05d.png"), 65535);
106
107 if (!file.empty())
108 {
109 TakeScreenshot(file, false);
110 }
111 else
112 {
113 CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
114 }
115 }
116 }
117