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("libavformat.%d.dylib", version),
133 wxString::Format("ffmpeg.%d.64bit.dylib", version),
134#elif defined(__OpenBSD__)
135 wxString::Format("libavformat.so"),
136#else
137 wxString::Format("libavformat.so.%d", version),
138#endif
139};
140}
141
143{
144 std::shared_ptr<wxDynamicLibrary> AVFormatLibrary;
145 std::shared_ptr<wxDynamicLibrary> AVCodecLibrary;
146 std::shared_ptr<wxDynamicLibrary> AVUtilLibrary;
147
148 std::unique_ptr<FFmpegLog> FFmpegLogCallbackSetter;
149
153
154 std::shared_ptr<wxDynamicLibrary> LibraryWithSymbol(const char* symbol, bool fromUserPathOnly) const
155 {
156 if (AVFormatLibrary->HasSymbol(symbol))
157 return AVFormatLibrary;
158
159 void* addr = GetSymbolFromProcess(symbol);
160
161 if (addr == nullptr)
162 return nullptr;
163
164 const wxString path = FileNames::PathFromAddr(addr);
165
166 if (path.empty())
167 return nullptr;
168
169 return LoadLibrary(wxFileNameFromPath(path), fromUserPathOnly);
170 }
171
172 bool Load(FFmpegFunctions& functions, const wxString& path, bool fromUserPathOnly)
173 {
174 // We start by loading AVFormat
175 AVFormatLibrary = LoadLibrary(path, fromUserPathOnly);
176
177 if (AVFormatLibrary == nullptr)
178 return false;
179
180 if ((AVCodecLibrary = LibraryWithSymbol("avcodec_version", fromUserPathOnly)) == nullptr)
181 return false;
182
183 if ((AVUtilLibrary = LibraryWithSymbol("avutil_version", fromUserPathOnly)) == nullptr)
184 return false;
185
186 if (
188 !LoadAVCodecFunctions(*AVCodecLibrary, functions) ||
190 return false;
191
192 if (!FFmpegAPIResolver::Get().GetAVFormatFactories(
194 return false;
195
196 if (!FFmpegAPIResolver::Get().GetAVCodecFactories(
198 return false;
199
200 AVCodecIDResolver codecResolvers;
201
202 if (!FFmpegAPIResolver::Get().GetAVCodecIDResolver(
203 functions.AVCodecVersion.Major, codecResolvers))
204 return false;
205
206 functions.GetAVCodecID = codecResolvers.GetAVCodecID;
207 functions.GetAudacityCodecID = codecResolvers.GetAudacityCodecID;
208
209 if (!FFmpegAPIResolver::Get().GetAVUtilFactories(
211 return false;
212
213 wxLogInfo("FFmpeg libraries loaded successfully from: %s",
214 FileNames::PathFromAddr(AVFormatLibrary->GetSymbol("avformat_version")));
215
216 if (functions.avcodec_register_all)
217 functions.avcodec_register_all();
218
219 if (functions.av_register_all)
220 functions.av_register_all();
221
224
225 return true;
226 }
227
228 std::shared_ptr<wxDynamicLibrary> LoadLibrary(const wxString& libraryName, bool fromUserPathOnly) const
229 {
230#if defined(__WXMAC__)
231 // On macOS dyld reads environment only when application starts.
232 // Let's emulate the process manually
233 for (const wxString& path : FFmpegFunctions::GetSearchPaths(fromUserPathOnly))
234 {
235 const wxString fullName = wxFileName(path, libraryName).GetFullPath();
236
237 if (!wxFileExists(fullName))
238 continue;
239
240 auto library = std::make_shared<wxDynamicLibrary>(fullName);
241
242 if (library->IsLoaded())
243 return library;
244 }
245#endif
246 auto library = std::make_shared<wxDynamicLibrary> (libraryName);
247
248 if (library->IsLoaded())
249 return library;
250
251 return {};
252 }
253};
254
256 : mPrivate(std::make_unique<Private>())
257{
258}
259
261{
262}
263
264std::shared_ptr<FFmpegFunctions> FFmpegFunctions::Load(bool fromUserPathOnly)
265{
266 static std::weak_ptr<FFmpegFunctions> weakFunctions;
267
268 auto functions = weakFunctions.lock();
269
270 if (functions)
271 return functions;
272
273 std::shared_ptr<FFmpegFunctions> ffmpeg =
274 std::make_shared<FFmpegFunctions>();
275
276 const auto supportedVersions =
278
279#if !defined(__WXMAC__)
280 EnvSetter envSetter(fromUserPathOnly);
281#endif
282
283 for (int version : supportedVersions)
284 {
285 for (const wxString& path : BuildAVFormatPaths(version))
286 {
287 if (ffmpeg->mPrivate->Load(*ffmpeg, path, fromUserPathOnly))
288 {
289 weakFunctions = ffmpeg;
290 return ffmpeg;
291 }
292 }
293 }
294
295 return {};
296}
297
298StringSetting AVFormatPath { L"/FFmpeg/FFmpegLibPath", L"" };
299
300std::vector<wxString> FFmpegFunctions::GetSearchPaths(bool fromUserPathOnly)
301{
302 std::vector<wxString> paths;
303
304 const wxString userAVFormatFullPath = AVFormatPath.Read();
305
306 if (!userAVFormatFullPath.empty())
307 {
308 // For some directories, wxPathOnly will fail.
309 // For example, if path is `c:\ffmpeg-4.4`
310 // wxPathOnly will return `c:\`
311 if (wxDirExists(userAVFormatFullPath))
312 paths.emplace_back(userAVFormatFullPath);
313 else
314 paths.emplace_back(wxPathOnly(userAVFormatFullPath));
315 }
316
317 if (fromUserPathOnly)
318 return paths;
319
320#if defined(__WXMSW__)
321 wxRegKey reg(wxT("HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity"));
322 wxString path;
323
324 if (reg.Exists())
325 reg.QueryValue(wxT("InstallPath"), path);
326
327 if (!path.empty())
328 paths.emplace_back(path);
329
330#elif defined(__WXMAC__)
331 paths.emplace_back(wxT("/Library/Application Support/audacity/libs"));
332 paths.emplace_back(wxT("/usr/local/lib/audacity"));
333 // x86_64 Homebrew
334 paths.emplace_back(wxT("/usr/local/lib"));
335 // ARM64 Homebrew
336 paths.emplace_back(wxT("/opt/homebrew/lib"));
337#endif
338
339 return paths;
340}
341
342std::unique_ptr<AVIOContextWrapper> FFmpegFunctions::CreateAVIOContext() const
343{
344 return mPrivate->FormatFactories.CreateAVIOContextWrapper(*this);
345}
346
347std::unique_ptr<AVFormatContextWrapper>
349{
350 return mPrivate->FormatFactories.CreateAVFormatContextWrapper(*this);
351}
352
353std::unique_ptr<AVStreamWrapper>
354FFmpegFunctions::CreateAVStreamWrapper(AVStream* stream, bool forEncoding) const
355{
356 return mPrivate->FormatFactories.CreateAVStreamWrapper(*this, stream, forEncoding);
357}
358
359std::unique_ptr<AVPacketWrapper> FFmpegFunctions::CreateAVPacketWrapper() const
360{
361 return mPrivate->CodecFactories.CreateAVPacketWrapper(*this);
362}
363
364std::unique_ptr<AVFrameWrapper> FFmpegFunctions::CreateAVFrameWrapper() const
365{
366 return mPrivate->UtilFactories.CreateAVFrameWrapper(*this);
367}
368
369std::unique_ptr<AVInputFormatWrapper>
371 AVInputFormat* inputFormat) const
372{
373 return mPrivate->FormatFactories.CreateAVInputFormatWrapper(inputFormat);
374}
375
376std::unique_ptr<AVOutputFormatWrapper> FFmpegFunctions::GuessOutputFormat(
377 const char* short_name, const char* filename, const char* mime_type)
378{
379 AVOutputFormat* outputFormat =
380 av_guess_format(short_name, filename, mime_type);
381
382 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
383}
384
385std::unique_ptr<AVChannelLayoutWrapper>
387{
388 return mPrivate->UtilFactories.CreateDefaultChannelLayout(
389 *this, channelsCount);
390}
391
392std::unique_ptr<AVChannelLayoutWrapper>
394 uint64_t layout, int channelsCount) const
395{
396 return mPrivate->UtilFactories.CreateLegacyChannelLayout(
397 *this, layout, channelsCount);
398}
399
400std::unique_ptr<AVChannelLayoutWrapper>
402{
403 return mPrivate->UtilFactories.CreateAVChannelLayout(*this, layout);
404}
405
406std::unique_ptr<AVOutputFormatWrapper>
408 const AVOutputFormat* outputFormat) const
409{
410 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
411}
412
413std::unique_ptr<AVCodecWrapper>
415{
416 AVCodec* codec = avcodec_find_decoder(codecID);
417
418 if (codec == nullptr)
419 return {};
420
421 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
422}
423
424std::unique_ptr<AVCodecWrapper>
426{
427 auto codec = avcodec_find_encoder(codecID);
428
429 if (codec == nullptr)
430 return {};
431
432 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
433}
434
435std::unique_ptr<AVCodecWrapper>
437{
439
440 if (codec == nullptr)
441 return {};
442
443 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
444}
445
446std::unique_ptr<AVCodecContextWrapper>
448{
449 return mPrivate->CodecFactories.CreateAVCodecContextWrapper(
450 *this, context);
451}
452
453std::unique_ptr<AVCodecContextWrapper>
455 std::unique_ptr<AVCodecWrapper> codec) const
456{
457 if (codec == nullptr)
458 return {};
459
460 return mPrivate->CodecFactories.CreateAVCodecContextWrapperFromCodec(
461 *this, std::move(codec));
462}
463
464const std::vector<const AVOutputFormatWrapper*>&
466{
467 if (mOutputFormats.empty())
468 const_cast<FFmpegFunctions*>(this)->FillOuptutFormatsList();
469
471}
472
473const std::vector<const AVCodecWrapper*>& FFmpegFunctions::GetCodecs() const
474{
475 if (mCodecs.empty())
476 const_cast<FFmpegFunctions*>(this)->FillCodecsList();
477
478 return mCodecPointers;
479}
480
482{
483 mCodecs.clear();
484 mCodecPointers.clear();
485
486 if (av_codec_iterate != nullptr)
487 {
488 const AVCodec* currentCodec = nullptr;
489 void* i = 0;
490
491 while ((currentCodec = av_codec_iterate(&i)))
492 {
493 mCodecs.emplace_back(
494 mPrivate->CodecFactories.CreateAVCodecWrapper(currentCodec));
495 }
496 }
497 else if (av_codec_next != nullptr)
498 {
499 AVCodec* currentCodec = nullptr;
500
501 while ((currentCodec = av_codec_next(currentCodec)) != nullptr)
502 {
503 mCodecs.emplace_back(
504 mPrivate->CodecFactories.CreateAVCodecWrapper(currentCodec));
505 }
506 }
507
508 mCodecPointers.reserve(mCodecs.size());
509
510 for (const auto& codec : mCodecs)
511 mCodecPointers.emplace_back(codec.get());
512}
513
515{
516 mOutputFormats.clear();
517 mOutputFormatPointers.clear();
518
519 if (av_muxer_iterate != nullptr)
520 {
521 const AVOutputFormat* currentFormat = nullptr;
522 void* i = 0;
523
524 while ((currentFormat = av_muxer_iterate(&i)))
525 {
526 mOutputFormats.emplace_back(
527 mPrivate->FormatFactories.CreateAVOutputFormatWrapper(
528 currentFormat));
529 }
530 }
531 else if (av_oformat_next != nullptr)
532 {
533 AVOutputFormat* currentFormat = nullptr;
534
535 while ((currentFormat = av_oformat_next(currentFormat)) != nullptr)
536 {
537 mOutputFormats.emplace_back(
538 mPrivate->FormatFactories.CreateAVOutputFormatWrapper(currentFormat));
539 }
540 }
541
543
544 for (const auto& format : mOutputFormats)
545 mOutputFormatPointers.emplace_back(format.get());
546}
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)
wxT("CloseDown"))
StringSetting AVFormatPath
void * GetSymbolFromProcess(const char *name)
std::vector< wxString > BuildAVFormatPaths(int version)
wxString name
Definition: TagsEditor.cpp:166
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:207
Specialization of Setting for strings.
Definition: Prefs.h:370
FILES_API FilePath PathFromAddr(void *addr)
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
STL namespace.
AVCodec *(* avcodec_find_encoder_by_name)(const char *name)
FFMPegVersion AVCodecVersion
const AVCodec *(* av_codec_iterate)(void **opaque)
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)
const AVOutputFormat *(* av_muxer_iterate)(void **opaque)
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:169
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
const std::vector< const AVOutputFormatWrapper * > & GetOutputFormats() const
std::unique_ptr< AVInputFormatWrapper > CreateAVInputFormatWrapper(AVInputFormat *inputFormat) const
AudacityAVCodecID(* GetAudacityCodecID)(AVCodecIDFwd)
std::unique_ptr< AVFormatContextWrapper > CreateAVFormatContext() const
std::unique_ptr< AVCodecContextWrapper > CreateAVCodecContextWrapperFromCodec(std::unique_ptr< AVCodecWrapper > codec) const
std::vector< const AVOutputFormatWrapper * > mOutputFormatPointers
static std::vector< wxString > GetSearchPaths(bool fromUserPathOnly)
std::unique_ptr< AVChannelLayoutWrapper > CreateLegacyChannelLayout(uint64_t layout, int channelsCount) const
std::unique_ptr< AVFrameWrapper > CreateAVFrameWrapper() const
std::unique_ptr< AVChannelLayoutWrapper > CreateDefaultChannelLayout(int channelsCount) 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< AVChannelLayoutWrapper > CreateAVChannelLayout(const AVChannelLayout *layout) const
std::unique_ptr< AVIOContextWrapper > CreateAVIOContext() const
std::vector< const AVCodecWrapper * > mCodecPointers
std::vector< std::unique_ptr< AVCodecWrapper > > mCodecs
const std::vector< const AVCodecWrapper * > & GetCodecs() const
std::vector< std::unique_ptr< AVOutputFormatWrapper > > mOutputFormats
std::unique_ptr< AVStreamWrapper > CreateAVStreamWrapper(AVStream *stream, bool forEncoding) const
std::unique_ptr< AVPacketWrapper > CreateAVPacketWrapper() const
std::unique_ptr< AVOutputFormatWrapper > CreateAVOutputFormatWrapper(const AVOutputFormat *outputFormat) 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