Audacity  2.2.2
FFmpeg.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 FFmpeg.cpp
6 
7 Audacity(R) is copyright (c) 1999-2009 Audacity Team.
8 License: GPL v2. See License.txt.
9 
10 ******************************************************************//*******************************************************************/
16 
17 // Store function pointers here when including FFmpeg.h
18 #define DEFINE_FFMPEG_POINTERS
19 
20 #include "Audacity.h" // for config*.h
21 #include "FFmpeg.h"
22 #include "FileNames.h"
23 #include "Internat.h"
24 #include "widgets/HelpSystem.h"
25 #include "widgets/ErrorDialog.h"
26 
27 #include <wx/file.h>
28 #include <wx/filedlg.h>
29 
30 #include "Experimental.h"
31 
32 #if !defined(USE_FFMPEG)
33 wxString GetFFmpegVersion(wxWindow *parent)
36 {
37  return wxString(_("FFmpeg support not compiled in"));
38 }
39 
40 #else
41 
44 std::unique_ptr<FFmpegLibs> FFmpegLibsPtr{};
45 FFmpegLibs *FFmpegLibsInst()
46 {
47  return FFmpegLibsPtr.get();
48 }
49 
50 FFmpegLibs *PickFFmpegLibs()
51 {
52  if (FFmpegLibsPtr)
53  FFmpegLibsPtr->refcount++;
54  else
55  FFmpegLibsPtr = std::make_unique<FFmpegLibs>();
56 
57  return FFmpegLibsPtr.get();
58 }
59 
60 void DropFFmpegLibs()
61 {
62  if (FFmpegLibsPtr)
63  {
64  FFmpegLibsPtr->refcount--;
65  if (FFmpegLibsPtr->refcount == 0)
66  FFmpegLibsPtr.reset();
67  }
68 }
69 
70 bool LoadFFmpeg(bool showerror)
71 {
72  PickFFmpegLibs();
73  if (FFmpegLibsInst()->ValidLibsLoaded())
74  {
75  DropFFmpegLibs();
76  return true;
77  }
78  if (!FFmpegLibsInst()->LoadLibs(NULL, showerror))
79  {
80  DropFFmpegLibs();
81  gPrefs->Write(wxT("/FFmpeg/Enabled"), false);
82  gPrefs->Flush();
83  return false;
84  }
85  else
86  {
87  gPrefs->Write(wxT("/FFmpeg/Enabled"), true);
88  gPrefs->Flush();
89  return true;
90  }
91 }
92 
94 void FFmpegStartup()
95 {
96  bool enabled = false;
97  gPrefs->Read(wxT("/FFmpeg/Enabled"),&enabled);
98  // 'false' means that no errors should be shown whatsoever
99  if (!LoadFFmpeg(false))
100  {
101  if (enabled)
102  {
103  AudacityMessageBox(_("FFmpeg was configured in Preferences and successfully loaded before, \
104  \nbut this time Audacity failed to load it at startup. \
105  \n\nYou may want to go back to Preferences > Libraries and re-configure it."),
106  _("FFmpeg startup failed"));
107  }
108  }
109 }
110 
111 wxString GetFFmpegVersion(wxWindow * WXUNUSED(parent))
112 {
113  PickFFmpegLibs();
114 
115  wxString versionString = _("FFmpeg library not found");
116 
117  if (FFmpegLibsInst()->ValidLibsLoaded()) {
118  versionString = FFmpegLibsInst()->GetLibraryVersion();
119  }
120 
121  DropFFmpegLibs();
122 
123  return versionString;
124 }
125 
126 void av_log_wx_callback(void* ptr, int level, const char* fmt, va_list vl)
127 {
128  //Most of this stuff is taken from FFmpeg tutorials and FFmpeg itself
129  int av_log_level = AV_LOG_INFO;
130  AVClass* avc = ptr ? *(AVClass**)ptr : NULL;
131  if (level > av_log_level)
132  return;
133  wxString printstring(wxT(""));
134 
135  if (avc) {
136  printstring.Append(wxString::Format(wxT("[%s @ %p] "), wxString::FromUTF8(avc->item_name(ptr)), avc));
137  }
138 
139  wxString frm(fmt,wxConvLibc);
140 
141  printstring.Append(wxString::FormatV(frm,vl));
142  wxString cpt;
143  switch (level)
144  {
145  case 0: cpt = wxT("Error"); break;
146  case 1: cpt = wxT("Info"); break;
147  case 2: cpt = wxT("Debug"); break;
148  default: cpt = wxT("Log"); break;
149  }
150  wxLogDebug(wxT("%s: %s"),cpt,printstring);
151 }
152 
153 //======================= Unicode aware uri protocol for FFmpeg
154 // Code inspired from ffmpeg-users mailing list sample
155 
156 static int ufile_read(void *opaque, uint8_t *buf, int size)
157 {
158  int ret = (int)((wxFile *) opaque)->Read(buf, size);
159  return ret;
160 }
161 
162 static int ufile_write(void *opaque, uint8_t *buf, int size)
163 {
164  auto bytes = (int) ((wxFile *) opaque)->Write(buf, size);
165  if (bytes != size)
166  return -ENOSPC;
167  return bytes;
168 }
169 
170 static int64_t ufile_seek(void *opaque, int64_t pos, int whence)
171 {
172  wxSeekMode mode = wxFromStart;
173 
174 #if !defined(AVSEEK_FORCE)
175 #define AVSEEK_FORCE 0
176 #endif
177 
178  switch (whence & ~AVSEEK_FORCE)
179  {
180  case (SEEK_SET):
181  mode = wxFromStart;
182  break;
183  case (SEEK_CUR):
184  mode = wxFromCurrent;
185  break;
186  case (SEEK_END):
187  mode = wxFromEnd;
188  break;
189  case (AVSEEK_SIZE):
190  return ((wxFile *) opaque)->Length();
191  }
192 
193  return ((wxFile *) opaque)->Seek(pos, mode);
194 }
195 
196 int ufile_close(AVIOContext *pb)
197 {
198  std::unique_ptr<wxFile> f{ (wxFile *)pb->opaque };
199 
200  bool success = true;
201  if (f) {
202  success = f->Flush() && f->Close();
203  pb->opaque = nullptr;
204  }
205 
206  // We're not certain that a close error is for want of space, but we'll
207  // guess that
208  return success ? 0 : -ENOSPC;
209 
210  // Implicitly destroy the wxFile object here
211 }
212 
213 // Open a file with a (possibly) Unicode filename
214 int ufile_fopen(AVIOContext **s, const wxString & name, int flags)
215 {
216  wxFile::OpenMode mode;
217 
218  auto f = std::make_unique<wxFile>();
219  if (!f) {
220  return -ENOMEM;
221  }
222 
223  if (flags == (AVIO_FLAG_READ | AVIO_FLAG_WRITE)) {
224  return -EINVAL;
225  } else if (flags == AVIO_FLAG_WRITE) {
226  mode = wxFile::write;
227  } else {
228  mode = wxFile::read;
229  }
230 
231  if (!f->Open(name, mode)) {
232  return -ENOENT;
233  }
234 
235  *s = avio_alloc_context((unsigned char*)av_malloc(32768), 32768,
236  flags & AVIO_FLAG_WRITE,
237  /*opaque*/f.get(),
238  ufile_read,
239  ufile_write,
240  ufile_seek);
241  if (!*s) {
242  return -ENOMEM;
243  }
244 
245  f.release(); // s owns the file object now
246 
247  return 0;
248 }
249 
250 
251 // Detect type of input file and open it if recognized. Routine
252 // based on the av_open_input_file() libavformat function.
253 int ufile_fopen_input(std::unique_ptr<FFmpegContext> &context_ptr, wxString & name)
254 {
255  context_ptr.reset();
256  auto context = std::make_unique<FFmpegContext>();
257 
258  wxFileName ff{ name };
259  wxCharBuffer fname;
260  const char *filename;
261  int err;
262 
263  fname = ff.GetFullName().mb_str();
264  filename = (const char *) fname;
265 
266  // Open the file to prepare for probing
267  if ((err = ufile_fopen(&context->pb, name, AVIO_FLAG_READ)) < 0) {
268  goto fail;
269  }
270 
271  context->ic_ptr = avformat_alloc_context();
272  context->ic_ptr->pb = context->pb;
273 
274  // And finally, attempt to associate an input stream with the file
275  err = avformat_open_input(&context->ic_ptr, filename, NULL, NULL);
276  if (err) {
277  goto fail;
278  }
279 
280  // success
281  context_ptr = std::move(context);
282  return 0;
283 
284 fail:
285 
286  return err;
287 }
288 
289 FFmpegContext::~FFmpegContext()
290 {
291  if (FFmpegLibsInst()->ValidLibsLoaded())
292  {
293  if (ic_ptr)
294  avformat_close_input(&ic_ptr);
295  av_log_set_callback(av_log_default_callback);
296  }
297 
298  if (pb) {
299  ufile_close(pb);
300  if (FFmpegLibsInst()->ValidLibsLoaded())
301  {
302  av_free(pb->buffer);
303  av_free(pb);
304  }
305  }
306 }
307 
308 streamContext *import_ffmpeg_read_next_frame(AVFormatContext* formatContext,
309  streamContext** streams,
310  unsigned int numStreams)
311 {
312  streamContext *sc = NULL;
313  AVPacketEx pkt;
314 
315  if (av_read_frame(formatContext, &pkt) < 0)
316  {
317  return NULL;
318  }
319 
320  // Find a stream to which this frame belongs
321  for (unsigned int i = 0; i < numStreams; i++)
322  {
323  if (streams[i]->m_stream->index == pkt.stream_index)
324  sc = streams[i];
325  }
326 
327  // Off-stream packet. Don't panic, just skip it.
328  // When not all streams are selected for import this will happen very often.
329  if (sc == NULL)
330  {
331  return (streamContext*)1;
332  }
333 
334  // Copy the frame to the stream context
335  sc->m_pkt.create(std::move(pkt));
336 
337  sc->m_pktDataPtr = sc->m_pkt->data;
338  sc->m_pktRemainingSiz = sc->m_pkt->size;
339 
340  return sc;
341 }
342 
343 int import_ffmpeg_decode_frame(streamContext *sc, bool flushing)
344 {
345  int nBytesDecoded;
346  wxUint8 *pDecode = sc->m_pktDataPtr;
347  int nDecodeSiz = sc->m_pktRemainingSiz;
348 
349  sc->m_frameValid = 0;
350 
351  if (flushing)
352  {
353  // If we're flushing the decoders we don't actually have any NEW data to decode.
354  pDecode = NULL;
355  nDecodeSiz = 0;
356  }
357  else
358  {
359  if (!sc->m_pkt || (sc->m_pktRemainingSiz <= 0))
360  {
361  //No more data
362  return -1;
363  }
364  }
365 
366  AVPacketEx avpkt;
367  avpkt.data = pDecode;
368  avpkt.size = nDecodeSiz;
369 
370  AVFrameHolder frame{ av_frame_alloc() };
371  int got_output = 0;
372 
373  nBytesDecoded =
374  avcodec_decode_audio4(sc->m_codecCtx,
375  frame.get(), // out
376  &got_output, // out
377  &avpkt); // in
378 
379  if (nBytesDecoded < 0)
380  {
381  // Decoding failed. Don't stop.
382  return -1;
383  }
384 
385  sc->m_samplefmt = sc->m_codecCtx->sample_fmt;
386  sc->m_samplesize = static_cast<size_t>(av_get_bytes_per_sample(sc->m_samplefmt));
387 
388  int channels = sc->m_codecCtx->channels;
389  auto newsize = sc->m_samplesize * frame->nb_samples * channels;
390  sc->m_decodedAudioSamplesValidSiz = newsize;
391  // Reallocate the audio sample buffer if it's smaller than the frame size.
392  if (newsize > sc->m_decodedAudioSamplesSiz )
393  {
394  // Reallocate a bigger buffer. But av_realloc is NOT compatible with the returns of av_malloc!
395  // So do this:
396  sc->m_decodedAudioSamples.reset(static_cast<uint8_t *>(av_malloc(newsize)));
397  sc->m_decodedAudioSamplesSiz = newsize;
398  if (!sc->m_decodedAudioSamples)
399  {
400  //Can't allocate bytes
401  return -1;
402  }
403  }
404  if (frame->data[1]) {
405  for (int i = 0; i<frame->nb_samples; i++) {
406  for (int ch = 0; ch<channels; ch++) {
407  memcpy(sc->m_decodedAudioSamples.get() + sc->m_samplesize * (ch + channels*i),
408  frame->extended_data[ch] + sc->m_samplesize*i,
409  sc->m_samplesize);
410  }
411  }
412  } else {
413  memcpy(sc->m_decodedAudioSamples.get(), frame->data[0], newsize);
414  }
415 
416  // We may not have read all of the data from this packet. If so, the user can call again.
417  // Whether or not they do depends on if m_pktRemainingSiz == 0 (they can check).
418  sc->m_pktDataPtr += nBytesDecoded;
419  sc->m_pktRemainingSiz -= nBytesDecoded;
420 
421  // At this point it's normally safe to assume that we've read some samples. However, the MPEG
422  // audio decoder is broken. If this is the case then we just return with m_frameValid == 0
423  // but m_pktRemainingSiz perhaps != 0, so the user can call again.
424  if (sc->m_decodedAudioSamplesValidSiz > 0)
425  {
426  sc->m_frameValid = 1;
427  }
428  return 0;
429 }
430 
431 
432 
433 /*******************************************************/
434 
435 class FFmpegNotFoundDialog;
436 
437 //----------------------------------------------------------------------------
438 // FindFFmpegDialog
439 //----------------------------------------------------------------------------
440 
441 #define ID_FFMPEG_BROWSE 5000
442 #define ID_FFMPEG_DLOAD 5001
443 
445 class FindFFmpegDialog final : public wxDialogWrapper
446 {
447 public:
448 
449  FindFFmpegDialog(wxWindow *parent, const wxString &path, const wxString &name, const wxString &type)
450  : wxDialogWrapper(parent, wxID_ANY, wxString(_("Locate FFmpeg")))
451  {
452  SetName(GetTitle());
453  ShuttleGui S(this, eIsCreating);
454 
455  mPath = path;
456  mName = name;
457  mType = type;
458 
459  mLibPath.Assign(mPath, mName);
460 
461  PopulateOrExchange(S);
462  }
463 
464  void PopulateOrExchange(ShuttleGui & S)
465  {
466  wxString text;
467 
468  S.SetBorder(10);
469  S.StartVerticalLay(true);
470  {
471  text.Printf(_("Audacity needs the file '%s' to import and export audio via FFmpeg."), mName);
472  S.AddTitle(text);
473 
474  S.SetBorder(3);
475  S.StartHorizontalLay(wxALIGN_LEFT, true);
476  {
477  text.Printf(_("Location of '%s':"), mName);
478  S.AddTitle(text);
479  }
480  S.EndHorizontalLay();
481 
482  S.StartMultiColumn(2, wxEXPAND);
483  S.SetStretchyCol(0);
484  {
485  if (mLibPath.GetFullPath().IsEmpty()) {
486  text.Printf(_("To find '%s', click here -->"), mName);
487  mPathText = S.AddTextBox( {}, text, 0);
488  }
489  else {
490  mPathText = S.AddTextBox( {}, mLibPath.GetFullPath(), 0);
491  }
492  S.Id(ID_FFMPEG_BROWSE).AddButton(_("Browse..."), wxALIGN_RIGHT);
493  S.AddVariableText(_("To get a free copy of FFmpeg, click here -->"), true);
494  S.Id(ID_FFMPEG_DLOAD).AddButton(_("Download"), wxALIGN_RIGHT);
495  }
496  S.EndMultiColumn();
497 
498  S.AddStandardButtons();
499  }
500  S.EndVerticalLay();
501 
502  Layout();
503  Fit();
504  SetMinSize(GetSize());
505  Center();
506 
507  return;
508  }
509 
510  void OnBrowse(wxCommandEvent & WXUNUSED(event))
511  {
512  wxString question;
513  /* i18n-hint: It's asking for the location of a file, for
514  example, "Where is lame_enc.dll?" - you could translate
515  "Where would I find the file '%s'?" instead if you want. */
516  question.Printf(_("Where is '%s'?"), mName);
517 
519  question,
520  mLibPath.GetPath(),
521  mLibPath.GetName(),
522  wxT(""),
523  mType,
524  wxFD_OPEN | wxRESIZE_BORDER,
525  this);
526  if (!path.IsEmpty()) {
527  mLibPath = path;
528  mPathText->SetValue(path);
529  }
530  }
531 
532  void OnDownload(wxCommandEvent & WXUNUSED(event))
533  {
534  HelpSystem::ShowHelp(this, wxT("FAQ:Installing_the_FFmpeg_Import_Export_Library"));
535  }
536 
537  wxString GetLibPath()
538  {
539  return mLibPath.GetFullPath();
540  }
541 
542 private:
543 
544  wxFileName mLibPath;
545 
546  wxString mPath;
547  wxString mName;
548  wxString mType;
549 
550  wxTextCtrl *mPathText;
551 
552  DECLARE_EVENT_TABLE()
553 };
554 
555 BEGIN_EVENT_TABLE(FindFFmpegDialog, wxDialogWrapper)
556  EVT_BUTTON(ID_FFMPEG_BROWSE, FindFFmpegDialog::OnBrowse)
557  EVT_BUTTON(ID_FFMPEG_DLOAD, FindFFmpegDialog::OnDownload)
559 
560 
561 //----------------------------------------------------------------------------
562 // FFmpegNotFoundDialog
563 //----------------------------------------------------------------------------
564 
565 BEGIN_EVENT_TABLE(FFmpegNotFoundDialog, wxDialogWrapper)
566  EVT_BUTTON(wxID_OK, FFmpegNotFoundDialog::OnOk)
568 
569 
570 //----------------------------------------------------------------------------
571 // FFmpegLibs
572 //----------------------------------------------------------------------------
573 
575 {
576  mLibsLoaded = false;
577  refcount = 1;
578  if (gPrefs) {
579  mLibAVFormatPath = gPrefs->Read(wxT("/FFmpeg/FFmpegLibPath"), wxT(""));
580  }
581 
582 }
583 
584 FFmpegLibs::~FFmpegLibs()
585 {
586  FreeLibs();
587 };
588 
589 bool FFmpegLibs::FindLibs(wxWindow *parent)
590 {
591  wxString path;
592  wxString name;
593 
594  wxLogMessage(wxT("Looking for FFmpeg libraries..."));
595  if (!mLibAVFormatPath.IsEmpty()) {
596  wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty."), mLibAVFormatPath);
597  const wxFileName fn{ mLibAVFormatPath };
598  path = fn.GetPath();
599  name = fn.GetFullName();
600  }
601  else {
602  path = GetLibAVFormatPath();
603  name = GetLibAVFormatName();
604  wxLogMessage(wxT("mLibAVFormatPath is empty, starting with path '%s', name '%s'."),
605  path, name);
606  }
607 
608  FindFFmpegDialog fd(parent,
609  path,
610  name,
611  GetLibraryTypeString());
612 
613  if (fd.ShowModal() == wxID_CANCEL) {
614  wxLogMessage(wxT("User canceled the dialog. Failed to find FFmpeg libraries."));
615  return false;
616  }
617 
618  path = fd.GetLibPath();
619 
620  wxLogMessage(wxT("User-specified path = '%s'"), path);
621  if (!::wxFileExists(path)) {
622  wxLogError(wxT("User-specified file does not exist. Failed to find FFmpeg libraries."));
623  return false;
624  }
625  wxLogMessage(wxT("User-specified FFmpeg file exists. Success."));
626  mLibAVFormatPath = path;
627  gPrefs->Write(wxT("/FFmpeg/FFmpegLibPath"), mLibAVFormatPath);
628  gPrefs->Flush();
629 
630  return true;
631 }
632 
633 bool FFmpegLibs::LoadLibs(wxWindow * WXUNUSED(parent), bool showerr)
634 {
635 #if defined(DISABLE_DYNAMIC_LOADING_FFMPEG)
636  mLibsLoaded = InitLibs(wxEmptyString, showerr);
637  return mLibsLoaded;
638 #endif
639 
640  wxLogMessage(wxT("Trying to load FFmpeg libraries..."));
641  if (ValidLibsLoaded()) {
642  wxLogMessage(wxT("FFmpeg libraries are already loaded."));
643  FreeLibs();
644  }
645 
646  // First try loading it from a previously located path
647  if (!mLibAVFormatPath.IsEmpty()) {
648  wxLogMessage(wxT("mLibAVFormatPath ('%s') is not empty. Loading from it."),mLibAVFormatPath);
649  mLibsLoaded = InitLibs(mLibAVFormatPath,showerr);
650  }
651 
652  // If not successful, try loading it from default path
653  if (!mLibsLoaded && !GetLibAVFormatPath().IsEmpty()) {
654  const wxFileName fn{ GetLibAVFormatPath(), GetLibAVFormatName() };
655  wxString path = fn.GetFullPath();
656  wxLogMessage(wxT("Trying to load FFmpeg libraries from default path, '%s'."), path);
657  mLibsLoaded = InitLibs(path,showerr);
658  if (mLibsLoaded) {
659  mLibAVFormatPath = path;
660  }
661  }
662 
663 #if defined(__WXMAC__)
664  // If not successful, try loading it from legacy path
665  if (!mLibsLoaded && !GetLibAVFormatPath().IsEmpty()) {
666  const wxFileName fn{wxT("/usr/local/lib/audacity"), GetLibAVFormatName()};
667  wxString path = fn.GetFullPath();
668  wxLogMessage(wxT("Trying to load FFmpeg libraries from legacy path, '%s'."), path);
669  mLibsLoaded = InitLibs(path,showerr);
670  if (mLibsLoaded) {
671  mLibAVFormatPath = path;
672  }
673  }
674 #endif
675 
676  // If not successful, try loading using system search paths
677  if (!ValidLibsLoaded()) {
678  wxString path = GetLibAVFormatName();
679  wxLogMessage(wxT("Trying to load FFmpeg libraries from system paths. File name is '%s'."), path);
680  mLibsLoaded = InitLibs(path,showerr);
681  if (mLibsLoaded) {
682  mLibAVFormatPath = path;
683  }
684  }
685 
686  // If libraries aren't loaded - nag user about that
687  /*
688  if (!ValidLibsLoaded())
689  {
690  wxLogError(wxT("Failed to load libraries altogether."));
691  int dontShowDlg;
692  gPrefs->Read(wxT("/FFmpeg/NotFoundDontShow"),&dontShowDlg,0);
693  if ((dontShowDlg == 0) && (showerr))
694  FFmpegNotFoundDialog{nullptr}.ShowModal();
695  }
696  */
697  // Oh well, just give up
698  if (!ValidLibsLoaded()) {
699  wxString msg = _("Failed to find compatible FFmpeg libraries.");
700  if (showerr)
701  AudacityMessageBox(msg);
702  wxLogError(msg);
703  return false;
704  }
705 
706  wxLogMessage(wxT("FFmpeg libraries loaded successfully."));
707  return true;
708 }
709 
710 bool FFmpegLibs::ValidLibsLoaded()
711 {
712  return mLibsLoaded;
713 }
714 
715 bool FFmpegLibs::InitLibs(const wxString &libpath_format, bool WXUNUSED(showerr))
716 {
717 #if !defined(DISABLE_DYNAMIC_LOADING_FFMPEG)
718  FreeLibs();
719 
720 #if defined(__WXMSW__)
721  wxString syspath;
722  bool pathfix = false;
723 
724  wxLogMessage(wxT("Looking up PATH environment variable..."));
725  // First take PATH environment variable and store its content.
726  if (wxGetEnv(wxT("PATH"),&syspath))
727  {
728  wxLogMessage(wxT("PATH = '%s'"), syspath);
729  const wxString &fmtdir{ wxPathOnly(libpath_format) };
730  wxString fmtdirsc = fmtdir + wxT(";");
731  wxString scfmtdir = wxT(";") + fmtdir;
732  wxLogMessage(wxT("Checking that '%s' is in PATH..."), fmtdir);
733  // If the directory, where libavformat is, is not in PATH - add it
734  if (!syspath.Contains(fmtdirsc) && !syspath.Contains(scfmtdir) && !syspath.Contains(fmtdir))
735  {
736  wxLogWarning(wxT("FFmpeg directory '%s' is not in PATH."), fmtdir);
737  if (syspath.Left(1) == wxT(';'))
738  {
739  wxLogMessage(wxT("Temporarily prepending '%s' to PATH..."), fmtdir);
740  syspath.Prepend(scfmtdir);
741  }
742  else
743  {
744  wxLogMessage(wxT("Temporarily prepending '%s' to PATH..."), scfmtdir);
745  syspath.Prepend(fmtdirsc);
746  }
747 
748  if (wxSetEnv(wxT("PATH"),syspath))
749  // Remember to change PATH back to normal after we're done
750  pathfix = true;
751  else
752  wxLogSysError(wxT("Setting PATH via wxSetEnv('%s') failed."),syspath);
753  }
754  else
755  {
756  wxLogMessage(wxT("FFmpeg directory is in PATH."));
757  }
758  }
759  else
760  {
761  wxLogSysError(wxT("PATH does not exist."));
762  }
763 #endif
764 
765  //Load libavformat
766  // Initially we don't know where are the avcodec and avutl libs
767  wxDynamicLibrary *codec = NULL;
768  wxDynamicLibrary *util = NULL;
769  wxFileName avcodec_filename;
770  wxFileName avutil_filename;
771  wxFileName name{ libpath_format };
772  wxString nameFull{name.GetFullPath()};
773  bool gotError = false;
774 
775  // Check for a monolithic avformat
776  avformat = std::make_unique<wxDynamicLibrary>();
777  wxLogMessage(wxT("Checking for monolithic avformat from '%s'."), nameFull);
778  gotError = !avformat->Load(nameFull, wxDL_LAZY);
779 
780  // Verify it really is monolithic
781  if (!gotError) {
782  avutil_filename = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avutil_version")));
783  avcodec_filename = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avcodec_version")));
784  if (avutil_filename.GetFullPath().IsSameAs(nameFull)) {
785  if (avcodec_filename.GetFullPath().IsSameAs(nameFull)) {
786  util = avformat.get();
787  codec = avformat.get();
788  }
789  }
790  if (!avcodec_filename.FileExists()) {
791  avcodec_filename = GetLibAVCodecName();
792  }
793  if (!avutil_filename.FileExists()) {
794  avutil_filename = GetLibAVUtilName();
795  }
796 
797  if (util == NULL || codec == NULL) {
798  wxLogMessage(wxT("avformat not monolithic"));
799  avformat->Unload();
800  util = NULL;
801  codec = NULL;
802  }
803  else {
804  wxLogMessage(wxT("avformat is monolithic"));
805  }
806  }
807 
808  // The two wxFileNames don't change after this
809  const wxString avcodec_filename_full{ avcodec_filename.GetFullPath() };
810  const wxString avutil_filename_full{ avutil_filename.GetFullPath() };
811 
812  if (!util) {
813  util = (avutil = std::make_unique<wxDynamicLibrary>()).get();
814  wxLogMessage(wxT("Loading avutil from '%s'."), avutil_filename_full);
815  util->Load(avutil_filename_full, wxDL_LAZY);
816  }
817 
818  if (!codec) {
819  codec = (avcodec = std::make_unique<wxDynamicLibrary>()).get();
820  wxLogMessage(wxT("Loading avcodec from '%s'."), avcodec_filename_full);
821  codec->Load(avcodec_filename_full, wxDL_LAZY);
822  }
823 
824  if (!avformat->IsLoaded()) {
825  name.SetFullName(libpath_format);
826  nameFull = name.GetFullPath();
827  wxLogMessage(wxT("Loading avformat from '%s'."), nameFull);
828  gotError = !avformat->Load(nameFull, wxDL_LAZY);
829  }
830 
831 #if defined(__WXMSW__)
832  //Return PATH to normal
833  if ( pathfix )
834  {
835  wxString oldpath = syspath.BeforeLast(wxT(';'));
836  wxLogMessage(wxT("Returning PATH to previous setting..."));
837  wxSetEnv(wxT("PATH"),oldpath);
838  }
839 #endif
840 
841  if (gotError) {
842  wxLogError(wxT("Failed to load FFmpeg libraries."));
843  FreeLibs();
844  return false;
845  }
846 
847  // Show the actual libraries loaded
848  if (avutil) {
849  wxLogMessage(wxT("Actual avutil path %s"),
850  FileNames::PathFromAddr(avutil->GetSymbol(wxT("avutil_version"))));
851  }
852  if (avcodec) {
853  wxLogMessage(wxT("Actual avcodec path %s"),
854  FileNames::PathFromAddr(avcodec->GetSymbol(wxT("avcodec_version"))));
855  }
856  if (avformat) {
857  wxLogMessage(wxT("Actual avformat path %s"),
858  FileNames::PathFromAddr(avformat->GetSymbol(wxT("avformat_version"))));
859  }
860 
861  wxLogMessage(wxT("Importing symbols..."));
862  FFMPEG_INITDYN(avformat, av_register_all);
863  FFMPEG_INITDYN(avformat, avformat_find_stream_info);
864  FFMPEG_INITDYN(avformat, av_read_frame);
865  FFMPEG_INITDYN(avformat, av_seek_frame);
866  FFMPEG_INITDYN(avformat, avformat_close_input);
867  FFMPEG_INITDYN(avformat, avformat_write_header);
868  FFMPEG_INITDYN(avformat, av_interleaved_write_frame);
869  FFMPEG_INITDYN(avformat, av_oformat_next);
870  FFMPEG_INITDYN(avformat, avformat_new_stream);
871  FFMPEG_INITDYN(avformat, avformat_alloc_context);
872  FFMPEG_INITDYN(avformat, av_write_trailer);
873  FFMPEG_INITDYN(avformat, av_codec_get_tag);
874  FFMPEG_INITDYN(avformat, avformat_version);
875  FFMPEG_INITDYN(avformat, avformat_open_input);
876  FFMPEG_INITDYN(avformat, avio_size);
877  FFMPEG_INITDYN(avformat, avio_alloc_context);
878  FFMPEG_INITALT(avformat, av_guess_format, avformat, guess_format);
879  FFMPEG_INITDYN(avformat, avformat_free_context);
880 
881  FFMPEG_INITDYN(avcodec, av_init_packet);
882  FFMPEG_INITDYN(avcodec, av_free_packet);
883  FFMPEG_INITDYN(avcodec, avcodec_find_encoder);
884  FFMPEG_INITDYN(avcodec, avcodec_find_encoder_by_name);
885  FFMPEG_INITDYN(avcodec, avcodec_find_decoder);
886  FFMPEG_INITDYN(avcodec, avcodec_get_name);
887  FFMPEG_INITDYN(avcodec, avcodec_open2);
888  FFMPEG_INITDYN(avcodec, avcodec_decode_audio4);
889  FFMPEG_INITDYN(avcodec, avcodec_encode_audio2);
890  FFMPEG_INITDYN(avcodec, avcodec_close);
891  FFMPEG_INITDYN(avcodec, avcodec_register_all);
892  FFMPEG_INITDYN(avcodec, avcodec_version);
893  FFMPEG_INITDYN(avcodec, av_codec_next);
894  FFMPEG_INITDYN(avcodec, av_codec_is_encoder);
895  FFMPEG_INITDYN(avcodec, avcodec_fill_audio_frame);
896 
897  FFMPEG_INITDYN(avutil, av_free);
898  FFMPEG_INITDYN(avutil, av_dict_free);
899  FFMPEG_INITDYN(avutil, av_dict_get);
900  FFMPEG_INITDYN(avutil, av_dict_set);
901  FFMPEG_INITDYN(avutil, av_get_bytes_per_sample);
902  FFMPEG_INITDYN(avutil, av_log_set_callback);
903  FFMPEG_INITDYN(avutil, av_log_default_callback);
904  FFMPEG_INITDYN(avutil, av_fifo_alloc);
905  FFMPEG_INITDYN(avutil, av_fifo_generic_read);
906  FFMPEG_INITDYN(avutil, av_fifo_realloc2);
907  FFMPEG_INITDYN(avutil, av_fifo_free);
908  FFMPEG_INITDYN(avutil, av_fifo_size);
909  FFMPEG_INITDYN(avutil, av_malloc);
910  FFMPEG_INITDYN(avutil, av_fifo_generic_write);
911  // FFMPEG_INITDYN(avutil, av_freep);
912  FFMPEG_INITDYN(avutil, av_rescale_q);
913  FFMPEG_INITDYN(avutil, avutil_version);
914  FFMPEG_INITALT(avutil, av_frame_alloc, avcodec, avcodec_alloc_frame);
915  FFMPEG_INITALT(avutil, av_frame_free, avcodec, avcodec_free_frame);
916  FFMPEG_INITDYN(avutil, av_samples_get_buffer_size);
917  FFMPEG_INITDYN(avutil, av_get_default_channel_layout);
918 
919  wxLogMessage(wxT("All symbols loaded successfully. Initializing the library."));
920 #endif
921 
922  //FFmpeg initialization
923  avcodec_register_all();
924  av_register_all();
925 
926  wxLogMessage(wxT("Retrieving FFmpeg library version numbers:"));
927  int avfver = avformat_version();
928  int avcver = avcodec_version();
929  int avuver = avutil_version();
930  mAVCodecVersion = wxString::Format(wxT("%d.%d.%d"),avcver >> 16 & 0xFF, avcver >> 8 & 0xFF, avcver & 0xFF);
931  mAVFormatVersion = wxString::Format(wxT("%d.%d.%d"),avfver >> 16 & 0xFF, avfver >> 8 & 0xFF, avfver & 0xFF);
932  mAVUtilVersion = wxString::Format(wxT("%d.%d.%d"),avuver >> 16 & 0xFF, avuver >> 8 & 0xFF, avuver & 0xFF);
933 
934  wxLogMessage(wxT(" AVCodec version 0x%06x - %s (built against 0x%06x - %s)"),
935  avcver, mAVCodecVersion, LIBAVCODEC_VERSION_INT,
936  wxString::FromUTF8(AV_STRINGIFY(LIBAVCODEC_VERSION)));
937  wxLogMessage(wxT(" AVFormat version 0x%06x - %s (built against 0x%06x - %s)"),
938  avfver, mAVFormatVersion, LIBAVFORMAT_VERSION_INT,
939  wxString::FromUTF8(AV_STRINGIFY(LIBAVFORMAT_VERSION)));
940  wxLogMessage(wxT(" AVUtil version 0x%06x - %s (built against 0x%06x - %s)"),
941  avuver,mAVUtilVersion, LIBAVUTIL_VERSION_INT,
942  wxString::FromUTF8(AV_STRINGIFY(LIBAVUTIL_VERSION)));
943 
944  int avcverdiff = (avcver >> 16 & 0xFF) - (int)(LIBAVCODEC_VERSION_MAJOR);
945  int avfverdiff = (avfver >> 16 & 0xFF) - (int)(LIBAVFORMAT_VERSION_MAJOR);
946  int avuverdiff = (avuver >> 16 & 0xFF) - (int)(LIBAVUTIL_VERSION_MAJOR);
947  if (avcverdiff != 0)
948  wxLogError(wxT("AVCodec version mismatch = %d"), avcverdiff);
949  if (avfverdiff != 0)
950  wxLogError(wxT("AVFormat version mismatch = %d"), avfverdiff);
951  if (avuverdiff != 0)
952  wxLogError(wxT("AVUtil version mismatch = %d"), avuverdiff);
953  //make sure that header and library major versions are the same
954  if (avcverdiff != 0 || avfverdiff != 0 || avuverdiff != 0)
955  {
956  wxLogError(wxT("Version mismatch. FFmpeg libraries are unusable."));
957  return false;
958  }
959 
960  return true;
961 }
962 
963 void FFmpegLibs::FreeLibs()
964 {
965  avformat.reset();
966  avcodec.reset();
967  avutil.reset();
968  mLibsLoaded = false;
969  return;
970 }
971 
972 #endif //USE_FFMPEG
void av_log_wx_callback(void *ptr, int level, const char *fmt, va_list vl)
Callback function to catch FFmpeg log messages.
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
void EndMultiColumn()
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
static wxString PathFromAddr(void *addr)
Definition: FileNames.cpp:305
void EndHorizontalLay()
void EndVerticalLay()
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:540
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
ShuttleGui & Id(int id)
static void ShowHelp(wxWindow *parent, const wxString &localFileName, const wxString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:194
void AddTitle(const wxString &Prompt)
Centred text string.
Definition: ShuttleGui.cpp:274
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
static wxString SelectFile(Operation op, const wxString &message, const wxString &default_path, const wxString &default_filename, const wxString &default_extension, const wxString &wildcard, int flags, wxWindow *parent)
Definition: FileNames.cpp:411
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
const wxChar * name
Definition: Distortion.cpp:94
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:414
wxString GetFFmpegVersion(wxWindow *parent)
Definition: FFmpeg.cpp:35
Class used to dynamically load FFmpeg libraries.
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxButton *extra=NULL)
END_EVENT_TABLE()
void SetBorder(int Border)
Definition: ShuttleGui.h:286
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:341
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:203
void StartVerticalLay(int iProp=1)