/**********************************************************************

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

*//*******************************************************************/



#include "Audacity.h"	// for config*.h
#include "FFmpeg.h"
#include "AudacityApp.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

#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 (enabled && !LoadFFmpeg(false))
   {
     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;
   }
   wxLogMessage(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)
{
   wxFile *f;
   wxFile::OpenMode mode;

   f = new wxFile;
   if (!f) {
      return AVERROR(ENOMEM);
   }

   if (flags & URL_RDWR) {
      mode = wxFile::read_write;
   } else if (flags & URL_WRONLY) {
      mode = wxFile::write;
   } else {
      mode = wxFile::read;
   }

   if (!f->Open((const wxChar *) filename, 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);
   static int totalret = 0;
   int ret = (int) ((wxFile *) h->priv_data)->Read(buf, size);
   totalret += ret;
   return ret;
}

static int ufile_write(URLContext *h, unsigned char *buf, int size)
{
   return (int) ((wxFile *) h->priv_data)->Write(buf, size);
}

#if LIBAVFORMAT_VERSION_MAJOR >= 52
static int64_t ufile_seek(URLContext *h, int64_t pos, int whence)
#else
static offset_t ufile_seek(URLContext *h, offset_t pos, int whence)
#endif
{
   wxSeekMode mode;

   switch (whence)
   {
   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",
    ufile_open,
    ufile_read,
    ufile_write,
    ufile_seek,
    ufile_close,
};

// Open a file with a (possibly) Unicode filename
int ufile_fopen(ByteIOContext **s, const wxString & name, int flags)
{
   URLContext *h;
   int err;

   // Open the file using our custom protocol and passing the (possibly) Unicode
   // filename.  This is playing a slight trick since our ufile_open() routine above
   // knows that the "char *" name may actually be wide characters.
   err = FFmpegLibsInst->url_open_protocol(&h, &ufile_protocol, (const char *) name.c_str(), flags);
   if (err < 0) {
      return err;
   }

   // Associate the file with a context
   err = FFmpegLibsInst->url_fdopen(s, h);
   if (err < 0) {
      FFmpegLibsInst->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;
   ByteIOContext *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"));
   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 *) FFmpegLibsInst->av_malloc(PROBE_BUF_MAX + AVPROBE_PADDING_SIZE);
   if (pd.buf == NULL) {
      err = AVERROR_NOMEM;
      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 = FFmpegLibsInst->get_buffer(pb, pd.buf, probe_size);

      // 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 (FFmpegLibsInst->url_fseek(pb, 0, SEEK_SET) < 0) {
         err = AVERROR(EIO);
         goto fail;
      }

      // Scan all input formats
      fmt = NULL;
      for (fmt1 = FFmpegLibsInst->av_iformat_next(NULL); fmt1 != NULL; fmt1 = FFmpegLibsInst->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 (FFmpegLibsInst->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_NOFMT;
      goto fail;
   }

   // And finally, attempt to associate an input stream with the file
   err = FFmpegLibsInst->av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
   if (err) {
      goto fail;
   }

   // Done with the probe buffer
   FFmpegLibsInst->av_freep(&pd.buf);

   return 0;

fail:
   if (pd.buf) {
      FFmpegLibsInst->av_freep(&pd.buf);
   }

   if (pb) {
      FFmpegLibsInst->url_fclose(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 is not empty, = %s"),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 libraries."));
      return false;
   }

   path = fd.GetLibPath();

   wxLogMessage(wxT("User-specified path = %s"),path.c_str());
   if (!::wxFileExists(path)) {
      wxLogMessage(wxT("User-specified file doesn't exist! Failed to find libraries."));
      return false;
   }
   wxLogMessage(wxT("User-specified 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("Libraries already loaded - freeing"));
      FreeLibs();
   }

   // First try loading it from a previously located path
   if (!mLibAVFormatPath.IsEmpty()) {
      wxLogMessage(wxT("mLibAVFormatPath is not empty, = %s. 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 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 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())
   {
      wxLogMessage(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()) {
      if (showerr) wxMessageBox(_("Failed to find compatible FFmpeg libraries"));
      return false;
   }

   wxLogMessage(wxT("Libraries loaded successfully!"));
   return true;
}

bool FFmpegLibs::ValidLibsLoaded()
{
   return mLibsLoaded;
}

bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
{
   // Initially we don't know where are the avcodec and avutl libs
   wxDynamicLibrary *codec = NULL;
   wxDynamicLibrary *util = NULL;
   wxFileName name(libpath_format);

   wxLogWindow* mLogger = wxGetApp().mLogger;

   bool gotError = false;

   wxString syspath;
   bool pathfix = false;

   FreeLibs();

#if defined(__WXMSW__)
   wxLogMessage(wxT("Looking up PATH..."));
   // First take PATH environment variable (store it's 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))
      {
         wxLogMessage(wxT("not in PATH!"));
         if (syspath.Last() == wxT(';'))
         {
            wxLogMessage(wxT("Appending %s ..."),fmtdir.c_str());
            syspath.Append(fmtdirsc);
         }
         else
         {
            wxLogMessage(wxT("Appending %s ..."),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
         {
            wxLogMessage(wxT("wxSetEnv(%s) failed."),syspath.c_str());
         }
      }
      else
      {
         wxLogMessage(wxT("in PATH."));
      }
   }
   else
   {
      wxLogMessage(wxT("PATH does not exist."));
   }
#endif

   //Load libavformat
   avformat = new wxDynamicLibrary();
   wxLogMessage(wxT("Loading avformat from %s"),libpath_format.c_str());
   if (showerr)
      mLogger->SetActiveTarget(NULL);
   gotError = !avformat->Load(libpath_format, wxDL_LAZY);
   if (showerr)
      mLogger->SetActiveTarget(mLogger);

   if (!gotError) {
      if (avformat->HasSymbol(wxT("av_free"))) {
         util = avformat;
      }
      if (avformat->HasSymbol(wxT("avcodec_init"))) {
         codec = avformat;
      }
   }

   if (!util) {
      name.SetFullName(GetLibAVUtilName());
      avutil = util =  new wxDynamicLibrary();
      wxLogMessage(wxT("Loading avutil from %s"),name.GetFullPath().c_str());
      if (showerr)
         mLogger->SetActiveTarget(NULL);
      util->Load(name.GetFullPath(), wxDL_LAZY);
      if (showerr)
         mLogger->SetActiveTarget(mLogger);
   }

   if (!codec) {
      name.SetFullName(GetLibAVCodecName());
      avcodec = codec = new wxDynamicLibrary();
      wxLogMessage(wxT("Loading avcodec from %s"),name.GetFullPath().c_str());
      if (showerr)
         mLogger->SetActiveTarget(NULL);
      codec->Load(name.GetFullPath(), wxDL_LAZY);
      if (showerr)
         mLogger->SetActiveTarget(mLogger);
   }

   if (!avformat->IsLoaded()) {
      if (showerr)
         mLogger->SetActiveTarget(NULL);
      gotError = !avformat->Load(libpath_format, wxDL_LAZY);
      if (showerr)
         mLogger->SetActiveTarget(mLogger);
   }

   //Return PATH to normal
   if ( pathfix )
   {
      wxString oldpath = syspath.BeforeLast(wxT(';'));
      wxLogMessage(wxT("Returning PATH to normal..."));
      wxSetEnv(wxT("PATH"),oldpath.c_str());
   }

   if (gotError) {
      wxLogMessage(wxT("Failed to load FFmpeg libs"));
      FreeLibs();
      return false;
   }

   wxLogMessage(wxT("Importing symbols..."));
   INITDYN(avformat,av_register_all);
   INITDYN(avformat,av_open_input_file);
   INITDYN(avformat,av_find_stream_info);
   INITDYN(avformat,av_read_frame);
   INITDYN(avformat,av_seek_frame);
   INITDYN(avformat,av_close_input_file);
   INITDYN(avformat,av_index_search_timestamp);
   INITDYN(avformat,av_write_header);
   INITDYN(avformat,av_interleaved_write_frame);
   INITDYN(avformat,av_write_frame);
   INITDYN(avformat,av_iformat_next);
   INITDYN(avformat,av_oformat_next);
   INITDYN(avformat,av_set_parameters);
#if LIBAVFORMAT_VERSION_MAJOR < 53
   INITDYN(avformat,register_protocol);
   av_register_protocol = register_protocol;
#else
   INITDYN(avformat,av_register_protocol);
#endif
   INITDYN(avformat,url_open_protocol);
   INITDYN(avformat,url_fdopen);
   INITDYN(avformat,url_close);
   INITDYN(avformat,url_fopen);
   INITDYN(avformat,url_fseek);
   INITDYN(avformat,url_fclose);
   INITDYN(avformat,url_fsize);
   INITDYN(avformat,av_new_stream);
   INITDYN(avformat,av_alloc_format_context);
   INITDYN(avformat,guess_format);
   INITDYN(avformat,av_write_trailer);
   INITDYN(avformat,av_codec_get_id);
   INITDYN(avformat,av_codec_get_tag);
   INITDYN(avformat,avformat_version);
   INITDYN(avformat,av_open_input_file);
   INITDYN(avformat,av_open_input_stream);
   INITDYN(avformat,get_buffer);
   INITDYN(avformat,match_ext);

#if FFMPEG_STABLE
   INITDYN(avformat,av_init_packet);
#else
   INITDYN(codec,av_init_packet);
   INITDYN(codec,av_free_packet);
#endif

   INITDYN(codec,avcodec_init);
   INITDYN(codec,avcodec_find_encoder);
   INITDYN(codec,avcodec_find_encoder_by_name);
   INITDYN(codec,avcodec_find_decoder);
   INITDYN(codec,avcodec_find_decoder_by_name);
   INITDYN(codec,avcodec_string);
   INITDYN(codec,avcodec_get_context_defaults);
   INITDYN(codec,avcodec_alloc_context);
   INITDYN(codec,avcodec_get_frame_defaults);
   INITDYN(codec,avcodec_alloc_frame);
   INITDYN(codec,avcodec_open);
   INITDYN(codec,avcodec_decode_audio2);
   INITDYN(codec,avcodec_encode_audio);
   INITDYN(codec,avcodec_close);
   INITDYN(codec,avcodec_register_all);
   INITDYN(codec,avcodec_flush_buffers);
   INITDYN(codec,av_get_bits_per_sample);
   INITDYN(codec,av_get_bits_per_sample_format);
   INITDYN(codec,avcodec_version);
   INITDYN(codec,av_fast_realloc);
   INITDYN(codec,av_codec_next);

   INITDYN(util,av_free);
   INITDYN(util,av_log_set_callback);
   INITDYN(util,av_log_default_callback);
#if FFMPEG_STABLE
   INITDYN(util,av_fifo_init);
   INITDYN(util,av_fifo_read);
   INITDYN(util,av_fifo_realloc);
#else
   INITDYN(util,av_fifo_alloc);
   INITDYN(util,av_fifo_generic_read);
   INITDYN(util,av_fifo_realloc2);
#endif
   INITDYN(util,av_fifo_free);
   INITDYN(util,av_fifo_size);
   INITDYN(util,av_malloc);
   INITDYN(util,av_fifo_generic_write);
   INITDYN(util,av_freep);
   INITDYN(util,av_rescale_q);
   INITDYN(util,av_strstart);
   INITDYN(util,avutil_version);

   //FFmpeg initialization
   wxLogMessage(wxT("All symbols loaded successfully. Initializing the library."));
   this->avcodec_init();
   this->avcodec_register_all();
   this->av_register_all();
   
   wxLogMessage(wxT("Retrieving library version."));
   int avcver = this->avcodec_version();
   int avfver = this->avformat_version();
   int avuver = this->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);
   wxLogMessage(wxT("AVCodec version mismatch is %d"),avcverdiff);
   wxLogMessage(wxT("AVFormat version mismatch is %d"),avfverdiff);
   wxLogMessage(wxT("AVUtil version mismatch is %d"),avuverdiff);
   //make sure that header and library major versions are the same
   if (avcverdiff != 0 || avfverdiff != 0 || avuverdiff != 0)
   {
      wxLogMessage(wxT("Version mismatch! Libraries are unusable."));
      return false;
   }

   av_register_protocol(&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
