Audacity 3.2.0
ExportFFmpeg.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ExportFFmpeg.cpp
6
7 Audacity(R) is copyright (c) 1999-2009 Audacity Team.
8 License: GPL v2 or later. See License.txt.
9
10 LRN
11
12******************************************************************//*******************************************************************/
20
21
22#include "../FFmpeg.h"
23#include "FFmpegFunctions.h"
24
25#include <wx/app.h>
26#include <wx/log.h>
27
28#include <wx/window.h>
29#include <wx/button.h>
30#include <wx/textctrl.h>
31
32#include "BasicSettings.h"
33#include "Mix.h"
34#include "Tags.h"
35#include "Track.h"
36#include "wxFileNameWrapper.h"
37
38#include "ExportFFmpegOptions.h"
39#include "SelectFile.h"
40#include "ShuttleGui.h"
41
42#include "ExportPluginHelpers.h"
44#include "FFmpegDefines.h"
47
48#if defined(WIN32) && _MSC_VER < 1900
49#define snprintf _snprintf
50#endif
51
52// Define this to automatically resample audio to the nearest supported sample rate
53#define FFMPEG_AUTO_RESAMPLE 1
54
56{
57 int subFormat = -1;
58 for (int i = 0; i <= FMT_OTHER; i++)
59 {
60 if (ExportFFmpegOptions::fmts[i].compiledIn) subFormat++;
61 if (subFormat == format || i == FMT_OTHER)
62 {
63 subFormat = i;
64 break;
65 }
66 }
67 return subFormat;
68}
69
70namespace
71{
72
73const int iAC3SampleRates[] =
74{ 32000, 44100, 48000, 0 };
75
76const int iWMASampleRates[] =
77{ 8000, 11025, 16000, 22050, 44100, 0};
78
81{
83 int index = 0;
84 while(rates[index] != 0)
85 list.push_back(rates[index++]);
86 return list;
87}
88
89// i18n-hint kbps abbreviates "thousands of bits per second"
90TranslatableString n_kbps(int n) { return XO("%d kbps").Format( n ); }
91TranslatableString f_kbps( double d ) { return XO("%.2f kbps").Format( d ); }
92
93enum : int
94{
96};
97
98const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AC3Options {
99 {
100 {
101 AC3OptionIDBitRate, XO("Bit Rate"),
102 160000,
104 {
105 32000,
106 40000,
107 48000,
108 56000,
109 64000,
110 80000,
111 96000,
112 112000,
113 128000,
114 160000,
115 192000,
116 224000,
117 256000,
118 320000,
119 384000,
120 448000,
121 512000,
122 576000,
123 640000
124 },
125 {
126 n_kbps( 32 ),
127 n_kbps( 40 ),
128 n_kbps( 48 ),
129 n_kbps( 56 ),
130 n_kbps( 64 ),
131 n_kbps( 80 ),
132 n_kbps( 96 ),
133 n_kbps( 112 ),
134 n_kbps( 128 ),
135 n_kbps( 160 ),
136 n_kbps( 192 ),
137 n_kbps( 224 ),
138 n_kbps( 256 ),
139 n_kbps( 320 ),
140 n_kbps( 384 ),
141 n_kbps( 448 ),
142 n_kbps( 512 ),
143 n_kbps( 576 ),
144 n_kbps( 640 ),
145 }
146 }, wxT("/FileFormats/AC3BitRate")
147 }
148};
149
150enum : int
151{
154
155//NB: user-entered values for AAC are not always followed; mono is clamped to 98-160, stereo 196-320
156const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AACOptions {
157 {
158 {
159 AACOptionIDQuality, XO("Quality (kbps)"),
160 256,
162 {98, 320}
163 }, wxT("/FileFormats/AACQuality")
164 }
165};
166
167enum : int
168{
171
172const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AMRNBOptions {
173 {
174 {
175 AMRNBOptionIDBitRate, XO("Bit Rate"),
176 12200,
178 {
179 4750,
180 5150,
181 5900,
182 6700,
183 7400,
184 7950,
185 10200,
186 12200,
187 },
188 {
189 f_kbps( 4.75 ),
190 f_kbps( 5.15 ),
191 f_kbps( 5.90 ),
192 f_kbps( 6.70 ),
193 f_kbps( 7.40 ),
194 f_kbps( 7.95 ),
195 f_kbps( 10.20 ),
196 f_kbps( 12.20 ),
197 }
198 }, wxT("/FileFormats/AMRNBBitRate")
199 }
200};
201
202#ifdef SHOW_FFMPEG_OPUS_EXPORT
203enum : int
204{
206 OPUSOptionIDCompression,
211};
212
213const std::initializer_list<PlainExportOptionsEditor::OptionDesc> OPUSOptions {
214 {
215 {
216 OPUSOptionIDBitRate, XO("Bit Rate"),
217 128000,
219 {
220 6000,
221 8000,
222 16000,
223 24000,
224 32000,
225 40000,
226 48000,
227 64000,
228 80000,
229 96000,
230 128000,
231 160000,
232 192000,
233 256000
234 },
235 {
236 n_kbps( 6 ),
237 n_kbps( 8 ),
238 n_kbps( 16 ),
239 n_kbps( 24 ),
240 n_kbps( 32 ),
241 n_kbps( 40 ),
242 n_kbps( 48 ),
243 n_kbps( 64 ),
244 n_kbps( 80 ),
245 n_kbps( 96 ),
246 n_kbps( 128 ),
247 n_kbps( 160 ),
248 n_kbps( 192 ),
249 n_kbps( 256 ),
250 }
251 }, wxT("/FileFormats/OPUSBitrate")
252 },
253 {
254 {
255 OPUSOptionIDCompression, XO("Compression"),
256 10,
258 { 0, 10 }
259 }, wxT("/FileFormats/OPUSCompression")
260 },
261 {
262 {
263 OPUSOptionIDFrameDuration, XO("Frame Duration"),
264 std::string("20"),
266 {
267 std::string("2.5"),
268 std::string("5"),
269 std::string("10"),
270 std::string("20"),
271 std::string("40"),
272 std::string("60")
273 },
274 {
275 XO("2.5 ms"),
276 XO("5 ms"),
277 XO("10 ms"),
278 XO("20 ms"),
279 XO("40 ms"),
280 XO("60 ms"),
281 }
282 }, wxT("/FileFormats/OPUSFrameDuration")
283 },
284 {
285 {
286 OPUSOptionIDVBRMode, XO("Vbr Mode"),
287 std::string("on"),
289 { std::string("off"), std::string("on"), std::string("constrained") },
290 { XO("Off"), XO("On"), XO("Constrained") }
291 }, wxT("/FileFormats/OPUSVbrMode")
292 },
293 {
294 {
295 OPUSOptionIDApplication, XO("Application"),
296 std::string("audio"),
298 { std::string("voip"), std::string("audio"), std::string("lowdelay") },
299 { XO("VOIP"), XO("Audio"), XO("Low Delay") }
300 }, wxT("/FileFormats/OPUSApplication")
301 },
302 {
303 {
304 OPUSOptionIDCutoff, XO("Cutoff"),
305 std::string("0"),
307 {
308 std::string("0"),
309 std::string("4000"),
310 std::string("6000"),
311 std::string("8000"),
312 std::string("12000"),
313 std::string("20000")
314 },
315 {
316 XO("Disabled"),
317 XO("Narrowband"),
318 XO("Mediumband"),
319 XO("Wideband"),
320 XO("Super Wideband"),
321 XO("Fullband")
322 }
323 }, wxT("/FileFormats/OPUSCutoff")
324 },
325};
326#endif
327
328enum : int
329{
332
333const std::initializer_list<PlainExportOptionsEditor::OptionDesc> WMAOptions {
334 {
335 {
336 WMAOptionIDBitRate, XO("Bit Rate"),
337 128000,
339 {
340 24000,
341 32000,
342 40000,
343 48000,
344 64000,
345 80000,
346 96000,
347 128000,
348 160000,
349 192000,
350 256000,
351 320000
352 },
353 {
354 n_kbps(24),
355 n_kbps(32),
356 n_kbps(40),
357 n_kbps(48),
358 n_kbps(64),
359 n_kbps(80),
360 n_kbps(96),
361 n_kbps(128),
362 n_kbps(160),
363 n_kbps(192),
364 n_kbps(256),
365 n_kbps(320),
366 }
367 }, wxT("/FileFormats/WMABitRate")
368 }
369};
370
371const std::vector<ExportOption> FFmpegOptions {
372 { FELanguageID, {}, std::string() },
373 { FESampleRateID, {}, 0 },
374 { FEBitrateID, {}, 0 },
375 { FETagID, {}, std::string() },
376 { FEQualityID, {}, 0 },
377 { FECutoffID, {}, 0},
378 { FEBitReservoirID, {}, true },
379 { FEVariableBlockLenID, {}, true },
380 { FECompLevelID, {}, -1 },
381 { FEFrameSizeID, {}, 0 },
382 { FELPCCoeffsID, {}, 0 },
383 { FEMinPredID, {}, -1 },
384 { FEMaxPredID, {}, -1 },
385 { FEMinPartOrderID, {}, -1 },
386 { FEMaxPartOrderID, {}, -1 },
387 { FEPredOrderID, {}, 0 },
388 { FEMuxRateID, {}, 0 },
389 { FEPacketSizeID, {}, 0 },
390 { FECodecID, {}, std::string() },
391 { FEFormatID, {}, std::string() }
392};
393
395 : public ExportOptionsEditor
397{
398 std::unordered_map<int, ExportValue> mValues;
399 std::shared_ptr<FFmpegFunctions> mFFmpeg;
401 //created on-demand
402 mutable std::unique_ptr<AVCodecWrapper> mAVCodec;
403public:
404
406 : mListener(listener)
407 {
408 }
409
410 void PopulateUI(ShuttleGui& S) override
411 {
412 CheckFFmpeg(true);
413 //Continue anyway, as we do not need ffmpeg functions to build and fill in the UI
414
415 mParent = S.GetParent();
416
417 S.StartHorizontalLay(wxCENTER);
418 {
419 S.StartVerticalLay(wxCENTER, 0);
420 {
421 S.AddButton(XXO("Open custom FFmpeg format options"))
422 ->Bind(wxEVT_BUTTON, &ExportOptionsFFmpegCustomEditor::OnOpen, this);
423 S.StartMultiColumn(2, wxCENTER);
424 {
425 S.AddPrompt(XXO("Current Format:"));
426 mFormat = S.Name(XXO("Current Format:"))
427 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
428 S.AddPrompt(XXO("Current Codec:"));
429 mCodec = S.Name(XXO("Current Codec:"))
430 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
431 }
432 S.EndMultiColumn();
433 }
434 S.EndHorizontalLay();
435 }
436 S.EndHorizontalLay();
437
438 UpdateCodecAndFormat();
439 }
440
442 {
443 Load(*gPrefs);
444 return true;
445 }
446
447 int GetOptionsCount() const override
448 {
449 return static_cast<int>(FFmpegOptions.size());
450 }
451
452 bool GetOption(int index, ExportOption& option) const override
453 {
454 if(index >= 0 && index < FFmpegOptions.size())
455 {
456 option = FFmpegOptions[index];
457 return true;
458 }
459 return false;
460 }
461
462 bool GetValue(int id, ExportValue& value) const override
463 {
464 auto it = mValues.find(id);
465 if(it != mValues.end())
466 {
467 value = it->second;
468 return true;
469 }
470 return false;
471 }
472
473 bool SetValue(int id, const ExportValue& value) override
474 {
475 return false;
476 }
477
479 {
480 if(!mAVCodec)
481 {
482 auto it = mValues.find(FECodecID);
483 if(it == mValues.end())
484 return {};
485
486 const auto codecId = *std::get_if<std::string>(&it->second);
487 if (mFFmpeg) {
488 mAVCodec = mFFmpeg->CreateEncoder(codecId.c_str());
489 }
490 }
491 if(!mAVCodec)
492 return {};
493
494 if(const auto rates = mAVCodec->GetSupportedSamplerates())
495 return ToSampleRateList(rates);
496 return {};
497 }
498
499 void Load(const audacity::BasicSettings& config) override
500 {
501 mValues[FELanguageID] = std::string(config.Read(wxT("/FileFormats/FFmpegLanguage"), wxT("")).ToUTF8());
502 mValues[FESampleRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegSampleRate"), 0L));
503 mValues[FEBitrateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegBitRate"), 0L));
504 mValues[FETagID] = std::string(config.Read(wxT("/FileFormats/FFmpegTag"), wxT(""))
505 .mb_str(wxConvUTF8));
506 mValues[FEQualityID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegQuality"), -99999L));
507 mValues[FECutoffID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCutOff"), 0L));
508 mValues[FEBitReservoirID] = config.ReadBool(wxT("/FileFormats/FFmpegBitReservoir"), true);
509 mValues[FEVariableBlockLenID] = config.ReadBool(wxT("/FileFormats/FFmpegVariableBlockLen"), true);
510 mValues[FECompLevelID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCompLevel"), -1L));
511 mValues[FEFrameSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegFrameSize"), 0L));
512
513 mValues[FELPCCoeffsID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegLPCCoefPrec"), 0L));
514 mValues[FEMinPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPredOrder"), -1L));
515 mValues[FEMaxPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPredOrder"), -1L));
516 mValues[FEMinPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPartOrder"), -1L));
517 mValues[FEMaxPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPartOrder"), -1L));
518 mValues[FEPredOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPredOrderMethod"), 0L));
519 mValues[FEMuxRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMuxRate"), 0L));
520 mValues[FEPacketSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPacketSize"), 0L));
521 mValues[FECodecID] = std::string(config.Read(wxT("/FileFormats/FFmpegCodec")));
522 mValues[FEFormatID] = std::string(config.Read(wxT("/FileFormats/FFmpegFormat")));
523 }
524
526 {
527
528 }
529
530private:
531
532 bool CheckFFmpeg(bool showError)
533 {
534 // Show "Locate FFmpeg" dialog
535 if(!mFFmpeg)
536 {
537 mFFmpeg = FFmpegFunctions::Load();
538 if (!mFFmpeg)
539 {
541 return LoadFFmpeg(showError);
542 }
543 }
544 return true;
545 }
546
548 {
549 mFormat->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"), wxT("")));
550 mCodec->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegCodec"), wxT("")));
551 }
552
553 void OnOpen(const wxCommandEvent&)
554 {
555 if(!CheckFFmpeg(true))
556 return;
557
558 #ifdef __WXMAC__
559 // Bug 2077 Must be a parent window on OSX or we will appear behind.
560 auto pWin = wxGetTopLevelParent( mParent );
561 #else
562 // Use GetTopWindow on windows as there is no hWnd with top level parent.
563 auto pWin = wxTheApp->GetTopWindow();
564 #endif
565
566 ExportFFmpegOptions od(pWin);
567 od.ShowModal();
568 //ExportFFmpegOptions uses gPrefs to store options
569 //Instead we could provide it with instance of wxConfigBase
570 //constructed locally and read from it later
571 Load(*gPrefs);
572 mAVCodec.reset();
573
574 UpdateCodecAndFormat();
575 if(mListener)
576 mListener->OnSampleRateListChange();
577 }
578
579 wxWindow *mParent {nullptr};
580 wxTextCtrl *mFormat {nullptr};
581 wxTextCtrl *mCodec {nullptr};
582};
583
584}
585
587class FFmpegExporter final
588{
589 static constexpr auto MaxAudioPacketSize { 128 * 1024 };
590public:
591
592 FFmpegExporter(std::shared_ptr<FFmpegFunctions> ffmpeg,
593 const wxFileNameWrapper& filename,
594 int numChannels,
595 int subformat);
596
598 bool Init(const char *shortname,
600 int sampleRate,
601 const Tags *metadata,
602 const ExportProcessor::Parameters& parameters);
603
605 bool EncodeAudioFrame(int16_t *pFrame, size_t frameSize);
606
608 bool Finalize();
609
610 std::unique_ptr<Mixer> CreateMixer(const TrackList &tracks,
611 bool selectionOnly,
612 double startTime, double stopTime,
613 MixerOptions::Downmix *mixerSpec);
614
615private:
616
618 bool AddTags(const Tags *metadata);
619
621 void SetMetadata(const Tags *tags, const char *name, const wxChar *tag);
622
624 bool CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates);
625
627 int AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates);
628
630 bool InitCodecs(int sampleRate,
631 const ExportProcessor::Parameters& parameters);
632
633 void WritePacket(AVPacketWrapper& packet);
634
636 int16_t* audio_samples,
637 int nb_samples);
638
639 std::shared_ptr<FFmpegFunctions> mFFmpeg;
640
641 std::unique_ptr<AVOutputFormatWrapper> mEncFormatDesc; // describes our output file to libavformat
643 std::unique_ptr<AVStreamWrapper> mEncAudioStream; // the output audio stream (may remain NULL)
645
647
649 int mBitRate{};
651 unsigned mChannels{};
652 bool mSupportsUTF8{true};
653
654 // Smart pointer fields, their order is the reverse in which they are reset in FreeResources():
655 std::unique_ptr<AVFifoBufferWrapper> mEncAudioFifo; // FIFO to write incoming audio samples into
656 AVDataBuffer<int16_t> mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into
657 std::unique_ptr<AVFormatContextWrapper> mEncFormatCtx; // libavformat's context for our output file
658 std::unique_ptr<AVCodecContextWrapper> mEncAudioCodecCtx; // the encoder for the output audio stream
659};
660
662{
663 std::shared_ptr<FFmpegFunctions> mFFmpeg;
664 struct
665 {
666 //same index as in GetFormatInfo, use AdjustFormatIndex to convert it to FFmpegExposedFormat
669 double t0;
670 double t1;
671 std::unique_ptr<Mixer> mixer;
672 std::unique_ptr<FFmpegExporter> exporter;
674
675public:
676 FFmpegExportProcessor(std::shared_ptr<FFmpegFunctions> ffmpeg, int format);
677
679 const Parameters& parameters,
680 const wxFileNameWrapper& filename,
681 double t0, double t1, bool selectedOnly,
682 double sampleRate, unsigned channels,
683 MixerOptions::Downmix* mixerSpec,
684 const Tags* tags) override;
685
686 ExportResult Process(ExportProcessorDelegate& delegate) override;
687
688};
689
690class ExportFFmpeg final : public ExportPlugin
691{
692public:
693
694 ExportFFmpeg();
695 ~ExportFFmpeg() override;
696
697 std::unique_ptr<ExportOptionsEditor>
698 CreateOptionsEditor(int format, ExportOptionsEditor::Listener* listener) const override;
699
700 int GetFormatCount() const override;
701 FormatInfo GetFormatInfo(int index) const override;
702
704 bool CheckFileName(wxFileName &filename, int format = 0) const override;
705
706 std::unique_ptr<ExportProcessor> CreateProcessor(int format) const override;
707
708private:
709 mutable std::shared_ptr<FFmpegFunctions> mFFmpeg;
710
711 std::vector<FormatInfo> mFormatInfos;
712};
713
714FFmpegExporter::FFmpegExporter(std::shared_ptr<FFmpegFunctions> ffmpeg,
715 const wxFileNameWrapper& filename,
716 int numChannels,
717 int subFormat)
718 : mFFmpeg(std::move(ffmpeg))
719 , mName(filename)
720 , mChannels(numChannels)
721 , mSubFormat(subFormat)
722{
723 if (!mFFmpeg) {
725 }
726}
727
728std::unique_ptr<Mixer> FFmpegExporter::CreateMixer(const TrackList& tracks, bool selectionOnly, double startTime, double stopTime, MixerOptions::Downmix* mixerSpec)
729{
730 return ExportPluginHelpers::CreateMixer(tracks, selectionOnly,
731 startTime, stopTime,
733 mSampleRate, int16Sample, mixerSpec);
734}
735
736
738{
740
741 int avfver = mFFmpeg ? mFFmpeg->AVFormatVersion.GetIntVersion() : 0;
742
743 int newfmt;
744 // Adds export types from the export type list
745 for (newfmt = 0; newfmt < FMT_LAST; newfmt++)
746 {
747 wxString shortname(ExportFFmpegOptions::fmts[newfmt].shortname);
748 // Don't hide export types when there's no av-libs, and don't hide FMT_OTHER
749 if (newfmt < FMT_OTHER && mFFmpeg)
750 {
751 // Format/Codec support is compiled in?
752 auto avoformat = mFFmpeg->GuessOutputFormat(shortname.mb_str(), nullptr, nullptr);
753 auto avcodec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(ExportFFmpegOptions::fmts[newfmt].codecid));
754
755 if (avoformat == NULL || avcodec == NULL)
756 {
758 continue;
759 }
760 }
761 FormatInfo formatInfo {};
762 formatInfo.format = ExportFFmpegOptions::fmts[newfmt].name;
763 formatInfo.extensions.push_back(ExportFFmpegOptions::fmts[newfmt].extension);
764 // For some types add other extensions
765 switch(newfmt)
766 {
767 case FMT_M4A:
768 formatInfo.extensions.push_back(wxT("3gp"));
769 formatInfo.extensions.push_back(wxT("m4r"));
770 formatInfo.extensions.push_back(wxT("mp4"));
771 break;
772 case FMT_WMA2:
773 formatInfo.extensions.push_back(wxT("asf"));
774 formatInfo.extensions.push_back(wxT("wmv"));
775 break;
776 default:
777 break;
778 }
779 formatInfo.maxChannels = ExportFFmpegOptions::fmts[newfmt].maxchannels;
780 formatInfo.description = ExportFFmpegOptions::fmts[newfmt].description;
781
782 const int canmeta = ExportFFmpegOptions::fmts[newfmt].canmetadata;
783 formatInfo.canMetaData = canmeta && (canmeta == AV_CANMETA || canmeta <= avfver);
784
785 mFormatInfos.push_back(std::move(formatInfo));
786 }
787}
788
790
791std::unique_ptr<ExportOptionsEditor>
793{
795 {
796 case FMT_M4A:
797 return std::make_unique<PlainExportOptionsEditor>(AACOptions, listener);
798 case FMT_AC3:
799 return std::make_unique<PlainExportOptionsEditor>(
802 listener);
803 case FMT_AMRNB:
804 return std::make_unique<PlainExportOptionsEditor>(
807 listener);
808#ifdef SHOW_FFMPEG_OPUS_EXPORT
809 case FMT_OPUS:
810 return std::make_unique<PlainExportOptionsEditor>(OPUSOptions, listener);
811#endif
812 case FMT_WMA2:
813 return std::make_unique<PlainExportOptionsEditor>(
816 listener);
817 case FMT_OTHER:
818 return std::make_unique<ExportOptionsFFmpegCustomEditor>(listener);
819 }
820 return {};
821}
822
824{
825 return static_cast<int>(mFormatInfos.size());
826}
827
829{
830 if(index >= 0 && index < mFormatInfos.size())
831 return mFormatInfos[index];
832 return mFormatInfos[FMT_OTHER];
833}
834
835bool ExportFFmpeg::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format)) const
836{
837 bool result = true;
838
839 // Show "Locate FFmpeg" dialog
841 if (!mFFmpeg)
842 {
845
846 return LoadFFmpeg(true);
847 }
848
849 return result;
850}
851
852std::unique_ptr<ExportProcessor> ExportFFmpeg::CreateProcessor(int format) const
853{
854 return std::make_unique<FFmpegExportProcessor>(mFFmpeg, format);
855}
856
857
858bool FFmpegExporter::Init(const char *shortname,
860 int sampleRate,
861 const Tags *metadata,
862 const ExportProcessor::Parameters& parameters)
863{
864 if (!mFFmpeg)
865 return false;
866
867 // See if libavformat has modules that can write our output format. If so, mEncFormatDesc
868 // will describe the functions used to write the format (used internally by libavformat)
869 // and the default video/audio codecs that the format uses.
870 const auto path = mName.GetFullPath();
871 if ((mEncFormatDesc = mFFmpeg->GuessOutputFormat(shortname, OSINPUT(path), nullptr)) == nullptr)
872 {
873 throw ExportException(_("FFmpeg : ERROR - Can't determine format description for file \"%s\".").Format(path));
874 }
875
876 // mEncFormatCtx is used by libavformat to carry around context data re our output file.
877 mEncFormatCtx = mFFmpeg->CreateAVFormatContext();
878 if (!mEncFormatCtx)
879 {
880 throw ExportException(_("FFmpeg : ERROR - Can't allocate output format context."));
881 }
882
883 // Initialise the output format context.
884 mEncFormatCtx->SetOutputFormat(mFFmpeg->CreateAVOutputFormatWrapper(mEncFormatDesc->GetWrappedValue()));
885 mEncFormatCtx->SetFilename(OSINPUT(path));
886
887 // At the moment Audacity can export only one audio stream
888 if ((mEncAudioStream = mEncFormatCtx->CreateStream()) == nullptr)
889 {
890 throw ExportException(_("FFmpeg : ERROR - Can't add audio stream to output file \"%s\"."));
891 }
892
893 // Documentation for avformat_new_stream says
894 // "User is required to call avcodec_close() and avformat_free_context() to clean
895 // up the allocation by avformat_new_stream()."
896
897 // We use smart pointers that ensure these cleanups either in their destructors or
898 // sooner if they are reset. These are std::unique_ptr with nondefault deleter
899 // template parameters.
900
901 // mEncFormatCtx takes care of avformat_free_context(), so
902 // mEncAudioStream can be a plain pointer.
903
904 // mEncAudioCodecCtx now becomes responsible for closing the codec:
905 mEncAudioCodecCtx = mEncAudioStream->GetAVCodecContext();
906 mEncAudioStream->SetId(0);
907
908 // Open the output file.
909 if (!(mEncFormatDesc->GetFlags() & AUDACITY_AVFMT_NOFILE))
910 {
912 mEncFormatCtx->OpenOutputContext(path);
913
915 {
916 throw ExportException(_("FFmpeg : ERROR - Can't open output file \"%s\" to write. Error code is %d.")
917 .Format(path, static_cast<int>(result)));
918 }
919 }
920
921 // Open the audio stream's codec and initialise any stream related data.
922 if(!InitCodecs(sampleRate, parameters))
923 return false;
924
925 if (mEncAudioStream->SetParametersFromContext(*mEncAudioCodecCtx) < 0)
926 return false;
927
928 if (metadata == NULL)
929 metadata = &Tags::Get( *project );
930
931 // Add metadata BEFORE writing the header.
932 // At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4.
934 const auto avfver = mFFmpeg->AVFormatVersion.GetIntVersion();
935 if (canmeta && (canmeta == AV_CANMETA || canmeta <= avfver))
936 {
938 AddTags(metadata);
939 }
940
941 // Write headers to the output file.
942 int err =
943 mFFmpeg->avformat_write_header(mEncFormatCtx->GetWrappedValue(), nullptr);
944
945 if (err < 0)
946 {
947 throw ExportException(XO("FFmpeg : ERROR - Can't write headers to output file \"%s\". Error code is %d.")
948 .Format( path, err )
949 .Translation());
950 }
951
952 return true;
953}
954
955bool FFmpegExporter::CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates)
956{
957 if (lowrate && highrate)
958 {
959 if (rate < lowrate || rate > highrate)
960 {
961 return false;
962 }
963 }
964
965 if (sampRates)
966 {
967 for (int i = 0; sampRates[i] > 0; i++)
968 {
969 if (rate == sampRates[i])
970 {
971 return true;
972 }
973 }
974 }
975
976 return false;
977}
978
980 const ExportProcessor::Parameters& parameters)
981{
982 std::unique_ptr<AVCodecWrapper> codec;
983
985
986 // Get the sample rate from the passed settings if we haven't set it before.
987 // Doing this only when not set allows us to carry the sample rate from one
988 // iteration of ExportMultiple to the next. This prevents multiple resampling
989 // dialogs in the event the codec can't support the specified rate.
990 if (!mSampleRate)
991 {
992 //TODO: Does not work with export multiple any more...
994 }
995
996 // Configure the audio stream's codec context.
997
998 const auto codecID = ExportFFmpegOptions::fmts[mSubFormat].codecid;
999
1000 mEncAudioCodecCtx->SetGlobalQuality(-99999); //quality mode is off by default;
1001
1002 // Each export type has its own settings
1003 switch (mSubFormat)
1004 {
1005 case FMT_M4A:
1006 {
1008
1009 q = wxClip( q, 98 * mChannels, 160 * mChannels );
1010 // Set bit rate to between 98 kbps and 320 kbps (if two channels)
1011 mEncAudioCodecCtx->SetBitRate(q * 1000);
1013 mEncAudioCodecCtx->SetCutoff(0);
1014
1015 break;
1016 }
1017 case FMT_AC3:
1019 if (!CheckSampleRate(
1021 iAC3SampleRates[2],
1022 &iAC3SampleRates[0]))
1023 {
1025 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1026 iAC3SampleRates[0],
1027 iAC3SampleRates[2],
1028 &iAC3SampleRates[0]);
1029 }
1030 break;
1031 case FMT_AMRNB:
1032 mSampleRate = 8000;
1034 break;
1035#ifdef SHOW_FFMPEG_OPUS_EXPORT
1036 case FMT_OPUS:
1037 options.Set("b", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDBitRate, "128000"), 0);
1038 options.Set("vbr", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDVBRMode, "on"), 0);
1039 options.Set("compression_level", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCompression, "10"), 0);
1040 options.Set("frame_duration", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDFrameDuration, "20"), 0);
1041 options.Set("application", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDApplication, "audio"), 0);
1042 options.Set("cutoff", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCutoff, "0"), 0);
1043 options.Set("mapping_family", mChannels <= 2 ? "0" : "255", 0);
1044 break;
1045#endif
1046 case FMT_WMA2:
1048 if (!CheckSampleRate(
1050 iWMASampleRates[4],
1051 &iWMASampleRates[0]))
1052 {
1054 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1055 iWMASampleRates[0],
1056 iWMASampleRates[4],
1057 &iWMASampleRates[0]);
1058 }
1059 break;
1060 case FMT_OTHER:
1061 {
1062 AVDictionaryWrapper streamMetadata = mEncAudioStream->GetMetadata();
1063 streamMetadata.Set(
1064 "language",
1065 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FELanguageID), 0);
1066
1067 mEncAudioStream->SetMetadata(streamMetadata);
1068
1069 mEncAudioCodecCtx->SetSampleRate(
1070 ExportPluginHelpers::GetParameterValue(parameters, FESampleRateID, 0));
1071
1072 if (mEncAudioCodecCtx->GetSampleRate() != 0)
1073 mSampleRate = mEncAudioCodecCtx->GetSampleRate();
1074
1075 mEncAudioCodecCtx->SetBitRate(
1076 ExportPluginHelpers::GetParameterValue(parameters, FEBitrateID, 0));
1077
1078 mEncAudioCodecCtx->SetCodecTagFourCC(
1079 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FETagID).c_str());
1080
1081 mEncAudioCodecCtx->SetGlobalQuality(
1082 ExportPluginHelpers::GetParameterValue(parameters, FEQualityID, -99999));
1083 mEncAudioCodecCtx->SetCutoff(
1084 ExportPluginHelpers::GetParameterValue(parameters, FECutoffID, 0));
1085 mEncAudioCodecCtx->SetFlags2(0);
1086
1087 if (ExportPluginHelpers::GetParameterValue(parameters, FEBitReservoirID, true))
1088 options.Set("reservoir", "1", 0);
1089
1090 if (ExportPluginHelpers::GetParameterValue(parameters, FEVariableBlockLenID, true))
1091 mEncAudioCodecCtx->SetFlags2(
1092 mEncAudioCodecCtx->GetFlags2() | 0x0004); // WMA only?
1093
1094 mEncAudioCodecCtx->SetCompressionLevel(
1095 ExportPluginHelpers::GetParameterValue(parameters, FECompLevelID, -1));
1096 mEncAudioCodecCtx->SetFrameSize(
1097 ExportPluginHelpers::GetParameterValue(parameters, FEFrameSizeID, 0));
1098
1099 // FIXME The list of supported options for the selected encoder should be
1100 // extracted instead of a few hardcoded
1101
1102 options.Set(
1103 "lpc_coeff_precision",
1104 ExportPluginHelpers::GetParameterValue(parameters, FELPCCoeffsID, 0));
1105 options.Set(
1106 "min_prediction_order",
1107 ExportPluginHelpers::GetParameterValue(parameters, FEMinPredID, -1));
1108 options.Set(
1109 "max_prediction_order",
1110 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPredID, -1));
1111 options.Set(
1112 "min_partition_order",
1113 ExportPluginHelpers::GetParameterValue(parameters, FEMinPartOrderID, -1));
1114 options.Set(
1115 "max_partition_order",
1116 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPartOrderID, -1));
1117 options.Set(
1118 "prediction_order_method",
1119 ExportPluginHelpers::GetParameterValue(parameters, FEPredOrderID, 0));
1120 options.Set(
1121 "muxrate",
1122 ExportPluginHelpers::GetParameterValue(parameters, FEMuxRateID, 0));
1123
1124 mEncFormatCtx->SetPacketSize(
1125 ExportPluginHelpers::GetParameterValue(parameters, FEPacketSizeID, 0));
1126
1127 codec = mFFmpeg->CreateEncoder(
1128 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FECodecID).c_str());
1129
1130 if (!codec)
1131 codec = mFFmpeg->CreateEncoder(mEncFormatDesc->GetAudioCodec());
1132 }
1133 break;
1134 default:
1135 return false;
1136 }
1137
1138 // This happens if user refused to resample the project
1139 if (mSampleRate == 0) return false;
1140
1141 if (mEncAudioCodecCtx->GetGlobalQuality() >= 0)
1142 {
1143 mEncAudioCodecCtx->SetFlags(
1145 }
1146 else
1147 {
1148 mEncAudioCodecCtx->SetGlobalQuality(0);
1149 }
1150
1151 mEncAudioCodecCtx->SetGlobalQuality(mEncAudioCodecCtx->GetGlobalQuality() * AUDACITY_FF_QP2LAMBDA);
1152 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1153 mEncAudioCodecCtx->SetChannels(mChannels);
1154 mEncAudioCodecCtx->SetChannelLayout(mFFmpeg->av_get_default_channel_layout(mChannels));
1155 mEncAudioCodecCtx->SetTimeBase({ 1, mSampleRate });
1157 mEncAudioCodecCtx->SetStrictStdCompliance(
1159
1160 if (codecID == AUDACITY_AV_CODEC_ID_AC3)
1161 {
1162 // As of Jan 4, 2011, the default AC3 encoder only accept SAMPLE_FMT_FLT samples.
1163 // But, currently, Audacity only supports SAMPLE_FMT_S16. So, for now, look for the
1164 // "older" AC3 codec. this is not a proper solution, but will suffice until other
1165 // encoders no longer support SAMPLE_FMT_S16.
1166 codec = mFFmpeg->CreateEncoder("ac3_fixed");
1167 }
1168
1169 if (!codec)
1170 {
1171 codec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(codecID));
1172 }
1173
1174 // Is the required audio codec compiled into libavcodec?
1175 if (codec == NULL)
1176 {
1177 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1178 throw ExportException(XO("FFmpeg cannot find audio codec 0x%x.\nSupport for this codec is probably not compiled in.")
1179 .Format(static_cast<const unsigned int>(codecID.value))
1180 .Translation());
1181 }
1182
1183 if (codec->GetSampleFmts()) {
1184 for (int i = 0; codec->GetSampleFmts()[i] != AUDACITY_AV_SAMPLE_FMT_NONE; i++)
1185 {
1186 AVSampleFormatFwd fmt = codec->GetSampleFmts()[i];
1187
1188 if (
1197 {
1198 mEncAudioCodecCtx->SetSampleFmt(fmt);
1199 }
1200
1201 if (
1204 break;
1205 }
1206 }
1207
1208 if (codec->GetSupportedSamplerates())
1209 {
1210 // Workaround for crash in bug #2378. Proper fix is to get a newer version of FFmpeg.
1211 if (codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_AAC))
1212 {
1213 std::vector<int> rates;
1214 int i = 0;
1215
1216 while (codec->GetSupportedSamplerates()[i] &&
1217 codec->GetSupportedSamplerates()[i] != 7350)
1218 {
1219 rates.push_back(codec->GetSupportedSamplerates()[i++]);
1220 }
1221
1222 rates.push_back(0);
1223
1224 if (!CheckSampleRate(mSampleRate, 0, 0, rates.data()))
1225 {
1226 mSampleRate = AskResample(0, mSampleRate, 0, 0, rates.data());
1227 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1228 }
1229 }
1230 else
1231 {
1232 if (!CheckSampleRate(
1233 mSampleRate, 0, 0, codec->GetSupportedSamplerates()))
1234 {
1236 0, mSampleRate, 0, 0, codec->GetSupportedSamplerates());
1237 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1238 }
1239 }
1240
1241 // This happens if user refused to resample the project
1242 if (mSampleRate == 0)
1243 {
1244 return false;
1245 }
1246 }
1247
1248 if (mEncFormatCtx->GetOutputFormat()->GetFlags() & AUDACITY_AVFMT_GLOBALHEADER)
1249 {
1252 }
1253
1254 // Open the codec.
1255 int rc = mEncAudioCodecCtx->Open(codec.get(), &options);
1256 if (rc < 0)
1257 {
1258 TranslatableString errmsg;
1259
1260 switch (rc)
1261 {
1262 case AUDACITY_AVERROR(EPERM):
1263 errmsg = XO("The codec reported a generic error (EPERM)");
1264 break;
1265 case AUDACITY_AVERROR(EINVAL):
1266 errmsg = XO("The codec reported an invalid parameter (EINVAL)");
1267 break;
1268 default:
1269 char buf[64];
1270 mFFmpeg->av_strerror(rc, buf, sizeof(buf));
1271 errmsg = Verbatim(buf);
1272 }
1273
1274 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1275 throw ExportException(XO("Can't open audio codec \"%s\" (0x%x)\n\n%s")
1276 .Format(codec->GetName(), codecID.value, errmsg)
1277 .Translation());
1278 }
1279
1280 mDefaultFrameSize = mEncAudioCodecCtx->GetFrameSize();
1281
1282 if (mDefaultFrameSize == 0)
1283 mDefaultFrameSize = 1024; // arbitrary non zero value;
1284
1285 wxLogDebug(
1286 wxT("FFmpeg : Audio Output Codec Frame Size: %d samples."),
1287 mEncAudioCodecCtx->GetFrameSize());
1288
1289 // The encoder may require a minimum number of raw audio samples for each encoding but we can't
1290 // guarantee we'll get this minimum each time an audio frame is decoded from the input file so
1291 // we use a FIFO to store up incoming raw samples until we have enough for one call to the codec.
1292 mEncAudioFifo = mFFmpeg->CreateFifoBuffer(mDefaultFrameSize);
1293
1295 // Allocate a buffer to read OUT of the FIFO into. The FIFO maintains its own buffer internally.
1296 mEncAudioFifoOutBuf = mFFmpeg->CreateMemoryBuffer<int16_t>(mEncAudioFifoOutBufSize);
1297
1298 if (mEncAudioFifoOutBuf.empty())
1299 {
1300 throw ExportException(_("FFmpeg : ERROR - Can't allocate buffer to read into from audio FIFO."));
1301 }
1302
1303 return true;
1304}
1305
1307{
1308 // Set presentation time of frame (currently in the codec's timebase) in the
1309 // stream timebase.
1312 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1313
1316 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1317
1318 if (pkt.GetDuration() > 0)
1319 pkt.RescaleDuration(
1320 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1321
1322 if (
1323 mFFmpeg->av_interleaved_write_frame(
1324 mEncFormatCtx->GetWrappedValue(), pkt.GetWrappedValue()) != 0)
1325 {
1326 throw ExportException(_("FFmpeg : ERROR - Couldn't write audio frame to output file."));
1327 }
1328}
1329
1330// Returns 0 if no more output, 1 if more output, negative if error
1331int FFmpegExporter::EncodeAudio(AVPacketWrapper& pkt, int16_t* audio_samples, int nb_samples)
1332{
1333 // Assume *pkt is already initialized.
1334
1335 int i, ch, buffer_size, ret, got_output = 0;
1336 AVDataBuffer<uint8_t> samples;
1337
1338 std::unique_ptr<AVFrameWrapper> frame;
1339
1340 if (audio_samples) {
1341 frame = mFFmpeg->CreateAVFrameWrapper();
1342
1343 if (!frame)
1344 return AUDACITY_AVERROR(ENOMEM);
1345
1346 frame->SetSamplesCount(nb_samples);
1347 frame->SetFormat(mEncAudioCodecCtx->GetSampleFmt());
1348 frame->SetChannelLayout(mEncAudioCodecCtx->GetChannelLayout());
1349
1350 buffer_size = mFFmpeg->av_samples_get_buffer_size(
1351 NULL, mEncAudioCodecCtx->GetChannels(), nb_samples,
1352 mEncAudioCodecCtx->GetSampleFmt(), 0);
1353
1354 if (buffer_size < 0) {
1355 throw ExportException(_("FFmpeg : ERROR - Could not get sample buffer size"));
1356 }
1357
1358 samples = mFFmpeg->CreateMemoryBuffer<uint8_t>(buffer_size);
1359
1360 if (samples.empty()) {
1361 throw ExportException(_("FFmpeg : ERROR - Could not allocate bytes for samples buffer"));
1362 }
1363 /* setup the data pointers in the AVFrame */
1364 ret = mFFmpeg->avcodec_fill_audio_frame(
1365 frame->GetWrappedValue(), mEncAudioCodecCtx->GetChannels(),
1366 mEncAudioCodecCtx->GetSampleFmt(), samples.data(), buffer_size, 0);
1367
1368 if (ret < 0) {
1369 throw ExportException(_("FFmpeg : ERROR - Could not setup audio frame"));
1370 }
1371
1372 const int channelsCount = mEncAudioCodecCtx->GetChannels();
1373
1374 for (ch = 0; ch < mEncAudioCodecCtx->GetChannels(); ch++)
1375 {
1376 for (i = 0; i < nb_samples; i++) {
1377 switch (static_cast<AudacityAVSampleFormat>(
1378 mEncAudioCodecCtx->GetSampleFmt()))
1379 {
1381 ((uint8_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount]/258 + 128;
1382 break;
1384 ((uint8_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount]/258 + 128;
1385 break;
1387 ((int16_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount];
1388 break;
1390 ((int16_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount];
1391 break;
1393 ((int32_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount]<<16;
1394 break;
1396 ((int32_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount]<<16;
1397 break;
1399 ((float*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount] / 32767.0;
1400 break;
1402 ((float*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount] / 32767.;
1403 break;
1404 default:
1405 wxASSERT(false);
1406 break;
1407 }
1408 }
1409 }
1410 }
1411
1412 pkt.ResetData();
1413
1414 pkt.SetStreamIndex(mEncAudioStream->GetIndex());
1415
1416 if (mFFmpeg->avcodec_send_frame != nullptr)
1417 {
1418 ret = mFFmpeg->avcodec_send_frame(
1419 mEncAudioCodecCtx->GetWrappedValue(),
1420 frame ? frame->GetWrappedValue() : nullptr);
1421
1422 while (ret >= 0)
1423 {
1424 ret = mFFmpeg->avcodec_receive_packet(
1425 mEncAudioCodecCtx->GetWrappedValue(), pkt.GetWrappedValue());
1426
1427 if (ret == AUDACITY_AVERROR(EAGAIN) || ret == AUDACITY_AVERROR_EOF)
1428 {
1429 ret = 0;
1430 break;
1431 }
1432 else if (ret < 0)
1433 break;
1434
1435 WritePacket(pkt);
1436
1437 got_output = true;
1438 }
1439 }
1440 else
1441 {
1442 ret = mFFmpeg->avcodec_encode_audio2(
1443 mEncAudioCodecCtx->GetWrappedValue(), pkt.GetWrappedValue(),
1444 frame ? frame->GetWrappedValue() : nullptr, &got_output);
1445
1446 if (ret == 0)
1447 {
1448 WritePacket(pkt);
1449 }
1450 }
1451
1452 if (ret < 0 && ret != AUDACITY_AVERROR_EOF) {
1453
1454 char buf[64];
1455 mFFmpeg->av_strerror(ret, buf, sizeof(buf));
1456 wxLogDebug(buf);
1457
1458 throw ExportException(_("FFmpeg : ERROR - encoding frame failed"));
1459 }
1460
1461 pkt.ResetTimestamps(); // We don't set frame timestamps thus don't trust the AVPacket timestamps
1462
1463 return got_output;
1464}
1465
1466
1468{
1469 // Flush the audio FIFO and encoder.
1470 for (;;)
1471 {
1472 std::unique_ptr<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();
1473
1474 const int nFifoBytes = mFFmpeg->av_fifo_size(
1475 mEncAudioFifo->GetWrappedValue()); // any bytes left in audio FIFO?
1476
1477 int encodeResult = 0;
1478
1479 // Flush the audio FIFO first if necessary. It won't contain a _full_ audio frame because
1480 // if it did we'd have pulled it from the FIFO during the last encodeAudioFrame() call
1481 if (nFifoBytes > 0)
1482 {
1483 const int nAudioFrameSizeOut = mDefaultFrameSize * mEncAudioCodecCtx->GetChannels() * sizeof(int16_t);
1484
1485 if (nAudioFrameSizeOut > mEncAudioFifoOutBufSize || nFifoBytes > mEncAudioFifoOutBufSize) {
1486 throw ExportException(_("FFmpeg : ERROR - Too much remaining data."));
1487 }
1488
1489 // We have an incomplete buffer of samples left, encode it.
1490 // If codec supports CODEC_CAP_SMALL_LAST_FRAME, we can feed it with smaller frame
1491 // Or if frame_size is 1, then it's some kind of PCM codec, they don't have frames and will be fine with the samples
1492 // Otherwise we'll send a full frame of audio + silence padding to ensure all audio is encoded
1493 int frame_size = mDefaultFrameSize;
1494 if (
1495 mEncAudioCodecCtx->GetCodec()->GetCapabilities() &
1497 frame_size == 1)
1498 {
1499 frame_size = nFifoBytes /
1500 (mEncAudioCodecCtx->GetChannels() * sizeof(int16_t));
1501 }
1502
1503 wxLogDebug(wxT("FFmpeg : Audio FIFO still contains %d bytes, writing %d sample frame ..."),
1504 nFifoBytes, frame_size);
1505
1506 // Fill audio buffer with zeroes. If codec tries to read the whole buffer,
1507 // it will just read silence. If not - who cares?
1509 //const AVCodec *codec = mEncAudioCodecCtx->codec;
1510
1511 // Pull the bytes out from the FIFO and feed them to the encoder.
1512 if (mFFmpeg->av_fifo_generic_read(mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(), nFifoBytes, nullptr) == 0)
1513 {
1514 encodeResult = EncodeAudio(*pkt, mEncAudioFifoOutBuf.data(), frame_size);
1515 }
1516 else
1517 {
1518 wxLogDebug(wxT("FFmpeg : Reading from Audio FIFO failed, aborting"));
1519 // TODO: more precise message
1520 throw ExportErrorException("FFmpeg:825");
1521 }
1522 }
1523 else
1524 {
1525 // Fifo is empty, flush encoder. May be called multiple times.
1526 encodeResult =
1527 EncodeAudio(*pkt.get(), nullptr, 0);
1528 }
1529
1530 if (encodeResult < 0) {
1531 // TODO: more precise message
1532 throw ExportErrorException("FFmpeg:837");
1533 }
1534 else if (encodeResult == 0)
1535 break;
1536 }
1537
1538 // Write any file trailers.
1539 if (mFFmpeg->av_write_trailer(mEncFormatCtx->GetWrappedValue()) != 0)
1540 {
1541 // TODO: more precise message
1542 throw ExportErrorException("FFmpeg:868");
1543 }
1544
1545 return true;
1546}
1547
1548// All paths in this that fail must report their error to the user.
1549bool FFmpegExporter::EncodeAudioFrame(int16_t *pFrame, size_t numSamples)
1550{
1551 const auto frameSize = numSamples * sizeof(int16_t) * mChannels;
1552 int nBytesToWrite = 0;
1553 uint8_t *pRawSamples = nullptr;
1554 int nAudioFrameSizeOut = mDefaultFrameSize * mEncAudioCodecCtx->GetChannels() * sizeof(int16_t);
1555 int ret;
1556
1557 nBytesToWrite = frameSize;
1558 pRawSamples = (uint8_t*)pFrame;
1559 if (mFFmpeg->av_fifo_realloc2(mEncAudioFifo->GetWrappedValue(), mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) + frameSize) < 0) {
1560 throw ExportErrorException("FFmpeg:905");
1561 }
1562
1563 // Put the raw audio samples into the FIFO.
1564 ret = mFFmpeg->av_fifo_generic_write(
1565 mEncAudioFifo->GetWrappedValue(), pRawSamples, nBytesToWrite, nullptr);
1566
1567 if (ret != nBytesToWrite) {
1568 throw ExportErrorException("FFmpeg:913");
1569 }
1570
1571 if (nAudioFrameSizeOut > mEncAudioFifoOutBufSize) {
1572 throw ExportException(_("FFmpeg : ERROR - nAudioFrameSizeOut too large."));
1573 }
1574
1575 // Read raw audio samples out of the FIFO in nAudioFrameSizeOut byte-sized groups to encode.
1576 while (mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) >= nAudioFrameSizeOut)
1577 {
1578 ret = mFFmpeg->av_fifo_generic_read(
1579 mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(),
1580 nAudioFrameSizeOut, nullptr);
1581
1582 std::unique_ptr<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();
1583
1584 ret = EncodeAudio(*pkt, // out
1585 mEncAudioFifoOutBuf.data(), // in
1587
1588 if (ret < 0)
1589 return false;
1590 }
1591 return true;
1592}
1593
1594FFmpegExportProcessor::FFmpegExportProcessor(std::shared_ptr<FFmpegFunctions> ffmpeg, int subformat )
1595 : mFFmpeg(std::move(ffmpeg))
1596{
1597 context.subformat = subformat;
1598}
1599
1601 const Parameters& parameters,
1602 const wxFileNameWrapper& fName,
1603 double t0, double t1, bool selectionOnly,
1604 double sampleRate, unsigned channels,
1605 MixerOptions::Downmix* mixerSpec,
1606 const Tags* metadata)
1607{
1608 context.t0 = t0;
1609 context.t1 = t1;
1610
1611 if (!FFmpegFunctions::Load())
1612 {
1613 throw ExportException(_("Properly configured FFmpeg is required to proceed.\nYou can configure it at Preferences > Libraries."));
1614 }
1615 // subformat index may not correspond directly to fmts[] index, convert it
1616 const auto adjustedFormatIndex = AdjustFormatIndex(context.subformat);
1617 if (channels > ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels)
1618 {
1619 throw ExportException(XO("Attempted to export %d channels, but maximum number of channels for selected output format is %d")
1620 .Format(
1621 channels,
1622 ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels )
1623 .Translation());
1624 }
1625
1626 const auto &tracks = TrackList::Get( project );
1627 bool ret = true;
1628
1629 if (adjustedFormatIndex >= FMT_LAST) {
1630 // TODO: more precise message
1631 throw ExportErrorException("FFmpeg:996");
1632 }
1633
1634 wxString shortname(ExportFFmpegOptions::fmts[adjustedFormatIndex].shortname);
1635 if (adjustedFormatIndex == FMT_OTHER)
1636 shortname = ExportPluginHelpers::GetParameterValue<std::string>(parameters, FEFormatID, "matroska");
1637
1638 context.exporter = std::make_unique<FFmpegExporter>(mFFmpeg, fName, channels, adjustedFormatIndex);
1639
1640 ret = context.exporter->Init(shortname.mb_str(), &project, static_cast<int>(sampleRate), metadata, parameters);
1641
1642 if (!ret) {
1643 // TODO: more precise message
1644 throw ExportErrorException("FFmpeg:1008");
1645 }
1646
1647 context.mixer = context.exporter->CreateMixer(tracks, selectionOnly,
1648 t0, t1,
1649 mixerSpec);
1650
1651 context.status = selectionOnly
1652 ? XO("Exporting selected audio as %s")
1653 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description )
1654 : XO("Exporting the audio as %s")
1655 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description );
1656
1657 return true;
1658}
1659
1661{
1662 delegate.SetStatusString(context.status);
1663 auto exportResult = ExportResult::Success;
1664 {
1665 while (exportResult == ExportResult::Success) {
1666 auto pcmNumSamples = context.mixer->Process();
1667 if (pcmNumSamples == 0)
1668 break;
1669
1670 short *pcmBuffer = (short *)context.mixer->GetBuffer();
1671
1672 if (!context.exporter->EncodeAudioFrame(pcmBuffer, pcmNumSamples))
1673 // All errors should already have been reported.
1674 return ExportResult::Error;
1675
1676 if(exportResult == ExportResult::Success)
1678 delegate, *context.mixer, context.t0, context.t1);
1679 }
1680 }
1681
1682 if ( exportResult != ExportResult::Cancelled )
1683 if ( !context.exporter->Finalize() ) // Finalize makes its own messages
1684 return ExportResult::Error;
1685 return exportResult;
1686}
1687
1688
1689void AddStringTagUTF8(char field[], int size, wxString value)
1690{
1691 memset(field,0,size);
1692 memcpy(field,value.ToUTF8(),(int)strlen(value.ToUTF8()) > size -1 ? size -1 : strlen(value.ToUTF8()));
1693}
1694
1695void AddStringTagANSI(char field[], int size, wxString value)
1696{
1697 memset(field,0,size);
1698 memcpy(field,value.mb_str(),(int)strlen(value.mb_str()) > size -1 ? size -1 : strlen(value.mb_str()));
1699}
1700
1702{
1703 if (tags == NULL)
1704 {
1705 return false;
1706 }
1707
1708 SetMetadata(tags, "album", TAG_ALBUM);
1709 SetMetadata(tags, "comment", TAG_COMMENTS);
1710 SetMetadata(tags, "genre", TAG_GENRE);
1711 SetMetadata(tags, "title", TAG_TITLE);
1712 SetMetadata(tags, "track", TAG_TRACK);
1713
1714 // Bug 2564: Add m4a tags
1715 if (mEncFormatDesc->GetAudioCodec() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_AAC))
1716 {
1717 SetMetadata(tags, "artist", TAG_ARTIST);
1718 SetMetadata(tags, "date", TAG_YEAR);
1719 }
1720 else
1721 {
1722 SetMetadata(tags, "author", TAG_ARTIST);
1723 SetMetadata(tags, "year", TAG_YEAR);
1724 }
1725
1726 return true;
1727}
1728
1729void FFmpegExporter::SetMetadata(const Tags *tags, const char *name, const wxChar *tag)
1730{
1731 if (tags->HasTag(tag))
1732 {
1733 wxString value = tags->GetTag(tag);
1734
1735 AVDictionaryWrapper metadata = mEncFormatCtx->GetMetadata();
1736
1737 metadata.Set(name, mSupportsUTF8 ? value : value.mb_str(), 0);
1738 mEncFormatCtx->SetMetadata(metadata);
1739 }
1740}
1741
1742
1743//----------------------------------------------------------------------------
1744// AskResample dialog
1745//----------------------------------------------------------------------------
1746
1747int FFmpegExporter::AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates)
1748{
1749#if defined(FFMPEG_AUTO_RESAMPLE)
1750 std::vector<int> rates;
1751
1752 for (int i = 0; sampRates[i]; ++i)
1753 {
1754 rates.push_back(sampRates[i]);
1755 }
1756
1757 std::sort(rates.begin(), rates.end());
1758
1759 int bestRate = 0;
1760 for (auto i : rates)
1761 {
1762 bestRate = i;
1763 if (i > rate)
1764 {
1765 break;
1766 }
1767 }
1768
1769 return bestRate;
1770#else
1771 wxDialogWrapper d(nullptr, wxID_ANY, XO("Invalid sample rate"));
1772 d.SetName();
1773 wxChoice *choice;
1775
1776 int selected = -1;
1777
1778 S.StartVerticalLay();
1779 {
1780 S.SetBorder(10);
1781 S.StartStatic(XO("Resample"));
1782 {
1783 S.StartHorizontalLay(wxALIGN_CENTER, false);
1784 {
1785 S.AddTitle(
1786 (bitrate == 0
1787 ? XO(
1788"The project sample rate (%d) is not supported by the current output\nfile format. ")
1789 .Format( rate )
1790 : XO(
1791"The project sample rate (%d) and bit rate (%d kbps) combination is not\nsupported by the current output file format. ")
1792 .Format( rate, bitrate/1000))
1793 + XO("You may resample to one of the rates below.")
1794 );
1795 }
1796 S.EndHorizontalLay();
1797
1798 S.StartHorizontalLay(wxALIGN_CENTER, false);
1799 {
1800 choice = S.AddChoice(XO("Sample Rates"),
1801 [&]{
1802 TranslatableStrings choices;
1803 for (int i = 0; sampRates[i] > 0; i++)
1804 {
1805 int label = sampRates[i];
1806 if ((!lowrate || label >= lowrate) && (!highrate || label <= highrate))
1807 {
1808 wxString name = wxString::Format(wxT("%d"),label);
1809 choices.push_back( Verbatim( name ) );
1810 if (label <= rate)
1811 selected = i;
1812 }
1813 }
1814 return choices;
1815 }(),
1816 std::max( 0, selected )
1817 );
1818 }
1819 S.EndHorizontalLay();
1820 }
1821 S.EndStatic();
1822
1823 S.AddStandardButtons();
1824 }
1825 S.EndVerticalLay();
1826
1827 d.Layout();
1828 d.Fit();
1829 d.SetMinSize(d.GetSize());
1830 d.Center();
1831
1832 if (d.ShowModal() == wxID_CANCEL) {
1833 return 0;
1834 }
1835
1836 return wxAtoi(choice->GetStringSelection());
1837#endif
1838}
1839
1841 []{ return std::make_unique< ExportFFmpeg >(); }
1842};
1843
@ AUDACITY_AV_CODEC_ID_AC3
Definition: AVCodecID.h:297
@ AUDACITY_AV_CODEC_ID_AAC
Definition: AVCodecID.h:296
wxT("CloseDown"))
const TranslatableString name
Definition: Distortion.cpp:76
void AddStringTagUTF8(char field[], int size, wxString value)
static ExportPluginRegistry::RegisteredPlugin sRegisteredPlugin
void AddStringTagANSI(char field[], int size, wxString value)
static int AdjustFormatIndex(int format)
@ FMT_M4A
@ FMT_OTHER
@ FMT_AC3
@ FMT_WMA2
@ FMT_AMRNB
@ FMT_LAST
static const std::vector< int > sampRates
Definition: ExportMP3.cpp:201
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:123
std::variant< bool, int, double, std::string > ExportValue
A type of option values (parameters) used by exporting plugins.
Definition: ExportTypes.h:38
ExportResult
Definition: ExportTypes.h:24
bool FindFFmpegLibs(wxWindow *parent)
Definition: FFmpeg.cpp:303
bool LoadFFmpeg(bool showerror)
Definition: FFmpeg.cpp:39
#define AV_CANMETA
Definition: FFmpegDefines.h:20
std::vector< T, AVAllocator< T > > AVDataBuffer
#define AUDACITY_FF_PROFILE_AAC_LOW
Definition: FFmpegTypes.h:103
#define AUDACITY_AV_CODEC_CAP_SMALL_LAST_FRAME
Definition: FFmpegTypes.h:84
#define AUDACITY_AV_CODEC_FLAG_GLOBAL_HEADER
Definition: FFmpegTypes.h:115
#define AUDACITY_AVERROR(e)
Definition: FFmpegTypes.h:28
#define AUDACITY_FF_COMPLIANCE_EXPERIMENTAL
Definition: FFmpegTypes.h:98
#define AUDACITY_AVERROR_EOF
Definition: FFmpegTypes.h:32
#define AUDACITY_FF_QP2LAMBDA
Definition: FFmpegTypes.h:89
#define AUDACITY_AV_CODEC_FLAG_QSCALE
Definition: FFmpegTypes.h:82
#define AUDACITY_AVFMT_GLOBALHEADER
Definition: FFmpegTypes.h:40
#define AUDACITY_AV_NOPTS_VALUE
Definition: FFmpegTypes.h:74
#define AUDACITY_AVFMT_NOFILE
Definition: FFmpegTypes.h:34
int AVSampleFormatFwd
Definition: FFmpegTypes.h:132
AudacityAVSampleFormat
Definition: FFmpegTypes.h:148
@ AUDACITY_AV_SAMPLE_FMT_S32P
signed 32 bits, planar
Definition: FFmpegTypes.h:158
@ AUDACITY_AV_SAMPLE_FMT_S16P
signed 16 bits, planar
Definition: FFmpegTypes.h:157
@ AUDACITY_AV_SAMPLE_FMT_FLTP
float, planar
Definition: FFmpegTypes.h:159
@ AUDACITY_AV_SAMPLE_FMT_NONE
Definition: FFmpegTypes.h:149
@ AUDACITY_AV_SAMPLE_FMT_S16
signed 16 bits
Definition: FFmpegTypes.h:151
@ AUDACITY_AV_SAMPLE_FMT_S32
signed 32 bits
Definition: FFmpegTypes.h:152
@ AUDACITY_AV_SAMPLE_FMT_FLT
float
Definition: FFmpegTypes.h:153
@ AUDACITY_AV_SAMPLE_FMT_U8
unsigned 8 bits
Definition: FFmpegTypes.h:150
@ AUDACITY_AV_SAMPLE_FMT_U8P
unsigned 8 bits, planar
Definition: FFmpegTypes.h:156
#define OSINPUT(X)
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define field(n, t)
Definition: ImportAUP.cpp:165
#define _(s)
Definition: Internat.h:73
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
@ eIsCreating
Definition: ShuttleGui.h:37
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
TranslatableString label
Definition: TagsEditor.cpp:165
const auto tracks
const auto project
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
static Settings & settings()
Definition: TrackInfo.cpp:69
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
void Set(const std::string_view &key, const std::string &value, int flags=0) noexcept
virtual void ResetData() noexcept=0
virtual void SetStreamIndex(int index) noexcept=0
virtual int GetDuration() const noexcept=0
virtual void RescalePresentationTimestamp(AudacityAVRational bq, AudacityAVRational cq) noexcept=0
virtual int64_t GetDecompressionTimestamp() const noexcept=0
AVPacket * GetWrappedValue() noexcept
virtual void RescaleDecompressionTimestamp(AudacityAVRational bq, AudacityAVRational cq) noexcept=0
virtual void RescaleDuration(AudacityAVRational bq, AudacityAVRational cq) noexcept=0
virtual int64_t GetPresentationTimestamp() const noexcept=0
virtual void ResetTimestamps() noexcept=0
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Controlling class for FFmpeg exporting. Creates the options dialog of the appropriate type,...
int GetFormatCount() const override
bool CheckFileName(wxFileName &filename, int format=0) const override
Callback, called from GetFilename.
~ExportFFmpeg() override
std::unique_ptr< ExportProcessor > CreateProcessor(int format) const override
std::unique_ptr< ExportOptionsEditor > CreateOptionsEditor(int format, ExportOptionsEditor::Listener *listener) const override
Creates format-dependent options editor, that is used to create a valid set of parameters to be used ...
std::vector< FormatInfo > mFormatInfos
FormatInfo GetFormatInfo(int index) const override
Returns FormatInfo structure for given index if it's valid, or a default one. FormatInfo::format isn'...
std::shared_ptr< FFmpegFunctions > mFFmpeg
Custom FFmpeg export dialog.
static ExposedFormat fmts[]
List of export types.
Listener object that is used to report on option changes.
Editor objects are used to retrieve a set of export options, and configure exporting parameters accor...
std::vector< int > SampleRateList
static T GetParameterValue(const ExportProcessor::Parameters &parameters, int id, T defaultValue=T())
static ExportResult UpdateProgress(ExportProcessorDelegate &delegate, Mixer &mixer, double t0, double t1)
Sends progress update to delegate and retrieves state update from it. Typically used inside each expo...
static std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerOptions::Downmix *mixerSpec)
virtual void SetStatusString(const TranslatableString &str)=0
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
Definition: ExportPlugin.h:93
std::shared_ptr< FFmpegFunctions > mFFmpeg
TranslatableString status
ExportResult Process(ExportProcessorDelegate &delegate) override
struct FFmpegExportProcessor::@144 context
FFmpegExportProcessor(std::shared_ptr< FFmpegFunctions > ffmpeg, int format)
std::unique_ptr< Mixer > mixer
bool Initialize(AudacityProject &project, const Parameters &parameters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double sampleRate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
Called before start processing.
std::unique_ptr< FFmpegExporter > exporter
Performs actual export.
bool CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates)
Check whether or not current project sample rate is compatible with the export codec.
FFmpegExporter(std::shared_ptr< FFmpegFunctions > ffmpeg, const wxFileNameWrapper &filename, int numChannels, int subformat)
bool Finalize()
Flushes audio encoder.
std::unique_ptr< AVFormatContextWrapper > mEncFormatCtx
std::unique_ptr< AVFifoBufferWrapper > mEncAudioFifo
bool EncodeAudioFrame(int16_t *pFrame, size_t frameSize)
Encodes audio.
std::shared_ptr< FFmpegFunctions > mFFmpeg
bool Init(const char *shortname, AudacityProject *project, int sampleRate, const Tags *metadata, const ExportProcessor::Parameters &parameters)
Format initialization.
std::unique_ptr< AVStreamWrapper > mEncAudioStream
bool AddTags(const Tags *metadata)
Writes metadata.
int EncodeAudio(AVPacketWrapper &pkt, int16_t *audio_samples, int nb_samples)
void SetMetadata(const Tags *tags, const char *name, const wxChar *tag)
Sets individual metadata values.
AVDataBuffer< int16_t > mEncAudioFifoOutBuf
int AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates)
Asks user to resample the project or cancel the export procedure.
void WritePacket(AVPacketWrapper &packet)
static constexpr auto MaxAudioPacketSize
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, MixerOptions::Downmix *mixerSpec)
wxFileNameWrapper mName
unsigned mChannels
bool InitCodecs(int sampleRate, const ExportProcessor::Parameters &parameters)
Codec initialization.
std::unique_ptr< AVCodecContextWrapper > mEncAudioCodecCtx
std::unique_ptr< AVOutputFormatWrapper > mEncFormatDesc
Abstract base class used in importing a file.
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
ID3 Tags (for MP3)
Definition: Tags.h:73
bool HasTag(const wxString &name) const
Definition: Tags.cpp:397
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:406
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:993
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Holds a msgid for the translation catalog; may also bind format arguments.
void Store(audacity::BasicSettings &settings) const override
ExportOptionsFFmpegCustomEditor(ExportOptionsEditor::Listener *listener=nullptr)
void Load(const audacity::BasicSettings &config) override
bool GetValue(int id, ExportValue &value) const override
bool GetOption(int index, ExportOption &option) const override
bool SetValue(int id, const ExportValue &value) override
Base class for objects that provide facility to store data persistently, and access it with string ke...
Definition: BasicSettings.h:31
bool ReadBool(const wxString &key, bool defaultValue) const
virtual bool Read(const wxString &key, bool *value) const =0
void SetName(const TranslatableString &title)
THEME_RESOURCES_API void Load()
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > AMRNBOptions
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > AC3Options
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > AACOptions
const std::vector< ExportOption > FFmpegOptions
TranslatableString f_kbps(double d)
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > WMAOptions
ExportOptionsEditor::SampleRateList ToSampleRateList(const int *rates)
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > OPUSOptions
Definition: ExportOpus.cpp:111
void OnOpen(const CommandContext &context)
Definition: FileMenus.cpp:197
std::string ToUTF8(const std::wstring &wstr)
STL namespace.
A type that provides a description of an exporting option. Isn't allowed to change except non-type re...
Definition: ExportTypes.h:43
@ TypeEnum
List/enum option. values holds items, and names text to be displayed.
Definition: ExportTypes.h:48
@ TypeRange
Range option. values holds [min, max].
Definition: ExportTypes.h:47
bool compiledIn
support for this codec/format is compiled in (checked at runtime)
const int canmetadata
!=0 if format supports metadata, AV_CANMETA any avformat version, otherwise version support added
const TranslatableString description
format description (will be shown in export dialog)
const wxChar * name
format name (internal, should be unique; if not - export dialog may show unusual behaviour)
bool canutf8
true if format supports metadata in UTF-8, false otherwise
AudacityAVCodecID codecid
codec ID (see libavcodec/avcodec.h)
unsigned maxchannels
how many channels this format could handle
static std::shared_ptr< FFmpegFunctions > Load(bool fromUserPathOnly=false)
wxString format
Definition: ExportPlugin.h:35