Audacity 3.2.0
FFmpegFunctions.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 FFmpegFunctions.cpp
6
7 Dmitry Vedenko
8
9**********************************************************************/
10
11#include "FFmpegFunctions.h"
12
13#include <wx/string.h>
14#include <wx/dynlib.h>
15#include <wx/log.h>
16#include <wx/utils.h>
17
18#if defined(__WXMSW__)
19# include <wx/buffer.h>
20# include <wx/msw/registry.h>
21
22# define WIN32_LEAN_AND_MEAN
23# include <windows.h>
24# include <psapi.h>
25#else
26# include <dlfcn.h>
27#endif
28
29#include "Prefs.h"
30#include "FileNames.h"
31
35
37#include "impl/FFmpegLog.h"
38
39void* GetSymbolFromProcess(const char* name)
40{
41#if defined(__WXMSW__)
42 std::vector<HMODULE> handles(256);
43
44 DWORD neededMemory;
45
46 if (!EnumProcessModules(
47 GetCurrentProcess(), handles.data(), sizeof(HMODULE) * handles.size(),
48 &neededMemory))
49 {
50 return nullptr;
51 }
52
53 const int modulesCount = neededMemory / sizeof(HMODULE);
54
55 if (modulesCount > handles.size())
56 {
57 handles.resize(modulesCount);
58
59 if (!EnumProcessModules(
60 GetCurrentProcess(), handles.data(),
61 sizeof(HMODULE) * handles.size(), &neededMemory))
62 {
63 return nullptr;
64 }
65 }
66
67 for (HMODULE handle : handles)
68 {
69 void* addr = GetProcAddress(handle, name);
70
71 if (addr != nullptr)
72 return addr;
73 }
74
75 return nullptr;
76#else
77 return dlsym(RTLD_DEFAULT, name);
78#endif
79}
80
81struct EnvSetter final
82{
83 static const wxString VariableName;
84 static const wxString Separator;
85
86 explicit EnvSetter(bool fromUserPathOnly)
87 {
89
90 wxString value;
91
92 for (const wxString& path : FFmpegFunctions::GetSearchPaths(fromUserPathOnly))
93 {
94 if (!value.empty())
95 value += Separator;
96
97 value += path;
98 }
99
100 wxSetEnv(VariableName, value);
101 };
102
104 {
105 if (ValueExisted)
106 wxSetEnv(VariableName, OldValue);
107 else
108 wxUnsetEnv(VariableName);
109 }
110
111 wxString OldValue;
113};
114
115#if defined(__WXMSW__)
116const wxString EnvSetter::VariableName("PATH");
117const wxString EnvSetter::Separator(";");
118#elif defined(__WXMAC__)
119const wxString EnvSetter::VariableName("DYLD_LIBRARY_PATH");
120const wxString EnvSetter::Separator(":");
121#else
122const wxString EnvSetter::VariableName("LD_LIBRARY_PATH");
123const wxString EnvSetter::Separator(":");
124#endif
125
126std::vector<wxString> BuildAVFormatPaths(int version)
127{
128 return {
129#if defined(__WXMSW__)
130 wxString::Format("avformat-%d.dll", version),
131#elif defined(__WXMAC__)
132 wxString::Format("ffmpeg.%d.64bit.dylib", version),
133 wxString::Format("libavformat.%d.dylib", version),
134#else
135 wxString::Format("libavformat.so.%d", version)
136#endif
137};
138}
139
141{
142 std::shared_ptr<wxDynamicLibrary> AVFormatLibrary;
143 std::shared_ptr<wxDynamicLibrary> AVCodecLibrary;
144 std::shared_ptr<wxDynamicLibrary> AVUtilLibrary;
145
146 std::unique_ptr<FFmpegLog> FFmpegLogCallbackSetter;
147
151
152 std::shared_ptr<wxDynamicLibrary> LibraryWithSymbol(const char* symbol, bool fromUserPathOnly) const
153 {
154 if (AVFormatLibrary->HasSymbol(symbol))
155 return AVFormatLibrary;
156
157 void* addr = GetSymbolFromProcess(symbol);
158
159 if (addr == nullptr)
160 return nullptr;
161
162 const wxString path = FileNames::PathFromAddr(addr);
163
164 if (path.empty())
165 return nullptr;
166
167 return LoadLibrary(wxFileNameFromPath(path), fromUserPathOnly);
168 }
169
170 bool Load(FFmpegFunctions& functions, const wxString& path, bool fromUserPathOnly)
171 {
172 // We start by loading AVFormat
173 AVFormatLibrary = LoadLibrary(path, fromUserPathOnly);
174
175 if (AVFormatLibrary == nullptr)
176 return false;
177
178 if ((AVCodecLibrary = LibraryWithSymbol("avcodec_version", fromUserPathOnly)) == nullptr)
179 return false;
180
181 if ((AVUtilLibrary = LibraryWithSymbol("avutil_version", fromUserPathOnly)) == nullptr)
182 return false;
183
184 if (
186 !LoadAVCodecFunctions(*AVCodecLibrary, functions) ||
188 return false;
189
190 if (!FFmpegAPIResolver::Get().GetAVFormatFactories(
192 return false;
193
194 if (!FFmpegAPIResolver::Get().GetAVCodecFactories(
196 return false;
197
198 AVCodecIDResolver codecResolvers;
199
200 if (!FFmpegAPIResolver::Get().GetAVCodecIDResolver(
201 functions.AVCodecVersion.Major, codecResolvers))
202 return false;
203
204 functions.GetAVCodecID = codecResolvers.GetAVCodecID;
205 functions.GetAudacityCodecID = codecResolvers.GetAudacityCodecID;
206
207 if (!FFmpegAPIResolver::Get().GetAVUtilFactories(
209 return false;
210
211 functions.avcodec_register_all();
212
213 if (functions.av_register_all)
214 functions.av_register_all();
215
218
219 return true;
220 }
221
222 std::shared_ptr<wxDynamicLibrary> LoadLibrary(const wxString& libraryName, bool fromUserPathOnly) const
223 {
224#if defined(__WXMAC__)
225 // On macOS dyld reads environment only when application starts.
226 // Let's emulate the process manually
227 for (const wxString& path : FFmpegFunctions::GetSearchPaths(fromUserPathOnly))
228 {
229 const wxString fullName = wxFileName(path, libraryName).GetFullPath();
230
231 auto library = std::make_shared<wxDynamicLibrary>(fullName);
232
233 if (library->IsLoaded())
234 return library;
235 }
236#endif
237 auto library = std::make_shared<wxDynamicLibrary> (libraryName);
238
239 if (library->IsLoaded())
240 return library;
241
242 // Loading has failed.
243 // wxLogSysError doesn't report errors correctly on *NIX
244#if defined(_WIN32)
245 wxLogSysError("Failed to load %s", libraryName.c_str());
246#else
247 const char* errorString = dlerror();
248 wxLogError("Failed to load %s (%s)", libraryName.c_str(), errorString);
249#endif
250 return {};
251 }
252};
253
255 : mPrivate(std::make_unique<Private>())
256{
257}
258
260{
261}
262
263std::shared_ptr<FFmpegFunctions> FFmpegFunctions::Load(bool fromUserPathOnly)
264{
265 static std::weak_ptr<FFmpegFunctions> weakFunctions;
266
267 auto functions = weakFunctions.lock();
268
269 if (functions)
270 return functions;
271
272 std::shared_ptr<FFmpegFunctions> ffmpeg =
273 std::make_shared<FFmpegFunctions>();
274
275 const auto supportedVersions =
277
278#if !defined(__WXMAC__)
279 EnvSetter envSetter(fromUserPathOnly);
280#endif
281
282 for (int version : supportedVersions)
283 {
284 for (const wxString& path : BuildAVFormatPaths(version))
285 {
286 if (ffmpeg->mPrivate->Load(*ffmpeg, path, fromUserPathOnly))
287 {
288 weakFunctions = ffmpeg;
289 return ffmpeg;
290 }
291 }
292 }
293
294 return {};
295}
296
297StringSetting AVFormatPath { L"/FFmpeg/FFmpegLibPath", L"" };
298
299std::vector<wxString> FFmpegFunctions::GetSearchPaths(bool fromUserPathOnly)
300{
301 std::vector<wxString> paths;
302
303 const wxString userAVFormatFullPath = AVFormatPath.Read();
304
305 if (!userAVFormatFullPath.empty())
306 {
307 // For some directories, wxPathOnly will fail.
308 // For example, if path is `c:\ffmpeg-4.4`
309 // wxPathOnly will return `c:\`
310 if (wxDirExists(userAVFormatFullPath))
311 paths.emplace_back(userAVFormatFullPath);
312 else
313 paths.emplace_back(wxPathOnly(userAVFormatFullPath));
314 }
315
316 if (fromUserPathOnly)
317 return paths;
318
319#if defined(__WXMSW__)
320 wxRegKey reg(wxT("HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity"));
321 wxString path;
322
323 if (reg.Exists())
324 reg.QueryValue(wxT("InstallPath"), path);
325
326 if (!path.empty())
327 paths.emplace_back(path);
328
329#elif defined(__WXMAC__)
330 paths.emplace_back(wxT("/Library/Application Support/audacity/libs"));
331 paths.emplace_back(wxT("/usr/local/lib/audacity"));
332#endif
333
334 return paths;
335}
336
337std::unique_ptr<AVIOContextWrapper> FFmpegFunctions::CreateAVIOContext() const
338{
339 return mPrivate->FormatFactories.CreateAVIOContextWrapper(*this);
340}
341
342std::unique_ptr<AVFormatContextWrapper>
344{
345 return mPrivate->FormatFactories.CreateAVFormatContextWrapper(*this);
346}
347
348std::unique_ptr<AVStreamWrapper>
350{
351 return mPrivate->FormatFactories.CreateAVStreamWrapper(*this, stream);
352}
353
354std::unique_ptr<AVPacketWrapper> FFmpegFunctions::CreateAVPacketWrapper() const
355{
356 return mPrivate->CodecFactories.CreateAVPacketWrapper(*this);
357}
358
359std::unique_ptr<AVFrameWrapper> FFmpegFunctions::CreateAVFrameWrapper() const
360{
361 return mPrivate->UtilFactories.CreateAVFrameWrapper(*this);
362}
363
364std::unique_ptr<AVInputFormatWrapper>
366 AVInputFormat* inputFormat) const
367{
368 return mPrivate->FormatFactories.CreateAVInputFormatWrapper(inputFormat);
369}
370
371std::unique_ptr<AVOutputFormatWrapper> FFmpegFunctions::GuessOutputFormat(
372 const char* short_name, const char* filename, const char* mime_type)
373{
374 AVOutputFormat* outputFormat =
375 av_guess_format(short_name, filename, mime_type);
376
377 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
378}
379
380std::unique_ptr<AVOutputFormatWrapper>
382 AVOutputFormat* outputFormat) const
383{
384 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
385}
386
387std::unique_ptr<AVCodecWrapper>
389{
390 AVCodec* codec = avcodec_find_decoder(codecID);
391
392 if (codec == nullptr)
393 return {};
394
395 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
396}
397
398std::unique_ptr<AVCodecWrapper>
400{
401 AVCodec* codec = avcodec_find_encoder(codecID);
402
403 if (codec == nullptr)
404 return {};
405
406 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
407}
408
409std::unique_ptr<AVCodecWrapper>
411{
413
414 if (codec == nullptr)
415 return {};
416
417 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
418}
419
420std::unique_ptr<AVCodecContextWrapper>
422{
423 return mPrivate->CodecFactories.CreateAVCodecContextWrapper(
424 *this, context);
425}
426
427std::unique_ptr<AVCodecContextWrapper>
429 std::unique_ptr<AVCodecWrapper> codec) const
430{
431 if (codec == nullptr)
432 return {};
433
434 return mPrivate->CodecFactories.CreateAVCodecContextWrapperFromCodec(
435 *this, std::move(codec));
436}
437
438std::unique_ptr<AVOutputFormatWrapper>
440{
441 AVOutputFormat* outputFormat =
442 av_oformat_next(fmt != nullptr ? fmt->GetWrappedValue() : nullptr);
443
444 if (outputFormat == nullptr)
445 return {};
446
447 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
448}
449
450std::unique_ptr<AVCodecWrapper>
452{
453 AVCodec* nextCodec =
454 av_codec_next(codec != nullptr ? codec->GetWrappedValue() : nullptr);
455
456 if (nextCodec == nullptr)
457 return {};
458
459 return mPrivate->CodecFactories.CreateAVCodecWrapper(nextCodec);
460}
461
462std::unique_ptr<AVFifoBufferWrapper>
464{
465 return std::make_unique<AVFifoBufferWrapper>(*this, size);
466}
bool LoadAVCodecFunctions(const wxDynamicLibrary &lib, AVCodecFunctions &functions)
int AVCodecIDFwd
Definition: AVCodecID.h:407
bool LoadAVFormatFunctions(const wxDynamicLibrary &lib, AVFormatFunctions &functions)
bool LoadAVUtilFunctions(const wxDynamicLibrary &lib, AVUtilFunctions &functions)
const TranslatableString name
Definition: Distortion.cpp:82
StringSetting AVFormatPath
void * GetSymbolFromProcess(const char *name)
std::vector< wxString > BuildAVFormatPaths(int version)
AVCodec * GetWrappedValue() noexcept
AVOutputFormat * GetWrappedValue() noexcept
std::vector< int > GetSuportedAVFormatVersions() const
static FFmpegAPIResolver & Get()
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
Specialization of Setting for strings.
Definition: Prefs.h:363
FILES_API FilePath PathFromAddr(void *addr)
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
STL namespace.
AVCodec *(* avcodec_find_encoder_by_name)(const char *name)
FFMPegVersion AVCodecVersion
AVCodec *(* av_codec_next)(const AVCodec *c)
void(* avcodec_register_all)(void)
AVCodec *(* avcodec_find_encoder)(AVCodecIDFwd id)
AVCodec *(* avcodec_find_decoder)(AVCodecIDFwd id)
AudacityAVCodecID(* GetAudacityCodecID)(AVCodecIDFwd)
AVCodecIDFwd(* GetAVCodecID)(AudacityAVCodecID)
AVOutputFormat *(* av_guess_format)(const char *short_name, const char *filename, const char *mime_type)
void(* av_register_all)(void)
FFMPegVersion AVFormatVersion
AVOutputFormat *(* av_oformat_next)(const AVOutputFormat *f)
std::unique_ptr< FFmpegLog >(* CreateLogCallbackSetter)(const FFmpegFunctions &)
FFMPegVersion AVUtilVersion
EnvSetter(bool fromUserPathOnly)
static const wxString Separator
static const wxString VariableName
wxString OldValue
unsigned Major
Definition: FFmpegTypes.h:164
std::shared_ptr< wxDynamicLibrary > LibraryWithSymbol(const char *symbol, bool fromUserPathOnly) const
AVCodecFactories CodecFactories
AVFormatFactories FormatFactories
bool Load(FFmpegFunctions &functions, const wxString &path, bool fromUserPathOnly)
std::shared_ptr< wxDynamicLibrary > AVFormatLibrary
std::shared_ptr< wxDynamicLibrary > AVCodecLibrary
std::unique_ptr< FFmpegLog > FFmpegLogCallbackSetter
std::shared_ptr< wxDynamicLibrary > AVUtilLibrary
std::shared_ptr< wxDynamicLibrary > LoadLibrary(const wxString &libraryName, bool fromUserPathOnly) const
std::unique_ptr< AVInputFormatWrapper > CreateAVInputFormatWrapper(AVInputFormat *inputFormat) const
AudacityAVCodecID(* GetAudacityCodecID)(AVCodecIDFwd)
std::unique_ptr< AVFormatContextWrapper > CreateAVFormatContext() const
std::unique_ptr< AVOutputFormatWrapper > CreateAVOutputFormatWrapper(AVOutputFormat *outputFormat) const
std::unique_ptr< AVCodecContextWrapper > CreateAVCodecContextWrapperFromCodec(std::unique_ptr< AVCodecWrapper > codec) const
static std::vector< wxString > GetSearchPaths(bool fromUserPathOnly)
std::unique_ptr< AVFrameWrapper > CreateAVFrameWrapper() const
std::unique_ptr< AVOutputFormatWrapper > GetNextOutputFormat(const AVOutputFormatWrapper *fmt) const
std::unique_ptr< AVOutputFormatWrapper > GuessOutputFormat(const char *short_name, const char *filename, const char *mime_type)
std::unique_ptr< AVCodecWrapper > CreateEncoder(AVCodecIDFwd codecID) const
AVCodecIDFwd(* GetAVCodecID)(AudacityAVCodecID)
std::unique_ptr< AVIOContextWrapper > CreateAVIOContext() const
std::unique_ptr< AVCodecWrapper > GetNextCodec(const AVCodecWrapper *codec) const
std::unique_ptr< AVPacketWrapper > CreateAVPacketWrapper() const
static std::shared_ptr< FFmpegFunctions > Load(bool fromUserPathOnly=false)
std::unique_ptr< AVCodecContextWrapper > CreateAVCodecContextWrapper(AVCodecContext *context) const
std::unique_ptr< Private > mPrivate
std::unique_ptr< AVCodecWrapper > CreateDecoder(AVCodecIDFwd codecID) const
std::unique_ptr< AVStreamWrapper > CreateAVStreamWrapper(AVStream *stream) const
std::unique_ptr< AVFifoBufferWrapper > CreateFifoBuffer(int size) const