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
202enum : int
203{
211
212const std::initializer_list<PlainExportOptionsEditor::OptionDesc> OPUSOptions {
213 {
214 {
215 OPUSOptionIDBitRate, XO("Bit Rate"),
216 128000,
218 {
219 6000,
220 8000,
221 16000,
222 24000,
223 32000,
224 40000,
225 48000,
226 64000,
227 80000,
228 96000,
229 128000,
230 160000,
231 192000,
232 256000
233 },
234 {
235 n_kbps( 6 ),
236 n_kbps( 8 ),
237 n_kbps( 16 ),
238 n_kbps( 24 ),
239 n_kbps( 32 ),
240 n_kbps( 40 ),
241 n_kbps( 48 ),
242 n_kbps( 64 ),
243 n_kbps( 80 ),
244 n_kbps( 96 ),
245 n_kbps( 128 ),
246 n_kbps( 160 ),
247 n_kbps( 192 ),
248 n_kbps( 256 ),
249 }
250 }, wxT("/FileFormats/OPUSBitrate")
251 },
252 {
253 {
254 OPUSOptionIDCompression, XO("Compression"),
255 10,
257 { 0, 10 }
258 }, wxT("/FileFormats/OPUSCompression")
259 },
260 {
261 {
262 OPUSOptionIDFrameDuration, XO("Frame Duration"),
263 std::string("20"),
265 {
266 std::string("2.5"),
267 std::string("5"),
268 std::string("10"),
269 std::string("20"),
270 std::string("40"),
271 std::string("60")
272 },
273 {
274 XO("2.5 ms"),
275 XO("5 ms"),
276 XO("10 ms"),
277 XO("20 ms"),
278 XO("40 ms"),
279 XO("60 ms"),
280 }
281 }, wxT("/FileFormats/OPUSFrameDuration")
282 },
283 {
284 {
285 OPUSOptionIDVBRMode, XO("Vbr Mode"),
286 std::string("on"),
288 { std::string("off"), std::string("on"), std::string("constrained") },
289 { XO("Off"), XO("On"), XO("Constrained") }
290 }, wxT("/FileFormats/OPUSVbrMode")
291 },
292 {
293 {
294 OPUSOptionIDApplication, XO("Application"),
295 std::string("audio"),
297 { std::string("voip"), std::string("audio"), std::string("lowdelay") },
298 { XO("VOIP"), XO("Audio"), XO("Low Delay") }
299 }, wxT("/FileFormats/OPUSApplication")
300 },
301 {
302 {
303 OPUSOptionIDCutoff, XO("Cutoff"),
304 std::string("0"),
306 {
307 std::string("0"),
308 std::string("4000"),
309 std::string("6000"),
310 std::string("8000"),
311 std::string("12000"),
312 std::string("20000")
313 },
314 {
315 XO("Disabled"),
316 XO("Narrowband"),
317 XO("Mediumband"),
318 XO("Wideband"),
319 XO("Super Wideband"),
320 XO("Fullband")
321 }
322 }, wxT("/FileFormats/OPUSCutoff")
323 },
324};
325
326enum : int
327{
330
331const std::initializer_list<PlainExportOptionsEditor::OptionDesc> WMAOptions {
332 {
333 {
334 WMAOptionIDBitRate, XO("Bit Rate"),
335 128000,
337 {
338 24000,
339 32000,
340 40000,
341 48000,
342 64000,
343 80000,
344 96000,
345 128000,
346 160000,
347 192000,
348 256000,
349 320000
350 },
351 {
352 n_kbps(24),
353 n_kbps(32),
354 n_kbps(40),
355 n_kbps(48),
356 n_kbps(64),
357 n_kbps(80),
358 n_kbps(96),
359 n_kbps(128),
360 n_kbps(160),
361 n_kbps(192),
362 n_kbps(256),
363 n_kbps(320),
364 }
365 }, wxT("/FileFormats/WMABitRate")
366 }
367};
368
369const std::vector<ExportOption> FFmpegOptions {
370 { FELanguageID, {}, std::string() },
371 { FESampleRateID, {}, 0 },
372 { FEBitrateID, {}, 0 },
373 { FETagID, {}, std::string() },
374 { FEQualityID, {}, 0 },
375 { FECutoffID, {}, 0},
376 { FEBitReservoirID, {}, true },
377 { FEVariableBlockLenID, {}, true },
378 { FECompLevelID, {}, -1 },
379 { FEFrameSizeID, {}, 0 },
380 { FELPCCoeffsID, {}, 0 },
381 { FEMinPredID, {}, -1 },
382 { FEMaxPredID, {}, -1 },
383 { FEMinPartOrderID, {}, -1 },
384 { FEMaxPartOrderID, {}, -1 },
385 { FEPredOrderID, {}, 0 },
386 { FEMuxRateID, {}, 0 },
387 { FEPacketSizeID, {}, 0 },
388 { FECodecID, {}, std::string() },
389 { FEFormatID, {}, std::string() }
390};
391
393 : public ExportOptionsEditor
395{
396 std::unordered_map<int, ExportValue> mValues;
397 std::shared_ptr<FFmpegFunctions> mFFmpeg;
399 //created on-demand
400 mutable std::unique_ptr<AVCodecWrapper> mAVCodec;
401public:
402
404 : mListener(listener)
405 {
406
407 }
408
409 void PopulateUI(ShuttleGui& S) override
410 {
411 CheckFFmpeg(true);
412 //Continue anyway, as we do not need ffmpeg functions to build and fill in the UI
413
414 mParent = S.GetParent();
415
416 S.StartHorizontalLay(wxCENTER);
417 {
418 S.StartVerticalLay(wxCENTER, 0);
419 {
420 S.AddButton(XXO("Open custom FFmpeg format options"))
421 ->Bind(wxEVT_BUTTON, &ExportOptionsFFmpegCustomEditor::OnOpen, this);
422 S.StartMultiColumn(2, wxCENTER);
423 {
424 S.AddPrompt(XXO("Current Format:"));
425 mFormat = S.Name(XXO("Current Format:"))
426 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
427 S.AddPrompt(XXO("Current Codec:"));
428 mCodec = S.Name(XXO("Current Codec:"))
429 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
430 }
431 S.EndMultiColumn();
432 }
433 S.EndHorizontalLay();
434 }
435 S.EndHorizontalLay();
436
437 UpdateCodecAndFormat();
438 }
439
441 {
442 Load(*gPrefs);
443 return true;
444 }
445
446 int GetOptionsCount() const override
447 {
448 return static_cast<int>(FFmpegOptions.size());
449 }
450
451 bool GetOption(int, ExportOption&) const override
452 {
453 return false;
454 }
455
456 bool GetValue(int id, ExportValue& value) const override
457 {
458 auto it = mValues.find(id);
459 if(it != mValues.end())
460 {
461 value = it->second;
462 return true;
463 }
464 return false;
465 }
466
467 bool SetValue(int id, const ExportValue& value) override
468 {
469 return false;
470 }
471
473 {
474 if(!mAVCodec)
475 {
476 auto it = mValues.find(FECodecID);
477 if(it == mValues.end())
478 return {};
479
480 const auto codecId = *std::get_if<std::string>(&it->second);
481 mAVCodec = mFFmpeg->CreateEncoder(codecId.c_str());
482 }
483 if(!mAVCodec)
484 return {};
485
486 if(const auto rates = mAVCodec->GetSupportedSamplerates())
487 return ToSampleRateList(rates);
488 return {};
489 }
490
491 void Load(const audacity::BasicSettings& config) override
492 {
493 mValues[FELanguageID] = std::string(config.Read(wxT("/FileFormats/FFmpegLanguage"), wxT("")).ToUTF8());
494 mValues[FESampleRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegSampleRate"), 0L));
495 mValues[FEBitrateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegBitRate"), 0L));
496 mValues[FETagID] = std::string(config.Read(wxT("/FileFormats/FFmpegTag"), wxT(""))
497 .mb_str(wxConvUTF8));
498 mValues[FEQualityID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegQuality"), -99999L));
499 mValues[FECutoffID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCutOff"), 0L));
500 mValues[FEBitReservoirID] = config.ReadBool(wxT("/FileFormats/FFmpegBitReservoir"), true);
501 mValues[FEVariableBlockLenID] = config.ReadBool(wxT("/FileFormats/FFmpegVariableBlockLen"), true);
502 mValues[FECompLevelID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCompLevel"), -1L));
503 mValues[FEFrameSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegFrameSize"), 0L));
504
505 mValues[FELPCCoeffsID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegLPCCoefPrec"), 0L));
506 mValues[FEMinPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPredOrder"), -1L));
507 mValues[FEMaxPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPredOrder"), -1L));
508 mValues[FEMinPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPartOrder"), -1L));
509 mValues[FEMaxPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPartOrder"), -1L));
510 mValues[FEPredOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPredOrderMethod"), 0L));
511 mValues[FEMuxRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMuxRate"), 0L));
512 mValues[FEPacketSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPacketSize"), 0L));
513 mValues[FECodecID] = std::string(config.Read(wxT("/FileFormats/FFmpegCodec")));
514 mValues[FEFormatID] = std::string(config.Read(wxT("/FileFormats/FFmpegFormat")));
515 }
516
518 {
519
520 }
521
522private:
523
524 bool CheckFFmpeg(bool showError)
525 {
526 // Show "Locate FFmpeg" dialog
527 if(!mFFmpeg)
528 {
529 mFFmpeg = FFmpegFunctions::Load();
530 if (!mFFmpeg)
531 {
533 return LoadFFmpeg(showError);
534 }
535 }
536 return true;
537 }
538
540 {
541 mFormat->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"), wxT("")));
542 mCodec->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegCodec"), wxT("")));
543 }
544
545 void OnOpen(const wxCommandEvent&)
546 {
547 if(!CheckFFmpeg(true))
548 return;
549
550 #ifdef __WXMAC__
551 // Bug 2077 Must be a parent window on OSX or we will appear behind.
552 auto pWin = wxGetTopLevelParent( mParent );
553 #else
554 // Use GetTopWindow on windows as there is no hWnd with top level parent.
555 auto pWin = wxTheApp->GetTopWindow();
556 #endif
557
558 ExportFFmpegOptions od(pWin);
559 od.ShowModal();
560 //ExportFFmpegOptions uses gPrefs to store options
561 //Instead we could provide it with instance of wxConfigBase
562 //constructed locally and read from it later
563 Load(*gPrefs);
564 mAVCodec.reset();
565
566 UpdateCodecAndFormat();
567 if(mListener)
568 mListener->OnSampleRateListChange();
569 }
570
571 wxWindow *mParent {nullptr};
572 wxTextCtrl *mFormat {nullptr};
573 wxTextCtrl *mCodec {nullptr};
574};
575
576}
577
579class FFmpegExporter final
580{
581 static constexpr auto MaxAudioPacketSize { 128 * 1024 };
582public:
583
584 FFmpegExporter(std::shared_ptr<FFmpegFunctions> ffmpeg,
585 const wxFileNameWrapper& filename,
586 int numChannels,
587 int subformat);
588
590 bool Init(const char *shortname,
592 int sampleRate,
593 const Tags *metadata,
594 const ExportProcessor::Parameters& parameters);
595
597 bool EncodeAudioFrame(int16_t *pFrame, size_t frameSize);
598
600 bool Finalize();
601
602 std::unique_ptr<Mixer> CreateMixer(const TrackList &tracks,
603 bool selectionOnly,
604 double startTime, double stopTime,
605 MixerOptions::Downmix *mixerSpec);
606
607private:
608
610 bool AddTags(const Tags *metadata);
611
613 void SetMetadata(const Tags *tags, const char *name, const wxChar *tag);
614
616 bool CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates);
617
619 int AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates);
620
622 bool InitCodecs(int sampleRate,
623 const ExportProcessor::Parameters& parameters);
624
625 void WritePacket(AVPacketWrapper& packet);
626
628 int16_t* audio_samples,
629 int nb_samples);
630
631 std::shared_ptr<FFmpegFunctions> mFFmpeg;
632
633 std::unique_ptr<AVOutputFormatWrapper> mEncFormatDesc; // describes our output file to libavformat
635 std::unique_ptr<AVStreamWrapper> mEncAudioStream; // the output audio stream (may remain NULL)
637
639
641 int mBitRate{};
643 unsigned mChannels{};
644 bool mSupportsUTF8{true};
645
646 // Smart pointer fields, their order is the reverse in which they are reset in FreeResources():
647 std::unique_ptr<AVFifoBufferWrapper> mEncAudioFifo; // FIFO to write incoming audio samples into
648 AVDataBuffer<int16_t> mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into
649 std::unique_ptr<AVFormatContextWrapper> mEncFormatCtx; // libavformat's context for our output file
650 std::unique_ptr<AVCodecContextWrapper> mEncAudioCodecCtx; // the encoder for the output audio stream
651};
652
654{
655 std::shared_ptr<FFmpegFunctions> mFFmpeg;
656 struct
657 {
658 //same index as in GetFormatInfo, use AdjustFormatIndex to convert it to FFmpegExposedFormat
661 double t0;
662 double t1;
663 std::unique_ptr<Mixer> mixer;
664 std::unique_ptr<FFmpegExporter> exporter;
666
667public:
668 FFmpegExportProcessor(std::shared_ptr<FFmpegFunctions> ffmpeg, int format);
669
671 const Parameters& parameters,
672 const wxFileNameWrapper& filename,
673 double t0, double t1, bool selectedOnly,
674 double sampleRate, unsigned channels,
675 MixerOptions::Downmix* mixerSpec,
676 const Tags* tags) override;
677
678 ExportResult Process(ExportProcessorDelegate& delegate) override;
679
680};
681
682class ExportFFmpeg final : public ExportPlugin
683{
684public:
685
686 ExportFFmpeg();
687 ~ExportFFmpeg() override;
688
689 std::unique_ptr<ExportOptionsEditor>
690 CreateOptionsEditor(int format, ExportOptionsEditor::Listener* listener) const override;
691
692 int GetFormatCount() const override;
693 FormatInfo GetFormatInfo(int index) const override;
694
696 bool CheckFileName(wxFileName &filename, int format = 0) const override;
697
698 std::unique_ptr<ExportProcessor> CreateProcessor(int format) const override;
699
700private:
701 mutable std::shared_ptr<FFmpegFunctions> mFFmpeg;
702
703 std::vector<FormatInfo> mFormatInfos;
704};
705
706FFmpegExporter::FFmpegExporter(std::shared_ptr<FFmpegFunctions> ffmpeg,
707 const wxFileNameWrapper& filename,
708 int numChannels,
709 int subFormat)
710 : mFFmpeg(std::move(ffmpeg))
711 , mName(filename)
712 , mChannels(numChannels)
713 , mSubFormat(subFormat)
714{
715}
716
717std::unique_ptr<Mixer> FFmpegExporter::CreateMixer(const TrackList& tracks, bool selectionOnly, double startTime, double stopTime, MixerOptions::Downmix* mixerSpec)
718{
719 return ExportPluginHelpers::CreateMixer(tracks, selectionOnly,
720 startTime, stopTime,
722 mSampleRate, int16Sample, mixerSpec);
723}
724
725
727{
729
730 int avfver = mFFmpeg ? mFFmpeg->AVFormatVersion.GetIntVersion() : 0;
731
732 int newfmt;
733 // Adds export types from the export type list
734 for (newfmt = 0; newfmt < FMT_LAST; newfmt++)
735 {
736 wxString shortname(ExportFFmpegOptions::fmts[newfmt].shortname);
737 // Don't hide export types when there's no av-libs, and don't hide FMT_OTHER
738 if (newfmt < FMT_OTHER && mFFmpeg)
739 {
740 // Format/Codec support is compiled in?
741 auto avoformat = mFFmpeg->GuessOutputFormat(shortname.mb_str(), nullptr, nullptr);
742 auto avcodec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(ExportFFmpegOptions::fmts[newfmt].codecid));
743
744 if (avoformat == NULL || avcodec == NULL)
745 {
747 continue;
748 }
749 }
750 FormatInfo formatInfo {};
751 formatInfo.format = ExportFFmpegOptions::fmts[newfmt].name;
752 formatInfo.extensions.push_back(ExportFFmpegOptions::fmts[newfmt].extension);
753 // For some types add other extensions
754 switch(newfmt)
755 {
756 case FMT_M4A:
757 formatInfo.extensions.push_back(wxT("3gp"));
758 formatInfo.extensions.push_back(wxT("m4r"));
759 formatInfo.extensions.push_back(wxT("mp4"));
760 break;
761 case FMT_WMA2:
762 formatInfo.extensions.push_back(wxT("asf"));
763 formatInfo.extensions.push_back(wxT("wmv"));
764 break;
765 default:
766 break;
767 }
768 formatInfo.maxChannels = ExportFFmpegOptions::fmts[newfmt].maxchannels;
769 formatInfo.description = ExportFFmpegOptions::fmts[newfmt].description;
770
771 const int canmeta = ExportFFmpegOptions::fmts[newfmt].canmetadata;
772 formatInfo.canMetaData = canmeta && (canmeta == AV_CANMETA || canmeta <= avfver);
773
774 mFormatInfos.push_back(std::move(formatInfo));
775 }
776}
777
779
780std::unique_ptr<ExportOptionsEditor>
782{
784 {
785 case FMT_M4A:
786 return std::make_unique<PlainExportOptionsEditor>(AACOptions, listener);
787 case FMT_AC3:
788 return std::make_unique<PlainExportOptionsEditor>(
791 listener);
792 case FMT_AMRNB:
793 return std::make_unique<PlainExportOptionsEditor>(
796 listener);
797 case FMT_OPUS:
798 return std::make_unique<PlainExportOptionsEditor>(OPUSOptions, listener);
799 case FMT_WMA2:
800 return std::make_unique<PlainExportOptionsEditor>(
803 listener);
804 case FMT_OTHER:
805 return std::make_unique<ExportOptionsFFmpegCustomEditor>(listener);
806 }
807 return {};
808}
809
811{
812 return static_cast<int>(mFormatInfos.size());
813}
814
816{
817 if(index >= 0 && index < mFormatInfos.size())
818 return mFormatInfos[index];
819 return mFormatInfos[FMT_OTHER];
820}
821
822bool ExportFFmpeg::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format)) const
823{
824 bool result = true;
825
826 // Show "Locate FFmpeg" dialog
828 if (!mFFmpeg)
829 {
832
833 return LoadFFmpeg(true);
834 }
835
836 return result;
837}
838
839std::unique_ptr<ExportProcessor> ExportFFmpeg::CreateProcessor(int format) const
840{
841 return std::make_unique<FFmpegExportProcessor>(mFFmpeg, format);
842}
843
844
845bool FFmpegExporter::Init(const char *shortname,
847 int sampleRate,
848 const Tags *metadata,
849 const ExportProcessor::Parameters& parameters)
850{
851 if (!mFFmpeg)
852 return false;
853
854 // See if libavformat has modules that can write our output format. If so, mEncFormatDesc
855 // will describe the functions used to write the format (used internally by libavformat)
856 // and the default video/audio codecs that the format uses.
857 const auto path = mName.GetFullPath();
858 if ((mEncFormatDesc = mFFmpeg->GuessOutputFormat(shortname, OSINPUT(path), nullptr)) == nullptr)
859 {
860 throw ExportException(_("FFmpeg : ERROR - Can't determine format description for file \"%s\".").Format(path));
861 }
862
863 // mEncFormatCtx is used by libavformat to carry around context data re our output file.
864 mEncFormatCtx = mFFmpeg->CreateAVFormatContext();
865 if (!mEncFormatCtx)
866 {
867 throw ExportException(_("FFmpeg : ERROR - Can't allocate output format context."));
868 }
869
870 // Initialise the output format context.
871 mEncFormatCtx->SetOutputFormat(mFFmpeg->CreateAVOutputFormatWrapper(mEncFormatDesc->GetWrappedValue()));
872 mEncFormatCtx->SetFilename(OSINPUT(path));
873
874 // At the moment Audacity can export only one audio stream
875 if ((mEncAudioStream = mEncFormatCtx->CreateStream()) == nullptr)
876 {
877 throw ExportException(_("FFmpeg : ERROR - Can't add audio stream to output file \"%s\"."));
878 }
879
880 // Documentation for avformat_new_stream says
881 // "User is required to call avcodec_close() and avformat_free_context() to clean
882 // up the allocation by avformat_new_stream()."
883
884 // We use smart pointers that ensure these cleanups either in their destructors or
885 // sooner if they are reset. These are std::unique_ptr with nondefault deleter
886 // template parameters.
887
888 // mEncFormatCtx takes care of avformat_free_context(), so
889 // mEncAudioStream can be a plain pointer.
890
891 // mEncAudioCodecCtx now becomes responsible for closing the codec:
892 mEncAudioCodecCtx = mEncAudioStream->GetAVCodecContext();
893 mEncAudioStream->SetId(0);
894
895 // Open the output file.
896 if (!(mEncFormatDesc->GetFlags() & AUDACITY_AVFMT_NOFILE))
897 {
899 mEncFormatCtx->OpenOutputContext(path);
900
902 {
903 throw ExportException(_("FFmpeg : ERROR - Can't open output file \"%s\" to write. Error code is %d.")
904 .Format(path, static_cast<int>(result)));
905 }
906 }
907
908 // Open the audio stream's codec and initialise any stream related data.
909 if(!InitCodecs(sampleRate, parameters))
910 return false;
911
912 if (mEncAudioStream->SetParametersFromContext(*mEncAudioCodecCtx) < 0)
913 return false;
914
915 if (metadata == NULL)
916 metadata = &Tags::Get( *project );
917
918 // Add metadata BEFORE writing the header.
919 // At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4.
921 const auto avfver = mFFmpeg->AVFormatVersion.GetIntVersion();
922 if (canmeta && (canmeta == AV_CANMETA || canmeta <= avfver))
923 {
925 AddTags(metadata);
926 }
927
928 // Write headers to the output file.
929 int err =
930 mFFmpeg->avformat_write_header(mEncFormatCtx->GetWrappedValue(), nullptr);
931
932 if (err < 0)
933 {
934 throw ExportException(XO("FFmpeg : ERROR - Can't write headers to output file \"%s\". Error code is %d.")
935 .Format( path, err )
936 .Translation());
937 }
938
939 return true;
940}
941
942bool FFmpegExporter::CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates)
943{
944 if (lowrate && highrate)
945 {
946 if (rate < lowrate || rate > highrate)
947 {
948 return false;
949 }
950 }
951
952 if (sampRates)
953 {
954 for (int i = 0; sampRates[i] > 0; i++)
955 {
956 if (rate == sampRates[i])
957 {
958 return true;
959 }
960 }
961 }
962
963 return false;
964}
965
967 const ExportProcessor::Parameters& parameters)
968{
969 std::unique_ptr<AVCodecWrapper> codec;
970
972
973 // Get the sample rate from the passed settings if we haven't set it before.
974 // Doing this only when not set allows us to carry the sample rate from one
975 // iteration of ExportMultiple to the next. This prevents multiple resampling
976 // dialogs in the event the codec can't support the specified rate.
977 if (!mSampleRate)
978 {
979 //TODO: Does not work with export multiple any more...
981 }
982
983 // Configure the audio stream's codec context.
984
985 const auto codecID = ExportFFmpegOptions::fmts[mSubFormat].codecid;
986
987 mEncAudioCodecCtx->SetGlobalQuality(-99999); //quality mode is off by default;
988
989 // Each export type has its own settings
990 switch (mSubFormat)
991 {
992 case FMT_M4A:
993 {
995
996 q = wxClip( q, 98 * mChannels, 160 * mChannels );
997 // Set bit rate to between 98 kbps and 320 kbps (if two channels)
998 mEncAudioCodecCtx->SetBitRate(q * 1000);
1000 mEncAudioCodecCtx->SetCutoff(0);
1001
1002 break;
1003 }
1004 case FMT_AC3:
1006 if (!CheckSampleRate(
1008 iAC3SampleRates[2],
1009 &iAC3SampleRates[0]))
1010 {
1012 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1013 iAC3SampleRates[0],
1014 iAC3SampleRates[2],
1015 &iAC3SampleRates[0]);
1016 }
1017 break;
1018 case FMT_AMRNB:
1019 mSampleRate = 8000;
1021 break;
1022 case FMT_OPUS:
1023 options.Set("b", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDBitRate, "128000"), 0);
1024 options.Set("vbr", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDVBRMode, "on"), 0);
1025 options.Set("compression_level", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCompression, "10"), 0);
1026 options.Set("frame_duration", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDFrameDuration, "20"), 0);
1027 options.Set("application", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDApplication, "audio"), 0);
1028 options.Set("cutoff", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCutoff, "0"), 0);
1029 options.Set("mapping_family", mChannels <= 2 ? "0" : "255", 0);
1030 break;
1031 case FMT_WMA2:
1033 if (!CheckSampleRate(
1035 iWMASampleRates[4],
1036 &iWMASampleRates[0]))
1037 {
1039 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1040 iWMASampleRates[0],
1041 iWMASampleRates[4],
1042 &iWMASampleRates[0]);
1043 }
1044 break;
1045 case FMT_OTHER:
1046 {
1047 AVDictionaryWrapper streamMetadata = mEncAudioStream->GetMetadata();
1048 streamMetadata.Set(
1049 "language",
1050 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FELanguageID), 0);
1051
1052 mEncAudioStream->SetMetadata(streamMetadata);
1053
1054 mEncAudioCodecCtx->SetSampleRate(
1055 ExportPluginHelpers::GetParameterValue(parameters, FESampleRateID, 0));
1056
1057 if (mEncAudioCodecCtx->GetSampleRate() != 0)
1058 mSampleRate = mEncAudioCodecCtx->GetSampleRate();
1059
1060 mEncAudioCodecCtx->SetBitRate(
1061 ExportPluginHelpers::GetParameterValue(parameters, FEBitrateID, 0));
1062
1063 mEncAudioCodecCtx->SetCodecTagFourCC(
1064 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FETagID).c_str());
1065
1066 mEncAudioCodecCtx->SetGlobalQuality(
1067 ExportPluginHelpers::GetParameterValue(parameters, FEQualityID, -99999));
1068 mEncAudioCodecCtx->SetCutoff(
1069 ExportPluginHelpers::GetParameterValue(parameters, FECutoffID, 0));
1070 mEncAudioCodecCtx->SetFlags2(0);
1071
1072 if (ExportPluginHelpers::GetParameterValue(parameters, FEBitReservoirID, true))
1073 options.Set("reservoir", "1", 0);
1074
1075 if (ExportPluginHelpers::GetParameterValue(parameters, FEVariableBlockLenID, true))
1076 mEncAudioCodecCtx->SetFlags2(
1077 mEncAudioCodecCtx->GetFlags2() | 0x0004); // WMA only?
1078
1079 mEncAudioCodecCtx->SetCompressionLevel(
1080 ExportPluginHelpers::GetParameterValue(parameters, FECompLevelID, -1));
1081 mEncAudioCodecCtx->SetFrameSize(
1082 ExportPluginHelpers::GetParameterValue(parameters, FEFrameSizeID, 0));
1083
1084 // FIXME The list of supported options for the selected encoder should be
1085 // extracted instead of a few hardcoded
1086
1087 options.Set(
1088 "lpc_coeff_precision",
1089 ExportPluginHelpers::GetParameterValue(parameters, FELPCCoeffsID, 0));
1090 options.Set(
1091 "min_prediction_order",
1092 ExportPluginHelpers::GetParameterValue(parameters, FEMinPredID, -1));
1093 options.Set(
1094 "max_prediction_order",
1095 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPredID, -1));
1096 options.Set(
1097 "min_partition_order",
1098 ExportPluginHelpers::GetParameterValue(parameters, FEMinPartOrderID, -1));
1099 options.Set(
1100 "max_partition_order",
1101 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPartOrderID, -1));
1102 options.Set(
1103 "prediction_order_method",
1104 ExportPluginHelpers::GetParameterValue(parameters, FEPredOrderID, 0));
1105 options.Set(
1106 "muxrate",
1107 ExportPluginHelpers::GetParameterValue(parameters, FEMuxRateID, 0));
1108
1109 mEncFormatCtx->SetPacketSize(
1110 ExportPluginHelpers::GetParameterValue(parameters, FEPacketSizeID, 0));
1111
1112 codec = mFFmpeg->CreateEncoder(
1113 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FECodecID).c_str());
1114
1115 if (!codec)
1116 codec = mFFmpeg->CreateEncoder(mEncFormatDesc->GetAudioCodec());
1117 }
1118 break;
1119 default:
1120 return false;
1121 }
1122
1123 // This happens if user refused to resample the project
1124 if (mSampleRate == 0) return false;
1125
1126 if (mEncAudioCodecCtx->GetGlobalQuality() >= 0)
1127 {
1128 mEncAudioCodecCtx->SetFlags(
1130 }
1131 else
1132 {
1133 mEncAudioCodecCtx->SetGlobalQuality(0);
1134 }
1135
1136 mEncAudioCodecCtx->SetGlobalQuality(mEncAudioCodecCtx->GetGlobalQuality() * AUDACITY_FF_QP2LAMBDA);
1137 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1138 mEncAudioCodecCtx->SetChannels(mChannels);
1139 mEncAudioCodecCtx->SetChannelLayout(mFFmpeg->av_get_default_channel_layout(mChannels));
1140 mEncAudioCodecCtx->SetTimeBase({ 1, mSampleRate });
1142 mEncAudioCodecCtx->SetStrictStdCompliance(
1144
1145 if (codecID == AUDACITY_AV_CODEC_ID_AC3)
1146 {
1147 // As of Jan 4, 2011, the default AC3 encoder only accept SAMPLE_FMT_FLT samples.
1148 // But, currently, Audacity only supports SAMPLE_FMT_S16. So, for now, look for the
1149 // "older" AC3 codec. this is not a proper solution, but will suffice until other
1150 // encoders no longer support SAMPLE_FMT_S16.
1151 codec = mFFmpeg->CreateEncoder("ac3_fixed");
1152 }
1153
1154 if (!codec)
1155 {
1156 codec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(codecID));
1157 }
1158
1159 // Is the required audio codec compiled into libavcodec?
1160 if (codec == NULL)
1161 {
1162 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1163 throw ExportException(XO("FFmpeg cannot find audio codec 0x%x.\nSupport for this codec is probably not compiled in.")
1164 .Format(static_cast<const unsigned int>(codecID.value))
1165 .Translation());
1166 }
1167
1168 if (codec->GetSampleFmts()) {
1169 for (int i = 0; codec->GetSampleFmts()[i] != AUDACITY_AV_SAMPLE_FMT_NONE; i++)
1170 {
1171 AVSampleFormatFwd fmt = codec->GetSampleFmts()[i];
1172
1173 if (
1182 {
1183 mEncAudioCodecCtx->SetSampleFmt(fmt);
1184 }
1185
1186 if (
1189 break;
1190 }
1191 }
1192
1193 if (codec->GetSupportedSamplerates())
1194 {
1195 // Workaround for crash in bug #2378. Proper fix is to get a newer version of FFmpeg.
1196 if (codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_AAC))
1197 {
1198 std::vector<int> rates;
1199 int i = 0;
1200
1201 while (codec->GetSupportedSamplerates()[i] &&
1202 codec->GetSupportedSamplerates()[i] != 7350)
1203 {
1204 rates.push_back(codec->GetSupportedSamplerates()[i++]);
1205 }
1206
1207 rates.push_back(0);
1208
1209 if (!CheckSampleRate(mSampleRate, 0, 0, rates.data()))
1210 {
1211 mSampleRate = AskResample(0, mSampleRate, 0, 0, rates.data());
1212 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1213 }
1214 }
1215 else
1216 {
1217 if (!CheckSampleRate(
1218 mSampleRate, 0, 0, codec->GetSupportedSamplerates()))
1219 {
1221 0, mSampleRate, 0, 0, codec->GetSupportedSamplerates());
1222 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1223 }
1224 }
1225
1226 // This happens if user refused to resample the project
1227 if (mSampleRate == 0)
1228 {
1229 return false;
1230 }
1231 }
1232
1233 if (mEncFormatCtx->GetOutputFormat()->GetFlags() & AUDACITY_AVFMT_GLOBALHEADER)
1234 {
1237 }
1238
1239 // Open the codec.
1240 int rc = mEncAudioCodecCtx->Open(codec.get(), &options);
1241 if (rc < 0)
1242 {
1243 TranslatableString errmsg;
1244
1245 switch (rc)
1246 {
1247 case AUDACITY_AVERROR(EPERM):
1248 errmsg = XO("The codec reported a generic error (EPERM)");
1249 break;
1250 case AUDACITY_AVERROR(EINVAL):
1251 errmsg = XO("The codec reported an invalid parameter (EINVAL)");
1252 break;
1253 default:
1254 char buf[64];
1255 mFFmpeg->av_strerror(rc, buf, sizeof(buf));
1256 errmsg = Verbatim(buf);
1257 }
1258
1259 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1260 throw ExportException(XO("Can't open audio codec \"%s\" (0x%x)\n\n%s")
1261 .Format(codec->GetName(), codecID.value, errmsg)
1262 .Translation());
1263 }
1264
1265 mDefaultFrameSize = mEncAudioCodecCtx->GetFrameSize();
1266
1267 if (mDefaultFrameSize == 0)
1268 mDefaultFrameSize = 1024; // arbitrary non zero value;
1269
1270 wxLogDebug(
1271 wxT("FFmpeg : Audio Output Codec Frame Size: %d samples."),
1272 mEncAudioCodecCtx->GetFrameSize());
1273
1274 // The encoder may require a minimum number of raw audio samples for each encoding but we can't
1275 // guarantee we'll get this minimum each time an audio frame is decoded from the input file so
1276 // we use a FIFO to store up incoming raw samples until we have enough for one call to the codec.
1277 mEncAudioFifo = mFFmpeg->CreateFifoBuffer(mDefaultFrameSize);
1278
1280 // Allocate a buffer to read OUT of the FIFO into. The FIFO maintains its own buffer internally.
1281 mEncAudioFifoOutBuf = mFFmpeg->CreateMemoryBuffer<int16_t>(mEncAudioFifoOutBufSize);
1282
1283 if (mEncAudioFifoOutBuf.empty())
1284 {
1285 throw ExportException(_("FFmpeg : ERROR - Can't allocate buffer to read into from audio FIFO."));
1286 }
1287
1288 return true;
1289}
1290
1292{
1293 // Set presentation time of frame (currently in the codec's timebase) in the
1294 // stream timebase.
1297 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1298
1301 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1302
1303 if (pkt.GetDuration() > 0)
1304 pkt.RescaleDuration(
1305 mEncAudioCodecCtx->GetTimeBase(), mEncAudioStream->GetTimeBase());
1306
1307 if (
1308 mFFmpeg->av_interleaved_write_frame(
1309 mEncFormatCtx->GetWrappedValue(), pkt.GetWrappedValue()) != 0)
1310 {
1311 throw ExportException(_("FFmpeg : ERROR - Couldn't write audio frame to output file."));
1312 }
1313}
1314
1315// Returns 0 if no more output, 1 if more output, negative if error
1316int FFmpegExporter::EncodeAudio(AVPacketWrapper& pkt, int16_t* audio_samples, int nb_samples)
1317{
1318 // Assume *pkt is already initialized.
1319
1320 int i, ch, buffer_size, ret, got_output = 0;
1321 AVDataBuffer<uint8_t> samples;
1322
1323 std::unique_ptr<AVFrameWrapper> frame;
1324
1325 if (audio_samples) {
1326 frame = mFFmpeg->CreateAVFrameWrapper();
1327
1328 if (!frame)
1329 return AUDACITY_AVERROR(ENOMEM);
1330
1331 frame->SetSamplesCount(nb_samples);
1332 frame->SetFormat(mEncAudioCodecCtx->GetSampleFmt());
1333 frame->SetChannelLayout(mEncAudioCodecCtx->GetChannelLayout());
1334
1335 buffer_size = mFFmpeg->av_samples_get_buffer_size(
1336 NULL, mEncAudioCodecCtx->GetChannels(), nb_samples,
1337 mEncAudioCodecCtx->GetSampleFmt(), 0);
1338
1339 if (buffer_size < 0) {
1340 throw ExportException(_("FFmpeg : ERROR - Could not get sample buffer size"));
1341 }
1342
1343 samples = mFFmpeg->CreateMemoryBuffer<uint8_t>(buffer_size);
1344
1345 if (samples.empty()) {
1346 throw ExportException(_("FFmpeg : ERROR - Could not allocate bytes for samples buffer"));
1347 }
1348 /* setup the data pointers in the AVFrame */
1349 ret = mFFmpeg->avcodec_fill_audio_frame(
1350 frame->GetWrappedValue(), mEncAudioCodecCtx->GetChannels(),
1351 mEncAudioCodecCtx->GetSampleFmt(), samples.data(), buffer_size, 0);
1352
1353 if (ret < 0) {
1354 throw ExportException(_("FFmpeg : ERROR - Could not setup audio frame"));
1355 }
1356
1357 const int channelsCount = mEncAudioCodecCtx->GetChannels();
1358
1359 for (ch = 0; ch < mEncAudioCodecCtx->GetChannels(); ch++)
1360 {
1361 for (i = 0; i < nb_samples; i++) {
1362 switch (static_cast<AudacityAVSampleFormat>(
1363 mEncAudioCodecCtx->GetSampleFmt()))
1364 {
1366 ((uint8_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount]/258 + 128;
1367 break;
1369 ((uint8_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount]/258 + 128;
1370 break;
1372 ((int16_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount];
1373 break;
1375 ((int16_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount];
1376 break;
1378 ((int32_t*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount]<<16;
1379 break;
1381 ((int32_t*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount]<<16;
1382 break;
1384 ((float*)(frame->GetData(0)))[ch + i*channelsCount] = audio_samples[ch + i*channelsCount] / 32767.0;
1385 break;
1387 ((float*)(frame->GetData(ch)))[i] = audio_samples[ch + i*channelsCount] / 32767.;
1388 break;
1389 default:
1390 wxASSERT(false);
1391 break;
1392 }
1393 }
1394 }
1395 }
1396
1397 pkt.ResetData();
1398
1399 pkt.SetStreamIndex(mEncAudioStream->GetIndex());
1400
1401 if (mFFmpeg->avcodec_send_frame != nullptr)
1402 {
1403 ret = mFFmpeg->avcodec_send_frame(
1404 mEncAudioCodecCtx->GetWrappedValue(),
1405 frame ? frame->GetWrappedValue() : nullptr);
1406
1407 while (ret >= 0)
1408 {
1409 ret = mFFmpeg->avcodec_receive_packet(
1410 mEncAudioCodecCtx->GetWrappedValue(), pkt.GetWrappedValue());
1411
1412 if (ret == AUDACITY_AVERROR(EAGAIN) || ret == AUDACITY_AVERROR_EOF)
1413 {
1414 ret = 0;
1415 break;
1416 }
1417 else if (ret < 0)
1418 break;
1419
1420 WritePacket(pkt);
1421
1422 got_output = true;
1423 }
1424 }
1425 else
1426 {
1427 ret = mFFmpeg->avcodec_encode_audio2(
1428 mEncAudioCodecCtx->GetWrappedValue(), pkt.GetWrappedValue(),
1429 frame ? frame->GetWrappedValue() : nullptr, &got_output);
1430
1431 if (ret == 0)
1432 {
1433 WritePacket(pkt);
1434 }
1435 }
1436
1437 if (ret < 0 && ret != AUDACITY_AVERROR_EOF) {
1438
1439 char buf[64];
1440 mFFmpeg->av_strerror(ret, buf, sizeof(buf));
1441 wxLogDebug(buf);
1442
1443 throw ExportException(_("FFmpeg : ERROR - encoding frame failed"));
1444 }
1445
1446 pkt.ResetTimestamps(); // We don't set frame timestamps thus don't trust the AVPacket timestamps
1447
1448 return got_output;
1449}
1450
1451
1453{
1454 // Flush the audio FIFO and encoder.
1455 for (;;)
1456 {
1457 std::unique_ptr<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();
1458
1459 const int nFifoBytes = mFFmpeg->av_fifo_size(
1460 mEncAudioFifo->GetWrappedValue()); // any bytes left in audio FIFO?
1461
1462 int encodeResult = 0;
1463
1464 // Flush the audio FIFO first if necessary. It won't contain a _full_ audio frame because
1465 // if it did we'd have pulled it from the FIFO during the last encodeAudioFrame() call
1466 if (nFifoBytes > 0)
1467 {
1468 const int nAudioFrameSizeOut = mDefaultFrameSize * mEncAudioCodecCtx->GetChannels() * sizeof(int16_t);
1469
1470 if (nAudioFrameSizeOut > mEncAudioFifoOutBufSize || nFifoBytes > mEncAudioFifoOutBufSize) {
1471 throw ExportException(_("FFmpeg : ERROR - Too much remaining data."));
1472 }
1473
1474 // We have an incomplete buffer of samples left, encode it.
1475 // If codec supports CODEC_CAP_SMALL_LAST_FRAME, we can feed it with smaller frame
1476 // 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
1477 // Otherwise we'll send a full frame of audio + silence padding to ensure all audio is encoded
1478 int frame_size = mDefaultFrameSize;
1479 if (
1480 mEncAudioCodecCtx->GetCodec()->GetCapabilities() &
1482 frame_size == 1)
1483 {
1484 frame_size = nFifoBytes /
1485 (mEncAudioCodecCtx->GetChannels() * sizeof(int16_t));
1486 }
1487
1488 wxLogDebug(wxT("FFmpeg : Audio FIFO still contains %d bytes, writing %d sample frame ..."),
1489 nFifoBytes, frame_size);
1490
1491 // Fill audio buffer with zeroes. If codec tries to read the whole buffer,
1492 // it will just read silence. If not - who cares?
1494 //const AVCodec *codec = mEncAudioCodecCtx->codec;
1495
1496 // Pull the bytes out from the FIFO and feed them to the encoder.
1497 if (mFFmpeg->av_fifo_generic_read(mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(), nFifoBytes, nullptr) == 0)
1498 {
1499 encodeResult = EncodeAudio(*pkt, mEncAudioFifoOutBuf.data(), frame_size);
1500 }
1501 else
1502 {
1503 wxLogDebug(wxT("FFmpeg : Reading from Audio FIFO failed, aborting"));
1504 // TODO: more precise message
1505 throw ExportErrorException("FFmpeg:825");
1506 }
1507 }
1508 else
1509 {
1510 // Fifo is empty, flush encoder. May be called multiple times.
1511 encodeResult =
1512 EncodeAudio(*pkt.get(), nullptr, 0);
1513 }
1514
1515 if (encodeResult < 0) {
1516 // TODO: more precise message
1517 throw ExportErrorException("FFmpeg:837");
1518 }
1519 else if (encodeResult == 0)
1520 break;
1521 }
1522
1523 // Write any file trailers.
1524 if (mFFmpeg->av_write_trailer(mEncFormatCtx->GetWrappedValue()) != 0)
1525 {
1526 // TODO: more precise message
1527 throw ExportErrorException("FFmpeg:868");
1528 }
1529
1530 return true;
1531}
1532
1533// All paths in this that fail must report their error to the user.
1534bool FFmpegExporter::EncodeAudioFrame(int16_t *pFrame, size_t numSamples)
1535{
1536 const auto frameSize = numSamples * sizeof(int16_t) * mChannels;
1537 int nBytesToWrite = 0;
1538 uint8_t *pRawSamples = nullptr;
1539 int nAudioFrameSizeOut = mDefaultFrameSize * mEncAudioCodecCtx->GetChannels() * sizeof(int16_t);
1540 int ret;
1541
1542 nBytesToWrite = frameSize;
1543 pRawSamples = (uint8_t*)pFrame;
1544 if (mFFmpeg->av_fifo_realloc2(mEncAudioFifo->GetWrappedValue(), mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) + frameSize) < 0) {
1545 throw ExportErrorException("FFmpeg:905");
1546 }
1547
1548 // Put the raw audio samples into the FIFO.
1549 ret = mFFmpeg->av_fifo_generic_write(
1550 mEncAudioFifo->GetWrappedValue(), pRawSamples, nBytesToWrite, nullptr);
1551
1552 if (ret != nBytesToWrite) {
1553 throw ExportErrorException("FFmpeg:913");
1554 }
1555
1556 if (nAudioFrameSizeOut > mEncAudioFifoOutBufSize) {
1557 throw ExportException(_("FFmpeg : ERROR - nAudioFrameSizeOut too large."));
1558 }
1559
1560 // Read raw audio samples out of the FIFO in nAudioFrameSizeOut byte-sized groups to encode.
1561 while (mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) >= nAudioFrameSizeOut)
1562 {
1563 ret = mFFmpeg->av_fifo_generic_read(
1564 mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(),
1565 nAudioFrameSizeOut, nullptr);
1566
1567 std::unique_ptr<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();
1568
1569 ret = EncodeAudio(*pkt, // out
1570 mEncAudioFifoOutBuf.data(), // in
1572
1573 if (ret < 0)
1574 return false;
1575 }
1576 return true;
1577}
1578
1579FFmpegExportProcessor::FFmpegExportProcessor(std::shared_ptr<FFmpegFunctions> ffmpeg, int subformat )
1580 : mFFmpeg(std::move(ffmpeg))
1581{
1582 context.subformat = subformat;
1583}
1584
1586 const Parameters& parameters,
1587 const wxFileNameWrapper& fName,
1588 double t0, double t1, bool selectionOnly,
1589 double sampleRate, unsigned channels,
1590 MixerOptions::Downmix* mixerSpec,
1591 const Tags* metadata)
1592{
1593 context.t0 = t0;
1594 context.t1 = t1;
1595
1596 if (!FFmpegFunctions::Load())
1597 {
1598 throw ExportException(_("Properly configured FFmpeg is required to proceed.\nYou can configure it at Preferences > Libraries."));
1599 }
1600 // subformat index may not correspond directly to fmts[] index, convert it
1601 const auto adjustedFormatIndex = AdjustFormatIndex(context.subformat);
1602 if (channels > ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels)
1603 {
1604 throw ExportException(XO("Attempted to export %d channels, but maximum number of channels for selected output format is %d")
1605 .Format(
1606 channels,
1607 ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels )
1608 .Translation());
1609 }
1610
1611 const auto &tracks = TrackList::Get( project );
1612 bool ret = true;
1613
1614 if (adjustedFormatIndex >= FMT_LAST) {
1615 // TODO: more precise message
1616 throw ExportErrorException("FFmpeg:996");
1617 }
1618
1619 wxString shortname(ExportFFmpegOptions::fmts[adjustedFormatIndex].shortname);
1620 if (adjustedFormatIndex == FMT_OTHER)
1621 shortname = ExportPluginHelpers::GetParameterValue<std::string>(parameters, FEFormatID, "matroska");
1622
1623 context.exporter = std::make_unique<FFmpegExporter>(mFFmpeg, fName, channels, adjustedFormatIndex);
1624
1625 ret = context.exporter->Init(shortname.mb_str(), &project, static_cast<int>(sampleRate), metadata, parameters);
1626
1627 if (!ret) {
1628 // TODO: more precise message
1629 throw ExportErrorException("FFmpeg:1008");
1630 }
1631
1632 context.mixer = context.exporter->CreateMixer(tracks, selectionOnly,
1633 t0, t1,
1634 mixerSpec);
1635
1636 context.status = selectionOnly
1637 ? XO("Exporting selected audio as %s")
1638 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description )
1639 : XO("Exporting the audio as %s")
1640 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description );
1641
1642 return true;
1643}
1644
1646{
1647 delegate.SetStatusString(context.status);
1648 auto exportResult = ExportResult::Success;
1649 {
1650 while (exportResult == ExportResult::Success) {
1651 auto pcmNumSamples = context.mixer->Process();
1652 if (pcmNumSamples == 0)
1653 break;
1654
1655 short *pcmBuffer = (short *)context.mixer->GetBuffer();
1656
1657 if (!context.exporter->EncodeAudioFrame(pcmBuffer, pcmNumSamples))
1658 // All errors should already have been reported.
1659 return ExportResult::Error;
1660
1661 if(exportResult == ExportResult::Success)
1663 delegate, *context.mixer, context.t0, context.t1);
1664 }
1665 }
1666
1667 if ( exportResult != ExportResult::Cancelled )
1668 if ( !context.exporter->Finalize() ) // Finalize makes its own messages
1669 return ExportResult::Error;
1670 return exportResult;
1671}
1672
1673
1674void AddStringTagUTF8(char field[], int size, wxString value)
1675{
1676 memset(field,0,size);
1677 memcpy(field,value.ToUTF8(),(int)strlen(value.ToUTF8()) > size -1 ? size -1 : strlen(value.ToUTF8()));
1678}
1679
1680void AddStringTagANSI(char field[], int size, wxString value)
1681{
1682 memset(field,0,size);
1683 memcpy(field,value.mb_str(),(int)strlen(value.mb_str()) > size -1 ? size -1 : strlen(value.mb_str()));
1684}
1685
1687{
1688 if (tags == NULL)
1689 {
1690 return false;
1691 }
1692
1693 SetMetadata(tags, "album", TAG_ALBUM);
1694 SetMetadata(tags, "comment", TAG_COMMENTS);
1695 SetMetadata(tags, "genre", TAG_GENRE);
1696 SetMetadata(tags, "title", TAG_TITLE);
1697 SetMetadata(tags, "track", TAG_TRACK);
1698
1699 // Bug 2564: Add m4a tags
1700 if (mEncFormatDesc->GetAudioCodec() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_AAC))
1701 {
1702 SetMetadata(tags, "artist", TAG_ARTIST);
1703 SetMetadata(tags, "date", TAG_YEAR);
1704 }
1705 else
1706 {
1707 SetMetadata(tags, "author", TAG_ARTIST);
1708 SetMetadata(tags, "year", TAG_YEAR);
1709 }
1710
1711 return true;
1712}
1713
1714void FFmpegExporter::SetMetadata(const Tags *tags, const char *name, const wxChar *tag)
1715{
1716 if (tags->HasTag(tag))
1717 {
1718 wxString value = tags->GetTag(tag);
1719
1720 AVDictionaryWrapper metadata = mEncFormatCtx->GetMetadata();
1721
1722 metadata.Set(name, mSupportsUTF8 ? value : value.mb_str(), 0);
1723 mEncFormatCtx->SetMetadata(metadata);
1724 }
1725}
1726
1727
1728//----------------------------------------------------------------------------
1729// AskResample dialog
1730//----------------------------------------------------------------------------
1731
1732int FFmpegExporter::AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates)
1733{
1734#if defined(FFMPEG_AUTO_RESAMPLE)
1735 std::vector<int> rates;
1736
1737 for (int i = 0; sampRates[i]; ++i)
1738 {
1739 rates.push_back(sampRates[i]);
1740 }
1741
1742 std::sort(rates.begin(), rates.end());
1743
1744 int bestRate = 0;
1745 for (auto i : rates)
1746 {
1747 bestRate = i;
1748 if (i > rate)
1749 {
1750 break;
1751 }
1752 }
1753
1754 return bestRate;
1755#else
1756 wxDialogWrapper d(nullptr, wxID_ANY, XO("Invalid sample rate"));
1757 d.SetName();
1758 wxChoice *choice;
1760
1761 int selected = -1;
1762
1763 S.StartVerticalLay();
1764 {
1765 S.SetBorder(10);
1766 S.StartStatic(XO("Resample"));
1767 {
1768 S.StartHorizontalLay(wxALIGN_CENTER, false);
1769 {
1770 S.AddTitle(
1771 (bitrate == 0
1772 ? XO(
1773"The project sample rate (%d) is not supported by the current output\nfile format. ")
1774 .Format( rate )
1775 : XO(
1776"The project sample rate (%d) and bit rate (%d kbps) combination is not\nsupported by the current output file format. ")
1777 .Format( rate, bitrate/1000))
1778 + XO("You may resample to one of the rates below.")
1779 );
1780 }
1781 S.EndHorizontalLay();
1782
1783 S.StartHorizontalLay(wxALIGN_CENTER, false);
1784 {
1785 choice = S.AddChoice(XO("Sample Rates"),
1786 [&]{
1787 TranslatableStrings choices;
1788 for (int i = 0; sampRates[i] > 0; i++)
1789 {
1790 int label = sampRates[i];
1791 if ((!lowrate || label >= lowrate) && (!highrate || label <= highrate))
1792 {
1793 wxString name = wxString::Format(wxT("%d"),label);
1794 choices.push_back( Verbatim( name ) );
1795 if (label <= rate)
1796 selected = i;
1797 }
1798 }
1799 return choices;
1800 }(),
1801 std::max( 0, selected )
1802 );
1803 }
1804 S.EndHorizontalLay();
1805 }
1806 S.EndStatic();
1807
1808 S.AddStandardButtons();
1809 }
1810 S.EndVerticalLay();
1811
1812 d.Layout();
1813 d.Fit();
1814 d.SetMinSize(d.GetSize());
1815 d.Center();
1816
1817 if (d.ShowModal() == wxID_CANCEL) {
1818 return 0;
1819 }
1820
1821 return wxAtoi(choice->GetStringSelection());
1822#endif
1823}
1824
1826 []{ return std::make_unique< ExportFFmpeg >(); }
1827};
1828
@ 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_OPUS
@ FMT_M4A
@ FMT_OTHER
@ FMT_AC3
@ FMT_WMA2
@ FMT_AMRNB
@ FMT_LAST
static const std::vector< int > sampRates
Definition: ExportMP3.cpp:197
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:119
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:167
#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:83
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
FFmpegExportProcessor(std::shared_ptr< FFmpegFunctions > ffmpeg, int format)
struct FFmpegExportProcessor::@149 context
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:987
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
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 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 > OPUSOptions
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > WMAOptions
ExportOptionsEditor::SampleRateList ToSampleRateList(const int *rates)
void OnOpen(const CommandContext &context)
Definition: FileMenus.cpp:193
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