/********************************************************************** Audacity: A Digital Audio Editor FFmpeg.cpp Audacity(R) is copyright (c) 1999-2009 Audacity Team. License: GPL v2 or later. See License.txt. ******************************************************************//** \class FFmpegLibs \brief Class used to dynamically load FFmpeg libraries *//*******************************************************************/ // Store function pointers here when including FFmpeg.h #define DEFINE_FFMPEG_POINTERS #include "FFmpeg.h" #include "FFmpegFunctions.h" #include "ModuleConstants.h" #include "FileNames.h" #include "SelectFile.h" #include "HelpSystem.h" #include "AudacityMessageBox.h" #include "ShuttleGui.h" #include #include #include #include #include static BoolSetting FFmpegEnabled{ L"/FFmpeg/Enabled", false }; bool LoadFFmpeg(bool showerror) { auto ffmpeg = FFmpegFunctions::Load(); if (!ffmpeg) { FFmpegEnabled.Write(false); gPrefs->Flush(); return false; } else { FFmpegEnabled.Write(true); gPrefs->Flush(); return true; } } /** Called during Audacity start-up to try and load the ffmpeg libraries */ void FFmpegStartup() { bool enabled = FFmpegEnabled.Read(); // 'false' means that no errors should be shown whatsoever if (!LoadFFmpeg(false)) { if (enabled) { AudacityMessageBox(XO( "FFmpeg was configured in Preferences and successfully loaded before, \ \nbut this time Tenacity failed to load it at startup. \ \n\nYou may want to go back to Preferences > Libraries and re-configure it."), XO("FFmpeg startup failed")); } } } TranslatableString GetFFmpegVersion() { auto ffmpeg = FFmpegFunctions::Load(); if (ffmpeg) { return Verbatim( wxString::Format( wxT("F(%d.%d.%d),C(%d.%d.%d),U(%d.%d.%d)"), ffmpeg->AVFormatVersion.Major, ffmpeg->AVFormatVersion.Minor, ffmpeg->AVFormatVersion.Micro, ffmpeg->AVCodecVersion.Major, ffmpeg->AVCodecVersion.Minor, ffmpeg->AVCodecVersion.Micro, ffmpeg->AVUtilVersion.Major, ffmpeg->AVUtilVersion.Minor, ffmpeg->AVUtilVersion.Micro )); } return XO("FFmpeg library not found"); } /*******************************************************/ class FFmpegNotFoundDialog; //---------------------------------------------------------------------------- // FindFFmpegDialog //---------------------------------------------------------------------------- #define ID_FFMPEG_BROWSE 5000 #define ID_FFMPEG_DLOAD 5001 /// Allows user to locate libav* libraries class FindFFmpegDialog final : public wxDialogWrapper { public: FindFFmpegDialog(wxWindow *parent, const wxString &path, const wxString &name) : wxDialogWrapper(parent, wxID_ANY, XO("Locate FFmpeg")) , mName(name) , mFullPath(path, {}) { SetName(); ShuttleGui S(this, eIsCreating); PopulateOrExchange(S); } void PopulateOrExchange(ShuttleGui & S) { S.SetBorder(10); S.StartVerticalLay(true); { S.AddTitle( XO( "Tenacity needs the file '%s' to import and export audio via FFmpeg.") .Format( mName ) ); S.SetBorder(3); S.StartHorizontalLay(wxALIGN_LEFT, true); { S.AddTitle( XO("Location of '%s':").Format( mName ) ); } S.EndHorizontalLay(); S.StartMultiColumn(2, wxEXPAND); S.SetStretchyCol(0); { if (mFullPath.GetFullPath().empty()) { mPathText = S.AddTextBox( {}, XO("To find '%s', click here -->") .Format(mName) .Translation(), 0); } else { mPathText = S.AddTextBox({}, mFullPath.GetFullPath(), 0); } S.Id(ID_FFMPEG_BROWSE).AddButton(XXO("Browse..."), wxALIGN_RIGHT); S.AddVariableText( XO("To get a free copy of FFmpeg, click here -->"), true); S.Id(ID_FFMPEG_DLOAD).AddButton(XXO("Download"), wxALIGN_RIGHT); } S.EndMultiColumn(); S.AddStandardButtons(); } S.EndVerticalLay(); Layout(); Fit(); SetMinSize(GetSize()); Center(); return; } void OnBrowse(wxCommandEvent & WXUNUSED(event)) { static const FileNames::FileTypes types = { # if defined(__WXMSW__) { XO("Only avformat.dll"), { wxT("avformat-*.dll") } }, # elif defined(__WXMAC__) { XO("Only libavformat.dylib"), { wxT("ffmpeg.*.dylib"), wxT("libavformat.*.dylib") } }, # else { XO("Only libavformat.so"), { wxT("libavformat.so.*") } }, # endif FileNames::DynamicLibraries, FileNames::AllFiles }; UpdatePath(); /* i18n-hint: It's asking for the location of a file, for example, "Where is lame_enc.dll?" - you could translate "Where would I find the file '%s'?" instead if you want. */ auto question = XO("Where is '%s'?").Format( mName ); wxString path = SelectFile( FileNames::Operation::_None, question, mFullPath.GetPath(), mFullPath.GetFullName(), wxT(""), types, wxFD_OPEN | wxRESIZE_BORDER, this); if (!path.empty()) { mFullPath = path; mPathText->SetValue(path); } } void OnDownload(wxCommandEvent & WXUNUSED(event)) { HelpSystem::ShowHelp(this, L"FAQ:Installing_the_FFmpeg_Import_Export_Library"); } void UpdatePath() { const wxString path = mPathText->GetValue(); if (wxDirExists(path)) mFullPath = wxFileName(path, {}, wxPATH_NATIVE); else mFullPath = mPathText->GetValue(); } wxString GetLibPath() { UpdatePath(); return mFullPath.GetFullPath(); } private: wxString mName; wxFileName mFullPath; wxTextCtrl *mPathText; DECLARE_EVENT_TABLE() }; BEGIN_EVENT_TABLE(FindFFmpegDialog, wxDialogWrapper) EVT_BUTTON(ID_FFMPEG_BROWSE, FindFFmpegDialog::OnBrowse) EVT_BUTTON(ID_FFMPEG_DLOAD, FindFFmpegDialog::OnDownload) END_EVENT_TABLE() //---------------------------------------------------------------------------- // FFmpegNotFoundDialog //---------------------------------------------------------------------------- FFmpegNotFoundDialog::FFmpegNotFoundDialog(wxWindow *parent) : wxDialogWrapper(parent, wxID_ANY, XO("FFmpeg not found")) { SetName(); ShuttleGui S(this, eIsCreating); PopulateOrExchange(S); } void FFmpegNotFoundDialog::PopulateOrExchange(ShuttleGui & S) { wxString text; S.SetBorder(10); S.StartVerticalLay(true); { S.AddFixedText(XO( "Tenacity attempted to use FFmpeg to import an audio file,\n\ but the libraries were not found.\n\n\ To use FFmpeg import, go to Edit > Preferences > Libraries\n\ to download or locate the FFmpeg libraries." )); mDontShow = S .AddCheckBox(XXO("Do not show this warning again"), FFmpegNotFoundDontShow.Read() ); S.AddStandardButtons(eOkButton); } S.EndVerticalLay(); Layout(); Fit(); SetMinSize(GetSize()); Center(); return; } void FFmpegNotFoundDialog::OnOk(wxCommandEvent & WXUNUSED(event)) { if (mDontShow->GetValue()) { FFmpegNotFoundDontShow.Write(true); gPrefs->Flush(); } this->EndModal(0); } BEGIN_EVENT_TABLE(FFmpegNotFoundDialog, wxDialogWrapper) EVT_BUTTON(wxID_OK, FFmpegNotFoundDialog::OnOk) END_EVENT_TABLE() bool FindFFmpegLibs(wxWindow* parent) { wxString path; #if defined(__WXMSW__) const wxString name = wxT("avformat.dll"); #elif defined(__WXMAC__) const wxString name = wxT("libavformat.dylib"); #else const wxString name = wxT("libavformat.so"); #endif wxLogMessage(wxT("Looking for FFmpeg libraries...")); auto searchPaths = FFmpegFunctions::GetSearchPaths(false); if (!searchPaths.empty()) path = searchPaths.front(); FindFFmpegDialog fd(parent, path, name); if (fd.ShowModal() == wxID_CANCEL) { wxLogMessage(wxT("User canceled the dialog. Failed to find FFmpeg libraries.")); return false; } path = fd.GetLibPath(); const wxFileName fileName(path); if (fileName.FileExists()) path = fileName.GetPath(); wxLogMessage(wxT("User-specified path = '%s'"), path); SettingTransaction transaction; AVFormatPath.Write(path); // Try to load FFmpeg from the user provided path if (!FFmpegFunctions::Load(true)) { wxLogError(wxT("User-specified path does not contain FFmpeg libraries.")); return false; } transaction.Commit(); wxLogMessage(wxT("User-specified FFmpeg file exists. Success.")); return true; } BoolSetting FFmpegNotFoundDontShow{ L"/FFmpeg/NotFoundDontShow", false }; DEFINE_VERSION_CHECK extern "C" DLL_API int ModuleDispatch(ModuleDispatchTypes type) { if(type == ModuleInitialize) FFmpegStartup(); return 1; }