2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
2011-04-02 02:10:50 +00:00
|
|
|
// Store function pointers here when including FFmpeg.h
|
|
|
|
#define DEFINE_FFMPEG_POINTERS
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2021-05-09 15:16:56 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "FFmpeg.h"
|
2018-11-11 02:40:37 +00:00
|
|
|
|
Fixes detection problems on Mac and possibly Windows (Bug #290)
This should fix the detection problems on the Mac and may even help with issues
on Windows and Linux.
On any of the platforms, the main issue is the search path for libraries and
how absolute path names are handled. The Mac seems to be especially
susceptible since there isn't one concrete location where libraries are stored.
Windows handles absolute paths differently and allows runtime updates to the
environment variables (and if push comes to shove, provides the
SetDllDirectory() function), so if problems still exist, they should be easy to
circumvent.
This patch does three things:
1) It adds a shell script on OSX that takes care of starting Audacity after
reassigning and clearing the DYLD_LIBRARY_PATH environment variable. This will
allow loading of libraries from their absolute path rather than searching
DYLD_LIBRARY_PATH first. This script should be transparent to the user, but it
will affect people running Audacity with gdb as they will have to specifically
target Audacity.app/Contents/MacOS/Audacity instead of the Audacity.app bundle.
Not big deal really. If ppl no enough to use gdb from the command line, they
should be able to figure it out.
2) It corrects detection of a monolithic FFmpeg library. This is one where
avformat, avcodec, and avutil have all been linked into one large library. The
main issue here was that under OSX and Linux, looking for symbols that should
reside in avutil and avcodec, would always succeed since dlsym() on these
platforms not only scans the requested library, but any dependent libraries as
well. And when avformat was loaded, it would pull in it's dependent libraries
as well.
Another issue here was the if it was determined that the library was not
monolithic, the library was never unloaded, so those dependent libraries may
have come from the wrong location since they would have been loaded via the
normal search paths and not by absolute path name.
3) It adds a method to FileNames which returns the full path name of a loaded
module that contains an address. In the case of FFmpeg, it is used to verify
that a routine from a specific library is actually being used from that library
or not. It is also used to show (in Help->Show log) the directory from which a
library was actually loaded.
2011-02-23 01:41:40 +00:00
|
|
|
#include "FileNames.h"
|
2014-06-26 19:40:35 +00:00
|
|
|
#include "widgets/HelpSystem.h"
|
2019-05-20 18:27:11 +00:00
|
|
|
#include "widgets/AudacityMessageBox.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-11-11 22:37:25 +00:00
|
|
|
#include <wx/checkbox.h>
|
2018-11-14 20:23:25 +00:00
|
|
|
#include <wx/dynlib.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/file.h>
|
2018-11-14 20:23:25 +00:00
|
|
|
#include <wx/log.h>
|
2019-05-20 18:27:11 +00:00
|
|
|
#include <wx/textctrl.h>
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
#if !defined(USE_FFMPEG)
|
|
|
|
/// FFmpeg support may or may not be compiled in,
|
|
|
|
/// but Preferences dialog requires this function nevertheless
|
2020-05-23 10:41:26 +00:00
|
|
|
TranslatableString GetFFmpegVersion()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2020-05-23 10:41:26 +00:00
|
|
|
return XO("FFmpeg support not compiled in");
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
/** This pointer to the shared object has global scope and is used to track the
|
|
|
|
* singleton object which wraps the FFmpeg codecs */
|
2016-08-01 15:44:49 +00:00
|
|
|
std::unique_ptr<FFmpegLibs> FFmpegLibsPtr{};
|
|
|
|
FFmpegLibs *FFmpegLibsInst()
|
|
|
|
{
|
|
|
|
return FFmpegLibsPtr.get();
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
FFmpegLibs *PickFFmpegLibs()
|
|
|
|
{
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsPtr)
|
|
|
|
FFmpegLibsPtr->refcount++;
|
2010-01-23 19:44:49 +00:00
|
|
|
else
|
2016-08-01 15:44:49 +00:00
|
|
|
FFmpegLibsPtr = std::make_unique<FFmpegLibs>();
|
|
|
|
|
|
|
|
return FFmpegLibsPtr.get();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DropFFmpegLibs()
|
|
|
|
{
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsPtr)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-08-01 15:44:49 +00:00
|
|
|
FFmpegLibsPtr->refcount--;
|
|
|
|
if (FFmpegLibsPtr->refcount == 0)
|
|
|
|
FFmpegLibsPtr.reset();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadFFmpeg(bool showerror)
|
|
|
|
{
|
|
|
|
PickFFmpegLibs();
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsInst()->ValidLibsLoaded())
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2014-05-27 22:06:58 +00:00
|
|
|
DropFFmpegLibs();
|
|
|
|
return true;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2016-08-01 15:44:49 +00:00
|
|
|
if (!FFmpegLibsInst()->LoadLibs(NULL, showerror))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
DropFFmpegLibs();
|
|
|
|
gPrefs->Write(wxT("/FFmpeg/Enabled"), false);
|
2012-08-02 06:03:19 +00:00
|
|
|
gPrefs->Flush();
|
2010-01-23 19:44:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gPrefs->Write(wxT("/FFmpeg/Enabled"), true);
|
2012-08-02 06:03:19 +00:00
|
|
|
gPrefs->Flush();
|
2010-01-23 19:44:49 +00:00
|
|
|
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
|
2011-03-06 20:30:10 +00:00
|
|
|
if (!LoadFFmpeg(false))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2011-03-06 20:30:10 +00:00
|
|
|
if (enabled)
|
|
|
|
{
|
2019-12-07 19:30:07 +00:00
|
|
|
AudacityMessageBox(XO(
|
2019-12-17 21:13:49 +00:00
|
|
|
"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."),
|
2019-12-07 19:30:07 +00:00
|
|
|
XO("FFmpeg startup failed"));
|
2011-03-06 20:30:10 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 10:41:26 +00:00
|
|
|
TranslatableString GetFFmpegVersion()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
PickFFmpegLibs();
|
|
|
|
|
2020-05-23 10:41:26 +00:00
|
|
|
auto versionString = XO("FFmpeg library not found");
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsInst()->ValidLibsLoaded()) {
|
2020-05-23 10:41:26 +00:00
|
|
|
versionString = Verbatim( FFmpegLibsInst()->GetLibraryVersion() );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2014-05-27 21:47:29 +00:00
|
|
|
int av_log_level = AV_LOG_INFO;
|
2010-01-23 19:44:49 +00:00
|
|
|
AVClass* avc = ptr ? *(AVClass**)ptr : NULL;
|
|
|
|
if (level > av_log_level)
|
|
|
|
return;
|
|
|
|
wxString printstring(wxT(""));
|
|
|
|
|
|
|
|
if (avc) {
|
2017-10-09 05:03:14 +00:00
|
|
|
printstring.Append(wxString::Format(wxT("[%s @ %p] "), wxString::FromUTF8(avc->item_name(ptr)), avc));
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxString frm(fmt,wxConvLibc);
|
2018-01-24 17:35:22 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-01-24 18:22:26 +00:00
|
|
|
wxLogDebug(wxT("%s: %s"),cpt,printstring);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//======================= Unicode aware uri protocol for FFmpeg
|
|
|
|
// Code inspired from ffmpeg-users mailing list sample
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
static int ufile_read(void *opaque, uint8_t *buf, int size)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2014-05-23 20:31:34 +00:00
|
|
|
int ret = (int)((wxFile *) opaque)->Read(buf, size);
|
|
|
|
return ret;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
static int ufile_write(void *opaque, uint8_t *buf, int size)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2018-01-23 12:57:52 +00:00
|
|
|
auto bytes = (int) ((wxFile *) opaque)->Write(buf, size);
|
|
|
|
if (bytes != size)
|
|
|
|
return -ENOSPC;
|
|
|
|
return bytes;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
static int64_t ufile_seek(void *opaque, int64_t pos, int whence)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2011-02-07 20:24:04 +00:00
|
|
|
wxSeekMode mode = wxFromStart;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2020-08-19 10:54:41 +00:00
|
|
|
#if !defined(AVSEEK_FORCE)
|
|
|
|
#define AVSEEK_FORCE 0
|
|
|
|
#endif
|
|
|
|
|
2011-04-02 02:10:50 +00:00
|
|
|
switch (whence & ~AVSEEK_FORCE)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
case (SEEK_SET):
|
2014-05-27 22:06:58 +00:00
|
|
|
mode = wxFromStart;
|
|
|
|
break;
|
2010-01-23 19:44:49 +00:00
|
|
|
case (SEEK_CUR):
|
2014-05-27 22:06:58 +00:00
|
|
|
mode = wxFromCurrent;
|
|
|
|
break;
|
2010-01-23 19:44:49 +00:00
|
|
|
case (SEEK_END):
|
2014-05-27 22:06:58 +00:00
|
|
|
mode = wxFromEnd;
|
|
|
|
break;
|
2010-01-23 19:44:49 +00:00
|
|
|
case (AVSEEK_SIZE):
|
2014-05-27 22:06:58 +00:00
|
|
|
return ((wxFile *) opaque)->Length();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
return ((wxFile *) opaque)->Seek(pos, mode);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
int ufile_close(AVIOContext *pb)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-04-09 04:18:20 +00:00
|
|
|
std::unique_ptr<wxFile> f{ (wxFile *)pb->opaque };
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-01-22 19:52:27 +00:00
|
|
|
bool success = true;
|
|
|
|
if (f) {
|
2020-04-05 17:43:37 +00:00
|
|
|
if (pb->write_flag) {
|
|
|
|
success = f->Flush();
|
|
|
|
}
|
|
|
|
if (success) {
|
|
|
|
success = f->Close();
|
|
|
|
}
|
2018-01-22 19:52:27 +00:00
|
|
|
pb->opaque = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're not certain that a close error is for want of space, but we'll
|
|
|
|
// guess that
|
|
|
|
return success ? 0 : -ENOSPC;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-01-22 19:52:27 +00:00
|
|
|
// Implicitly destroy the wxFile object here
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Open a file with a (possibly) Unicode filename
|
2019-03-07 19:50:22 +00:00
|
|
|
int ufile_fopen(AVIOContext **s, const FilePath & name, int flags)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2014-05-23 20:31:34 +00:00
|
|
|
wxFile::OpenMode mode;
|
|
|
|
|
2016-04-09 04:18:20 +00:00
|
|
|
auto f = std::make_unique<wxFile>();
|
2014-05-23 20:31:34 +00:00
|
|
|
if (!f) {
|
2014-12-06 11:23:44 +00:00
|
|
|
return -ENOMEM;
|
2014-05-23 20:31:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags == (AVIO_FLAG_READ | AVIO_FLAG_WRITE)) {
|
2014-12-06 11:23:44 +00:00
|
|
|
return -EINVAL;
|
2014-05-23 20:31:34 +00:00
|
|
|
} else if (flags == AVIO_FLAG_WRITE) {
|
|
|
|
mode = wxFile::write;
|
|
|
|
} else {
|
|
|
|
mode = wxFile::read;
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
if (!f->Open(name, mode)) {
|
2014-12-06 11:23:44 +00:00
|
|
|
return -ENOENT;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 20:31:34 +00:00
|
|
|
*s = avio_alloc_context((unsigned char*)av_malloc(32768), 32768,
|
|
|
|
flags & AVIO_FLAG_WRITE,
|
2016-04-09 04:18:20 +00:00
|
|
|
/*opaque*/f.get(),
|
2014-05-23 20:31:34 +00:00
|
|
|
ufile_read,
|
|
|
|
ufile_write,
|
|
|
|
ufile_seek);
|
|
|
|
if (!*s) {
|
2014-12-06 11:23:44 +00:00
|
|
|
return -ENOMEM;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2016-04-09 04:18:20 +00:00
|
|
|
f.release(); // s owns the file object now
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Detect type of input file and open it if recognized. Routine
|
|
|
|
// based on the av_open_input_file() libavformat function.
|
2019-03-07 19:50:22 +00:00
|
|
|
int ufile_fopen_input(std::unique_ptr<FFmpegContext> &context_ptr, FilePath & name)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-04-09 04:18:20 +00:00
|
|
|
context_ptr.reset();
|
|
|
|
auto context = std::make_unique<FFmpegContext>();
|
|
|
|
|
2016-02-22 05:17:20 +00:00
|
|
|
wxFileName ff{ name };
|
2010-01-23 19:44:49 +00:00
|
|
|
wxCharBuffer fname;
|
|
|
|
const char *filename;
|
|
|
|
int err;
|
|
|
|
|
2016-02-22 05:17:20 +00:00
|
|
|
fname = ff.GetFullName().mb_str();
|
2010-01-23 19:44:49 +00:00
|
|
|
filename = (const char *) fname;
|
|
|
|
|
|
|
|
// Open the file to prepare for probing
|
2016-04-09 04:18:20 +00:00
|
|
|
if ((err = ufile_fopen(&context->pb, name, AVIO_FLAG_READ)) < 0) {
|
2010-01-23 19:44:49 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-04-09 04:18:20 +00:00
|
|
|
context->ic_ptr = avformat_alloc_context();
|
|
|
|
context->ic_ptr->pb = context->pb;
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// And finally, attempt to associate an input stream with the file
|
2016-04-09 04:18:20 +00:00
|
|
|
err = avformat_open_input(&context->ic_ptr, filename, NULL, NULL);
|
2010-01-23 19:44:49 +00:00
|
|
|
if (err) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-04-09 04:18:20 +00:00
|
|
|
// success
|
|
|
|
context_ptr = std::move(context);
|
2010-01-23 19:44:49 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
2016-04-09 04:18:20 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
FFmpegContext::~FFmpegContext()
|
|
|
|
{
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsInst()->ValidLibsLoaded())
|
2016-04-09 04:18:20 +00:00
|
|
|
{
|
|
|
|
if (ic_ptr)
|
|
|
|
avformat_close_input(&ic_ptr);
|
|
|
|
av_log_set_callback(av_log_default_callback);
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (pb) {
|
2014-05-23 20:31:34 +00:00
|
|
|
ufile_close(pb);
|
2016-08-01 15:44:49 +00:00
|
|
|
if (FFmpegLibsInst()->ValidLibsLoaded())
|
2016-04-13 08:50:23 +00:00
|
|
|
{
|
|
|
|
av_free(pb->buffer);
|
|
|
|
av_free(pb);
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-01 06:11:07 +00:00
|
|
|
streamContext *import_ffmpeg_read_next_frame(AVFormatContext* formatContext,
|
|
|
|
streamContext** streams,
|
|
|
|
unsigned int numStreams)
|
|
|
|
{
|
|
|
|
streamContext *sc = NULL;
|
2016-04-13 08:50:23 +00:00
|
|
|
AVPacketEx pkt;
|
2012-06-01 06:11:07 +00:00
|
|
|
|
2016-04-13 08:50:23 +00:00
|
|
|
if (av_read_frame(formatContext, &pkt) < 0)
|
2012-06-01 06:11:07 +00:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-13 08:50:23 +00:00
|
|
|
// Find a stream to which this frame belongs
|
2012-07-16 23:32:54 +00:00
|
|
|
for (unsigned int i = 0; i < numStreams; i++)
|
2012-06-01 06:11:07 +00:00
|
|
|
{
|
|
|
|
if (streams[i]->m_stream->index == pkt.stream_index)
|
|
|
|
sc = streams[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Off-stream packet. Don't panic, just skip it.
|
|
|
|
// When not all streams are selected for import this will happen very often.
|
|
|
|
if (sc == NULL)
|
|
|
|
{
|
|
|
|
return (streamContext*)1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the frame to the stream context
|
2020-01-19 14:51:50 +00:00
|
|
|
sc->m_pkt.emplace(std::move(pkt));
|
2012-06-01 06:11:07 +00:00
|
|
|
|
2016-04-13 08:50:23 +00:00
|
|
|
sc->m_pktDataPtr = sc->m_pkt->data;
|
|
|
|
sc->m_pktRemainingSiz = sc->m_pkt->size;
|
2012-06-01 06:11:07 +00:00
|
|
|
|
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int import_ffmpeg_decode_frame(streamContext *sc, bool flushing)
|
|
|
|
{
|
2014-05-27 22:06:58 +00:00
|
|
|
int nBytesDecoded;
|
2012-06-01 06:11:07 +00:00
|
|
|
wxUint8 *pDecode = sc->m_pktDataPtr;
|
|
|
|
int nDecodeSiz = sc->m_pktRemainingSiz;
|
|
|
|
|
|
|
|
sc->m_frameValid = 0;
|
|
|
|
|
|
|
|
if (flushing)
|
|
|
|
{
|
2016-02-13 15:43:16 +00:00
|
|
|
// If we're flushing the decoders we don't actually have any NEW data to decode.
|
2012-06-01 06:11:07 +00:00
|
|
|
pDecode = NULL;
|
|
|
|
nDecodeSiz = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-13 08:50:23 +00:00
|
|
|
if (!sc->m_pkt || (sc->m_pktRemainingSiz <= 0))
|
2012-06-01 06:11:07 +00:00
|
|
|
{
|
|
|
|
//No more data
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-13 08:50:23 +00:00
|
|
|
AVPacketEx avpkt;
|
2014-05-23 20:27:54 +00:00
|
|
|
avpkt.data = pDecode;
|
|
|
|
avpkt.size = nDecodeSiz;
|
|
|
|
|
2016-04-13 08:50:23 +00:00
|
|
|
AVFrameHolder frame{ av_frame_alloc() };
|
2014-05-23 20:27:54 +00:00
|
|
|
int got_output = 0;
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2014-05-23 20:27:54 +00:00
|
|
|
nBytesDecoded =
|
|
|
|
avcodec_decode_audio4(sc->m_codecCtx,
|
2016-04-13 08:50:23 +00:00
|
|
|
frame.get(), // out
|
2014-05-23 20:27:54 +00:00
|
|
|
&got_output, // out
|
|
|
|
&avpkt); // in
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2014-05-23 20:27:54 +00:00
|
|
|
if (nBytesDecoded < 0)
|
|
|
|
{
|
|
|
|
// Decoding failed. Don't stop.
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-06-01 06:11:07 +00:00
|
|
|
sc->m_samplefmt = sc->m_codecCtx->sample_fmt;
|
2016-08-16 17:53:21 +00:00
|
|
|
sc->m_samplesize = static_cast<size_t>(av_get_bytes_per_sample(sc->m_samplefmt));
|
2012-06-01 06:11:07 +00:00
|
|
|
|
2014-05-23 20:27:54 +00:00
|
|
|
int channels = sc->m_codecCtx->channels;
|
2016-08-16 17:53:21 +00:00
|
|
|
auto newsize = sc->m_samplesize * frame->nb_samples * channels;
|
2014-05-23 20:28:24 +00:00
|
|
|
sc->m_decodedAudioSamplesValidSiz = newsize;
|
2012-06-01 06:11:07 +00:00
|
|
|
// Reallocate the audio sample buffer if it's smaller than the frame size.
|
|
|
|
if (newsize > sc->m_decodedAudioSamplesSiz )
|
|
|
|
{
|
2016-04-13 08:50:23 +00:00
|
|
|
// Reallocate a bigger buffer. But av_realloc is NOT compatible with the returns of av_malloc!
|
|
|
|
// So do this:
|
|
|
|
sc->m_decodedAudioSamples.reset(static_cast<uint8_t *>(av_malloc(newsize)));
|
2012-06-01 06:11:07 +00:00
|
|
|
sc->m_decodedAudioSamplesSiz = newsize;
|
2016-04-13 08:50:23 +00:00
|
|
|
if (!sc->m_decodedAudioSamples)
|
2012-06-01 06:11:07 +00:00
|
|
|
{
|
|
|
|
//Can't allocate bytes
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 20:27:54 +00:00
|
|
|
if (frame->data[1]) {
|
2014-05-27 22:06:58 +00:00
|
|
|
for (int i = 0; i<frame->nb_samples; i++) {
|
|
|
|
for (int ch = 0; ch<channels; ch++) {
|
2016-04-13 08:50:23 +00:00
|
|
|
memcpy(sc->m_decodedAudioSamples.get() + sc->m_samplesize * (ch + channels*i),
|
2014-05-27 22:06:58 +00:00
|
|
|
frame->extended_data[ch] + sc->m_samplesize*i,
|
|
|
|
sc->m_samplesize);
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 20:27:54 +00:00
|
|
|
} else {
|
2016-04-13 08:50:23 +00:00
|
|
|
memcpy(sc->m_decodedAudioSamples.get(), frame->data[0], newsize);
|
2012-06-01 06:11:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We may not have read all of the data from this packet. If so, the user can call again.
|
|
|
|
// Whether or not they do depends on if m_pktRemainingSiz == 0 (they can check).
|
|
|
|
sc->m_pktDataPtr += nBytesDecoded;
|
|
|
|
sc->m_pktRemainingSiz -= nBytesDecoded;
|
|
|
|
|
|
|
|
// At this point it's normally safe to assume that we've read some samples. However, the MPEG
|
|
|
|
// audio decoder is broken. If this is the case then we just return with m_frameValid == 0
|
|
|
|
// but m_pktRemainingSiz perhaps != 0, so the user can call again.
|
|
|
|
if (sc->m_decodedAudioSamplesValidSiz > 0)
|
|
|
|
{
|
|
|
|
sc->m_frameValid = 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/*******************************************************/
|
|
|
|
|
|
|
|
class FFmpegNotFoundDialog;
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// FindFFmpegDialog
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#define ID_FFMPEG_BROWSE 5000
|
|
|
|
#define ID_FFMPEG_DLOAD 5001
|
|
|
|
|
|
|
|
/// Allows user to locate libav* libraries
|
2016-07-10 21:10:50 +00:00
|
|
|
class FindFFmpegDialog final : public wxDialogWrapper
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2019-12-27 03:48:00 +00:00
|
|
|
FindFFmpegDialog(wxWindow *parent, const wxString &path, const wxString &name,
|
|
|
|
FileNames::FileTypes types)
|
2019-12-08 05:25:47 +00:00
|
|
|
: wxDialogWrapper(parent, wxID_ANY, XO("Locate FFmpeg"))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-12-08 05:25:47 +00:00
|
|
|
SetName();
|
2010-01-23 19:44:49 +00:00
|
|
|
ShuttleGui S(this, eIsCreating);
|
|
|
|
|
|
|
|
mPath = path;
|
|
|
|
mName = name;
|
2019-12-27 03:48:00 +00:00
|
|
|
mTypes = std::move( types );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
mLibPath.Assign(mPath, mName);
|
|
|
|
|
|
|
|
PopulateOrExchange(S);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PopulateOrExchange(ShuttleGui & S)
|
|
|
|
{
|
|
|
|
S.SetBorder(10);
|
|
|
|
S.StartVerticalLay(true);
|
|
|
|
{
|
2019-12-22 23:14:40 +00:00
|
|
|
S.AddTitle(
|
|
|
|
XO(
|
|
|
|
"Audacity needs the file '%s' to import and export audio via FFmpeg.")
|
|
|
|
.Format( mName ) );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
S.SetBorder(3);
|
|
|
|
S.StartHorizontalLay(wxALIGN_LEFT, true);
|
|
|
|
{
|
2019-12-22 23:14:40 +00:00
|
|
|
S.AddTitle( XO("Location of '%s':").Format( mName ) );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
S.EndHorizontalLay();
|
|
|
|
|
|
|
|
S.StartMultiColumn(2, wxEXPAND);
|
|
|
|
S.SetStretchyCol(0);
|
|
|
|
{
|
2019-02-12 00:10:48 +00:00
|
|
|
if (mLibPath.GetFullPath().empty()) {
|
2018-01-31 01:03:46 +00:00
|
|
|
mPathText = S.AddTextBox( {},
|
|
|
|
wxString::Format(_("To find '%s', click here -->"), mName), 0);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
else {
|
2017-09-28 01:20:14 +00:00
|
|
|
mPathText = S.AddTextBox( {}, mLibPath.GetFullPath(), 0);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2020-05-11 15:28:14 +00:00
|
|
|
S.Id(ID_FFMPEG_BROWSE).AddButton(XXO("Browse..."), wxALIGN_RIGHT);
|
2019-12-22 22:01:20 +00:00
|
|
|
S.AddVariableText(
|
|
|
|
XO("To get a free copy of FFmpeg, click here -->"), true);
|
2020-05-11 15:28:14 +00:00
|
|
|
S.Id(ID_FFMPEG_DLOAD).AddButton(XXO("Download"), wxALIGN_RIGHT);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
S.EndMultiColumn();
|
|
|
|
|
|
|
|
S.AddStandardButtons();
|
|
|
|
}
|
|
|
|
S.EndVerticalLay();
|
|
|
|
|
|
|
|
Layout();
|
|
|
|
Fit();
|
|
|
|
SetMinSize(GetSize());
|
|
|
|
Center();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void OnBrowse(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
/* i18n-hint: It's asking for the location of a file, for
|
|
|
|
example, "Where is lame_enc.dll?" - you could translate
|
2010-08-17 23:14:21 +00:00
|
|
|
"Where would I find the file '%s'?" instead if you want. */
|
2019-12-18 21:32:21 +00:00
|
|
|
auto question = XO("Where is '%s'?").Format( mName );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-08-03 15:29:11 +00:00
|
|
|
wxString path = FileNames::SelectFile(FileNames::Operation::_None,
|
2017-08-02 16:41:29 +00:00
|
|
|
question,
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibPath.GetPath(),
|
2021-02-01 06:03:47 +00:00
|
|
|
mLibPath.GetFullName(),
|
2010-01-23 19:44:49 +00:00
|
|
|
wxT(""),
|
2019-12-27 03:48:00 +00:00
|
|
|
mTypes,
|
2010-01-23 19:44:49 +00:00
|
|
|
wxFD_OPEN | wxRESIZE_BORDER,
|
|
|
|
this);
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!path.empty()) {
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibPath = path;
|
|
|
|
mPathText->SetValue(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void OnDownload(wxCommandEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2021-06-06 19:40:11 +00:00
|
|
|
HelpSystem::ShowHelp(this, L"FAQ:Installing_the_FFmpeg_Import_Export_Library");
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxString GetLibPath()
|
|
|
|
{
|
|
|
|
return mLibPath.GetFullPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
wxFileName mLibPath;
|
|
|
|
|
|
|
|
wxString mPath;
|
|
|
|
wxString mName;
|
2019-12-27 03:48:00 +00:00
|
|
|
FileNames::FileTypes mTypes;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
wxTextCtrl *mPathText;
|
|
|
|
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
|
|
};
|
|
|
|
|
2016-07-10 21:10:50 +00:00
|
|
|
BEGIN_EVENT_TABLE(FindFFmpegDialog, wxDialogWrapper)
|
2010-01-23 19:44:49 +00:00
|
|
|
EVT_BUTTON(ID_FFMPEG_BROWSE, FindFFmpegDialog::OnBrowse)
|
|
|
|
EVT_BUTTON(ID_FFMPEG_DLOAD, FindFFmpegDialog::OnDownload)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// FFmpegNotFoundDialog
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2018-11-11 22:37:25 +00:00
|
|
|
FFmpegNotFoundDialog::FFmpegNotFoundDialog(wxWindow *parent)
|
2019-12-08 05:25:47 +00:00
|
|
|
: wxDialogWrapper(parent, wxID_ANY, XO("FFmpeg not found"))
|
2018-11-11 22:37:25 +00:00
|
|
|
{
|
2019-12-08 05:25:47 +00:00
|
|
|
SetName();
|
2018-11-11 22:37:25 +00:00
|
|
|
ShuttleGui S(this, eIsCreating);
|
|
|
|
PopulateOrExchange(S);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FFmpegNotFoundDialog::PopulateOrExchange(ShuttleGui & S)
|
|
|
|
{
|
|
|
|
wxString text;
|
|
|
|
|
|
|
|
S.SetBorder(10);
|
|
|
|
S.StartVerticalLay(true);
|
|
|
|
{
|
2019-12-23 00:55:59 +00:00
|
|
|
S.AddFixedText(XO(
|
2018-11-11 22:37:25 +00:00
|
|
|
"Audacity attempted to use FFmpeg to import an audio file,\n\
|
|
|
|
but the libraries were not found.\n\n\
|
2019-07-25 12:38:02 +00:00
|
|
|
To use FFmpeg import, go to Edit > Preferences > Libraries\n\
|
2018-11-11 22:37:25 +00:00
|
|
|
to download or locate the FFmpeg libraries."
|
|
|
|
));
|
|
|
|
|
2018-01-31 01:03:46 +00:00
|
|
|
mDontShow = S
|
2020-05-11 15:28:14 +00:00
|
|
|
.AddCheckBox(XXO("Do not show this warning again"),
|
2018-01-31 01:03:46 +00:00
|
|
|
gPrefs->ReadBool(wxT("/FFmpeg/NotFoundDontShow"), false) );
|
2018-11-11 22:37:25 +00:00
|
|
|
|
|
|
|
S.AddStandardButtons(eOkButton);
|
|
|
|
}
|
|
|
|
S.EndVerticalLay();
|
|
|
|
|
|
|
|
Layout();
|
|
|
|
Fit();
|
|
|
|
SetMinSize(GetSize());
|
|
|
|
Center();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FFmpegNotFoundDialog::OnOk(wxCommandEvent & WXUNUSED(event))
|
|
|
|
{
|
|
|
|
if (mDontShow->GetValue())
|
|
|
|
{
|
|
|
|
gPrefs->Write(wxT("/FFmpeg/NotFoundDontShow"),1);
|
|
|
|
gPrefs->Flush();
|
|
|
|
}
|
|
|
|
this->EndModal(0);
|
|
|
|
}
|
|
|
|
|
2016-07-10 21:10:50 +00:00
|
|
|
BEGIN_EVENT_TABLE(FFmpegNotFoundDialog, wxDialogWrapper)
|
2010-01-23 19:44:49 +00:00
|
|
|
EVT_BUTTON(wxID_OK, FFmpegNotFoundDialog::OnOk)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// FFmpegLibs
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
FFmpegLibs::FFmpegLibs()
|
|
|
|
{
|
|
|
|
mLibsLoaded = false;
|
|
|
|
refcount = 1;
|
|
|
|
if (gPrefs) {
|
|
|
|
mLibAVFormatPath = gPrefs->Read(wxT("/FFmpeg/FFmpegLibPath"), wxT(""));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
FFmpegLibs::~FFmpegLibs()
|
|
|
|
{
|
|
|
|
FreeLibs();
|
|
|
|
};
|
|
|
|
|
|
|
|
bool FFmpegLibs::FindLibs(wxWindow *parent)
|
|
|
|
{
|
|
|
|
wxString path;
|
|
|
|
wxString name;
|
|
|
|
|
2019-02-13 08:46:03 +00:00
|
|
|
// If we're looking for the lib, use the standard name, as the
|
|
|
|
// configured name is not found.
|
|
|
|
name = GetLibAVFormatName();
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogMessage(wxT("Looking for FFmpeg libraries..."));
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!mLibAVFormatPath.empty()) {
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty."), mLibAVFormatPath);
|
2016-02-22 05:17:20 +00:00
|
|
|
const wxFileName fn{ mLibAVFormatPath };
|
2010-01-23 19:44:49 +00:00
|
|
|
path = fn.GetPath();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
path = GetLibAVFormatPath();
|
2014-05-27 22:06:58 +00:00
|
|
|
wxLogMessage(wxT("mLibAVFormatPath is empty, starting with path '%s', name '%s'."),
|
2017-10-09 05:03:14 +00:00
|
|
|
path, name);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FindFFmpegDialog fd(parent,
|
2010-08-17 23:14:21 +00:00
|
|
|
path,
|
|
|
|
name,
|
2019-12-27 03:48:00 +00:00
|
|
|
GetLibraryTypes());
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
if (fd.ShowModal() == wxID_CANCEL) {
|
2011-04-02 02:10:50 +00:00
|
|
|
wxLogMessage(wxT("User canceled the dialog. Failed to find FFmpeg libraries."));
|
2010-01-23 19:44:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = fd.GetLibPath();
|
|
|
|
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("User-specified path = '%s'"), path);
|
2010-01-23 19:44:49 +00:00
|
|
|
if (!::wxFileExists(path)) {
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogError(wxT("User-specified file does not exist. Failed to find FFmpeg libraries."));
|
2010-01-23 19:44:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogMessage(wxT("User-specified FFmpeg file exists. Success."));
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibAVFormatPath = path;
|
|
|
|
gPrefs->Write(wxT("/FFmpeg/FFmpegLibPath"), mLibAVFormatPath);
|
2012-08-02 06:03:19 +00:00
|
|
|
gPrefs->Flush();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
bool FFmpegLibs::LoadLibs(wxWindow * WXUNUSED(parent), bool showerr)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2012-08-29 21:31:11 +00:00
|
|
|
#if defined(DISABLE_DYNAMIC_LOADING_FFMPEG)
|
|
|
|
mLibsLoaded = InitLibs(wxEmptyString, showerr);
|
|
|
|
return mLibsLoaded;
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries..."));
|
2010-01-23 19:44:49 +00:00
|
|
|
if (ValidLibsLoaded()) {
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogMessage(wxT("FFmpeg libraries are already loaded."));
|
2010-01-23 19:44:49 +00:00
|
|
|
FreeLibs();
|
|
|
|
}
|
|
|
|
|
|
|
|
// First try loading it from a previously located path
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!mLibAVFormatPath.empty()) {
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty. Loading from it."),mLibAVFormatPath);
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibsLoaded = InitLibs(mLibAVFormatPath,showerr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not successful, try loading it from default path
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!mLibsLoaded && !GetLibAVFormatPath().empty()) {
|
2016-02-22 05:17:20 +00:00
|
|
|
const wxFileName fn{ GetLibAVFormatPath(), GetLibAVFormatName() };
|
2010-01-23 19:44:49 +00:00
|
|
|
wxString path = fn.GetFullPath();
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries from default path, '%s'."), path);
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibsLoaded = InitLibs(path,showerr);
|
|
|
|
if (mLibsLoaded) {
|
|
|
|
mLibAVFormatPath = path;
|
|
|
|
}
|
|
|
|
}
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2012-03-25 05:16:46 +00:00
|
|
|
#if defined(__WXMAC__)
|
|
|
|
// If not successful, try loading it from legacy path
|
2019-02-12 00:10:48 +00:00
|
|
|
if (!mLibsLoaded && !GetLibAVFormatPath().empty()) {
|
2016-02-22 05:17:20 +00:00
|
|
|
const wxFileName fn{wxT("/usr/local/lib/audacity"), GetLibAVFormatName()};
|
2012-03-25 05:16:46 +00:00
|
|
|
wxString path = fn.GetFullPath();
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries from legacy path, '%s'."), path);
|
2012-03-25 05:16:46 +00:00
|
|
|
mLibsLoaded = InitLibs(path,showerr);
|
|
|
|
if (mLibsLoaded) {
|
|
|
|
mLibAVFormatPath = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// If not successful, try loading using system search paths
|
|
|
|
if (!ValidLibsLoaded()) {
|
|
|
|
wxString path = GetLibAVFormatName();
|
2017-10-09 05:03:14 +00:00
|
|
|
wxLogMessage(wxT("Trying to load FFmpeg libraries from system paths. File name is '%s'."), path);
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibsLoaded = InitLibs(path,showerr);
|
|
|
|
if (mLibsLoaded) {
|
|
|
|
mLibAVFormatPath = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 10:54:41 +00:00
|
|
|
// If libraries aren't loaded - nag user about that
|
|
|
|
/*
|
|
|
|
if (!ValidLibsLoaded())
|
|
|
|
{
|
|
|
|
wxLogError(wxT("Failed to load libraries altogether."));
|
|
|
|
int dontShowDlg;
|
|
|
|
gPrefs->Read(wxT("/FFmpeg/NotFoundDontShow"),&dontShowDlg,0);
|
|
|
|
if ((dontShowDlg == 0) && (showerr))
|
|
|
|
FFmpegNotFoundDialog{nullptr}.ShowModal();
|
|
|
|
}
|
|
|
|
*/
|
2010-01-23 19:44:49 +00:00
|
|
|
// Oh well, just give up
|
|
|
|
if (!ValidLibsLoaded()) {
|
2019-12-07 19:30:07 +00:00
|
|
|
auto msg = XO("Failed to find compatible FFmpeg libraries.");
|
2014-05-27 22:06:58 +00:00
|
|
|
if (showerr)
|
2019-12-07 19:30:07 +00:00
|
|
|
AudacityMessageBox( msg );
|
|
|
|
wxLogError(msg.Debug());
|
2010-01-23 19:44:49 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-17 23:14:21 +00:00
|
|
|
wxLogMessage(wxT("FFmpeg libraries loaded successfully."));
|
2010-01-23 19:44:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FFmpegLibs::ValidLibsLoaded()
|
|
|
|
{
|
|
|
|
return mLibsLoaded;
|
|
|
|
}
|
|
|
|
|
2016-02-23 02:18:11 +00:00
|
|
|
bool FFmpegLibs::InitLibs(const wxString &libpath_format, bool WXUNUSED(showerr))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2012-08-29 21:31:11 +00:00
|
|
|
#if !defined(DISABLE_DYNAMIC_LOADING_FFMPEG)
|
2010-01-23 19:44:49 +00:00
|
|
|
FreeLibs();
|
|
|
|
|
|
|
|
#if defined(__WXMSW__)
|
2021-02-02 06:37:52 +00:00
|
|
|
wxString oldpath;
|
Fixes detection problems on Mac and possibly Windows (Bug #290)
This should fix the detection problems on the Mac and may even help with issues
on Windows and Linux.
On any of the platforms, the main issue is the search path for libraries and
how absolute path names are handled. The Mac seems to be especially
susceptible since there isn't one concrete location where libraries are stored.
Windows handles absolute paths differently and allows runtime updates to the
environment variables (and if push comes to shove, provides the
SetDllDirectory() function), so if problems still exist, they should be easy to
circumvent.
This patch does three things:
1) It adds a shell script on OSX that takes care of starting Audacity after
reassigning and clearing the DYLD_LIBRARY_PATH environment variable. This will
allow loading of libraries from their absolute path rather than searching
DYLD_LIBRARY_PATH first. This script should be transparent to the user, but it
will affect people running Audacity with gdb as they will have to specifically
target Audacity.app/Contents/MacOS/Audacity instead of the Audacity.app bundle.
Not big deal really. If ppl no enough to use gdb from the command line, they
should be able to figure it out.
2) It corrects detection of a monolithic FFmpeg library. This is one where
avformat, avcodec, and avutil have all been linked into one large library. The
main issue here was that under OSX and Linux, looking for symbols that should
reside in avutil and avcodec, would always succeed since dlsym() on these
platforms not only scans the requested library, but any dependent libraries as
well. And when avformat was loaded, it would pull in it's dependent libraries
as well.
Another issue here was the if it was determined that the library was not
monolithic, the library was never unloaded, so those dependent libraries may
have come from the wrong location since they would have been loaded via the
normal search paths and not by absolute path name.
3) It adds a method to FileNames which returns the full path name of a loaded
module that contains an address. In the case of FFmpeg, it is used to verify
that a routine from a specific library is actually being used from that library
or not. It is also used to show (in Help->Show log) the directory from which a
library was actually loaded.
2011-02-23 01:41:40 +00:00
|
|
|
wxString syspath;
|
2021-02-02 06:37:52 +00:00
|
|
|
bool pathExisted = false;
|
2020-08-19 10:54:41 +00:00
|
|
|
|
2021-02-02 06:37:52 +00:00
|
|
|
// Return PATH to normal
|
|
|
|
auto restorePath = finally([&]()
|
2020-08-19 10:54:41 +00:00
|
|
|
{
|
2021-05-10 15:33:01 +00:00
|
|
|
if (oldpath != syspath)
|
|
|
|
{
|
|
|
|
if (pathExisted)
|
|
|
|
{
|
|
|
|
wxLogMessage(wxT("Returning PATH to previous setting: %s"), oldpath);
|
2021-02-02 06:37:52 +00:00
|
|
|
wxSetEnv(wxT("PATH"), oldpath);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2021-05-10 15:33:01 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
wxLogMessage(wxT("Removing PATH environment variable"));
|
2021-02-02 06:37:52 +00:00
|
|
|
wxUnsetEnv(wxT("PATH"));
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-02 06:37:52 +00:00
|
|
|
});
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogMessage(wxT("Looking up PATH environment variable..."));
|
2021-02-02 06:37:52 +00:00
|
|
|
// First take PATH environment variable and store its content.
|
|
|
|
pathExisted = wxGetEnv(wxT("PATH"),&syspath);
|
|
|
|
oldpath = syspath;
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogMessage(wxT("PATH = '%s'"), syspath);
|
2021-02-02 06:37:52 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
const wxString &fmtdir{ wxPathOnly(libpath_format) };
|
|
|
|
wxString fmtdirsc = fmtdir + wxT(";");
|
|
|
|
wxString scfmtdir = wxT(";") + fmtdir;
|
2021-02-02 06:37:52 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (syspath.Left(1) == wxT(';'))
|
|
|
|
{
|
|
|
|
wxLogMessage(wxT("Temporarily prepending '%s' to PATH..."), fmtdir);
|
|
|
|
syspath.Prepend(scfmtdir);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxLogMessage(wxT("Temporarily prepending '%s' to PATH..."), scfmtdir);
|
|
|
|
syspath.Prepend(fmtdirsc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wxSetEnv(wxT("PATH"),syspath))
|
|
|
|
{
|
|
|
|
wxLogSysError(wxT("Setting PATH via wxSetEnv('%s') failed."), syspath);
|
2020-08-19 10:54:41 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
//Load libavformat
|
2021-05-10 15:33:01 +00:00
|
|
|
// Initially we don't know where are the avcodec and avutl libs
|
|
|
|
wxDynamicLibrary *codec = NULL;
|
|
|
|
wxDynamicLibrary *util = NULL;
|
2014-06-10 21:34:15 +00:00
|
|
|
wxFileName avcodec_filename;
|
|
|
|
wxFileName avutil_filename;
|
2016-02-22 05:17:20 +00:00
|
|
|
wxFileName name{ libpath_format };
|
2021-05-10 15:33:01 +00:00
|
|
|
wxString nameFull{name.GetFullPath()};
|
2010-08-17 23:14:21 +00:00
|
|
|
bool gotError = false;
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
// Check for a monolithic avformat
|
|
|
|
avformat = std::make_unique<wxDynamicLibrary>();
|
|
|
|
wxLogMessage(wxT("Checking for monolithic avformat from '%s'."), nameFull);
|
|
|
|
gotError = !avformat->Load(nameFull, wxDL_LAZY);
|
|
|
|
|
|
|
|
// Verify it really is monolithic
|
|
|
|
if (!gotError) {
|
|
|
|
avutil_filename = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avutil_version")));
|
|
|
|
avcodec_filename = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avcodec_version")));
|
|
|
|
if (avutil_filename.GetFullPath() == nameFull) {
|
|
|
|
if (avcodec_filename.GetFullPath() == nameFull) {
|
|
|
|
util = avformat.get();
|
|
|
|
codec = avformat.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!avcodec_filename.FileExists()) {
|
|
|
|
avcodec_filename = GetLibAVCodecName();
|
|
|
|
}
|
|
|
|
if (!avutil_filename.FileExists()) {
|
|
|
|
avutil_filename = GetLibAVUtilName();
|
2020-08-19 10:54:41 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (util == NULL || codec == NULL) {
|
|
|
|
wxLogMessage(wxT("avformat not monolithic"));
|
|
|
|
avformat->Unload();
|
|
|
|
util = NULL;
|
|
|
|
codec = NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
wxLogMessage(wxT("avformat is monolithic"));
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
// The two wxFileNames don't change after this
|
|
|
|
const wxString avcodec_filename_full{ avcodec_filename.GetFullPath() };
|
|
|
|
const wxString avutil_filename_full{ avutil_filename.GetFullPath() };
|
2021-05-05 15:54:36 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (!util) {
|
|
|
|
util = (avutil = std::make_unique<wxDynamicLibrary>()).get();
|
|
|
|
wxLogMessage(wxT("Loading avutil from '%s'."), avutil_filename_full);
|
|
|
|
util->Load(avutil_filename_full, wxDL_LAZY);
|
2020-08-19 10:54:41 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (!codec) {
|
|
|
|
codec = (avcodec = std::make_unique<wxDynamicLibrary>()).get();
|
|
|
|
wxLogMessage(wxT("Loading avcodec from '%s'."), avcodec_filename_full);
|
|
|
|
codec->Load(avcodec_filename_full, wxDL_LAZY);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (!avformat->IsLoaded()) {
|
|
|
|
name.SetFullName(libpath_format);
|
|
|
|
nameFull = name.GetFullPath();
|
|
|
|
wxLogMessage(wxT("Loading avformat from '%s'."), nameFull);
|
|
|
|
gotError = !avformat->Load(nameFull, wxDL_LAZY);
|
|
|
|
}
|
2021-05-05 15:54:36 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
if (gotError) {
|
|
|
|
wxLogError(wxT("Failed to load FFmpeg libraries."));
|
2021-05-05 15:54:36 +00:00
|
|
|
FreeLibs();
|
|
|
|
return false;
|
2020-08-19 10:54:41 +00:00
|
|
|
}
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
// Show the actual libraries loaded
|
|
|
|
if (avutil) {
|
|
|
|
wxLogMessage(wxT("Actual avutil path %s"),
|
|
|
|
FileNames::PathFromAddr(avutil->GetSymbol(wxT("avutil_version"))));
|
|
|
|
}
|
|
|
|
if (avcodec) {
|
|
|
|
wxLogMessage(wxT("Actual avcodec path %s"),
|
|
|
|
FileNames::PathFromAddr(avcodec->GetSymbol(wxT("avcodec_version"))));
|
|
|
|
}
|
|
|
|
if (avformat) {
|
|
|
|
wxLogMessage(wxT("Actual avformat path %s"),
|
|
|
|
FileNames::PathFromAddr(avformat->GetSymbol(wxT("avformat_version"))));
|
|
|
|
}
|
|
|
|
|
|
|
|
wxLogMessage(wxT("Importing symbols..."));
|
|
|
|
FFMPEG_INITDYN(avformat, av_register_all);
|
|
|
|
FFMPEG_INITDYN(avformat, avformat_find_stream_info);
|
|
|
|
FFMPEG_INITDYN(avformat, av_read_frame);
|
|
|
|
FFMPEG_INITDYN(avformat, av_seek_frame);
|
|
|
|
FFMPEG_INITDYN(avformat, avformat_close_input);
|
|
|
|
FFMPEG_INITDYN(avformat, avformat_write_header);
|
|
|
|
FFMPEG_INITDYN(avformat, av_interleaved_write_frame);
|
|
|
|
FFMPEG_INITDYN(avformat, av_oformat_next);
|
|
|
|
FFMPEG_INITDYN(avformat, avformat_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, avformat_open_input);
|
|
|
|
FFMPEG_INITDYN(avformat, avio_size);
|
|
|
|
FFMPEG_INITDYN(avformat, avio_alloc_context);
|
|
|
|
FFMPEG_INITALT(avformat, av_guess_format, avformat, guess_format);
|
|
|
|
FFMPEG_INITDYN(avformat, avformat_free_context);
|
|
|
|
|
|
|
|
FFMPEG_INITDYN(avcodec, av_init_packet);
|
|
|
|
FFMPEG_INITDYN(avcodec, av_free_packet);
|
|
|
|
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_name);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_open2);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_decode_audio4);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_encode_audio2);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_close);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_register_all);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_version);
|
|
|
|
FFMPEG_INITDYN(avcodec, av_codec_next);
|
|
|
|
FFMPEG_INITDYN(avcodec, av_codec_is_encoder);
|
|
|
|
FFMPEG_INITDYN(avcodec, avcodec_fill_audio_frame);
|
|
|
|
|
|
|
|
FFMPEG_INITDYN(avutil, av_free);
|
|
|
|
FFMPEG_INITDYN(avutil, av_dict_free);
|
|
|
|
FFMPEG_INITDYN(avutil, av_dict_get);
|
|
|
|
FFMPEG_INITDYN(avutil, av_dict_set);
|
|
|
|
FFMPEG_INITDYN(avutil, av_get_bytes_per_sample);
|
|
|
|
FFMPEG_INITDYN(avutil, av_log_set_callback);
|
|
|
|
FFMPEG_INITDYN(avutil, av_log_default_callback);
|
|
|
|
FFMPEG_INITDYN(avutil, av_fifo_alloc);
|
|
|
|
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_INITALT(avutil, av_frame_alloc, avcodec, avcodec_alloc_frame);
|
|
|
|
FFMPEG_INITALT(avutil, av_frame_free, avcodec, avcodec_free_frame);
|
|
|
|
FFMPEG_INITDYN(avutil, av_samples_get_buffer_size);
|
|
|
|
FFMPEG_INITDYN(avutil, av_get_default_channel_layout);
|
|
|
|
FFMPEG_INITDYN(avutil, av_strerror);
|
|
|
|
|
|
|
|
wxLogMessage(wxT("All symbols loaded successfully. Initializing the library."));
|
2012-08-29 21:31:11 +00:00
|
|
|
#endif
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
//FFmpeg initialization
|
2011-04-02 02:10:50 +00:00
|
|
|
avcodec_register_all();
|
|
|
|
av_register_all();
|
2014-05-27 22:06:58 +00:00
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogMessage(wxT("Retrieving FFmpeg library version numbers:"));
|
2011-04-02 02:10:50 +00:00
|
|
|
int avfver = avformat_version();
|
|
|
|
int avcver = avcodec_version();
|
|
|
|
int avuver = avutil_version();
|
2010-01-23 19:44:49 +00:00
|
|
|
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);
|
|
|
|
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogMessage(wxT(" AVCodec version 0x%06x - %s (built against 0x%06x - %s)"),
|
2021-05-05 15:54:36 +00:00
|
|
|
avcver, mAVCodecVersion, LIBAVCODEC_VERSION_INT,
|
|
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVCODEC_VERSION)));
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogMessage(wxT(" AVFormat version 0x%06x - %s (built against 0x%06x - %s)"),
|
|
|
|
avfver, mAVFormatVersion, LIBAVFORMAT_VERSION_INT,
|
|
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVFORMAT_VERSION)));
|
|
|
|
wxLogMessage(wxT(" AVUtil version 0x%06x - %s (built against 0x%06x - %s)"),
|
2017-10-09 05:03:14 +00:00
|
|
|
avuver,mAVUtilVersion, LIBAVUTIL_VERSION_INT,
|
|
|
|
wxString::FromUTF8(AV_STRINGIFY(LIBAVUTIL_VERSION)));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2021-05-05 15:54:36 +00:00
|
|
|
int avcverdiff = (avcver >> 16 & 0xFF) - (int)(LIBAVCODEC_VERSION_MAJOR);
|
2021-05-10 15:33:01 +00:00
|
|
|
int avfverdiff = (avfver >> 16 & 0xFF) - (int)(LIBAVFORMAT_VERSION_MAJOR);
|
2016-09-20 12:26:42 +00:00
|
|
|
int avuverdiff = (avuver >> 16 & 0xFF) - (int)(LIBAVUTIL_VERSION_MAJOR);
|
2020-08-19 10:54:41 +00:00
|
|
|
if (avcverdiff != 0)
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogError(wxT("AVCodec version mismatch = %d"), avcverdiff);
|
2020-08-19 10:54:41 +00:00
|
|
|
if (avfverdiff != 0)
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogError(wxT("AVFormat version mismatch = %d"), avfverdiff);
|
2020-08-19 10:54:41 +00:00
|
|
|
if (avuverdiff != 0)
|
2021-05-10 15:33:01 +00:00
|
|
|
wxLogError(wxT("AVUtil version mismatch = %d"), avuverdiff);
|
2020-08-19 10:54:41 +00:00
|
|
|
//make sure that header and library major versions are the same
|
2021-05-10 15:33:01 +00:00
|
|
|
if (avcverdiff != 0 || avfverdiff != 0 || avuverdiff != 0)
|
|
|
|
{
|
|
|
|
wxLogError(wxT("Version mismatch. FFmpeg libraries are unusable."));
|
2020-08-19 10:54:41 +00:00
|
|
|
return false;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FFmpegLibs::FreeLibs()
|
|
|
|
{
|
2021-05-10 15:33:01 +00:00
|
|
|
avformat.reset();
|
|
|
|
avcodec.reset();
|
|
|
|
avutil.reset();
|
2010-01-23 19:44:49 +00:00
|
|
|
mLibsLoaded = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif //USE_FFMPEG
|