Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2025 MuseScore Limited
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "videoencoderresolver.h"
24 :
25 : #include "global/timer.h"
26 : #include "io/path.h"
27 : #include "log.h"
28 :
29 : #include "internal/ffmpeg/v9/videoencoder.h"
30 : #include "internal/ffmpeg/v8/videoencoder.h"
31 : #include "internal/ffmpeg/v7/videoencoder.h"
32 : #include "internal/ffmpeg/v6/videoencoder.h"
33 : #include "internal/ffmpeg/v5/videoencoder.h"
34 : #include "internal/ffmpeg/v4/videoencoder.h"
35 :
36 : #include "mediatypes.h"
37 :
38 : using namespace muse::media;
39 :
40 0 : void VideoEncoderResolver::init()
41 : {
42 0 : loadFFmpeg(configuration()->ffmpegLibsDir(), false);
43 :
44 : //! NOTE: We need to search for and reload the FFmpeg libraries with a delay,
45 : //! because this is a resource-intensive operation.
46 0 : m_reloadFfmpegTimer = std::make_shared<Timer>(std::chrono::seconds(1));
47 0 : m_reloadFfmpegTimer->onTimeout(this, [this](){
48 0 : loadFFmpeg(configuration()->ffmpegLibsDir(), false);
49 :
50 0 : m_reloadFfmpegTimer->stop();
51 0 : });
52 :
53 0 : m_ffmpegLibWatcher.directoryChanged().onReceive(this, [this](const std::string&) {
54 0 : if (m_reloadFfmpegTimer->isActive()) {
55 0 : return;
56 : }
57 :
58 0 : m_reloadFfmpegTimer->start();
59 : });
60 0 : }
61 :
62 0 : void VideoEncoderResolver::deinit()
63 : {
64 0 : m_ffmpegLibWatcher.stopWatching();
65 0 : m_reloadFfmpegTimer->stop();
66 0 : m_reloadFfmpegTimer.reset();
67 0 : }
68 :
69 0 : void VideoEncoderResolver::loadFFmpeg(const io::path_t& ffmpegLibsDir)
70 : {
71 0 : loadFFmpeg(ffmpegLibsDir, true);
72 0 : }
73 :
74 0 : void VideoEncoderResolver::loadFFmpeg(const io::path_t& ffmpegLibsDir, bool persistRequestedPath)
75 : {
76 0 : const FFmpegLibPathsList libraryPaths = findLibraryPaths(ffmpegLibsDir);
77 0 : for (const FFmpegLibPaths& paths : libraryPaths) {
78 0 : EncoderInfo encoderInfo = makeEncoder(paths);
79 0 : if (!encoderInfo.encoder) {
80 0 : continue;
81 : }
82 :
83 0 : setCurrentVideoEncoder(encoderInfo.encoder);
84 0 : m_loadedFFmpegDir = encoderInfo.ffmpegLibsDir;
85 0 : m_currentEncoderFFmpegVersion = encoderInfo.ffmpegVersion;
86 :
87 0 : if (persistRequestedPath) {
88 0 : const std::optional<io::path_t> pathToPersist = configuredPathToPersist(ffmpegLibsDir, paths);
89 0 : if (pathToPersist.has_value()) {
90 0 : configuration()->setFFmpegLibsDir(pathToPersist.value());
91 : }
92 0 : }
93 :
94 0 : m_loadedFFmpegChanged.notify();
95 0 : return;
96 0 : }
97 :
98 0 : if (persistRequestedPath && ffmpegLibsDir.empty()) {
99 0 : configuration()->setFFmpegLibsDir({});
100 : }
101 0 : resetFFmpegSettings();
102 0 : }
103 :
104 0 : void VideoEncoderResolver::setIsSettingMode(bool arg)
105 : {
106 0 : if (arg) {
107 0 : if (m_currentEncoderFFmpegVersion == FFMPEG_INVALID_VERSION) {
108 0 : startWatchingFfmpegsDirs();
109 : }
110 : } else {
111 0 : m_ffmpegLibWatcher.stopWatching();
112 : }
113 0 : }
114 :
115 0 : void VideoEncoderResolver::startWatchingFfmpegsDirs()
116 : {
117 0 : m_ffmpegLibWatcher.stopWatching();
118 :
119 0 : io::paths_t paths = defaultSearchPaths();
120 0 : paths.emplace_back(configuration()->ffmpegLibsDir());
121 :
122 0 : for (const io::path_t& p : paths) {
123 0 : m_ffmpegLibWatcher.startWatching(p.toStdString());
124 : }
125 0 : }
126 :
127 0 : muse::io::path_t VideoEncoderResolver::loadedFFmpegDir() const
128 : {
129 0 : return m_loadedFFmpegDir;
130 : }
131 :
132 0 : FFmpegVersion VideoEncoderResolver::loadedFFmpegVersion() const
133 : {
134 0 : return m_currentEncoderFFmpegVersion;
135 : }
136 :
137 0 : muse::async::Notification VideoEncoderResolver::loadedFFmpegChanged() const
138 : {
139 0 : return m_loadedFFmpegChanged;
140 : }
141 :
142 0 : IVideoEncoderPtr VideoEncoderResolver::currentVideoEncoder() const
143 : {
144 0 : return m_encoder;
145 : }
146 :
147 0 : void VideoEncoderResolver::setCurrentVideoEncoder(IVideoEncoderPtr encoder)
148 : {
149 0 : m_encoder = std::move(encoder);
150 0 : }
151 :
152 0 : void VideoEncoderResolver::resetFFmpegSettings()
153 : {
154 0 : setCurrentVideoEncoder(nullptr);
155 0 : m_currentEncoderFFmpegVersion = FFMPEG_INVALID_VERSION;
156 0 : m_loadedFFmpegDir = io::path_t();
157 :
158 0 : m_loadedFFmpegChanged.notify();
159 0 : }
160 :
161 : template<typename Encoder>
162 0 : static IVideoEncoderPtr tryCreateEncoder(const FFmpegLibPaths& paths)
163 : {
164 0 : auto encoder = std::make_shared<Encoder>();
165 0 : if (encoder->load(paths)) {
166 0 : return encoder;
167 : }
168 0 : return nullptr;
169 0 : }
170 :
171 0 : VideoEncoderResolver::EncoderInfo VideoEncoderResolver::makeEncoder(const FFmpegLibPaths& ffmpegLibsPaths) const
172 : {
173 0 : EncoderInfo result;
174 :
175 0 : const FFmpegVersion version = ffmpegLibsPaths.ffmpegVersion;
176 0 : switch (version) {
177 0 : case FFMPEG_V9:
178 0 : result.encoder = tryCreateEncoder<ffmpeg::v9::VideoEncoder>(ffmpegLibsPaths);
179 0 : break;
180 0 : case FFMPEG_V8:
181 0 : result.encoder = tryCreateEncoder<ffmpeg::v8::VideoEncoder>(ffmpegLibsPaths);
182 0 : break;
183 0 : case FFMPEG_V7:
184 0 : result.encoder = tryCreateEncoder<ffmpeg::v7::VideoEncoder>(ffmpegLibsPaths);
185 0 : break;
186 0 : case FFMPEG_V6:
187 0 : result.encoder = tryCreateEncoder<ffmpeg::v6::VideoEncoder>(ffmpegLibsPaths);
188 0 : break;
189 0 : case FFMPEG_V5:
190 0 : result.encoder = tryCreateEncoder<ffmpeg::v5::VideoEncoder>(ffmpegLibsPaths);
191 0 : break;
192 0 : case FFMPEG_V4:
193 0 : result.encoder = tryCreateEncoder<ffmpeg::v4::VideoEncoder>(ffmpegLibsPaths);
194 0 : break;
195 0 : default:
196 0 : break;
197 : }
198 :
199 0 : if (result.encoder) {
200 0 : result.ffmpegLibsDir = ffmpegLibsPaths.searchDir;
201 0 : result.ffmpegVersion = version;
202 :
203 0 : LOGD() << "FFmpeg loaded, version: " << version;
204 : }
205 :
206 0 : return result;
207 0 : }
|