bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, const Tags *metadata, int subformat) { int err; //FFmpegLibsInst->LoadLibs(NULL,true); //Loaded at startup or from Prefs now if (!FFmpegLibsInst->ValidLibsLoaded()) return false; av_log_set_callback(av_log_wx_callback); // See if libavformat has modules that can write our output format. If so, mEncFormatDesc // will describe the functions used to write the format (used internally by libavformat) // and the default video/audio codecs that the format uses. if ((mEncFormatDesc = av_guess_format(shortname, OSINPUT(mName), NULL)) == NULL) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't determine format description for file \"%s\"."), mName.c_str()), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // mEncFormatCtx is used by libavformat to carry around context data re our output file. if ((mEncFormatCtx = avformat_alloc_context()) == NULL) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't allocate output format context.")), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // Initialise the output format context. mEncFormatCtx->oformat = mEncFormatDesc; memcpy(mEncFormatCtx->filename, OSINPUT(mName), strlen(OSINPUT(mName))+1); // At the moment Audacity can export only one audio stream if ((mEncAudioStream = avformat_new_stream(mEncFormatCtx, NULL)) == NULL) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't add audio stream to output file \"%s\"."), mName.c_str()), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } mEncAudioStream->id = 0; // Open the output file. if (!(mEncFormatDesc->flags & AVFMT_NOFILE)) { if ((err = ufile_fopen(&mEncFormatCtx->pb, mName, AVIO_FLAG_WRITE)) < 0) { wxMessageBox(wxString::Format(wxT("FFmpeg : ERROR - Can't open output file \"%s\" to write. Error code is %d."), mName.c_str(), err), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } } // Open the audio stream's codec and initialise any stream related data. if (!InitCodecs(project)) return false; if (metadata == NULL) metadata = project->GetTags(); // Add metadata BEFORE writing the header. // At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4. if (GetCanMetaData(subformat)) { mSupportsUTF8 = ExportFFmpegOptions::fmts[mSubFormat].canutf8; AddTags(metadata); } // Write headers to the output file. if ((err = avformat_write_header(mEncFormatCtx, NULL)) < 0) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't write headers to output file \"%s\". Error code is %d."), mName.c_str(),err), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } return true; }
bool ExportFFmpeg::Init(const char *shortname, AudacityProject *project, const Tags *metadata, int subformat) { // This will undo the acquisition of resources along any early exit path: auto deleter = [](ExportFFmpeg *This) { if (This) This->FreeResources(); }; std::unique_ptr<ExportFFmpeg, decltype(deleter)> cleanup{ this, deleter }; int err; //FFmpegLibsInst->LoadLibs(NULL,true); //Loaded at startup or from Prefs now if (!FFmpegLibsInst->ValidLibsLoaded()) return false; av_log_set_callback(av_log_wx_callback); // See if libavformat has modules that can write our output format. If so, mEncFormatDesc // will describe the functions used to write the format (used internally by libavformat) // and the default video/audio codecs that the format uses. if ((mEncFormatDesc = av_guess_format(shortname, OSINPUT(mName), NULL)) == NULL) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't determine format description for file \"%s\"."), mName.c_str()), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // mEncFormatCtx is used by libavformat to carry around context data re our output file. mEncFormatCtx.reset(avformat_alloc_context()); if (!mEncFormatCtx) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't allocate output format context.")), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // Initialise the output format context. mEncFormatCtx->oformat = mEncFormatDesc; memcpy(mEncFormatCtx->filename, OSINPUT(mName), strlen(OSINPUT(mName))+1); // At the moment Audacity can export only one audio stream if ((mEncAudioStream = avformat_new_stream(mEncFormatCtx.get(), NULL)) == NULL) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't add audio stream to output file \"%s\"."), mName.c_str()), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // Documentation for avformat_new_stream says // "User is required to call avcodec_close() and avformat_free_context() to clean // up the allocation by avformat_new_stream()." // We use smart pointers that ensure these cleanups either in their destructors or // sooner if they are reset. These are std::unique_ptr with nondefault deleter // template parameters. // mEncFormatCtx takes care of avformat_free_context(), so // mEncAudioStream can be a plain pointer. // mEncAudioCodecCtx now becomes responsible for closing the codec: mEncAudioCodecCtx.reset(mEncAudioStream->codec); mEncAudioStream->id = 0; // Open the output file. if (!(mEncFormatDesc->flags & AVFMT_NOFILE)) { if ((err = ufile_fopen(&mEncFormatCtx->pb, mName, AVIO_FLAG_WRITE)) < 0) { wxMessageBox(wxString::Format(wxT("FFmpeg : ERROR - Can't open output file \"%s\" to write. Error code is %d."), mName.c_str(), err), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // Give mUfileCloser responsibility mUfileCloser.reset(mEncFormatCtx->pb); } // Open the audio stream's codec and initialise any stream related data. if (!InitCodecs(project)) return false; if (metadata == NULL) metadata = project->GetTags(); // Add metadata BEFORE writing the header. // At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4. if (GetCanMetaData(subformat)) { mSupportsUTF8 = ExportFFmpegOptions::fmts[mSubFormat].canutf8; AddTags(metadata); } // Write headers to the output file. if ((err = avformat_write_header(mEncFormatCtx.get(), NULL)) < 0) { wxMessageBox(wxString::Format(_("FFmpeg : ERROR - Can't write headers to output file \"%s\". Error code is %d."), mName.c_str(),err), _("FFmpeg Error"), wxOK|wxCENTER|wxICON_EXCLAMATION); return false; } // Only now, we can keep all the resources until Finalize(). // Cancel the local cleanup. cleanup.release(); return true; }