976 lines
28 KiB
C++
976 lines
28 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
FFmpeg.cpp
|
|
|
|
Audacity(R) is copyright (c) 1999-2009 Audacity Team.
|
|
License: GPL v2. 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 "Audacity.h" // for config*.h
|
|
#include "FFmpeg.h"
|
|
#include "AudacityApp.h"
|
|
#include "FileNames.h"
|
|
#include "Internat.h"
|
|
|
|
#include <wx/file.h>
|
|
|
|
#ifdef _DEBUG
|
|
#ifdef _MSC_VER
|
|
#undef THIS_FILE
|
|
static char*THIS_FILE= __FILE__;
|
|
#define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
|
|
#endif
|
|
#endif
|
|
|
|
#define UFILE_PROTOCOL "ufile"
|
|
|
|
#if !defined(USE_FFMPEG)
|
|
/// FFmpeg support may or may not be compiled in,
|
|
/// but Preferences dialog requires this function nevertheless
|
|
wxString GetFFmpegVersion(wxWindow *parent)
|
|
{
|
|
return wxString(_("FFmpeg support not compiled in"));
|
|
}
|
|
|
|
#else
|
|
|
|
/** This pointer to the shared object has global scope and is used to track the
|
|
* singleton object which wraps the FFmpeg codecs */
|
|
FFmpegLibs *FFmpegLibsInst = NULL;
|
|
|
|
FFmpegLibs *PickFFmpegLibs()
|
|
{
|
|
if (FFmpegLibsInst != NULL)
|
|
{
|
|
FFmpegLibsInst->refcount++;
|
|
return FFmpegLibsInst;
|
|
}
|
|
else
|
|
{
|
|
FFmpegLibsInst = new FFmpegLibs();
|
|
return FFmpegLibsInst;
|
|
}
|
|
}
|
|
|
|
void DropFFmpegLibs()
|
|
{
|
|
if (FFmpegLibsInst != NULL)
|
|
{
|
|
FFmpegLibsInst->refcount--;
|
|
if (FFmpegLibsInst->refcount == 0)
|
|
{
|
|
delete FFmpegLibsInst;
|
|
FFmpegLibsInst = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LoadFFmpeg(bool showerror)
|
|
{
|
|
PickFFmpegLibs();
|
|
if (FFmpegLibsInst->ValidLibsLoaded())
|
|
{
|
|
DropFFmpegLibs();
|
|
return true;
|
|
}
|
|
if (!FFmpegLibsInst->LoadLibs(NULL,showerror))
|
|
{
|
|
DropFFmpegLibs();
|
|
gPrefs->Write(wxT("/FFmpeg/Enabled"), false);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
gPrefs->Write(wxT("/FFmpeg/Enabled"), true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/** Called during Audacity start-up to try and load the ffmpeg libraries */
|
|
void FFmpegStartup()
|
|
{
|
|
bool enabled = false;
|
|
gPrefs->Read(wxT("/FFmpeg/Enabled"),&enabled);
|
|
// 'false' means that no errors should be shown whatsoever
|
|
if (!LoadFFmpeg(false))
|
|
{
|
|
if (enabled)
|
|
{
|
|
wxMessageBox(_("FFmpeg was configured in Preferences and successfully loaded before, \
|
|
\nbut this time Audacity failed to load it at startup. \
|
|
\n\nYou may want to go back to Preferences > Libraries and re-configure it."),
|
|
_("FFmpeg startup failed"));
|
|
}
|
|
}
|
|
}
|
|
|
|
wxString GetFFmpegVersion(wxWindow *parent)
|
|
{
|
|
PickFFmpegLibs();
|
|
|
|
wxString versionString = _("FFmpeg library not found");
|
|
|
|
if (FFmpegLibsInst->ValidLibsLoaded()) {
|
|
versionString = FFmpegLibsInst->GetLibraryVersion();
|
|
}
|
|
|
|
DropFFmpegLibs();
|
|
|
|
return versionString;
|
|
}
|
|
|
|
void av_log_wx_callback(void* ptr, int level, const char* fmt, va_list vl)
|
|
{
|
|
//Most of this stuff is taken from FFmpeg tutorials and FFmpeg itself
|
|
int av_log_level = AV_LOG_WARNING;
|
|
AVClass* avc = ptr ? *(AVClass**)ptr : NULL;
|
|
if (level > av_log_level)
|
|
return;
|
|
wxString printstring(wxT(""));
|
|
|
|
if (avc) {
|
|
printstring.Append(wxString::Format(wxT("[%s @ %p] "), wxString::FromUTF8(avc->item_name(ptr)).c_str(), avc));
|
|
}
|
|
|
|
wxString frm(fmt,wxConvLibc);
|
|
#if defined(__WXMSW__)
|
|
frm.Replace(wxT("%t"),wxT("%i"),true); //TODO: on Windows vprintf won't handle %t, and probably some others. Investigate.
|
|
#endif
|
|
#if defined(wxUSE_UNICODE)
|
|
// String comes with %s format field and a value in value list is ascii char*. Thus in Unicode configurations
|
|
// we have to convert %s to %S.
|
|
frm.Replace(wxT("%s"),wxT("%S"),true);
|
|
#endif
|
|
printstring.Append(wxString::FormatV(frm,vl));
|
|
wxString cpt;
|
|
switch (level)
|
|
{
|
|
case 0: cpt = wxT("Error"); break;
|
|
case 1: cpt = wxT("Info"); break;
|
|
case 2: cpt = wxT("Debug"); break;
|
|
default: cpt = wxT("Log"); break;
|
|
}
|
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
|
//if the decoding happens thru OD then this gets called from a non main thread, which means wxLogDebug
|
|
//will crash.
|
|
//TODO:find some workaround for the log. perhaps use ODManager as a bridge. for now just print
|
|
if(!wxThread::IsMain())
|
|
printf("%s: %s\n",(char*)cpt.char_str(),(char*)printstring.char_str());
|
|
else
|
|
#endif
|
|
wxLogDebug(wxT("%s: %s"),cpt.c_str(),printstring.c_str());
|
|
}
|
|
|
|
//======================= Unicode aware uri protocol for FFmpeg
|
|
// Code inspired from ffmpeg-users mailing list sample
|
|
|
|
static int ufile_open(URLContext *h, const char *filename, int flags)
|
|
{
|
|
wxString name(strchr(filename, ':') + 1, wxConvUTF8);
|
|
wxFile *f;
|
|
wxFile::OpenMode mode;
|
|
|
|
f = new wxFile;
|
|
if (!f) {
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
// LLL: These really should be logical AND tests, but on 2011/04/28, the URL_ open flags
|
|
// changed in the FFmpeg source to values that were not compatible with previous
|
|
// values.
|
|
//
|
|
// Since Audacity doesn't use any other open flags (there aren't any others defined
|
|
// anyway), making equality tests works for older and new FFmpeg headers.
|
|
if (flags == URL_RDWR) {
|
|
mode = wxFile::read_write;
|
|
} else if (flags == URL_WRONLY) {
|
|
mode = wxFile::write;
|
|
} else {
|
|
mode = wxFile::read;
|
|
}
|
|
|
|
if (!f->Open(name, mode)) {
|
|
delete f;
|
|
return AVERROR(ENOENT);
|
|
}
|
|
|
|
h->priv_data = (void *)f;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ufile_read(URLContext *h, unsigned char *buf, int size)
|
|
{
|
|
return (int) ((wxFile *) h->priv_data)->Read(buf, size);
|
|
}
|
|
|
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 68, 0)
|
|
static int ufile_write(URLContext *h, unsigned char *buf, int size)
|
|
#else
|
|
static int ufile_write(URLContext *h, const unsigned char *buf, int size)
|
|
#endif
|
|
{
|
|
return (int) ((wxFile *) h->priv_data)->Write(buf, size);
|
|
}
|
|
|
|
static int64_t ufile_seek(URLContext *h, int64_t pos, int whence)
|
|
{
|
|
wxSeekMode mode = wxFromStart;
|
|
|
|
#if !defined(AVSEEK_FORCE)
|
|
#define AVSEEK_FORCE 0
|
|
#endif
|
|
|
|
switch (whence & ~AVSEEK_FORCE)
|
|
{
|
|
case (SEEK_SET):
|
|
mode = wxFromStart;
|
|
break;
|
|
case (SEEK_CUR):
|
|
mode = wxFromCurrent;
|
|
break;
|
|
case (SEEK_END):
|
|
mode = wxFromEnd;
|
|
break;
|
|
case (AVSEEK_SIZE):
|
|
return ((wxFile *) h->priv_data)->Length();
|
|
}
|
|
|
|
return ((wxFile *) h->priv_data)->Seek(pos, mode);
|
|
}
|
|
|
|
static int ufile_close(URLContext *h)
|
|
{
|
|
wxFile *f = (wxFile *) h->priv_data;
|
|
|
|
if (f) {
|
|
f->Close();
|
|
delete f;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
URLProtocol ufile_protocol = {
|
|
UFILE_PROTOCOL,
|
|
ufile_open,
|
|
ufile_read,
|
|
ufile_write,
|
|
ufile_seek,
|
|
ufile_close,
|
|
};
|
|
|
|
// Open a file with a (possibly) Unicode filename
|
|
int ufile_fopen(AVIOContext **s, const wxString & name, int flags)
|
|
{
|
|
wxString url(wxString(wxT(UFILE_PROTOCOL)) + wxT(":") + name);
|
|
URLContext *h;
|
|
int err;
|
|
|
|
// Open the file using our custom protocol and passing the (possibly) Unicode
|
|
// filename. We convert the name to UTF8 here and it will be converted back
|
|
// to original encoding in ufile_open(). This allows us to support Unicode
|
|
// filenames even though FFmpeg does not.
|
|
err = url_open(&h, (const char *) url.ToUTF8(), flags);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
// Associate the file with a context
|
|
err = url_fdopen(s, h);
|
|
if (err < 0) {
|
|
url_close(h);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Size of probe buffer, for guessing file type from file contents
|
|
#define PROBE_BUF_MIN 2048
|
|
#define PROBE_BUF_MAX (1<<20)
|
|
|
|
// Detect type of input file and open it if recognized. Routine
|
|
// based on the av_open_input_file() libavformat function.
|
|
int ufile_fopen_input(AVFormatContext **ic_ptr, wxString & name)
|
|
{
|
|
wxFileName f(name);
|
|
wxCharBuffer fname;
|
|
const char *filename;
|
|
AVProbeData pd;
|
|
AVIOContext *pb = NULL;
|
|
AVInputFormat *fmt = NULL;
|
|
AVInputFormat *fmt1;
|
|
int probe_size;
|
|
int err;
|
|
|
|
// Create a dummy file name using the extension from the original
|
|
f.SetName(wxT(UFILE_PROTOCOL));
|
|
fname = f.GetFullName().mb_str();
|
|
filename = (const char *) fname;
|
|
|
|
// Initialize probe data...go ahead and preallocate the maximum buffer size.
|
|
pd.filename = filename;
|
|
pd.buf_size = 0;
|
|
pd.buf = (unsigned char *) av_malloc(PROBE_BUF_MAX + AVPROBE_PADDING_SIZE);
|
|
if (pd.buf == NULL) {
|
|
err = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
// Open the file to prepare for probing
|
|
if ((err = ufile_fopen(&pb, name, URL_RDONLY)) < 0) {
|
|
goto fail;
|
|
}
|
|
|
|
for (probe_size = PROBE_BUF_MIN; probe_size <= PROBE_BUF_MAX && !fmt; probe_size <<= 1) {
|
|
int score_max = probe_size < PROBE_BUF_MAX ? AVPROBE_SCORE_MAX / 4 : 0;
|
|
|
|
// Read up to a "probe_size" worth of data
|
|
pd.buf_size = avio_read(pb, pd.buf, probe_size);
|
|
|
|
// AWD: with zero-length input files buf_size can come back negative;
|
|
// this causes problems so we might as well just fail
|
|
if (pd.buf_size < 0) {
|
|
err = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
// Clear up to a "AVPROBE_PADDING_SIZE" worth of unused buffer
|
|
memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
|
|
|
|
// Reposition file for succeeding scan
|
|
if (avio_seek(pb, 0, SEEK_SET) < 0) {
|
|
err = AVERROR(EIO);
|
|
goto fail;
|
|
}
|
|
|
|
// Scan all input formats
|
|
fmt = NULL;
|
|
for (fmt1 = av_iformat_next(NULL); fmt1 != NULL; fmt1 = av_iformat_next(fmt1)) {
|
|
int score = 0;
|
|
|
|
// Ignore the ones that are not file based
|
|
if (fmt1->flags & AVFMT_NOFILE) {
|
|
continue;
|
|
}
|
|
|
|
// If the format can probe the file then try that first
|
|
if (fmt1->read_probe) {
|
|
score = fmt1->read_probe(&pd);
|
|
}
|
|
// Otherwize, resort to extension matching if available
|
|
else if (fmt1->extensions) {
|
|
if (av_match_ext(filename, fmt1->extensions)) {
|
|
score = 50;
|
|
}
|
|
}
|
|
|
|
// Remember this format if it scored higher than a previous match
|
|
if (score > score_max) {
|
|
score_max = score;
|
|
fmt = fmt1;
|
|
}
|
|
else if (score == score_max) {
|
|
fmt = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Didn't find a suitable format, so bail
|
|
if (!fmt) {
|
|
err = AVERROR(EILSEQ);
|
|
goto fail;
|
|
}
|
|
|
|
// And finally, attempt to associate an input stream with the file
|
|
err = av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
|
|
if (err) {
|
|
goto fail;
|
|
}
|
|
|
|
// Done with the probe buffer
|
|
av_freep(&pd.buf);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
if (pd.buf) {
|
|
av_freep(&pd.buf);
|
|
}
|
|
|
|
if (pb) {
|
|
avio_close(pb);
|
|
}
|
|
|
|
*ic_ptr = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
/*******************************************************/
|
|
|
|
class FFmpegNotFoundDialog;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FindFFmpegDialog
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define ID_FFMPEG_BROWSE 5000
|
|
#define ID_FFMPEG_DLOAD 5001
|
|
|
|
/// Allows user to locate libav* libraries
|
|
class FindFFmpegDialog : public wxDialog
|
|
{
|
|
public:
|
|
|
|
FindFFmpegDialog(wxWindow *parent, wxString path, wxString name, wxString type)
|
|
: wxDialog(parent, wxID_ANY, wxString(_("Locate FFmpeg")))
|
|
{
|
|
ShuttleGui S(this, eIsCreating);
|
|
|
|
mPath = path;
|
|
mName = name;
|
|
mType = type;
|
|
|
|
mLibPath.Assign(mPath, mName);
|
|
|
|
PopulateOrExchange(S);
|
|
}
|
|
|
|
void PopulateOrExchange(ShuttleGui & S)
|
|
{
|
|
wxString text;
|
|
|
|
S.SetBorder(10);
|
|
S.StartVerticalLay(true);
|
|
{
|
|
text.Printf(_("Audacity needs the file '%s' to import and export audio via FFmpeg."), mName.c_str());
|
|
S.AddTitle(text);
|
|
|
|
S.SetBorder(3);
|
|
S.StartHorizontalLay(wxALIGN_LEFT, true);
|
|
{
|
|
text.Printf(_("Location of '%s':"), mName.c_str());
|
|
S.AddTitle(text);
|
|
}
|
|
S.EndHorizontalLay();
|
|
|
|
S.StartMultiColumn(2, wxEXPAND);
|
|
S.SetStretchyCol(0);
|
|
{
|
|
if (mLibPath.GetFullPath().IsEmpty()) {
|
|
text.Printf(_("To find '%s', click here -->"), mName.c_str());
|
|
mPathText = S.AddTextBox(wxT(""), text, 0);
|
|
}
|
|
else {
|
|
mPathText = S.AddTextBox(wxT(""), mLibPath.GetFullPath(), 0);
|
|
}
|
|
S.Id(ID_FFMPEG_BROWSE).AddButton(_("Browse..."), wxALIGN_RIGHT);
|
|
S.AddVariableText(_("To get a free copy of FFmpeg, click here -->"), true);
|
|
S.Id(ID_FFMPEG_DLOAD).AddButton(_("Download"), wxALIGN_RIGHT);
|
|
}
|
|
S.EndMultiColumn();
|
|
|
|
S.AddStandardButtons();
|
|
}
|
|
S.EndVerticalLay();
|
|
|
|
Layout();
|
|
Fit();
|
|
SetMinSize(GetSize());
|
|
Center();
|
|
|
|
return;
|
|
}
|
|
|
|
void OnBrowse(wxCommandEvent & event)
|
|
{
|
|
wxString question;
|
|
/* 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. */
|
|
question.Printf(_("Where is '%s'?"), mName.c_str());
|
|
|
|
wxString path = FileSelector(question,
|
|
mLibPath.GetPath(),
|
|
mLibPath.GetName(),
|
|
wxT(""),
|
|
mType,
|
|
wxFD_OPEN | wxRESIZE_BORDER,
|
|
this);
|
|
if (!path.IsEmpty()) {
|
|
mLibPath = path;
|
|
mPathText->SetValue(path);
|
|
}
|
|
}
|
|
|
|
void OnDownload(wxCommandEvent & event)
|
|
{
|
|
wxString page = wxT("http://www.audacityteam.org/manual/index.php?title=FAQ:Installation_and_Plug-Ins%23installffmpeg");
|
|
::OpenInDefaultBrowser(page);
|
|
}
|
|
|
|
wxString GetLibPath()
|
|
{
|
|
return mLibPath.GetFullPath();
|
|
}
|
|
|
|
private:
|
|
|
|
wxFileName mLibPath;
|
|
|
|
wxString mPath;
|
|
wxString mName;
|
|
wxString mType;
|
|
|
|
wxTextCtrl *mPathText;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(FindFFmpegDialog, wxDialog)
|
|
EVT_BUTTON(ID_FFMPEG_BROWSE, FindFFmpegDialog::OnBrowse)
|
|
EVT_BUTTON(ID_FFMPEG_DLOAD, FindFFmpegDialog::OnDownload)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FFmpegNotFoundDialog
|
|
//----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(FFmpegNotFoundDialog, wxDialog)
|
|
EVT_BUTTON(wxID_OK, FFmpegNotFoundDialog::OnOk)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// FFmpegLibs
|
|
//----------------------------------------------------------------------------
|
|
|
|
FFmpegLibs::FFmpegLibs()
|
|
{
|
|
mLibsLoaded = false;
|
|
refcount = 1;
|
|
avformat = avcodec = avutil = NULL;
|
|
if (gPrefs) {
|
|
mLibAVFormatPath = gPrefs->Read(wxT("/FFmpeg/FFmpegLibPath"), wxT(""));
|
|
}
|
|
|
|
}
|
|
|
|
FFmpegLibs::~FFmpegLibs()
|
|
{
|
|
FreeLibs();
|
|
};
|
|
|
|
bool FFmpegLibs::FindLibs(wxWindow *parent)
|
|
{
|
|
wxString path;
|
|
wxString name;
|
|
|
|
wxLogMessage(wxT("Looking for FFmpeg libraries..."));
|
|
if (!mLibAVFormatPath.IsEmpty()) {
|
|
wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty."), mLibAVFormatPath.c_str());
|
|
wxFileName fn = mLibAVFormatPath;
|
|
path = fn.GetPath();
|
|
name = fn.GetFullName();
|
|
}
|
|
else {
|
|
path = GetLibAVFormatPath();
|
|
name = GetLibAVFormatName();
|
|
wxLogMessage(wxT("mLibAVFormatPath is empty, starting with path '%s', name '%s'."),
|
|
path.c_str(), name.c_str());
|
|
}
|
|
|
|
FindFFmpegDialog fd(parent,
|
|
path,
|
|
name,
|
|
GetLibraryTypeString());
|
|
|
|
if (fd.ShowModal() == wxID_CANCEL) {
|
|
wxLogMessage(wxT("User canceled the dialog. Failed to find FFmpeg libraries."));
|
|
return false;
|
|
}
|
|
|
|
path = fd.GetLibPath();
|
|
|
|
wxLogMessage(wxT("User-specified path = '%s'"), path.c_str());
|
|
if (!::wxFileExists(path)) {
|
|
wxLogError(wxT("User-specified file does not exist. Failed to find FFmpeg libraries."));
|
|
return false;
|
|
}
|
|
wxLogMessage(wxT("User-specified FFmpeg file exists. Success."));
|
|
mLibAVFormatPath = path;
|
|
gPrefs->Write(wxT("/FFmpeg/FFmpegLibPath"), mLibAVFormatPath);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FFmpegLibs::LoadLibs(wxWindow *parent, bool showerr)
|
|
{
|
|
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries..."));
|
|
if (ValidLibsLoaded()) {
|
|
wxLogMessage(wxT("FFmpeg libraries are already loaded."));
|
|
FreeLibs();
|
|
}
|
|
|
|
// First try loading it from a previously located path
|
|
if (!mLibAVFormatPath.IsEmpty()) {
|
|
wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty. Loading from it."),mLibAVFormatPath.c_str());
|
|
mLibsLoaded = InitLibs(mLibAVFormatPath,showerr);
|
|
}
|
|
|
|
// If not successful, try loading it from default path
|
|
if (!mLibsLoaded && !GetLibAVFormatPath().IsEmpty()) {
|
|
wxFileName fn(GetLibAVFormatPath(), GetLibAVFormatName());
|
|
wxString path = fn.GetFullPath();
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries from default path, '%s'."), path.c_str());
|
|
mLibsLoaded = InitLibs(path,showerr);
|
|
if (mLibsLoaded) {
|
|
mLibAVFormatPath = path;
|
|
}
|
|
}
|
|
|
|
// If not successful, try loading using system search paths
|
|
if (!ValidLibsLoaded()) {
|
|
wxString path = GetLibAVFormatName();
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries from system paths. File name is '%s'."), path.c_str());
|
|
mLibsLoaded = InitLibs(path,showerr);
|
|
if (mLibsLoaded) {
|
|
mLibAVFormatPath = path;
|
|
}
|
|
}
|
|
|
|
// If libraries aren't loaded - nag user about that
|
|
/*
|
|
if (!ValidLibsLoaded())
|
|
{
|
|
wxLogError(wxT("Failed to load libraries altogether."));
|
|
int dontShowDlg;
|
|
FFmpegNotFoundDialog *dlg;
|
|
gPrefs->Read(wxT("/FFmpeg/NotFoundDontShow"),&dontShowDlg,0);
|
|
if ((dontShowDlg == 0) && (showerr))
|
|
{
|
|
dlg = new FFmpegNotFoundDialog(NULL);
|
|
dlg->ShowModal();
|
|
delete dlg;
|
|
}
|
|
}
|
|
*/
|
|
// Oh well, just give up
|
|
if (!ValidLibsLoaded()) {
|
|
wxString msg = _("Failed to find compatible FFmpeg libraries.");
|
|
if (showerr)
|
|
wxMessageBox(msg);
|
|
wxLogError(msg);
|
|
return false;
|
|
}
|
|
|
|
wxLogMessage(wxT("FFmpeg libraries loaded successfully."));
|
|
return true;
|
|
}
|
|
|
|
bool FFmpegLibs::ValidLibsLoaded()
|
|
{
|
|
return mLibsLoaded;
|
|
}
|
|
|
|
bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
|
|
{
|
|
FreeLibs();
|
|
|
|
#if defined(__WXMSW__)
|
|
wxString syspath;
|
|
bool pathfix = false;
|
|
|
|
wxLogMessage(wxT("Looking up PATH environment variable..."));
|
|
// First take PATH environment variable and store its content.
|
|
if (wxGetEnv(wxT("PATH"),&syspath))
|
|
{
|
|
wxLogMessage(wxT("PATH = '%s'"), syspath.c_str());
|
|
wxString fmtdirsc = wxPathOnly(libpath_format) + wxT(";");
|
|
wxString scfmtdir = wxT(";") + wxPathOnly(libpath_format);
|
|
wxString fmtdir = wxPathOnly(libpath_format);
|
|
wxLogMessage(wxT("Checking that '%s' is in PATH..."), fmtdir.c_str());
|
|
// If the directory, where libavformat is, is not in PATH - add it
|
|
if (!syspath.Contains(fmtdirsc) && !syspath.Contains(scfmtdir) && !syspath.Contains(fmtdir))
|
|
{
|
|
wxLogWarning(wxT("FFmpeg directory is not in PATH."), fmtdir.c_str());
|
|
if (syspath.Last() == wxT(';'))
|
|
{
|
|
wxLogMessage(wxT("Temporarily appending '%s' to PATH..."), fmtdir.c_str());
|
|
syspath.Append(fmtdirsc);
|
|
}
|
|
else
|
|
{
|
|
wxLogMessage(wxT("Temporarily appending '%s' to PATH..."), scfmtdir.c_str());
|
|
syspath.Append(scfmtdir);
|
|
}
|
|
|
|
if (wxSetEnv(wxT("PATH"),syspath.c_str()))
|
|
// Remember to change PATH back to normal after we're done
|
|
pathfix = true;
|
|
else
|
|
wxLogSysError(wxT("Setting PATH via wxSetEnv('%s') failed."),syspath.c_str());
|
|
}
|
|
else
|
|
{
|
|
wxLogMessage(wxT("FFmpeg directory is in PATH."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxLogSysError(wxT("PATH does not exist."));
|
|
}
|
|
#endif
|
|
|
|
//Load libavformat
|
|
// Initially we don't know where are the avcodec and avutl libs
|
|
wxDynamicLibrary *codec = NULL;
|
|
wxDynamicLibrary *util = NULL;
|
|
wxFileName name(libpath_format);
|
|
bool gotError = false;
|
|
|
|
// Check for a monolithic avformat
|
|
avformat = new wxDynamicLibrary();
|
|
wxLogMessage(wxT("Checking for monolithic avformat from '%s'."), name.GetFullPath().c_str());
|
|
gotError = !avformat->Load(name.GetFullPath(), wxDL_LAZY);
|
|
|
|
// Verify it really is monolithic
|
|
if (!gotError) {
|
|
wxFileName actual;
|
|
|
|
actual = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avutil_version")));
|
|
if (actual.GetFullPath().IsSameAs(name.GetFullPath())) {
|
|
actual = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avcodec_version")));
|
|
if (actual.GetFullPath().IsSameAs(name.GetFullPath())) {
|
|
util = avformat;
|
|
codec = avformat;
|
|
}
|
|
}
|
|
|
|
if (util == NULL || codec == NULL) {
|
|
wxLogMessage(wxT("avformat not monolithic"));
|
|
avformat->Unload();
|
|
util = NULL;
|
|
codec = NULL;
|
|
}
|
|
else {
|
|
wxLogMessage(wxT("avformat is monolithic"));
|
|
}
|
|
}
|
|
|
|
if (!util) {
|
|
name.SetFullName(GetLibAVUtilName());
|
|
avutil = util = new wxDynamicLibrary();
|
|
wxLogMessage(wxT("Loading avutil from '%s'."), name.GetFullPath().c_str());
|
|
util->Load(name.GetFullPath(), wxDL_LAZY);
|
|
}
|
|
|
|
if (!codec) {
|
|
name.SetFullName(GetLibAVCodecName());
|
|
avcodec = codec = new wxDynamicLibrary();
|
|
wxLogMessage(wxT("Loading avcodec from '%s'."), name.GetFullPath().c_str());
|
|
codec->Load(name.GetFullPath(), wxDL_LAZY);
|
|
}
|
|
|
|
if (!avformat->IsLoaded()) {
|
|
name.SetFullName(libpath_format);
|
|
wxLogMessage(wxT("Loading avformat from '%s'."), name.GetFullPath().c_str());
|
|
gotError = !avformat->Load(name.GetFullPath(), wxDL_LAZY);
|
|
}
|
|
|
|
#if defined(__WXMSW__)
|
|
//Return PATH to normal
|
|
if ( pathfix )
|
|
{
|
|
wxString oldpath = syspath.BeforeLast(wxT(';'));
|
|
wxLogMessage(wxT("Returning PATH to previous setting..."));
|
|
wxSetEnv(wxT("PATH"),oldpath.c_str());
|
|
}
|
|
#endif
|
|
|
|
if (gotError) {
|
|
wxLogError(wxT("Failed to load FFmpeg libraries."));
|
|
FreeLibs();
|
|
return false;
|
|
}
|
|
|
|
// Show the actual libraries loaded
|
|
if (avutil) {
|
|
wxLogMessage(wxT("Actual avutil path %s"),
|
|
FileNames::PathFromAddr(avutil->GetSymbol(wxT("avutil_version"))).c_str());
|
|
}
|
|
if (avcodec) {
|
|
wxLogMessage(wxT("Actual avcodec path %s"),
|
|
FileNames::PathFromAddr(avcodec->GetSymbol(wxT("avcodec_version"))).c_str());
|
|
}
|
|
if (avformat) {
|
|
wxLogMessage(wxT("Actual avformat path %s"),
|
|
FileNames::PathFromAddr(avformat->GetSymbol(wxT("avformat_version"))).c_str());
|
|
}
|
|
|
|
wxLogMessage(wxT("Importing symbols..."));
|
|
FFMPEG_INITDYN(avformat, av_register_all);
|
|
FFMPEG_INITDYN(avformat, av_find_stream_info);
|
|
FFMPEG_INITDYN(avformat, av_read_frame);
|
|
FFMPEG_INITDYN(avformat, av_seek_frame);
|
|
FFMPEG_INITDYN(avformat, av_close_input_file);
|
|
FFMPEG_INITDYN(avformat, av_write_header);
|
|
FFMPEG_INITDYN(avformat, av_interleaved_write_frame);
|
|
FFMPEG_INITDYN(avformat, av_iformat_next);
|
|
FFMPEG_INITDYN(avformat, av_oformat_next);
|
|
FFMPEG_INITDYN(avformat, av_set_parameters);
|
|
FFMPEG_INITDYN(avformat, url_open_protocol);
|
|
FFMPEG_INITDYN(avformat, url_open);
|
|
FFMPEG_INITDYN(avformat, url_fdopen);
|
|
FFMPEG_INITDYN(avformat, url_close);
|
|
FFMPEG_INITDYN(avformat, url_fseek);
|
|
FFMPEG_INITDYN(avformat, url_fclose);
|
|
FFMPEG_INITDYN(avformat, av_new_stream);
|
|
FFMPEG_INITDYN(avformat, avformat_alloc_context);
|
|
FFMPEG_INITDYN(avformat, av_write_trailer);
|
|
FFMPEG_INITDYN(avformat, av_codec_get_tag);
|
|
FFMPEG_INITDYN(avformat, avformat_version);
|
|
FFMPEG_INITDYN(avformat, av_open_input_stream);
|
|
FFMPEG_INITDYN(avformat, av_metadata_get);
|
|
|
|
FFMPEG_INITALT(avformat, av_register_protocol2, av_register_protocol);
|
|
FFMPEG_INITALT(avformat, avio_read, get_buffer);
|
|
FFMPEG_INITALT(avformat, avio_seek, url_fseek);
|
|
FFMPEG_INITALT(avformat, avio_close, url_fclose);
|
|
FFMPEG_INITALT(avformat, av_metadata_set2, av_metadata_set);
|
|
FFMPEG_INITALT(avformat, av_guess_format, guess_format);
|
|
FFMPEG_INITALT(avformat, av_match_ext, match_ext);
|
|
|
|
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 58, 0)
|
|
FFMPEG_INITDYN(avcodec, av_init_packet);
|
|
#else
|
|
FFMPEG_INITDYN(avformat, av_init_packet);
|
|
#endif
|
|
|
|
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(52, 31, 0)
|
|
FFMPEG_INITDYN(avcodec, av_free_packet);
|
|
#endif
|
|
FFMPEG_INITDYN(avcodec, avcodec_init);
|
|
FFMPEG_INITDYN(avcodec, avcodec_find_encoder);
|
|
FFMPEG_INITDYN(avcodec, avcodec_find_encoder_by_name);
|
|
FFMPEG_INITDYN(avcodec, avcodec_find_decoder);
|
|
FFMPEG_INITDYN(avcodec, avcodec_get_context_defaults);
|
|
FFMPEG_INITDYN(avcodec, avcodec_open);
|
|
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 25, 0)
|
|
FFMPEG_INITDYN(avcodec, avcodec_decode_audio3);
|
|
#else
|
|
FFMPEG_INITDYN(avcodec, avcodec_decode_audio2);
|
|
#endif
|
|
FFMPEG_INITDYN(avcodec, avcodec_encode_audio);
|
|
FFMPEG_INITDYN(avcodec, avcodec_close);
|
|
FFMPEG_INITDYN(avcodec, avcodec_register_all);
|
|
FFMPEG_INITDYN(avcodec, avcodec_version);
|
|
FFMPEG_INITDYN(avcodec, av_fast_realloc);
|
|
FFMPEG_INITDYN(avcodec, av_codec_next);
|
|
FFMPEG_INITDYN(avcodec, av_get_bits_per_sample_format);
|
|
|
|
FFMPEG_INITALT(avcodec, av_get_bits_per_sample_fmt, av_get_bits_per_sample_format);
|
|
|
|
FFMPEG_INITDYN(avutil, av_free);
|
|
FFMPEG_INITDYN(avutil, av_log_set_callback);
|
|
FFMPEG_INITDYN(avutil, av_log_default_callback);
|
|
#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(49, 15, 0)
|
|
FFMPEG_INITDYN(avutil, av_fifo_alloc);
|
|
#else
|
|
FFMPEG_INITDYN(avutil, av_fifo_init);
|
|
#endif
|
|
FFMPEG_INITDYN(avutil, av_fifo_generic_read);
|
|
FFMPEG_INITDYN(avutil, av_fifo_realloc2);
|
|
FFMPEG_INITDYN(avutil, av_fifo_free);
|
|
FFMPEG_INITDYN(avutil, av_fifo_size);
|
|
FFMPEG_INITDYN(avutil, av_malloc);
|
|
FFMPEG_INITDYN(avutil, av_fifo_generic_write);
|
|
FFMPEG_INITDYN(avutil, av_freep);
|
|
FFMPEG_INITDYN(avutil, av_rescale_q);
|
|
FFMPEG_INITDYN(avutil, avutil_version);
|
|
|
|
//FFmpeg initialization
|
|
wxLogMessage(wxT("All symbols loaded successfully. Initializing the library."));
|
|
avcodec_init();
|
|
avcodec_register_all();
|
|
av_register_all();
|
|
|
|
wxLogMessage(wxT("Retrieving FFmpeg library version numbers:"));
|
|
int avfver = avformat_version();
|
|
int avcver = avcodec_version();
|
|
int avuver = avutil_version();
|
|
mAVCodecVersion = wxString::Format(wxT("%d.%d.%d"),avcver >> 16 & 0xFF, avcver >> 8 & 0xFF, avcver & 0xFF);
|
|
mAVFormatVersion = wxString::Format(wxT("%d.%d.%d"),avfver >> 16 & 0xFF, avfver >> 8 & 0xFF, avfver & 0xFF);
|
|
mAVUtilVersion = wxString::Format(wxT("%d.%d.%d"),avuver >> 16 & 0xFF, avuver >> 8 & 0xFF, avuver & 0xFF);
|
|
|
|
wxLogMessage(wxT(" AVCodec version 0x%06x - %s (built against 0x%06x - %s)"),
|
|
avcver, mAVCodecVersion.c_str(), LIBAVCODEC_VERSION_INT,
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVCODEC_VERSION)).c_str());
|
|
wxLogMessage(wxT(" AVFormat version 0x%06x - %s (built against 0x%06x - %s)"),
|
|
avfver, mAVFormatVersion.c_str(), LIBAVFORMAT_VERSION_INT,
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVFORMAT_VERSION)).c_str());
|
|
wxLogMessage(wxT(" AVUtil version 0x%06x - %s (built against 0x%06x - %s)"),
|
|
avuver,mAVUtilVersion.c_str(), LIBAVUTIL_VERSION_INT,
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVUTIL_VERSION)).c_str());
|
|
|
|
int avcverdiff = (avcver >> 16 & 0xFF) - int(LIBAVCODEC_VERSION_MAJOR);
|
|
int avfverdiff = (avfver >> 16 & 0xFF) - int(LIBAVFORMAT_VERSION_MAJOR);
|
|
int avuverdiff = (avuver >> 16 & 0xFF) - int(LIBAVUTIL_VERSION_MAJOR);
|
|
if (avcverdiff != 0)
|
|
wxLogError(wxT("AVCodec version mismatch = %d"), avcverdiff);
|
|
if (avfverdiff != 0)
|
|
wxLogError(wxT("AVFormat version mismatch = %d"), avfverdiff);
|
|
if (avuverdiff != 0)
|
|
wxLogError(wxT("AVUtil version mismatch = %d"), avuverdiff);
|
|
//make sure that header and library major versions are the same
|
|
if (avcverdiff != 0 || avfverdiff != 0 || avuverdiff != 0)
|
|
{
|
|
wxLogError(wxT("Version mismatch. FFmpeg libraries are unusable."));
|
|
return false;
|
|
}
|
|
|
|
av_register_protocol2(&ufile_protocol, sizeof(ufile_protocol));
|
|
|
|
return true;
|
|
}
|
|
|
|
void FFmpegLibs::FreeLibs()
|
|
{
|
|
if (avformat != NULL) {
|
|
delete avformat;
|
|
avformat = NULL;
|
|
}
|
|
|
|
if (avcodec != NULL) {
|
|
delete avcodec;
|
|
avcodec = NULL;
|
|
}
|
|
|
|
if (avutil != NULL) {
|
|
delete avutil;
|
|
avutil = NULL;
|
|
}
|
|
|
|
mLibsLoaded = false;
|
|
|
|
return;
|
|
}
|
|
|
|
#endif //USE_FFMPEG
|