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#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 wxLogInfo("FFmpeg libraries loaded successfully from: %s",
212 FileNames::PathFromAddr(AVFormatLibrary->GetSymbol("avformat_version")));
213
214 if (functions.avcodec_register_all)
215 functions.avcodec_register_all();
216
217 if (functions.av_register_all)
218 functions.av_register_all();
219
222
223 return true;
224 }
225
226 std::shared_ptr<wxDynamicLibrary> LoadLibrary(const wxString& libraryName, bool fromUserPathOnly) const
227 {
228#if defined(__WXMAC__)
229 // On macOS dyld reads environment only when application starts.
230 // Let's emulate the process manually
231 for (const wxString& path : FFmpegFunctions::GetSearchPaths(fromUserPathOnly))
232 {
233 const wxString fullName = wxFileName(path, libraryName).GetFullPath();
234
235 if (!wxFileExists(fullName))
236 continue;
237
238 auto library = std::make_shared<wxDynamicLibrary>(fullName);
239
240 if (library->IsLoaded())
241 return library;
242 }
243#endif
244 auto library = std::make_shared<wxDynamicLibrary> (libraryName);
245
246 if (library->IsLoaded())
247 return library;
248
249 return {};
250 }
251};
252
254 : mPrivate(std::make_unique<Private>())
255{
256}
257
259{
260}
261
262std::shared_ptr<FFmpegFunctions> FFmpegFunctions::Load(bool fromUserPathOnly)
263{
264 static std::weak_ptr<FFmpegFunctions> weakFunctions;
265
266 auto functions = weakFunctions.lock();
267
268 if (functions)
269 return functions;
270
271 std::shared_ptr<FFmpegFunctions> ffmpeg =
272 std::make_shared<FFmpegFunctions>();
273
274 const auto supportedVersions =
276
277#if !defined(__WXMAC__)
278 EnvSetter envSetter(fromUserPathOnly);
279#endif
280
281 for (int version : supportedVersions)
282 {
283 for (const wxString& path : BuildAVFormatPaths(version))
284 {
285 if (ffmpeg->mPrivate->Load(*ffmpeg, path, fromUserPathOnly))
286 {
287 weakFunctions = ffmpeg;
288 return ffmpeg;
289 }
290 }
291 }
292
293 return {};
294}
295
296StringSetting AVFormatPath { L"/FFmpeg/FFmpegLibPath", L"" };
297
298std::vector<wxString> FFmpegFunctions::GetSearchPaths(bool fromUserPathOnly)
299{
300 std::vector<wxString> paths;
301
302 const wxString userAVFormatFullPath = AVFormatPath.Read();
303
304 if (!userAVFormatFullPath.empty())
305 {
306 // For some directories, wxPathOnly will fail.
307 // For example, if path is `c:\ffmpeg-4.4`
308 // wxPathOnly will return `c:\`
309 if (wxDirExists(userAVFormatFullPath))
310 paths.emplace_back(userAVFormatFullPath);
311 else
312 paths.emplace_back(wxPathOnly(userAVFormatFullPath));
313 }
314
315 if (fromUserPathOnly)
316 return paths;
317
318#if defined(__WXMSW__)
319 wxRegKey reg(wxT("HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity"));
320 wxString path;
321
322 if (reg.Exists())
323 reg.QueryValue(wxT("InstallPath"), path);
324
325 if (!path.empty())
326 paths.emplace_back(path);
327
328#elif defined(__WXMAC__)
329 paths.emplace_back(wxT("/Library/Application Support/audacity/libs"));
330 paths.emplace_back(wxT("/usr/local/lib/audacity"));
331 // x86_64 Homebrew
332 paths.emplace_back(wxT("/usr/local/lib"));
333 // ARM64 Homebrew
334 paths.emplace_back(wxT("/opt/homebrew/lib"));
335#endif
336
337 return paths;
338}
339
340std::unique_ptr<AVIOContextWrapper> FFmpegFunctions::CreateAVIOContext() const
341{
342 return mPrivate->FormatFactories.CreateAVIOContextWrapper(*this);
343}
344
345std::unique_ptr<AVFormatContextWrapper>
347{
348 return mPrivate->FormatFactories.CreateAVFormatContextWrapper(*this);
349}
350
351std::unique_ptr<AVStreamWrapper>
352FFmpegFunctions::CreateAVStreamWrapper(AVStream* stream, bool forEncoding) const
353{
354 return mPrivate->FormatFactories.CreateAVStreamWrapper(*this, stream, forEncoding);
355}
356
357std::unique_ptr<AVPacketWrapper> FFmpegFunctions::CreateAVPacketWrapper() const
358{
359 return mPrivate->CodecFactories.CreateAVPacketWrapper(*this);
360}
361
362std::unique_ptr<AVFrameWrapper> FFmpegFunctions::CreateAVFrameWrapper() const
363{
364 return mPrivate->UtilFactories.CreateAVFrameWrapper(*this);
365}
366
367std::unique_ptr<AVInputFormatWrapper>
369 AVInputFormat* inputFormat) const
370{
371 return mPrivate->FormatFactories.CreateAVInputFormatWrapper(inputFormat);
372}
373
374std::unique_ptr<AVOutputFormatWrapper> FFmpegFunctions::GuessOutputFormat(
375 const char* short_name, const char* filename, const char* mime_type)
376{
377 AVOutputFormat* outputFormat =
378 av_guess_format(short_name, filename, mime_type);
379
380 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
381}
382
383std::unique_ptr<AVOutputFormatWrapper>
385 const AVOutputFormat* outputFormat) const
386{
387 return mPrivate->FormatFactories.CreateAVOutputFormatWrapper(outputFormat);
388}
389
390std::unique_ptr<AVCodecWrapper>
392{
393 AVCodec* codec = avcodec_find_decoder(codecID);
394
395 if (codec == nullptr)
396 return {};
397
398 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
399}
400
401std::unique_ptr<AVCodecWrapper>
403{
404 auto codec = avcodec_find_encoder(codecID);
405
406 if (codec == nullptr)
407 return {};
408
409 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
410}
411
412std::unique_ptr<AVCodecWrapper>
414{
416
417 if (codec == nullptr)
418 return {};
419
420 return mPrivate->CodecFactories.CreateAVCodecWrapper(codec);
421}
422
423std::unique_ptr<AVCodecContextWrapper>
425{
426 return mPrivate->CodecFactories.CreateAVCodecContextWrapper(
427 *this, context);
428}
429
430std::unique_ptr<AVCodecContextWrapper>
432 std::unique_ptr<AVCodecWrapper> codec) const
433{
434 if (codec == nullptr)
435 return {};
436
437 return mPrivate->CodecFactories.CreateAVCodecContextWrapperFromCodec(
438 *this, std::move(codec));
439}
440
441const std::vector<const AVOutputFormatWrapper*>&
443{
444 if (mOutputFormats.empty())
445 const_cast<FFmpegFunctions*>(this)->FillOuptutFormatsList();
446
448}
449
450const std::vector<const AVCodecWrapper*>& FFmpegFunctions::GetCodecs() const
451{
452 if (mCodecs.empty())
453 const_cast<FFmpegFunctions*>(this)->FillCodecsList();
454
455 return mCodecPointers;
456}
457
458std::unique_ptr<AVFifoBufferWrapper>
460{
461 return std::make_unique<AVFifoBufferWrapper>(*this, size);
462}
463
465{
466 mCodecs.clear();
467 mCodecPointers.clear();
468
469 if (av_codec_iterate != nullptr)
470 {
471 const AVCodec* currentCodec = nullptr;
472 void* i = 0;
473
474 while ((currentCodec = av_codec_iterate(&i)))
475 {
476 mCodecs.emplace_back(
477 mPrivate->CodecFactories.CreateAVCodecWrapper(currentCodec));
478 }
479 }
480 else if (av_codec_next != nullptr)
481 {
482 AVCodec* currentCodec = nullptr;
483
484 while ((currentCodec = av_codec_next(currentCodec)) != nullptr)
485 {
486 mCodecs.emplace_back(
487 mPrivate->CodecFactories.CreateAVCodecWrapper(currentCodec));
488 }
489 }
490
491 mCodecPointers.reserve(mCodecs.size());
492
493 for (const auto& codec : mCodecs)
494 mCodecPointers.emplace_back(codec.get());
495}
496
498{
499 mOutputFormats.clear();
500 mOutputFormatPointers.clear();
501
502 if (av_muxer_iterate != nullptr)
503 {
504 const AVOutputFormat* currentFormat = nullptr;
505 void* i = 0;
506
507 while ((currentFormat = av_muxer_iterate(&i)))
508 {
509 mOutputFormats.emplace_back(
510 mPrivate->FormatFactories.CreateAVOutputFormatWrapper(
511 currentFormat));
512 }
513 }
514 else if (av_oformat_next != nullptr)
515 {
516 AVOutputFormat* currentFormat = nullptr;
517
518 while ((currentFormat = av_oformat_next(currentFormat)) != nullptr)
519 {
520 mOutputFormats.emplace_back(
521 mPrivate->FormatFactories.CreateAVOutputFormatWrapper(currentFormat));
522 }
523 }
524
526
527 for (const auto& format : mOutputFormats)
528 mOutputFormatPointers.emplace_back(format.get());
529}
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"))
const TranslatableString name
Definition: Distortion.cpp:82
int format
Definition: ExportPCM.cpp:56
StringSetting AVFormatPath
void * GetSymbolFromProcess(const char *name)
std::vector< wxString > BuildAVFormatPaths(int version)
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
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< AVFrameWrapper > CreateAVFrameWrapper() 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::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
std::unique_ptr< AVFifoBufferWrapper > CreateFifoBuffer(int size) const