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#include "FifoBuffer.h"
25
26#include <wx/app.h>
27#include <wx/log.h>
28
29#include <wx/window.h>
30#include <wx/button.h>
31#include <wx/textctrl.h>
32
33#include "BasicSettings.h"
34#include "Mix.h"
35#include "Tags.h"
36#include "Track.h"
37#include "wxFileNameWrapper.h"
38
39#include "ExportFFmpegOptions.h"
40#include "SelectFile.h"
41#include "ShuttleGui.h"
42
43#include "ExportPluginHelpers.h"
45#include "FFmpegDefines.h"
48
49#if defined(WIN32) && _MSC_VER < 1900
50#define snprintf _snprintf
51#endif
52
53// Define this to automatically resample audio to the nearest supported sample rate
54#define FFMPEG_AUTO_RESAMPLE 1
55
57{
58 int subFormat = -1;
59 for (int i = 0; i <= FMT_OTHER; i++)
60 {
61 if (ExportFFmpegOptions::fmts[i].compiledIn) subFormat++;
62 if (subFormat == format || i == FMT_OTHER)
63 {
64 subFormat = i;
65 break;
66 }
67 }
68 return subFormat;
69}
70
71namespace
72{
73
74const int iAC3SampleRates[] =
75{ 32000, 44100, 48000, 0 };
76
77const int iWMASampleRates[] =
78{ 8000, 11025, 16000, 22050, 44100, 0};
79
82{
84 int index = 0;
85 while(rates[index] != 0)
86 list.push_back(rates[index++]);
87 return list;
88}
89
90// i18n-hint kbps abbreviates "thousands of bits per second"
91TranslatableString n_kbps(int n) { return XO("%d kbps").Format( n ); }
92TranslatableString f_kbps( double d ) { return XO("%.2f kbps").Format( d ); }
93
94enum : int
95{
97};
98
99const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AC3Options {
100 {
101 {
102 AC3OptionIDBitRate, XO("Bit Rate"),
103 160000,
105 {
106 32000,
107 40000,
108 48000,
109 56000,
110 64000,
111 80000,
112 96000,
113 112000,
114 128000,
115 160000,
116 192000,
117 224000,
118 256000,
119 320000,
120 384000,
121 448000,
122 512000,
123 576000,
124 640000
125 },
126 {
127 n_kbps( 32 ),
128 n_kbps( 40 ),
129 n_kbps( 48 ),
130 n_kbps( 56 ),
131 n_kbps( 64 ),
132 n_kbps( 80 ),
133 n_kbps( 96 ),
134 n_kbps( 112 ),
135 n_kbps( 128 ),
136 n_kbps( 160 ),
137 n_kbps( 192 ),
138 n_kbps( 224 ),
139 n_kbps( 256 ),
140 n_kbps( 320 ),
141 n_kbps( 384 ),
142 n_kbps( 448 ),
143 n_kbps( 512 ),
144 n_kbps( 576 ),
145 n_kbps( 640 ),
146 }
147 }, wxT("/FileFormats/AC3BitRate")
148 }
149};
150
151enum : int
152{
155
156//NB: user-entered values for AAC are not always followed; mono is clamped to 98-160, stereo 196-320
157const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AACOptions {
158 {
159 {
160 AACOptionIDQuality, XO("Quality (kbps)"),
161 256,
163 {98, 320}
164 }, wxT("/FileFormats/AACQuality")
165 }
166};
167
168enum : int
169{
172
173const std::initializer_list<PlainExportOptionsEditor::OptionDesc> AMRNBOptions {
174 {
175 {
176 AMRNBOptionIDBitRate, XO("Bit Rate"),
177 12200,
179 {
180 4750,
181 5150,
182 5900,
183 6700,
184 7400,
185 7950,
186 10200,
187 12200,
188 },
189 {
190 f_kbps( 4.75 ),
191 f_kbps( 5.15 ),
192 f_kbps( 5.90 ),
193 f_kbps( 6.70 ),
194 f_kbps( 7.40 ),
195 f_kbps( 7.95 ),
196 f_kbps( 10.20 ),
197 f_kbps( 12.20 ),
198 }
199 }, wxT("/FileFormats/AMRNBBitRate")
200 }
201};
202
203#ifdef SHOW_FFMPEG_OPUS_EXPORT
204enum : int
205{
207 OPUSOptionIDCompression,
212};
213
214const std::initializer_list<PlainExportOptionsEditor::OptionDesc> OPUSOptions {
215 {
216 {
217 OPUSOptionIDBitRate, XO("Bit Rate"),
218 128000,
220 {
221 6000,
222 8000,
223 16000,
224 24000,
225 32000,
226 40000,
227 48000,
228 64000,
229 80000,
230 96000,
231 128000,
232 160000,
233 192000,
234 256000
235 },
236 {
237 n_kbps( 6 ),
238 n_kbps( 8 ),
239 n_kbps( 16 ),
240 n_kbps( 24 ),
241 n_kbps( 32 ),
242 n_kbps( 40 ),
243 n_kbps( 48 ),
244 n_kbps( 64 ),
245 n_kbps( 80 ),
246 n_kbps( 96 ),
247 n_kbps( 128 ),
248 n_kbps( 160 ),
249 n_kbps( 192 ),
250 n_kbps( 256 ),
251 }
252 }, wxT("/FileFormats/OPUSBitrate")
253 },
254 {
255 {
256 OPUSOptionIDCompression, XO("Compression"),
257 10,
259 { 0, 10 }
260 }, wxT("/FileFormats/OPUSCompression")
261 },
262 {
263 {
264 OPUSOptionIDFrameDuration, XO("Frame Duration"),
265 std::string("20"),
267 {
268 std::string("2.5"),
269 std::string("5"),
270 std::string("10"),
271 std::string("20"),
272 std::string("40"),
273 std::string("60")
274 },
275 {
276 XO("2.5 ms"),
277 XO("5 ms"),
278 XO("10 ms"),
279 XO("20 ms"),
280 XO("40 ms"),
281 XO("60 ms"),
282 }
283 }, wxT("/FileFormats/OPUSFrameDuration")
284 },
285 {
286 {
287 OPUSOptionIDVBRMode, XO("Vbr Mode"),
288 std::string("on"),
290 { std::string("off"), std::string("on"), std::string("constrained") },
291 { XO("Off"), XO("On"), XO("Constrained") }
292 }, wxT("/FileFormats/OPUSVbrMode")
293 },
294 {
295 {
296 OPUSOptionIDApplication, XO("Application"),
297 std::string("audio"),
299 { std::string("voip"), std::string("audio"), std::string("lowdelay") },
300 { XO("VOIP"), XO("Audio"), XO("Low Delay") }
301 }, wxT("/FileFormats/OPUSApplication")
302 },
303 {
304 {
305 OPUSOptionIDCutoff, XO("Cutoff"),
306 std::string("0"),
308 {
309 std::string("0"),
310 std::string("4000"),
311 std::string("6000"),
312 std::string("8000"),
313 std::string("12000"),
314 std::string("20000")
315 },
316 {
317 XO("Disabled"),
318 XO("Narrowband"),
319 XO("Mediumband"),
320 XO("Wideband"),
321 XO("Super Wideband"),
322 XO("Fullband")
323 }
324 }, wxT("/FileFormats/OPUSCutoff")
325 },
326};
327#endif
328
329enum : int
330{
333
334const std::initializer_list<PlainExportOptionsEditor::OptionDesc> WMAOptions {
335 {
336 {
337 WMAOptionIDBitRate, XO("Bit Rate"),
338 128000,
340 {
341 24000,
342 32000,
343 40000,
344 48000,
345 64000,
346 80000,
347 96000,
348 128000,
349 160000,
350 192000,
351 256000,
352 320000
353 },
354 {
355 n_kbps(24),
356 n_kbps(32),
357 n_kbps(40),
358 n_kbps(48),
359 n_kbps(64),
360 n_kbps(80),
361 n_kbps(96),
362 n_kbps(128),
363 n_kbps(160),
364 n_kbps(192),
365 n_kbps(256),
366 n_kbps(320),
367 }
368 }, wxT("/FileFormats/WMABitRate")
369 }
370};
371
372const std::vector<ExportOption> FFmpegOptions {
373 { FELanguageID, {}, std::string() },
374 { FESampleRateID, {}, 0 },
375 { FEBitrateID, {}, 0 },
376 { FETagID, {}, std::string() },
377 { FEQualityID, {}, 0 },
378 { FECutoffID, {}, 0},
379 { FEBitReservoirID, {}, true },
380 { FEVariableBlockLenID, {}, true },
381 { FECompLevelID, {}, -1 },
382 { FEFrameSizeID, {}, 0 },
383 { FELPCCoeffsID, {}, 0 },
384 { FEMinPredID, {}, -1 },
385 { FEMaxPredID, {}, -1 },
386 { FEMinPartOrderID, {}, -1 },
387 { FEMaxPartOrderID, {}, -1 },
388 { FEPredOrderID, {}, 0 },
389 { FEMuxRateID, {}, 0 },
390 { FEPacketSizeID, {}, 0 },
391 { FECodecID, {}, std::string() },
392 { FEFormatID, {}, std::string() }
393};
394
396 : public ExportOptionsEditor
398{
399 std::unordered_map<int, ExportValue> mValues;
400 std::shared_ptr<FFmpegFunctions> mFFmpeg;
402 //created on-demand
403 mutable std::unique_ptr<AVCodecWrapper> mAVCodec;
404public:
405
407 : mListener(listener)
408 {
409 }
410
411 void PopulateUI(ShuttleGui& S) override
412 {
413 CheckFFmpeg(true);
414 //Continue anyway, as we do not need ffmpeg functions to build and fill in the UI
415
416 mParent = S.GetParent();
417
418 S.StartHorizontalLay(wxCENTER);
419 {
420 S.StartVerticalLay(wxCENTER, 0);
421 {
422 S.AddButton(XXO("Open custom FFmpeg format options"))
423 ->Bind(wxEVT_BUTTON, &ExportOptionsFFmpegCustomEditor::OnOpen, this);
424 S.StartMultiColumn(2, wxCENTER);
425 {
426 S.AddPrompt(XXO("Current Format:"));
427 mFormat = S.Name(XXO("Current Format:"))
428 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
429 S.AddPrompt(XXO("Current Codec:"));
430 mCodec = S.Name(XXO("Current Codec:"))
431 .Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
432 }
433 S.EndMultiColumn();
434 }
435 S.EndHorizontalLay();
436 }
437 S.EndHorizontalLay();
438
439 UpdateCodecAndFormat();
440 }
441
443 {
444 Load(*gPrefs);
445 return true;
446 }
447
448 int GetOptionsCount() const override
449 {
450 return static_cast<int>(FFmpegOptions.size());
451 }
452
453 bool GetOption(int index, ExportOption& option) const override
454 {
455 if(index >= 0 && index < FFmpegOptions.size())
456 {
457 option = FFmpegOptions[index];
458 return true;
459 }
460 return false;
461 }
462
463 bool GetValue(int id, ExportValue& value) const override
464 {
465 auto it = mValues.find(id);
466 if(it != mValues.end())
467 {
468 value = it->second;
469 return true;
470 }
471 return false;
472 }
473
474 bool SetValue(int id, const ExportValue& value) override
475 {
476 return false;
477 }
478
480 {
481 if(!mAVCodec)
482 {
483 auto it = mValues.find(FECodecID);
484 if(it == mValues.end())
485 return {};
486
487 const auto codecId = *std::get_if<std::string>(&it->second);
488 if (mFFmpeg) {
489 mAVCodec = mFFmpeg->CreateEncoder(codecId.c_str());
490 }
491 }
492 if(!mAVCodec)
493 return {};
494
495 if(const auto rates = mAVCodec->GetSupportedSamplerates())
496 return ToSampleRateList(rates);
497 return {};
498 }
499
500 void Load(const audacity::BasicSettings& config) override
501 {
502 mValues[FELanguageID] = std::string(config.Read(wxT("/FileFormats/FFmpegLanguage"), wxT("")).ToUTF8());
503 mValues[FESampleRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegSampleRate"), 0L));
504 mValues[FEBitrateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegBitRate"), 0L));
505 mValues[FETagID] = std::string(config.Read(wxT("/FileFormats/FFmpegTag"), wxT(""))
506 .mb_str(wxConvUTF8));
507 mValues[FEQualityID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegQuality"), -99999L));
508 mValues[FECutoffID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCutOff"), 0L));
509 mValues[FEBitReservoirID] = config.ReadBool(wxT("/FileFormats/FFmpegBitReservoir"), true);
510 mValues[FEVariableBlockLenID] = config.ReadBool(wxT("/FileFormats/FFmpegVariableBlockLen"), true);
511 mValues[FECompLevelID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegCompLevel"), -1L));
512 mValues[FEFrameSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegFrameSize"), 0L));
513
514 mValues[FELPCCoeffsID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegLPCCoefPrec"), 0L));
515 mValues[FEMinPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPredOrder"), -1L));
516 mValues[FEMaxPredID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPredOrder"), -1L));
517 mValues[FEMinPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMinPartOrder"), -1L));
518 mValues[FEMaxPartOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMaxPartOrder"), -1L));
519 mValues[FEPredOrderID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPredOrderMethod"), 0L));
520 mValues[FEMuxRateID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegMuxRate"), 0L));
521 mValues[FEPacketSizeID] = static_cast<int>(config.Read(wxT("/FileFormats/FFmpegPacketSize"), 0L));
522 mValues[FECodecID] = std::string(config.Read(wxT("/FileFormats/FFmpegCodec")));
523 mValues[FEFormatID] = std::string(config.Read(wxT("/FileFormats/FFmpegFormat")));
524 }
525
527 {
528
529 }
530
531private:
532
533 bool CheckFFmpeg(bool showError)
534 {
535 // Show "Locate FFmpeg" dialog
536 if(!mFFmpeg)
537 {
538 mFFmpeg = FFmpegFunctions::Load();
539 if (!mFFmpeg)
540 {
542 return LoadFFmpeg(showError);
543 }
544 }
545 return true;
546 }
547
549 {
550 mFormat->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"), wxT("")));
551 mCodec->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegCodec"), wxT("")));
552 }
553
554 void OnOpen(const wxCommandEvent&)
555 {
556 if(!CheckFFmpeg(true))
557 return;
558
559 #ifdef __WXMAC__
560 // Bug 2077 Must be a parent window on OSX or we will appear behind.
561 auto pWin = wxGetTopLevelParent( mParent );
562 #else
563 // Use GetTopWindow on windows as there is no hWnd with top level parent.
564 auto pWin = wxTheApp->GetTopWindow();
565 #endif
566
567 ExportFFmpegOptions od(pWin);
568 od.ShowModal();
569 //ExportFFmpegOptions uses gPrefs to store options
570 //Instead we could provide it with instance of wxConfigBase
571 //constructed locally and read from it later
572 Load(*gPrefs);
573 mAVCodec.reset();
574
575 UpdateCodecAndFormat();
576 if(mListener)
577 mListener->OnSampleRateListChange();
578 }
579
580 wxWindow *mParent {nullptr};
581 wxTextCtrl *mFormat {nullptr};
582 wxTextCtrl *mCodec {nullptr};
583};
584
585}
586
588class FFmpegExporter final
589{
590 static constexpr auto MaxAudioPacketSize { 128 * 1024 };
591public:
592
593 FFmpegExporter(std::shared_ptr<FFmpegFunctions> ffmpeg,
594 const wxFileNameWrapper& filename,
595 int numChannels,
596 int subformat);
597
599 bool Init(const char *shortname,
601 int sampleRate,
602 const Tags *metadata,
603 const ExportProcessor::Parameters& parameters);
604
606 bool EncodeAudioFrame(int16_t *pFrame, size_t frameSize);
607
609 bool Finalize();
610
611 std::unique_ptr<Mixer> CreateMixer(
612 const AudacityProject& project, bool selectionOnly, double startTime,
613 double stopTime, 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<FifoBuffer> 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(
729 const AudacityProject& project, bool selectionOnly, double startTime,
730 double stopTime, MixerOptions::Downmix* mixerSpec)
731{
733 project, selectionOnly, startTime, stopTime, mChannels, mDefaultFrameSize,
734 true, mSampleRate, int16Sample, mixerSpec);
735}
736
737
739{
741
742 int avfver = mFFmpeg ? mFFmpeg->AVFormatVersion.GetIntVersion() : 0;
743
744 int newfmt;
745 // Adds export types from the export type list
746 for (newfmt = 0; newfmt < FMT_LAST; newfmt++)
747 {
748 wxString shortname(ExportFFmpegOptions::fmts[newfmt].shortname);
749 // Don't hide export types when there's no av-libs, and don't hide FMT_OTHER
750 if (newfmt < FMT_OTHER && mFFmpeg)
751 {
752 // Format/Codec support is compiled in?
753 auto avoformat = mFFmpeg->GuessOutputFormat(shortname.mb_str(), nullptr, nullptr);
754 auto avcodec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(ExportFFmpegOptions::fmts[newfmt].codecid));
755
756 if (avoformat == NULL || avcodec == NULL)
757 {
759 continue;
760 }
761 }
762 FormatInfo formatInfo {};
763 formatInfo.format = ExportFFmpegOptions::fmts[newfmt].name;
764 formatInfo.extensions.push_back(ExportFFmpegOptions::fmts[newfmt].extension);
765 // For some types add other extensions
766 switch(newfmt)
767 {
768 case FMT_M4A:
769 formatInfo.extensions.push_back(wxT("3gp"));
770 formatInfo.extensions.push_back(wxT("m4r"));
771 formatInfo.extensions.push_back(wxT("mp4"));
772 break;
773 case FMT_WMA2:
774 formatInfo.extensions.push_back(wxT("asf"));
775 formatInfo.extensions.push_back(wxT("wmv"));
776 break;
777 default:
778 break;
779 }
780 formatInfo.maxChannels = ExportFFmpegOptions::fmts[newfmt].maxchannels;
781 formatInfo.description = ExportFFmpegOptions::fmts[newfmt].description;
782
783 const int canmeta = ExportFFmpegOptions::fmts[newfmt].canmetadata;
784 formatInfo.canMetaData = canmeta && (canmeta == AV_CANMETA || canmeta <= avfver);
785
786 mFormatInfos.push_back(std::move(formatInfo));
787 }
788}
789
791
792std::unique_ptr<ExportOptionsEditor>
794{
796 {
797 case FMT_M4A:
798 return std::make_unique<PlainExportOptionsEditor>(AACOptions, listener);
799 case FMT_AC3:
800 return std::make_unique<PlainExportOptionsEditor>(
803 listener);
804 case FMT_AMRNB:
805 return std::make_unique<PlainExportOptionsEditor>(
808 listener);
809#ifdef SHOW_FFMPEG_OPUS_EXPORT
810 case FMT_OPUS:
811 return std::make_unique<PlainExportOptionsEditor>(OPUSOptions, listener);
812#endif
813 case FMT_WMA2:
814 return std::make_unique<PlainExportOptionsEditor>(
817 listener);
818 case FMT_OTHER:
819 return std::make_unique<ExportOptionsFFmpegCustomEditor>(listener);
820 }
821 return {};
822}
823
825{
826 return static_cast<int>(mFormatInfos.size());
827}
828
830{
831 if(index >= 0 && index < mFormatInfos.size())
832 return mFormatInfos[index];
833 return mFormatInfos[FMT_OTHER];
834}
835
836bool ExportFFmpeg::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format)) const
837{
838 bool result = true;
839
840 // Show "Locate FFmpeg" dialog
842 if (!mFFmpeg)
843 {
846
847 return LoadFFmpeg(true);
848 }
849
850 return result;
851}
852
853std::unique_ptr<ExportProcessor> ExportFFmpeg::CreateProcessor(int format) const
854{
855 return std::make_unique<FFmpegExportProcessor>(mFFmpeg, format);
856}
857
858
859bool FFmpegExporter::Init(const char *shortname,
861 int sampleRate,
862 const Tags *metadata,
863 const ExportProcessor::Parameters& parameters)
864{
865 if (!mFFmpeg)
866 return false;
867
868 // See if libavformat has modules that can write our output format. If so, mEncFormatDesc
869 // will describe the functions used to write the format (used internally by libavformat)
870 // and the default video/audio codecs that the format uses.
871 const auto path = mName.GetFullPath();
872 if ((mEncFormatDesc = mFFmpeg->GuessOutputFormat(shortname, OSINPUT(path), nullptr)) == nullptr)
873 {
874 throw ExportException(_("FFmpeg : ERROR - Can't determine format description for file \"%s\".").Format(path));
875 }
876
877 // mEncFormatCtx is used by libavformat to carry around context data re our output file.
878 mEncFormatCtx = mFFmpeg->CreateAVFormatContext();
879 if (!mEncFormatCtx)
880 {
881 throw ExportException(_("FFmpeg : ERROR - Can't allocate output format context."));
882 }
883
884 // Initialise the output format context.
885 mEncFormatCtx->SetOutputFormat(mFFmpeg->CreateAVOutputFormatWrapper(mEncFormatDesc->GetWrappedValue()));
886 mEncFormatCtx->SetFilename(OSINPUT(path));
887
888 // At the moment Audacity can export only one audio stream
889 if ((mEncAudioStream = mEncFormatCtx->CreateStream()) == nullptr)
890 {
891 throw ExportException(_("FFmpeg : ERROR - Can't add audio stream to output file \"%s\"."));
892 }
893
894 // Documentation for avformat_new_stream says
895 // "User is required to call avcodec_close() and avformat_free_context() to clean
896 // up the allocation by avformat_new_stream()."
897
898 // We use smart pointers that ensure these cleanups either in their destructors or
899 // sooner if they are reset. These are std::unique_ptr with nondefault deleter
900 // template parameters.
901
902 // mEncFormatCtx takes care of avformat_free_context(), so
903 // mEncAudioStream can be a plain pointer.
904
905 // mEncAudioCodecCtx now becomes responsible for closing the codec:
906 mEncAudioCodecCtx = mEncAudioStream->GetAVCodecContext();
907 mEncAudioStream->SetId(0);
908
909 // Open the output file.
910 if (!(mEncFormatDesc->GetFlags() & AUDACITY_AVFMT_NOFILE))
911 {
913 mEncFormatCtx->OpenOutputContext(path);
914
916 {
917 throw ExportException(_("FFmpeg : ERROR - Can't open output file \"%s\" to write. Error code is %d.")
918 .Format(path, static_cast<int>(result)));
919 }
920 }
921
922 // Open the audio stream's codec and initialise any stream related data.
923 if(!InitCodecs(sampleRate, parameters))
924 return false;
925
926 if (mEncAudioStream->SetParametersFromContext(*mEncAudioCodecCtx) < 0)
927 return false;
928
929 if (metadata == NULL)
930 metadata = &Tags::Get( *project );
931
932 // Add metadata BEFORE writing the header.
933 // At the moment that works with ffmpeg-git and ffmpeg-0.5 for MP4.
935 const auto avfver = mFFmpeg->AVFormatVersion.GetIntVersion();
936 if (canmeta && (canmeta == AV_CANMETA || canmeta <= avfver))
937 {
939 AddTags(metadata);
940 }
941
942 // Write headers to the output file.
943 int err =
944 mFFmpeg->avformat_write_header(mEncFormatCtx->GetWrappedValue(), nullptr);
945
946 if (err < 0)
947 {
948 throw ExportException(XO("FFmpeg : ERROR - Can't write headers to output file \"%s\". Error code is %d.")
949 .Format( path, err )
950 .Translation());
951 }
952
953 return true;
954}
955
956bool FFmpegExporter::CheckSampleRate(int rate, int lowrate, int highrate, const int *sampRates)
957{
958 if (lowrate && highrate)
959 {
960 if (rate < lowrate || rate > highrate)
961 {
962 return false;
963 }
964 }
965
966 if (sampRates)
967 {
968 for (int i = 0; sampRates[i] > 0; i++)
969 {
970 if (rate == sampRates[i])
971 {
972 return true;
973 }
974 }
975 }
976
977 return false;
978}
979
981 const ExportProcessor::Parameters& parameters)
982{
983 std::unique_ptr<AVCodecWrapper> codec;
984
986
987 // Get the sample rate from the passed settings if we haven't set it before.
988 // Doing this only when not set allows us to carry the sample rate from one
989 // iteration of ExportMultiple to the next. This prevents multiple resampling
990 // dialogs in the event the codec can't support the specified rate.
991 if (!mSampleRate)
992 {
993 //TODO: Does not work with export multiple any more...
995 }
996
997 // Configure the audio stream's codec context.
998
999 const auto codecID = ExportFFmpegOptions::fmts[mSubFormat].codecid;
1000
1001 mEncAudioCodecCtx->SetGlobalQuality(-99999); //quality mode is off by default;
1002
1003 // Each export type has its own settings
1004 switch (mSubFormat)
1005 {
1006 case FMT_M4A:
1007 {
1009
1010 q = wxClip( q, 98 * mChannels, 160 * mChannels );
1011 // Set bit rate to between 98 kbps and 320 kbps (if two channels)
1012 mEncAudioCodecCtx->SetBitRate(q * 1000);
1014 mEncAudioCodecCtx->SetCutoff(0);
1015
1016 break;
1017 }
1018 case FMT_AC3:
1020 if (!CheckSampleRate(
1022 iAC3SampleRates[2],
1023 &iAC3SampleRates[0]))
1024 {
1026 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1027 iAC3SampleRates[0],
1028 iAC3SampleRates[2],
1029 &iAC3SampleRates[0]);
1030 }
1031 break;
1032 case FMT_AMRNB:
1033 mSampleRate = 8000;
1035 break;
1036#ifdef SHOW_FFMPEG_OPUS_EXPORT
1037 case FMT_OPUS:
1038 options.Set("b", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDBitRate, "128000"), 0);
1039 options.Set("vbr", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDVBRMode, "on"), 0);
1040 options.Set("compression_level", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCompression, "10"), 0);
1041 options.Set("frame_duration", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDFrameDuration, "20"), 0);
1042 options.Set("application", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDApplication, "audio"), 0);
1043 options.Set("cutoff", ExportPluginHelpers::GetParameterValue<std::string>(parameters, OPUSOptionIDCutoff, "0"), 0);
1044 options.Set("mapping_family", mChannels <= 2 ? "0" : "255", 0);
1045 break;
1046#endif
1047 case FMT_WMA2:
1049 if (!CheckSampleRate(
1051 iWMASampleRates[4],
1052 &iWMASampleRates[0]))
1053 {
1055 mEncAudioCodecCtx->GetBitRate(), mSampleRate,
1056 iWMASampleRates[0],
1057 iWMASampleRates[4],
1058 &iWMASampleRates[0]);
1059 }
1060 break;
1061 case FMT_OTHER:
1062 {
1063 AVDictionaryWrapper streamMetadata = mEncAudioStream->GetMetadata();
1064 streamMetadata.Set(
1065 "language",
1066 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FELanguageID), 0);
1067
1068 mEncAudioStream->SetMetadata(streamMetadata);
1069
1070 mEncAudioCodecCtx->SetSampleRate(
1071 ExportPluginHelpers::GetParameterValue(parameters, FESampleRateID, 0));
1072
1073 if (mEncAudioCodecCtx->GetSampleRate() != 0)
1074 mSampleRate = mEncAudioCodecCtx->GetSampleRate();
1075
1076 mEncAudioCodecCtx->SetBitRate(
1077 ExportPluginHelpers::GetParameterValue(parameters, FEBitrateID, 0));
1078
1079 mEncAudioCodecCtx->SetCodecTagFourCC(
1080 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FETagID).c_str());
1081
1082 mEncAudioCodecCtx->SetGlobalQuality(
1083 ExportPluginHelpers::GetParameterValue(parameters, FEQualityID, -99999));
1084 mEncAudioCodecCtx->SetCutoff(
1085 ExportPluginHelpers::GetParameterValue(parameters, FECutoffID, 0));
1086 mEncAudioCodecCtx->SetFlags2(0);
1087
1088 if (ExportPluginHelpers::GetParameterValue(parameters, FEBitReservoirID, true))
1089 options.Set("reservoir", "1", 0);
1090
1091 if (ExportPluginHelpers::GetParameterValue(parameters, FEVariableBlockLenID, true))
1092 mEncAudioCodecCtx->SetFlags2(
1093 mEncAudioCodecCtx->GetFlags2() | 0x0004); // WMA only?
1094
1095 mEncAudioCodecCtx->SetCompressionLevel(
1096 ExportPluginHelpers::GetParameterValue(parameters, FECompLevelID, -1));
1097 mEncAudioCodecCtx->SetFrameSize(
1098 ExportPluginHelpers::GetParameterValue(parameters, FEFrameSizeID, 0));
1099
1100 // FIXME The list of supported options for the selected encoder should be
1101 // extracted instead of a few hardcoded
1102
1103 options.Set(
1104 "lpc_coeff_precision",
1105 ExportPluginHelpers::GetParameterValue(parameters, FELPCCoeffsID, 0));
1106 options.Set(
1107 "min_prediction_order",
1108 ExportPluginHelpers::GetParameterValue(parameters, FEMinPredID, -1));
1109 options.Set(
1110 "max_prediction_order",
1111 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPredID, -1));
1112 options.Set(
1113 "min_partition_order",
1114 ExportPluginHelpers::GetParameterValue(parameters, FEMinPartOrderID, -1));
1115 options.Set(
1116 "max_partition_order",
1117 ExportPluginHelpers::GetParameterValue(parameters, FEMaxPartOrderID, -1));
1118 options.Set(
1119 "prediction_order_method",
1120 ExportPluginHelpers::GetParameterValue(parameters, FEPredOrderID, 0));
1121 options.Set(
1122 "muxrate",
1123 ExportPluginHelpers::GetParameterValue(parameters, FEMuxRateID, 0));
1124
1125 mEncFormatCtx->SetPacketSize(
1126 ExportPluginHelpers::GetParameterValue(parameters, FEPacketSizeID, 0));
1127
1128 codec = mFFmpeg->CreateEncoder(
1129 ExportPluginHelpers::GetParameterValue<std::string>(parameters, FECodecID).c_str());
1130
1131 if (!codec)
1132 codec = mFFmpeg->CreateEncoder(mEncFormatDesc->GetAudioCodec());
1133 }
1134 break;
1135 default:
1136 return false;
1137 }
1138
1139 // This happens if user refused to resample the project
1140 if (mSampleRate == 0) return false;
1141
1142 if (mEncAudioCodecCtx->GetGlobalQuality() >= 0)
1143 {
1144 mEncAudioCodecCtx->SetFlags(
1146 }
1147 else
1148 {
1149 mEncAudioCodecCtx->SetGlobalQuality(0);
1150 }
1151
1152 mEncAudioCodecCtx->SetGlobalQuality(mEncAudioCodecCtx->GetGlobalQuality() * AUDACITY_FF_QP2LAMBDA);
1153 mEncAudioCodecCtx->SetSampleRate(mSampleRate);
1154 mEncAudioCodecCtx->SetChannelLayout(mFFmpeg->CreateDefaultChannelLayout(mChannels).get());
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 = std::make_unique<FifoBuffer>(mDefaultFrameSize * mChannels * sizeof(int16_t));
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 auto nFifoBytes =
1475 mEncAudioFifo->GetAvailable(); // 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 %lld 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 (mEncAudioFifo->Read(mEncAudioFifoOutBuf.data(), nFifoBytes) == nFifoBytes)
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
1560 // Put the raw audio samples into the FIFO.
1561 ret = mEncAudioFifo->Write(pRawSamples, nBytesToWrite);
1562
1563 if (ret != nBytesToWrite) {
1564 throw ExportErrorException("FFmpeg:913");
1565 }
1566
1567 if (nAudioFrameSizeOut > mEncAudioFifoOutBufSize) {
1568 throw ExportException(_("FFmpeg : ERROR - nAudioFrameSizeOut too large."));
1569 }
1570
1571 // Read raw audio samples out of the FIFO in nAudioFrameSizeOut byte-sized groups to encode.
1572 while (mEncAudioFifo->GetAvailable() >= nAudioFrameSizeOut)
1573 {
1574 mEncAudioFifo->Read(
1575 mEncAudioFifoOutBuf.data(), nAudioFrameSizeOut);
1576
1577 std::unique_ptr<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();
1578
1579 ret = EncodeAudio(*pkt, // out
1580 mEncAudioFifoOutBuf.data(), // in
1582
1583 if (ret < 0)
1584 return false;
1585 }
1586 return true;
1587}
1588
1589FFmpegExportProcessor::FFmpegExportProcessor(std::shared_ptr<FFmpegFunctions> ffmpeg, int subformat )
1590 : mFFmpeg(std::move(ffmpeg))
1591{
1592 context.subformat = subformat;
1593}
1594
1596 const Parameters& parameters,
1597 const wxFileNameWrapper& fName,
1598 double t0, double t1, bool selectionOnly,
1599 double sampleRate, unsigned channels,
1600 MixerOptions::Downmix* mixerSpec,
1601 const Tags* metadata)
1602{
1603 context.t0 = t0;
1604 context.t1 = t1;
1605
1606 if (!FFmpegFunctions::Load())
1607 {
1608 throw ExportException(_("Properly configured FFmpeg is required to proceed.\nYou can configure it at Preferences > Libraries."));
1609 }
1610 // subformat index may not correspond directly to fmts[] index, convert it
1611 const auto adjustedFormatIndex = AdjustFormatIndex(context.subformat);
1612 if (channels > ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels)
1613 {
1614 throw ExportException(XO("Attempted to export %d channels, but maximum number of channels for selected output format is %d")
1615 .Format(
1616 channels,
1617 ExportFFmpegOptions::fmts[adjustedFormatIndex].maxchannels )
1618 .Translation());
1619 }
1620
1621 bool ret = true;
1622
1623 if (adjustedFormatIndex >= FMT_LAST) {
1624 // TODO: more precise message
1625 throw ExportErrorException("FFmpeg:996");
1626 }
1627
1628 wxString shortname(ExportFFmpegOptions::fmts[adjustedFormatIndex].shortname);
1629 if (adjustedFormatIndex == FMT_OTHER)
1630 shortname = ExportPluginHelpers::GetParameterValue<std::string>(parameters, FEFormatID, "matroska");
1631
1632 context.exporter = std::make_unique<FFmpegExporter>(mFFmpeg, fName, channels, adjustedFormatIndex);
1633
1634 ret = context.exporter->Init(shortname.mb_str(), &project, static_cast<int>(sampleRate), metadata, parameters);
1635
1636 if (!ret) {
1637 // TODO: more precise message
1638 throw ExportErrorException("FFmpeg:1008");
1639 }
1640
1641 context.mixer =
1642 context.exporter->CreateMixer(project, selectionOnly, t0, t1, mixerSpec);
1643
1644 context.status = selectionOnly
1645 ? XO("Exporting selected audio as %s")
1646 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description )
1647 : XO("Exporting the audio as %s")
1648 .Format( ExportFFmpegOptions::fmts[adjustedFormatIndex].description );
1649
1650 return true;
1651}
1652
1654{
1655 delegate.SetStatusString(context.status);
1656 auto exportResult = ExportResult::Success;
1657 {
1658 while (exportResult == ExportResult::Success) {
1659 auto pcmNumSamples = context.mixer->Process();
1660 if (pcmNumSamples == 0)
1661 break;
1662
1663 short *pcmBuffer = (short *)context.mixer->GetBuffer();
1664
1665 if (!context.exporter->EncodeAudioFrame(pcmBuffer, pcmNumSamples))
1666 // All errors should already have been reported.
1667 return ExportResult::Error;
1668
1669 if(exportResult == ExportResult::Success)
1671 delegate, *context.mixer, context.t0, context.t1);
1672 }
1673 }
1674
1675 if ( exportResult != ExportResult::Cancelled )
1676 if ( !context.exporter->Finalize() ) // Finalize makes its own messages
1677 return ExportResult::Error;
1678 return exportResult;
1679}
1680
1681
1682void AddStringTagUTF8(char field[], int size, wxString value)
1683{
1684 memset(field,0,size);
1685 memcpy(field,value.ToUTF8(),(int)strlen(value.ToUTF8()) > size -1 ? size -1 : strlen(value.ToUTF8()));
1686}
1687
1688void AddStringTagANSI(char field[], int size, wxString value)
1689{
1690 memset(field,0,size);
1691 memcpy(field,value.mb_str(),(int)strlen(value.mb_str()) > size -1 ? size -1 : strlen(value.mb_str()));
1692}
1693
1695{
1696 if (tags == NULL)
1697 {
1698 return false;
1699 }
1700
1701 SetMetadata(tags, "album", TAG_ALBUM);
1702 SetMetadata(tags, "comment", TAG_COMMENTS);
1703 SetMetadata(tags, "genre", TAG_GENRE);
1704 SetMetadata(tags, "title", TAG_TITLE);
1705 SetMetadata(tags, "track", TAG_TRACK);
1706
1707 // Bug 2564: Add m4a tags
1708 if (mEncFormatDesc->GetAudioCodec() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_AAC))
1709 {
1710 SetMetadata(tags, "artist", TAG_ARTIST);
1711 SetMetadata(tags, "date", TAG_YEAR);
1712 }
1713 else
1714 {
1715 SetMetadata(tags, "author", TAG_ARTIST);
1716 SetMetadata(tags, "year", TAG_YEAR);
1717 }
1718
1719 return true;
1720}
1721
1722void FFmpegExporter::SetMetadata(const Tags *tags, const char *name, const wxChar *tag)
1723{
1724 if (tags->HasTag(tag))
1725 {
1726 wxString value = tags->GetTag(tag);
1727
1728 AVDictionaryWrapper metadata = mEncFormatCtx->GetMetadata();
1729
1730 metadata.Set(name, mSupportsUTF8 ? value : value.mb_str(), 0);
1731 mEncFormatCtx->SetMetadata(metadata);
1732 }
1733}
1734
1735
1736//----------------------------------------------------------------------------
1737// AskResample dialog
1738//----------------------------------------------------------------------------
1739
1740int FFmpegExporter::AskResample(int bitrate, int rate, int lowrate, int highrate, const int *sampRates)
1741{
1742#if defined(FFMPEG_AUTO_RESAMPLE)
1743 std::vector<int> rates;
1744
1745 for (int i = 0; sampRates[i]; ++i)
1746 {
1747 rates.push_back(sampRates[i]);
1748 }
1749
1750 std::sort(rates.begin(), rates.end());
1751
1752 int bestRate = 0;
1753 for (auto i : rates)
1754 {
1755 bestRate = i;
1756 if (i > rate)
1757 {
1758 break;
1759 }
1760 }
1761
1762 return bestRate;
1763#else
1764 wxDialogWrapper d(nullptr, wxID_ANY, XO("Invalid sample rate"));
1765 d.SetName();
1766 wxChoice *choice;
1768
1769 int selected = -1;
1770
1771 S.StartVerticalLay();
1772 {
1773 S.SetBorder(10);
1774 S.StartStatic(XO("Resample"));
1775 {
1776 S.StartHorizontalLay(wxALIGN_CENTER, false);
1777 {
1778 S.AddTitle(
1779 (bitrate == 0
1780 ? XO(
1781"The project sample rate (%d) is not supported by the current output\nfile format. ")
1782 .Format( rate )
1783 : XO(
1784"The project sample rate (%d) and bit rate (%d kbps) combination is not\nsupported by the current output file format. ")
1785 .Format( rate, bitrate/1000))
1786 + XO("You may resample to one of the rates below.")
1787 );
1788 }
1789 S.EndHorizontalLay();
1790
1791 S.StartHorizontalLay(wxALIGN_CENTER, false);
1792 {
1793 choice = S.AddChoice(XO("Sample Rates"),
1794 [&]{
1795 TranslatableStrings choices;
1796 for (int i = 0; sampRates[i] > 0; i++)
1797 {
1798 int label = sampRates[i];
1799 if ((!lowrate || label >= lowrate) && (!highrate || label <= highrate))
1800 {
1801 wxString name = wxString::Format(wxT("%d"),label);
1802 choices.push_back( Verbatim( name ) );
1803 if (label <= rate)
1804 selected = i;
1805 }
1806 }
1807 return choices;
1808 }(),
1809 std::max( 0, selected )
1810 );
1811 }
1812 S.EndHorizontalLay();
1813 }
1814 S.EndStatic();
1815
1816 S.AddStandardButtons();
1817 }
1818 S.EndVerticalLay();
1819
1820 d.Layout();
1821 d.Fit();
1822 d.SetMinSize(d.GetSize());
1823 d.Center();
1824
1825 if (d.ShowModal() == wxID_CANCEL) {
1826 return 0;
1827 }
1828
1829 return wxAtoi(choice->GetStringSelection());
1830#endif
1831}
1832
1834 []{ return std::make_unique< ExportFFmpeg >(); }
1835};
1836
@ AUDACITY_AV_CODEC_ID_AC3
Definition: AVCodecID.h:297
@ AUDACITY_AV_CODEC_ID_AAC
Definition: AVCodecID.h:296
wxT("CloseDown"))
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:197
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:121
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
wxString name
Definition: TagsEditor.cpp:166
TranslatableString label
Definition: TagsEditor.cpp:165
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:51
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 AudacityProject &project, 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
struct FFmpegExportProcessor::@148 context
ExportResult Process(ExportProcessorDelegate &delegate) override
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
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
std::unique_ptr< FifoBuffer > mEncAudioFifo
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 AudacityProject &project, 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:640
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
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:105
void OnOpen(const CommandContext &context)
Definition: FileMenus.cpp:195
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