1 /**********************************************************************
2 
3    Audacity: A Digital Audio Editor
4 
5    ExportFFmpegDialogs.cpp
6 
7    Audacity(R) is copyright (c) 1999-2010 Audacity Team.
8    License: GPL v2.  See License.txt.
9 
10    LRN
11 
12 ******************************************************************//**
13 
14 \class ExportFFmpegAC3Options
15 \brief Options dialog for FFmpeg exporting of AC3 format.
16 
17 *//***************************************************************//**
18 
19 \class ExportFFmpegAACOptions
20 \brief Options dialog for FFmpeg exporting of AAC format.
21 
22 *//***************************************************************//**
23 
24 \class ExportFFmpegAMRNBOptions
25 \brief Options dialog for FFmpeg exporting of AMRNB format.
26 
27 *//***************************************************************//**
28 
29 \class ExportFFmpegOPUSOptions
30 \brief Options dialog for FFmpeg exporting of OPUS format.
31 
32 *//***************************************************************//**
33 
34 \class ExportFFmpegWMAOptions
35 \brief Options dialog for FFmpeg exporting of WMA format.
36 
37 *//***************************************************************//**
38 
39 \class ExportFFmpegOptions
40 \brief Options dialog for Custom FFmpeg export format.
41 
42 *//*******************************************************************/
43 
44 
45 #include "ExportFFmpegDialogs.h"
46 
47 #include "../FFmpeg.h"
48 #include "FFmpegFunctions.h"
49 
50 #include <wx/app.h>
51 #include <wx/checkbox.h>
52 #include <wx/choice.h>
53 #include <wx/intl.h>
54 #include <wx/timer.h>
55 #include <wx/string.h>
56 #include <wx/textctrl.h>
57 #include <wx/listbox.h>
58 #include <wx/window.h>
59 #include <wx/spinctrl.h>
60 #include <wx/combobox.h>
61 #include <wx/stattext.h>
62 
63 #include "../widgets/FileDialog/FileDialog.h"
64 
65 #include "../Mix.h"
66 #include "../Tags.h"
67 #include "../widgets/AudacityMessageBox.h"
68 #include "../widgets/HelpSystem.h"
69 
70 #include "Export.h"
71 #include "FFmpeg.h"
72 
73 #if defined(USE_FFMPEG)
74 
75 /// This construction defines a enumeration of UI element IDs, and a static
76 /// array of their string representations (this way they're always synchronized).
77 /// Do not store the enumerated values in external files, as they may change;
78 /// the strings may be stored.
79 #define FFMPEG_EXPORT_CTRL_ID_ENTRIES \
80    FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(FEFirstID, 20000), \
81    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatID), \
82    FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecID), \
83    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBitrateID), \
84    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEQualityID), \
85    FFMPEG_EXPORT_CTRL_ID_ENTRY(FESampleRateID), \
86    FFMPEG_EXPORT_CTRL_ID_ENTRY(FELanguageID), \
87    FFMPEG_EXPORT_CTRL_ID_ENTRY(FETagID), \
88    FFMPEG_EXPORT_CTRL_ID_ENTRY(FECutoffID), \
89    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFrameSizeID), \
90    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBufSizeID), \
91    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEProfileID), \
92    FFMPEG_EXPORT_CTRL_ID_ENTRY(FECompLevelID), \
93    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEUseLPCID), \
94    FFMPEG_EXPORT_CTRL_ID_ENTRY(FELPCCoeffsID), \
95    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMinPredID), \
96    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMaxPredID), \
97    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPredOrderID), \
98    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMinPartOrderID), \
99    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMaxPartOrderID), \
100    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMuxRateID), \
101    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPacketSizeID), \
102    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBitReservoirID), \
103    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEVariableBlockLenID), \
104    FFMPEG_EXPORT_CTRL_ID_ENTRY(FELastID), \
105  \
106    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatLabelID), \
107    FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecLabelID), \
108    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatNameID), \
109    FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecNameID), \
110    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPresetID), \
111    FFMPEG_EXPORT_CTRL_ID_ENTRY(FESavePresetID), \
112    FFMPEG_EXPORT_CTRL_ID_ENTRY(FELoadPresetID), \
113    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEDeletePresetID), \
114    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEAllFormatsID), \
115    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEAllCodecsID), \
116    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEImportPresetsID), \
117    FFMPEG_EXPORT_CTRL_ID_ENTRY(FEExportPresetsID) \
118 
119 // First the enumeration
120 #define FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(name, num)  name = num
121 #define FFMPEG_EXPORT_CTRL_ID_ENTRY(name)             name
122 
123 enum FFmpegExportCtrlID {
124    FFMPEG_EXPORT_CTRL_ID_ENTRIES
125 };
126 
127 // Now the string representations
128 #undef FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY
129 #define FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(name, num)  wxT(#name)
130 #undef FFMPEG_EXPORT_CTRL_ID_ENTRY
131 #define FFMPEG_EXPORT_CTRL_ID_ENTRY(name)             wxT(#name)
132 static const wxChar *FFmpegExportCtrlIDNames[] = {
133    FFMPEG_EXPORT_CTRL_ID_ENTRIES
134 };
135 
136 #undef FFMPEG_EXPORT_CTRL_ID_ENTRIES
137 #undef FFMPEG_EXPORT_CTRL_ID_ENTRY
138 #undef FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY
139 
140 //----------------------------------------------------------------------------
141 // ExportFFmpegAC3Options Class
142 //----------------------------------------------------------------------------
143 
144 namespace
145 {
146 
147 // i18n-hint kbps abbreviates "thousands of bits per second"
n_kbps(int n)148 inline TranslatableString n_kbps(int n) { return XO("%d kbps").Format( n ); }
149 
150 const TranslatableStrings AC3BitRateNames{
151    n_kbps( 32 ),
152    n_kbps( 40 ),
153    n_kbps( 48 ),
154    n_kbps( 56 ),
155    n_kbps( 64 ),
156    n_kbps( 80 ),
157    n_kbps( 96 ),
158    n_kbps( 112 ),
159    n_kbps( 128 ),
160    n_kbps( 160 ),
161    n_kbps( 192 ),
162    n_kbps( 224 ),
163    n_kbps( 256 ),
164    n_kbps( 320 ),
165    n_kbps( 384 ),
166    n_kbps( 448 ),
167    n_kbps( 512 ),
168    n_kbps( 576 ),
169    n_kbps( 640 ),
170 };
171 
172 const std::vector< int > AC3BitRateValues{
173    32000,
174    40000,
175    48000,
176    56000,
177    64000,
178    80000,
179    96000,
180    112000,
181    128000,
182    160000,
183    192000,
184    224000,
185    256000,
186    320000,
187    384000,
188    448000,
189    512000,
190    576000,
191    640000,
192 };
193 
194 }
195 
196 const int ExportFFmpegAC3Options::iAC3SampleRates[] = { 32000, 44100, 48000, 0 };
197 
ExportFFmpegAC3Options(wxWindow * parent,int WXUNUSED (format))198 ExportFFmpegAC3Options::ExportFFmpegAC3Options(wxWindow *parent, int WXUNUSED(format))
199 :  wxPanelWrapper(parent, wxID_ANY)
200 {
201    ShuttleGui S(this, eIsCreatingFromPrefs);
202    PopulateOrExchange(S);
203 
204    TransferDataToWindow();
205 }
206 
~ExportFFmpegAC3Options()207 ExportFFmpegAC3Options::~ExportFFmpegAC3Options()
208 {
209    TransferDataFromWindow();
210 }
211 
212 ///
213 ///
PopulateOrExchange(ShuttleGui & S)214 void ExportFFmpegAC3Options::PopulateOrExchange(ShuttleGui & S)
215 {
216    S.StartVerticalLay();
217    {
218       S.StartHorizontalLay(wxCENTER);
219       {
220          S.StartMultiColumn(2, wxCENTER);
221          {
222             S.TieNumberAsChoice(
223                XXO("Bit Rate:"),
224                {wxT("/FileFormats/AC3BitRate"),
225                 160000},
226                AC3BitRateNames,
227                &AC3BitRateValues
228             );
229          }
230          S.EndMultiColumn();
231       }
232       S.EndHorizontalLay();
233    }
234    S.EndVerticalLay();
235 }
236 
237 ///
238 ///
TransferDataToWindow()239 bool ExportFFmpegAC3Options::TransferDataToWindow()
240 {
241    return true;
242 }
243 
244 ///
245 ///
TransferDataFromWindow()246 bool ExportFFmpegAC3Options::TransferDataFromWindow()
247 {
248    ShuttleGui S(this, eIsSavingToPrefs);
249    PopulateOrExchange(S);
250 
251    gPrefs->Flush();
252 
253    return true;
254 }
255 
256 //----------------------------------------------------------------------------
257 // ExportFFmpegAACOptions Class
258 //----------------------------------------------------------------------------
259 
ExportFFmpegAACOptions(wxWindow * parent,int WXUNUSED (format))260 ExportFFmpegAACOptions::ExportFFmpegAACOptions(wxWindow *parent, int WXUNUSED(format))
261 :  wxPanelWrapper(parent, wxID_ANY)
262 {
263    ShuttleGui S(this, eIsCreatingFromPrefs);
264    PopulateOrExchange(S);
265 
266    TransferDataToWindow();
267 }
268 
~ExportFFmpegAACOptions()269 ExportFFmpegAACOptions::~ExportFFmpegAACOptions()
270 {
271    TransferDataFromWindow();
272 }
273 
274 ///
275 ///
PopulateOrExchange(ShuttleGui & S)276 void ExportFFmpegAACOptions::PopulateOrExchange(ShuttleGui & S)
277 {
278    S.StartVerticalLay();
279    {
280       S.StartHorizontalLay(wxEXPAND);
281       {
282          S.SetSizerProportion(1);
283          S.StartMultiColumn(2, wxCENTER);
284          {
285             S.SetStretchyCol(1);
286             S.Prop(1).TieSlider(
287                XXO("Quality (kbps):"),
288                {wxT("/FileFormats/AACQuality"), 160},320, 98);
289          }
290          S.EndMultiColumn();
291       }
292       S.EndHorizontalLay();
293    }
294    S.EndVerticalLay();
295 }
296 
297 ///
298 ///
TransferDataToWindow()299 bool ExportFFmpegAACOptions::TransferDataToWindow()
300 {
301    return true;
302 }
303 
304 ///
305 ///
TransferDataFromWindow()306 bool ExportFFmpegAACOptions::TransferDataFromWindow()
307 {
308    ShuttleGui S(this, eIsSavingToPrefs);
309    PopulateOrExchange(S);
310 
311    gPrefs->Flush();
312 
313    return true;
314 }
315 
316 //----------------------------------------------------------------------------
317 // ExportFFmpegAMRNBOptions Class
318 //----------------------------------------------------------------------------
319 
320 namespace {
321 
322 // i18n-hint kbps abbreviates "thousands of bits per second"
f_kbps(double d)323 inline TranslatableString f_kbps( double d ) { return XO("%.2f kbps").Format( d ); }
324 
325 /// Bit Rates supported by libAMR-NB encoder
326 /// Sample Rate is always 8 kHz
327 const TranslatableStrings AMRNBBitRateNames
328 {
329    f_kbps( 4.75 ),
330    f_kbps( 5.15 ),
331    f_kbps( 5.90 ),
332    f_kbps( 6.70 ),
333    f_kbps( 7.40 ),
334    f_kbps( 7.95 ),
335    f_kbps( 10.20 ),
336    f_kbps( 12.20 ),
337 };
338 
339 const std::vector< int > AMRNBBitRateValues
340 {
341    4750,
342    5150,
343    5900,
344    6700,
345    7400,
346    7950,
347    10200,
348    12200,
349 };
350 
351 }
352 
ExportFFmpegAMRNBOptions(wxWindow * parent,int WXUNUSED (format))353 ExportFFmpegAMRNBOptions::ExportFFmpegAMRNBOptions(wxWindow *parent, int WXUNUSED(format))
354 :  wxPanelWrapper(parent, wxID_ANY)
355 {
356    ShuttleGui S(this, eIsCreatingFromPrefs);
357    PopulateOrExchange(S);
358 
359    TransferDataToWindow();
360 }
361 
~ExportFFmpegAMRNBOptions()362 ExportFFmpegAMRNBOptions::~ExportFFmpegAMRNBOptions()
363 {
364    TransferDataFromWindow();
365 }
366 
367 ///
368 ///
PopulateOrExchange(ShuttleGui & S)369 void ExportFFmpegAMRNBOptions::PopulateOrExchange(ShuttleGui & S)
370 {
371    S.StartVerticalLay();
372    {
373       S.StartHorizontalLay(wxCENTER);
374       {
375          S.StartMultiColumn(2, wxCENTER);
376          {
377             S.TieNumberAsChoice(
378                XXO("Bit Rate:"),
379                {wxT("/FileFormats/AMRNBBitRate"),
380                 12200},
381                AMRNBBitRateNames,
382                &AMRNBBitRateValues
383             );
384          }
385          S.EndMultiColumn();
386       }
387       S.EndHorizontalLay();
388    }
389    S.EndVerticalLay();
390 }
391 
392 ///
393 ///
TransferDataToWindow()394 bool ExportFFmpegAMRNBOptions::TransferDataToWindow()
395 {
396    return true;
397 }
398 
399 ///
400 ///
TransferDataFromWindow()401 bool ExportFFmpegAMRNBOptions::TransferDataFromWindow()
402 {
403    ShuttleGui S(this, eIsSavingToPrefs);
404    PopulateOrExchange(S);
405 
406    gPrefs->Flush();
407 
408    return true;
409 }
410 
411 //----------------------------------------------------------------------------
412 // ExportFFmpegOPUSOptions Class
413 //----------------------------------------------------------------------------
414 
415 namespace {
416 
417    /// Bit Rates supported by OPUS encoder. Setting bit rate to other values will not result in different file size.
418    ChoiceSetting OPUSBitrate
419    {
420       wxT("/FileFormats/OPUSBitrate"),
421       {
422          ByColumns,
423          {
424             n_kbps( 6 ),
425             n_kbps( 8 ),
426             n_kbps( 16 ),
427             n_kbps( 24 ),
428             n_kbps( 32 ),
429             n_kbps( 40 ),
430             n_kbps( 48 ),
431             n_kbps( 64 ),
432             n_kbps( 80 ),
433             n_kbps( 96 ),
434             n_kbps( 128 ),
435             n_kbps( 160 ),
436             n_kbps( 192 ),
437             n_kbps( 256 ),
438          },
439          {
440             wxT("6000"),
441             wxT("8000"),
442             wxT("16000"),
443             wxT("24000"),
444             wxT("32000"),
445             wxT("40000"),
446             wxT("48000"),
447             wxT("64000"),
448             wxT("80000"),
449             wxT("96000"),
450             wxT("128000"),
451             wxT("160000"),
452             wxT("192000"),
453             wxT("256000"),
454          }
455       },
456       7 // "128 kbps"
457    };
458 
459    ChoiceSetting OPUSCompression
460    {
461       wxT("/FileFormats/OPUSCompression"),
462       {
463          ByColumns,
464          {
465             XO("0"),
466             XO("1"),
467             XO("2"),
468             XO("3"),
469             XO("4"),
470             XO("5"),
471             XO("6"),
472             XO("7"),
473             XO("8"),
474             XO("9"),
475             XO("10"),
476          },
477          {
478             wxT("0"),
479             wxT("1"),
480             wxT("2"),
481             wxT("3"),
482             wxT("4"),
483             wxT("5"),
484             wxT("6"),
485             wxT("7"),
486             wxT("8"),
487             wxT("9"),
488             wxT("10"),
489          }
490       },
491       10 // "10"
492    };
493 
494 
495    ChoiceSetting OPUSVbrMode
496    {
497       wxT("/FileFormats/OPUSVbrMode"),
498       {
499          ByColumns,
500          {
501             XO("Off"),
502             XO("On"),
503             XO("Constrained"),
504          },
505          {
506             wxT("off"),
507             wxT("on"),
508             wxT("constrained"),
509          }
510       },
511       1 // "On"
512    };
513 
514    ChoiceSetting OPUSApplication
515    {
516       wxT("/FileFormats/OPUSApplication"),
517       {
518          ByColumns,
519          {
520             XO("VOIP"),
521             XO("Audio"),
522             XO("Low Delay"),
523          },
524          {
525             wxT("voip"),
526             wxT("audio"),
527             wxT("lowdelay"),
528          }
529       },
530       1 // "Audio"
531    };
532 
533    ChoiceSetting OPUSFrameDuration
534    {
535       wxT("/FileFormats/OPUSFrameDuration"),
536       {
537          ByColumns,
538          {
539             XO("2.5 ms"),
540             XO("5 ms"),
541             XO("10 ms"),
542             XO("20 ms"),
543             XO("40 ms"),
544             XO("60 ms"),
545          },
546          {
547             wxT("2.5"),
548             wxT("5"),
549             wxT("10"),
550             wxT("20"),
551             wxT("40"),
552             wxT("60"),
553          }
554       },
555       3 // "20"
556    };
557 
558    ChoiceSetting OPUSCutoff
559    {
560       wxT("/FileFormats/OPUSCutoff"),
561       {
562          ByColumns,
563          {
564             XO("Disabled"),
565             XO("Narrowband"),
566             XO("Mediumband"),
567             XO("Wideband"),
568             XO("Super Wideband"),
569             XO("Fullband"),
570          },
571          {
572             wxT("0"),
573             wxT("4000"),
574             wxT("6000"),
575             wxT("8000"),
576             wxT("12000"),
577             wxT("20000"),
578          }
579       },
580       0 // "Disabled"
581    };
582 }
583 
ExportFFmpegOPUSOptions(wxWindow * parent,int WXUNUSED (format))584 ExportFFmpegOPUSOptions::ExportFFmpegOPUSOptions(wxWindow *parent, int WXUNUSED(format))
585 :  wxPanelWrapper(parent, wxID_ANY)
586 {
587    ShuttleGui S(this, eIsCreatingFromPrefs);
588    PopulateOrExchange(S);
589 
590    TransferDataToWindow();
591 }
592 
~ExportFFmpegOPUSOptions()593 ExportFFmpegOPUSOptions::~ExportFFmpegOPUSOptions()
594 {
595    TransferDataFromWindow();
596 }
597 
598 ///
599 ///
PopulateOrExchange(ShuttleGui & S)600 void ExportFFmpegOPUSOptions::PopulateOrExchange(ShuttleGui & S)
601 {
602    S.SetSizerProportion(1);
603    S.SetBorder(4);
604    S.StartVerticalLay();
605    {
606       S.StartHorizontalLay(wxCENTER);
607       {
608          S.StartMultiColumn(2, wxCENTER);
609          {
610             S.StartMultiColumn(2, wxCENTER);
611             {
612                S.TieChoice(
613                   XXO("Bit Rate:"),
614                   OPUSBitrate);
615 
616                S.TieChoice(
617                   XXO("Compression"),
618                   OPUSCompression);
619 
620                S.TieChoice(
621                   XXO("Frame Duration:"),
622                   OPUSFrameDuration);
623             }
624             S.EndMultiColumn();
625 
626             S.StartMultiColumn(2, wxCENTER);
627             {
628                S.TieChoice(
629                   XXO("Vbr Mode:"),
630                   OPUSVbrMode);
631 
632                S.TieChoice(
633                   XXO("Application:"),
634                   OPUSApplication);
635 
636                S.TieChoice(
637                   XXO("Cutoff:"),
638                   OPUSCutoff);
639 
640             }
641             S.EndMultiColumn();
642          }
643          S.EndMultiColumn();
644       }
645       S.EndHorizontalLay();
646    }
647    S.EndVerticalLay();
648 }
649 
650 ///
651 ///
TransferDataToWindow()652 bool ExportFFmpegOPUSOptions::TransferDataToWindow()
653 {
654    return true;
655 }
656 
657 ///
658 ///
TransferDataFromWindow()659 bool ExportFFmpegOPUSOptions::TransferDataFromWindow()
660 {
661    ShuttleGui S(this, eIsSavingToPrefs);
662    PopulateOrExchange(S);
663 
664    gPrefs->Flush();
665 
666    return true;
667 }
668 
669 //----------------------------------------------------------------------------
670 // ExportFFmpegWMAOptions Class
671 //----------------------------------------------------------------------------
672 
673 const int ExportFFmpegWMAOptions::iWMASampleRates[] =
674 { 8000, 11025, 16000, 22050, 44100, 0};
675 
676 namespace {
677 
678 /// Bit Rates supported by WMA encoder. Setting bit rate to other values will not result in different file size.
679 const TranslatableStrings WMABitRateNames
680 {
681    n_kbps(24),
682    n_kbps(32),
683    n_kbps(40),
684    n_kbps(48),
685    n_kbps(64),
686    n_kbps(80),
687    n_kbps(96),
688    n_kbps(128),
689    n_kbps(160),
690    n_kbps(192),
691    n_kbps(256),
692    n_kbps(320),
693 };
694 
695 const std::vector< int > WMABitRateValues{
696    24000,
697    32000,
698    40000,
699    48000,
700    64000,
701    80000,
702    96000,
703    128000,
704    160000,
705    192000,
706    256000,
707    320000,
708 };
709 
710 }
711 
ExportFFmpegWMAOptions(wxWindow * parent,int WXUNUSED (format))712 ExportFFmpegWMAOptions::ExportFFmpegWMAOptions(wxWindow *parent, int WXUNUSED(format))
713 :  wxPanelWrapper(parent, wxID_ANY)
714 {
715    ShuttleGui S(this, eIsCreatingFromPrefs);
716    PopulateOrExchange(S);
717 
718    TransferDataToWindow();
719 }
720 
~ExportFFmpegWMAOptions()721 ExportFFmpegWMAOptions::~ExportFFmpegWMAOptions()
722 {
723    TransferDataFromWindow();
724 }
725 
726 ///
727 ///
PopulateOrExchange(ShuttleGui & S)728 void ExportFFmpegWMAOptions::PopulateOrExchange(ShuttleGui & S)
729 {
730    S.StartVerticalLay();
731    {
732       S.StartHorizontalLay(wxCENTER);
733       {
734          S.StartMultiColumn(2, wxCENTER);
735          {
736             S.TieNumberAsChoice(
737                XXO("Bit Rate:"),
738                {wxT("/FileFormats/WMABitRate"),
739                 128000},
740                WMABitRateNames,
741                &WMABitRateValues
742             );
743          }
744          S.EndMultiColumn();
745       }
746       S.EndHorizontalLay();
747    }
748    S.EndVerticalLay();
749 }
750 
751 ///
752 ///
TransferDataToWindow()753 bool ExportFFmpegWMAOptions::TransferDataToWindow()
754 {
755    return true;
756 }
757 
758 ///
759 ///
TransferDataFromWindow()760 bool ExportFFmpegWMAOptions::TransferDataFromWindow()
761 {
762    ShuttleGui S(this, eIsSavingToPrefs);
763    PopulateOrExchange(S);
764 
765    gPrefs->Flush();
766 
767    return true;
768 }
769 
770 //----------------------------------------------------------------------------
771 // ExportFFmpegCustomOptions Class
772 //----------------------------------------------------------------------------
773 
774 #define OpenID 9000
775 
BEGIN_EVENT_TABLE(ExportFFmpegCustomOptions,wxPanelWrapper)776 BEGIN_EVENT_TABLE(ExportFFmpegCustomOptions, wxPanelWrapper)
777    EVT_BUTTON(OpenID, ExportFFmpegCustomOptions::OnOpen)
778 END_EVENT_TABLE()
779 
780 ExportFFmpegCustomOptions::ExportFFmpegCustomOptions(wxWindow *parent, int WXUNUSED(format))
781 :  wxPanelWrapper(parent, wxID_ANY),
782    mFormat(NULL),
783    mCodec(NULL)
784 {
785    ShuttleGui S(this, eIsCreatingFromPrefs);
786    PopulateOrExchange(S);
787 
788    TransferDataToWindow();
789 }
790 
~ExportFFmpegCustomOptions()791 ExportFFmpegCustomOptions::~ExportFFmpegCustomOptions()
792 {
793    TransferDataFromWindow();
794 }
795 
796 ///
797 ///
PopulateOrExchange(ShuttleGui & S)798 void ExportFFmpegCustomOptions::PopulateOrExchange(ShuttleGui & S)
799 {
800    S.StartHorizontalLay(wxCENTER);
801    {
802       S.StartVerticalLay(wxCENTER, 0);
803       {
804          S.Id(OpenID).AddButton(XXO("Open custom FFmpeg format options"));
805          S.StartMultiColumn(2, wxCENTER);
806          {
807             S.AddPrompt(XXO("Current Format:"));
808             mFormat = S.Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
809             S.AddPrompt(XXO("Current Codec:"));
810             mCodec = S.Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
811          }
812          S.EndMultiColumn();
813       }
814       S.EndHorizontalLay();
815    }
816    S.EndHorizontalLay();
817 }
818 
819 ///
820 ///
TransferDataToWindow()821 bool ExportFFmpegCustomOptions::TransferDataToWindow()
822 {
823    if (mFormat)
824    {
825       mFormat->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"), wxT("")));
826       mCodec->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegCodec"), wxT("")));
827    }
828    return true;
829 }
830 
831 ///
832 ///
TransferDataFromWindow()833 bool ExportFFmpegCustomOptions::TransferDataFromWindow()
834 {
835    return true;
836 }
837 
838 ///
839 ///
OnOpen(wxCommandEvent & WXUNUSED (evt))840 void ExportFFmpegCustomOptions::OnOpen(wxCommandEvent & WXUNUSED(evt))
841 {
842    // Show "Locate FFmpeg" dialog
843    auto ffmpeg = FFmpegFunctions::Load();
844    if (!ffmpeg)
845    {
846       FindFFmpegLibs();
847       if (!LoadFFmpeg(true))
848       {
849          return;
850       }
851    }
852 
853 #ifdef __WXMAC__
854    // Bug 2077 Must be a parent window on OSX or we will appear behind.
855    auto pWin = wxGetTopLevelParent( this );
856 #else
857    // Use GetTopWindow on windows as there is no hWnd with top level parent.
858    auto pWin = wxTheApp->GetTopWindow();
859 #endif
860 
861    ExportFFmpegOptions od(pWin);
862    od.ShowModal();
863 
864    TransferDataToWindow();
865 }
866 
FFmpegPreset()867 FFmpegPreset::FFmpegPreset()
868 {
869    mControlState.resize(FELastID - FEFirstID);
870 }
871 
~FFmpegPreset()872 FFmpegPreset::~FFmpegPreset()
873 {
874 }
875 
FFmpegPresets()876 FFmpegPresets::FFmpegPresets()
877 {
878    mPreset = NULL;
879    mAbortImport = false;
880 
881    XMLFileReader xmlfile;
882    wxFileName xmlFileName(FileNames::DataDir(), wxT("ffmpeg_presets.xml"));
883    xmlfile.Parse(this,xmlFileName.GetFullPath());
884 }
885 
~FFmpegPresets()886 FFmpegPresets::~FFmpegPresets()
887 {
888    // We're in a destructor!  Don't let exceptions out!
889    GuardedCall( [&] {
890       wxFileName xmlFileName{ FileNames::DataDir(), wxT("ffmpeg_presets.xml") };
891       XMLFileWriter writer{
892          xmlFileName.GetFullPath(), XO("Error Saving FFmpeg Presets") };
893       WriteXMLHeader(writer);
894       WriteXML(writer);
895       writer.Commit();
896    } );
897 }
898 
ImportPresets(wxString & filename)899 void FFmpegPresets::ImportPresets(wxString &filename)
900 {
901    mPreset = NULL;
902    mAbortImport = false;
903 
904    FFmpegPresetMap savePresets = mPresets;
905 
906    XMLFileReader xmlfile;
907    bool success = xmlfile.Parse(this,filename);
908    if (!success || mAbortImport) {
909       mPresets = savePresets;
910    }
911 }
912 
ExportPresets(wxString & filename)913 void FFmpegPresets::ExportPresets(wxString &filename)
914 {
915    GuardedCall( [&] {
916       XMLFileWriter writer{ filename, XO("Error Saving FFmpeg Presets") };
917       WriteXMLHeader(writer);
918       WriteXML(writer);
919       writer.Commit();
920    } );
921 }
922 
GetPresetList(wxArrayString & list)923 void FFmpegPresets::GetPresetList(wxArrayString &list)
924 {
925    list.clear();
926    FFmpegPresetMap::iterator iter;
927    for (iter = mPresets.begin(); iter != mPresets.end(); ++iter)
928    {
929       list.push_back(iter->second.mPresetName);
930    }
931 
932    std::sort( list.begin(), list.end() );
933 }
934 
DeletePreset(wxString & name)935 void FFmpegPresets::DeletePreset(wxString &name)
936 {
937    FFmpegPresetMap::iterator iter = mPresets.find(name);
938    if (iter != mPresets.end())
939    {
940       mPresets.erase(iter);
941    }
942 }
943 
FindPreset(wxString & name)944 FFmpegPreset *FFmpegPresets::FindPreset(wxString &name)
945 {
946    FFmpegPresetMap::iterator iter = mPresets.find(name);
947    if (iter != mPresets.end())
948    {
949       return &iter->second;
950    }
951 
952    return NULL;
953 }
954 
955 // return false if overwrite was not allowed.
OverwriteIsOk(wxString & name)956 bool FFmpegPresets::OverwriteIsOk( wxString &name )
957 {
958    FFmpegPreset *preset = FindPreset(name);
959    if (preset)
960    {
961       auto query = XO("Overwrite preset '%s'?").Format(name);
962       int action = AudacityMessageBox(
963          query,
964          XO("Confirm Overwrite"),
965          wxYES_NO | wxCENTRE);
966       if (action == wxNO) return false;
967    }
968    return true;
969 }
970 
971 
SavePreset(ExportFFmpegOptions * parent,wxString & name)972 bool FFmpegPresets::SavePreset(ExportFFmpegOptions *parent, wxString &name)
973 {
974    wxString format;
975    wxString codec;
976    FFmpegPreset *preset;
977 
978    {
979       wxWindow *wnd;
980       wxListBox *lb;
981 
982       wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(FEFormatID,parent);
983       lb = dynamic_cast<wxListBox*>(wnd);
984       if (lb->GetSelection() < 0)
985       {
986          AudacityMessageBox( XO("Please select format before saving a profile") );
987          return false;
988       }
989       format = lb->GetStringSelection();
990 
991       wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(FECodecID,parent);
992       lb = dynamic_cast<wxListBox*>(wnd);
993       if (lb->GetSelection() < 0)
994       {
995          /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
996          AudacityMessageBox( XO("Please select codec before saving a profile") );
997          return false;
998       }
999       codec = lb->GetStringSelection();
1000    }
1001 
1002    preset = &mPresets[name];
1003    preset->mPresetName = name;
1004 
1005    wxSpinCtrl *sc;
1006    wxTextCtrl *tc;
1007    wxCheckBox *cb;
1008    wxChoice *ch;
1009 
1010    for (int id = FEFirstID; id < FELastID; id++)
1011    {
1012       wxWindow *wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(id,parent);
1013       if (wnd != NULL)
1014       {
1015          switch(id)
1016          {
1017          case FEFormatID:
1018             preset->mControlState[id - FEFirstID] = format;
1019             break;
1020          case FECodecID:
1021             preset->mControlState[id - FEFirstID] = codec;
1022             break;
1023          // Spin control
1024          case FEBitrateID:
1025          case FEQualityID:
1026          case FESampleRateID:
1027          case FECutoffID:
1028          case FEFrameSizeID:
1029          case FEBufSizeID:
1030          case FECompLevelID:
1031          case FELPCCoeffsID:
1032          case FEMinPredID:
1033          case FEMaxPredID:
1034          case FEMinPartOrderID:
1035          case FEMaxPartOrderID:
1036          case FEMuxRateID:
1037          case FEPacketSizeID:
1038             sc = dynamic_cast<wxSpinCtrl*>(wnd);
1039             preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),sc->GetValue());
1040             break;
1041          // Text control
1042          case FELanguageID:
1043          case FETagID:
1044             tc = dynamic_cast<wxTextCtrl*>(wnd);
1045             preset->mControlState[id - FEFirstID] = tc->GetValue();
1046             break;
1047          // Choice
1048          case FEProfileID:
1049          case FEPredOrderID:
1050             ch = dynamic_cast<wxChoice*>(wnd);
1051             preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),ch->GetSelection());
1052             break;
1053          // Check box
1054          case FEUseLPCID:
1055          case FEBitReservoirID:
1056          case FEVariableBlockLenID:
1057             cb = dynamic_cast<wxCheckBox*>(wnd);
1058             preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),cb->GetValue());
1059             break;
1060          }
1061       }
1062    }
1063    return true;
1064 }
1065 
LoadPreset(ExportFFmpegOptions * parent,wxString & name)1066 void FFmpegPresets::LoadPreset(ExportFFmpegOptions *parent, wxString &name)
1067 {
1068    FFmpegPreset *preset = FindPreset(name);
1069    if (!preset)
1070    {
1071       AudacityMessageBox( XO("Preset '%s' does not exist." ).Format(name));
1072       return;
1073    }
1074 
1075    wxListBox *lb;
1076    wxSpinCtrl *sc;
1077    wxTextCtrl *tc;
1078    wxCheckBox *cb;
1079    wxChoice *ch;
1080 
1081    for (int id = FEFirstID; id < FELastID; id++)
1082    {
1083       wxWindow *wnd = parent->FindWindowById(id,parent);
1084       if (wnd != NULL)
1085       {
1086          wxString readstr;
1087          long readlong;
1088          bool readbool;
1089          switch(id)
1090          {
1091          // Listbox
1092          case FEFormatID:
1093          case FECodecID:
1094             lb = dynamic_cast<wxListBox*>(wnd);
1095             readstr = preset->mControlState[id - FEFirstID];
1096             readlong = lb->FindString(readstr);
1097             if (readlong > -1) lb->Select(readlong);
1098             break;
1099          // Spin control
1100          case FEBitrateID:
1101          case FEQualityID:
1102          case FESampleRateID:
1103          case FECutoffID:
1104          case FEFrameSizeID:
1105          case FEBufSizeID:
1106          case FECompLevelID:
1107          case FELPCCoeffsID:
1108          case FEMinPredID:
1109          case FEMaxPredID:
1110          case FEMinPartOrderID:
1111          case FEMaxPartOrderID:
1112          case FEMuxRateID:
1113          case FEPacketSizeID:
1114             sc = dynamic_cast<wxSpinCtrl*>(wnd);
1115             preset->mControlState[id - FEFirstID].ToLong(&readlong);
1116             sc->SetValue(readlong);
1117             break;
1118          // Text control
1119          case FELanguageID:
1120          case FETagID:
1121             tc = dynamic_cast<wxTextCtrl*>(wnd);
1122             tc->SetValue(preset->mControlState[id - FEFirstID]);
1123             break;
1124          // Choice
1125          case FEProfileID:
1126          case FEPredOrderID:
1127             ch = dynamic_cast<wxChoice*>(wnd);
1128             preset->mControlState[id - FEFirstID].ToLong(&readlong);
1129             if (readlong > -1) ch->Select(readlong);
1130             break;
1131          // Check box
1132          case FEUseLPCID:
1133          case FEBitReservoirID:
1134          case FEVariableBlockLenID:
1135             cb = dynamic_cast<wxCheckBox*>(wnd);
1136             preset->mControlState[id - FEFirstID].ToLong(&readlong);
1137             if (readlong) readbool = true; else readbool = false;
1138             cb->SetValue(readbool);
1139             break;
1140          }
1141       }
1142    }
1143 }
1144 
HandleXMLTag(const std::string_view & tag,const AttributesList & attrs)1145 bool FFmpegPresets::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1146 {
1147    if (mAbortImport)
1148    {
1149       return false;
1150    }
1151 
1152    if (tag == "ffmpeg_presets")
1153    {
1154       return true;
1155    }
1156 
1157    if (tag == "preset")
1158    {
1159       for (auto pair : attrs)
1160       {
1161          auto attr = pair.first;
1162          auto value = pair.second;
1163 
1164          if (attr == "name")
1165          {
1166             wxString strValue = value.ToWString();
1167             mPreset = FindPreset(strValue);
1168 
1169             if (mPreset)
1170             {
1171                auto query = XO("Replace preset '%s'?").Format( strValue );
1172                int action = AudacityMessageBox(
1173                   query,
1174                   XO("Confirm Overwrite"),
1175                   wxYES_NO | wxCANCEL | wxCENTRE);
1176                if (action == wxCANCEL)
1177                {
1178                   mAbortImport = true;
1179                   return false;
1180                }
1181                if (action == wxNO)
1182                {
1183                   mPreset = NULL;
1184                   return false;
1185                }
1186                *mPreset = FFmpegPreset();
1187             }
1188             else
1189             {
1190                mPreset = &mPresets[strValue];
1191             }
1192 
1193             mPreset->mPresetName = strValue;
1194          }
1195       }
1196       return true;
1197    }
1198 
1199    if (tag == "setctrlstate" && mPreset)
1200    {
1201       long id = -1;
1202       for (auto pair : attrs)
1203       {
1204          auto attr = pair.first;
1205          auto value = pair.second;
1206 
1207          if (attr == "id")
1208          {
1209             for (long i = FEFirstID; i < FELastID; i++)
1210                if (!wxStrcmp(FFmpegExportCtrlIDNames[i - FEFirstID], value.ToWString()))
1211                   id = i;
1212          }
1213          else if (attr == "state")
1214          {
1215             if (id > FEFirstID && id < FELastID)
1216                mPreset->mControlState[id - FEFirstID] = value.ToWString();
1217          }
1218       }
1219       return true;
1220    }
1221 
1222    return false;
1223 }
1224 
HandleXMLChild(const std::string_view & tag)1225 XMLTagHandler *FFmpegPresets::HandleXMLChild(const std::string_view& tag)
1226 {
1227    if (mAbortImport)
1228    {
1229       return NULL;
1230    }
1231 
1232    if (tag == "preset")
1233    {
1234       return this;
1235    }
1236    else if (tag == "setctrlstate")
1237    {
1238       return this;
1239    }
1240    return NULL;
1241 }
1242 
WriteXMLHeader(XMLWriter & xmlFile) const1243 void FFmpegPresets::WriteXMLHeader(XMLWriter &xmlFile) const
1244 // may throw
1245 {
1246    xmlFile.Write(wxT("<?xml "));
1247    xmlFile.Write(wxT("version=\"1.0\" "));
1248    xmlFile.Write(wxT("standalone=\"no\" "));
1249    xmlFile.Write(wxT("?>\n"));
1250 
1251    wxString dtdName = wxT("-//audacityffmpegpreset-1.0.0//DTD//EN");
1252    wxString dtdURI =
1253       wxT("http://audacity.sourceforge.net/xml/audacityffmpegpreset-1.0.0.dtd");
1254 
1255    xmlFile.Write(wxT("<!DOCTYPE "));
1256    xmlFile.Write(wxT("project "));
1257    xmlFile.Write(wxT("PUBLIC "));
1258    xmlFile.Write(wxT("\"-//audacityffmpegpreset-1.0.0//DTD//EN\" "));
1259    xmlFile.Write(wxT("\"http://audacity.sourceforge.net/xml/audacityffmpegpreset-1.0.0.dtd\" "));
1260    xmlFile.Write(wxT(">\n"));
1261 }
1262 
WriteXML(XMLWriter & xmlFile) const1263 void FFmpegPresets::WriteXML(XMLWriter &xmlFile) const
1264 // may throw
1265 {
1266    xmlFile.StartTag(wxT("ffmpeg_presets"));
1267    xmlFile.WriteAttr(wxT("version"),wxT("1.0"));
1268    FFmpegPresetMap::const_iterator iter;
1269    for (iter = mPresets.begin(); iter != mPresets.end(); ++iter)
1270    {
1271       auto preset = &iter->second;
1272       xmlFile.StartTag(wxT("preset"));
1273       xmlFile.WriteAttr(wxT("name"),preset->mPresetName);
1274       for (long i = FEFirstID + 1; i < FELastID; i++)
1275       {
1276          xmlFile.StartTag(wxT("setctrlstate"));
1277          xmlFile.WriteAttr(wxT("id"),wxString(FFmpegExportCtrlIDNames[i - FEFirstID]));
1278          xmlFile.WriteAttr(wxT("state"),preset->mControlState[i - FEFirstID]);
1279          xmlFile.EndTag(wxT("setctrlstate"));
1280       }
1281       xmlFile.EndTag(wxT("preset"));
1282    }
1283    xmlFile.EndTag(wxT("ffmpeg_presets"));
1284 }
1285 
1286 //----------------------------------------------------------------------------
1287 // ExportFFmpegOptions Class
1288 //----------------------------------------------------------------------------
1289 
1290 BEGIN_EVENT_TABLE(ExportFFmpegOptions, wxDialogWrapper)
1291    EVT_BUTTON(wxID_OK,ExportFFmpegOptions::OnOK)
1292    EVT_BUTTON(wxID_HELP,ExportFFmpegOptions::OnGetURL)
1293    EVT_LISTBOX(FEFormatID,ExportFFmpegOptions::OnFormatList)
1294    EVT_LISTBOX(FECodecID,ExportFFmpegOptions::OnCodecList)
1295    EVT_BUTTON(FEAllFormatsID,ExportFFmpegOptions::OnAllFormats)
1296    EVT_BUTTON(FEAllCodecsID,ExportFFmpegOptions::OnAllCodecs)
1297    EVT_BUTTON(FESavePresetID,ExportFFmpegOptions::OnSavePreset)
1298    EVT_BUTTON(FELoadPresetID,ExportFFmpegOptions::OnLoadPreset)
1299    EVT_BUTTON(FEDeletePresetID,ExportFFmpegOptions::OnDeletePreset)
1300    EVT_BUTTON(FEImportPresetsID,ExportFFmpegOptions::OnImportPresets)
1301    EVT_BUTTON(FEExportPresetsID,ExportFFmpegOptions::OnExportPresets)
1302 END_EVENT_TABLE()
1303 
1304 /// Format-codec compatibility list
1305 /// Must end with NULL entry
1306 CompatibilityEntry ExportFFmpegOptions::CompatibilityList[] =
1307 {
1308    { wxT("adts"), AUDACITY_AV_CODEC_ID_AAC },
1309 
1310    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1311    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1312    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S24BE },
1313    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S32BE },
1314    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1315    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1316    { wxT("aiff"), AUDACITY_AV_CODEC_ID_MACE3 },
1317    { wxT("aiff"), AUDACITY_AV_CODEC_ID_MACE6 },
1318    { wxT("aiff"), AUDACITY_AV_CODEC_ID_GSM },
1319    { wxT("aiff"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1320    { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1321    { wxT("aiff"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_QT },
1322    { wxT("aiff"), AUDACITY_AV_CODEC_ID_QDM2 },
1323 
1324    { wxT("amr"), AUDACITY_AV_CODEC_ID_AMR_NB },
1325    { wxT("amr"), AUDACITY_AV_CODEC_ID_AMR_WB },
1326 
1327    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1328    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1329    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1330    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1331    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1332    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1333    { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1334    { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1335    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1336    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1337    { wxT("asf"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1338    { wxT("asf"), AUDACITY_AV_CODEC_ID_GSM_MS },
1339    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1340    //{ wxT("asf"), AUDACITY_AV_CODEC_ID_MP2 }, Bug 59
1341    { wxT("asf"), AUDACITY_AV_CODEC_ID_MP3 },
1342 #if LIBAVCODEC_VERSION_MAJOR < 58
1343    { wxT("asf"), AUDACITY_AV_CODEC_ID_VOXWARE },
1344 #endif
1345    { wxT("asf"), AUDACITY_AV_CODEC_ID_AAC },
1346    { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAV1 },
1347    { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAV2 },
1348    { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAPRO },
1349    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1350    { wxT("asf"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1351    { wxT("asf"), AUDACITY_AV_CODEC_ID_IMC },
1352    { wxT("asf"), AUDACITY_AV_CODEC_ID_AC3 },
1353    { wxT("asf"), AUDACITY_AV_CODEC_ID_DTS },
1354    { wxT("asf"), AUDACITY_AV_CODEC_ID_FLAC },
1355    { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1356    { wxT("asf"), AUDACITY_AV_CODEC_ID_VORBIS },
1357 
1358    { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1359    { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1360    { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1361    { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1362 
1363    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1364    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1365    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1366    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1367    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1368    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1369    { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1370    { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1371    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1372    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1373    { wxT("avi"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1374    { wxT("avi"), AUDACITY_AV_CODEC_ID_GSM_MS },
1375    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1376    // { wxT("avi"), AUDACITY_AV_CODEC_ID_MP2 }, //Bug 59
1377    { wxT("avi"), AUDACITY_AV_CODEC_ID_MP3 },
1378 #if LIBAVCODEC_VERSION_MAJOR < 58
1379    { wxT("avi"), AUDACITY_AV_CODEC_ID_VOXWARE },
1380 #endif
1381    { wxT("avi"), AUDACITY_AV_CODEC_ID_AAC },
1382    { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAV1 },
1383    { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAV2 },
1384    { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAPRO },
1385    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1386    { wxT("avi"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1387    { wxT("avi"), AUDACITY_AV_CODEC_ID_IMC },
1388    { wxT("avi"), AUDACITY_AV_CODEC_ID_AC3 },
1389    { wxT("avi"), AUDACITY_AV_CODEC_ID_DTS },
1390    { wxT("avi"), AUDACITY_AV_CODEC_ID_FLAC },
1391    { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1392    { wxT("avi"), AUDACITY_AV_CODEC_ID_VORBIS },
1393 
1394    { wxT("crc"), AUDACITY_AV_CODEC_ID_NONE },
1395 
1396    { wxT("dv"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1397 
1398    { wxT("ffm"), AUDACITY_AV_CODEC_ID_NONE },
1399 
1400    { wxT("flv"), AUDACITY_AV_CODEC_ID_MP3 },
1401    { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1402    { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1403    { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1404    { wxT("flv"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1405    { wxT("flv"), AUDACITY_AV_CODEC_ID_AAC },
1406    { wxT("flv"), AUDACITY_AV_CODEC_ID_NELLYMOSER },
1407 
1408    { wxT("framecrc"), AUDACITY_AV_CODEC_ID_NONE },
1409 
1410    { wxT("gxf"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1411 
1412    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1413    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1414    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1415    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1416    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1417    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1418    { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1419    { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1420    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1421    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1422    { wxT("matroska"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1423    { wxT("matroska"), AUDACITY_AV_CODEC_ID_GSM_MS },
1424    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1425    // { wxT("matroska"), AUDACITY_AV_CODEC_ID_MP2 }, // Bug 59
1426    { wxT("matroska"), AUDACITY_AV_CODEC_ID_MP3 },
1427 #if LIBAVCODEC_VERSION_MAJOR < 58
1428    { wxT("matroska"), AUDACITY_AV_CODEC_ID_VOXWARE },
1429 #endif
1430    { wxT("matroska"), AUDACITY_AV_CODEC_ID_AAC },
1431    { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAV1 },
1432    { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAV2 },
1433    { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAPRO },
1434    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1435    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1436    { wxT("matroska"), AUDACITY_AV_CODEC_ID_IMC },
1437    { wxT("matroska"), AUDACITY_AV_CODEC_ID_AC3 },
1438    { wxT("matroska"), AUDACITY_AV_CODEC_ID_DTS },
1439    { wxT("matroska"), AUDACITY_AV_CODEC_ID_FLAC },
1440    { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1441    { wxT("matroska"), AUDACITY_AV_CODEC_ID_VORBIS },
1442 
1443    { wxT("mmf"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1444 
1445    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S32BE }, //mov
1446    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1447    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S24BE },
1448    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1449    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1450    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1451    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1452    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1453    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1454    { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1455    { wxT("mov"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_QT },
1456    { wxT("mov"), AUDACITY_AV_CODEC_ID_MACE3 },
1457    { wxT("mov"), AUDACITY_AV_CODEC_ID_MACE6 },
1458    { wxT("mov"), AUDACITY_AV_CODEC_ID_MP3 },
1459    { wxT("mov"), AUDACITY_AV_CODEC_ID_AAC },
1460    { wxT("mov"), AUDACITY_AV_CODEC_ID_AMR_NB },
1461    { wxT("mov"), AUDACITY_AV_CODEC_ID_AMR_WB },
1462    { wxT("mov"), AUDACITY_AV_CODEC_ID_GSM },
1463    { wxT("mov"), AUDACITY_AV_CODEC_ID_ALAC },
1464    { wxT("mov"), AUDACITY_AV_CODEC_ID_QCELP },
1465    { wxT("mov"), AUDACITY_AV_CODEC_ID_QDM2 },
1466    { wxT("mov"), AUDACITY_AV_CODEC_ID_DVAUDIO },
1467    { wxT("mov"), AUDACITY_AV_CODEC_ID_WMAV2 },
1468    { wxT("mov"), AUDACITY_AV_CODEC_ID_ALAC },
1469 
1470    { wxT("mp4"), AUDACITY_AV_CODEC_ID_AAC },
1471    { wxT("mp4"), AUDACITY_AV_CODEC_ID_QCELP },
1472    { wxT("mp4"), AUDACITY_AV_CODEC_ID_MP3 },
1473    { wxT("mp4"), AUDACITY_AV_CODEC_ID_VORBIS },
1474 
1475    { wxT("psp"), AUDACITY_AV_CODEC_ID_AAC },
1476    { wxT("psp"), AUDACITY_AV_CODEC_ID_QCELP },
1477    { wxT("psp"), AUDACITY_AV_CODEC_ID_MP3 },
1478    { wxT("psp"), AUDACITY_AV_CODEC_ID_VORBIS },
1479 
1480    { wxT("ipod"), AUDACITY_AV_CODEC_ID_AAC },
1481    { wxT("ipod"), AUDACITY_AV_CODEC_ID_QCELP },
1482    { wxT("ipod"), AUDACITY_AV_CODEC_ID_MP3 },
1483    { wxT("ipod"), AUDACITY_AV_CODEC_ID_VORBIS },
1484 
1485    { wxT("3gp"), AUDACITY_AV_CODEC_ID_AAC },
1486    { wxT("3gp"), AUDACITY_AV_CODEC_ID_AMR_NB },
1487    { wxT("3gp"), AUDACITY_AV_CODEC_ID_AMR_WB },
1488 
1489    { wxT("3g2"), AUDACITY_AV_CODEC_ID_AAC },
1490    { wxT("3g2"), AUDACITY_AV_CODEC_ID_AMR_NB },
1491    { wxT("3g2"), AUDACITY_AV_CODEC_ID_AMR_WB },
1492 
1493    { wxT("mp3"), AUDACITY_AV_CODEC_ID_MP3 },
1494 
1495    { wxT("mpeg"), AUDACITY_AV_CODEC_ID_AC3 },
1496    { wxT("mpeg"), AUDACITY_AV_CODEC_ID_DTS },
1497    { wxT("mpeg"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1498    //{ wxT("mpeg"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1499 
1500    { wxT("vcd"), AUDACITY_AV_CODEC_ID_AC3 },
1501    { wxT("vcd"), AUDACITY_AV_CODEC_ID_DTS },
1502    { wxT("vcd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1503    //{ wxT("vcd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1504 
1505    { wxT("vob"), AUDACITY_AV_CODEC_ID_AC3 },
1506    { wxT("vob"), AUDACITY_AV_CODEC_ID_DTS },
1507    { wxT("vob"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1508    //{ wxT("vob"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1509 
1510    { wxT("svcd"), AUDACITY_AV_CODEC_ID_AC3 },
1511    { wxT("svcd"), AUDACITY_AV_CODEC_ID_DTS },
1512    { wxT("svcd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1513    //{ wxT("svcd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1514 
1515    { wxT("dvd"), AUDACITY_AV_CODEC_ID_AC3 },
1516    { wxT("dvd"), AUDACITY_AV_CODEC_ID_DTS },
1517    { wxT("dvd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1518    //{ wxT("dvd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1519 
1520    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1521    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1522    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1523    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1524    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1525    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1526    { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1527    { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1528    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1529    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1530    { wxT("nut"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1531    { wxT("nut"), AUDACITY_AV_CODEC_ID_GSM_MS },
1532    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1533    //{ wxT("nut"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1534    { wxT("nut"), AUDACITY_AV_CODEC_ID_MP3 },
1535  #if LIBAVCODEC_VERSION_MAJOR < 58
1536    { wxT("nut"), AUDACITY_AV_CODEC_ID_VOXWARE },
1537  #endif
1538    { wxT("nut"), AUDACITY_AV_CODEC_ID_AAC },
1539    { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAV1 },
1540    { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAV2 },
1541    { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAPRO },
1542    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1543    { wxT("nut"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1544    { wxT("nut"), AUDACITY_AV_CODEC_ID_IMC },
1545    { wxT("nut"), AUDACITY_AV_CODEC_ID_AC3 },
1546    { wxT("nut"), AUDACITY_AV_CODEC_ID_DTS },
1547    { wxT("nut"), AUDACITY_AV_CODEC_ID_FLAC },
1548    { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1549    { wxT("nut"), AUDACITY_AV_CODEC_ID_VORBIS },
1550 
1551    { wxT("ogg"), AUDACITY_AV_CODEC_ID_VORBIS },
1552    { wxT("ogg"), AUDACITY_AV_CODEC_ID_FLAC },
1553 
1554    { wxT("ac3"), AUDACITY_AV_CODEC_ID_AC3 },
1555 
1556    { wxT("dts"), AUDACITY_AV_CODEC_ID_DTS },
1557 
1558    { wxT("flac"), AUDACITY_AV_CODEC_ID_FLAC },
1559 
1560    { wxT("RoQ"), AUDACITY_AV_CODEC_ID_ROQ_DPCM },
1561 
1562    { wxT("rm"), AUDACITY_AV_CODEC_ID_AC3 },
1563 
1564    { wxT("swf"), AUDACITY_AV_CODEC_ID_MP3 },
1565 
1566    { wxT("avm2"), AUDACITY_AV_CODEC_ID_MP3 },
1567 
1568    { wxT("voc"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1569 
1570    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1571    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1572    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1573    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1574    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1575    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1576    { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1577    { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1578    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1579    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1580    { wxT("wav"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1581    { wxT("wav"), AUDACITY_AV_CODEC_ID_GSM_MS },
1582    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1583    //{ wxT("wav"), AUDACITY_AV_CODEC_ID_MP2 }, Bug 59 - It crashes.
1584    { wxT("wav"), AUDACITY_AV_CODEC_ID_MP3 },
1585 #if LIBAVCODEC_VERSION_MAJOR < 58
1586    { wxT("wav"), AUDACITY_AV_CODEC_ID_VOXWARE },
1587 #endif
1588    { wxT("wav"), AUDACITY_AV_CODEC_ID_AAC },
1589    // { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAV1 },
1590    // { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAV2 },
1591    { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAPRO },
1592    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1593    { wxT("wav"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1594    { wxT("wav"), AUDACITY_AV_CODEC_ID_IMC },
1595    { wxT("wav"), AUDACITY_AV_CODEC_ID_AC3 },
1596    //{ wxT("wav"), AUDACITY_AV_CODEC_ID_DTS },
1597    { wxT("wav"), AUDACITY_AV_CODEC_ID_FLAC },
1598    { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1599    { wxT("wav"), AUDACITY_AV_CODEC_ID_VORBIS },
1600 
1601    { NULL, AUDACITY_AV_CODEC_ID_NONE }
1602 };
1603 
1604 /// AAC profiles
1605 // The FF_PROFILE_* enumeration is defined in the ffmpeg library
1606 // PRL:  I cant find where this preference is used!
1607 ChoiceSetting AACProfiles { wxT("/FileFormats/FFmpegAACProfile"),
1608    {
1609       {wxT("1") /*FF_PROFILE_AAC_LOW*/, XO("LC")},
1610       {wxT("0") /*FF_PROFILE_AAC_MAIN*/, XO("Main")},
1611       // {wxT("2") /*FF_PROFILE_AAC_SSR*/, XO("SSR")}, //SSR is not supported
1612       {wxT("3") /*FF_PROFILE_AAC_LTP*/, XO("LTP")},
1613    },
1614    0, // "1"
1615 };
1616 
1617 /// List of export types
1618 ExposedFormat ExportFFmpegOptions::fmts[] =
1619 {
1620    {FMT_M4A,   wxT("M4A"),    wxT("m4a"),  wxT("ipod"), 48,  AV_CANMETA,              true,  XO("M4A (AAC) Files (FFmpeg)"),         AUDACITY_AV_CODEC_ID_AAC,    true},
1621    {FMT_AC3,   wxT("AC3"),    wxT("ac3"),  wxT("ac3"),  7,   AV_VERSION_INT(0,0,0),   false, XO("AC3 Files (FFmpeg)"),               AUDACITY_AV_CODEC_ID_AC3,    true},
1622    {FMT_AMRNB, wxT("AMRNB"),  wxT("amr"),  wxT("amr"),  1,   AV_VERSION_INT(0,0,0),   false, XO("AMR (narrow band) Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_AMR_NB, true},
1623    {FMT_OPUS,  wxT("OPUS"),   wxT("opus"), wxT("opus"), 255, AV_CANMETA,              true,  XO("Opus (OggOpus) Files (FFmpeg)"),    AUDACITY_AV_CODEC_ID_OPUS,   true},
1624    {FMT_WMA2,  wxT("WMA"),    wxT("wma"),  wxT("asf"),  2,   AV_VERSION_INT(52,53,0), false, XO("WMA (version 2) Files (FFmpeg)"),   AUDACITY_AV_CODEC_ID_WMAV2,  true},
1625    {FMT_OTHER, wxT("FFMPEG"), wxT(""),     wxT(""),     255, AV_CANMETA,              true,  XO("Custom FFmpeg Export"),             AUDACITY_AV_CODEC_ID_NONE,   true}
1626 };
1627 
1628 /// Some controls (parameters they represent) are only applicable to a number
1629 /// of codecs and/or formats.
1630 /// Syntax: first, enable a control for each applicable format-codec combination
1631 /// then disable it for anything else
1632 /// "any" - any format
1633 /// AUDACITY_AV_CODEC_ID_NONE - any codec
1634 /// This list must end with {FALSE,FFmpegExportCtrlID(0),AUDACITY_AV_CODEC_ID_NONE,NULL}
1635 ApplicableFor ExportFFmpegOptions::apptable[] =
1636 {
1637    {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1638    {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_MP3,"any"},
1639    {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_VORBIS,"any"},
1640    {FALSE,FEQualityID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1641 
1642    {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_AC3,"any"},
1643    {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1644    {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_VORBIS,"any"},
1645    {FALSE,FECutoffID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1646 
1647    {TRUE,FEFrameSizeID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1648    {FALSE,FEFrameSizeID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1649 
1650    {TRUE,FEProfileID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1651    {FALSE,FEProfileID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1652 
1653    {TRUE,FECompLevelID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1654    {FALSE,FECompLevelID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1655 
1656    {TRUE,FEUseLPCID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1657    {FALSE,FEUseLPCID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1658 
1659    {TRUE,FELPCCoeffsID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1660    {FALSE,FELPCCoeffsID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1661 
1662    {TRUE,FEMinPredID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1663    {FALSE,FEMinPredID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1664 
1665    {TRUE,FEMaxPredID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1666    {FALSE,FEMaxPredID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1667 
1668    {TRUE,FEPredOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1669    {FALSE,FEPredOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1670 
1671    {TRUE,FEMinPartOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1672    {FALSE,FEMinPartOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1673 
1674    {TRUE,FEMaxPartOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1675    {FALSE,FEMaxPartOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1676 
1677    {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"mpeg"},
1678    {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"vcd"},
1679    {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"vob"},
1680    {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"svcd"},
1681    {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"dvd"},
1682    {FALSE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1683 
1684    {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"mpeg"},
1685    {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"vcd"},
1686    {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"vob"},
1687    {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"svcd"},
1688    {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"dvd"},
1689    {FALSE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1690 
1691    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"matroska"},
1692    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mov"},
1693    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"3gp"},
1694    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mp4"},
1695    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"psp"},
1696    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"3g2"},
1697    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"ipod"},
1698    {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mpegts"},
1699    {FALSE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1700 
1701    {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_MP3,"any"},
1702    {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_WMAV1,"any"},
1703    {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_WMAV2,"any"},
1704    {FALSE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1705 
1706    {TRUE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_WMAV1,"any"},
1707    {TRUE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_WMAV2,"any"},
1708    {FALSE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1709 
1710    {FALSE,FFmpegExportCtrlID(0),AUDACITY_AV_CODEC_ID_NONE,NULL}
1711 };
1712 
1713 namespace {
1714 
1715 /// Prediction order method - names.
1716 const TranslatableStrings PredictionOrderMethodNames {
1717    XO("Estimate"),
1718    XO("2-level"),
1719    XO("4-level"),
1720    XO("8-level"),
1721    XO("Full search"),
1722    XO("Log search"),
1723 };
1724 
1725 }
1726 
1727 
1728 
~ExportFFmpegOptions()1729 ExportFFmpegOptions::~ExportFFmpegOptions()
1730 {
1731 }
1732 
ExportFFmpegOptions(wxWindow * parent)1733 ExportFFmpegOptions::ExportFFmpegOptions(wxWindow *parent)
1734 :  wxDialogWrapper(parent, wxID_ANY,
1735             XO("Configure custom FFmpeg options"))
1736 {
1737    SetName();
1738    ShuttleGui S(this, eIsCreatingFromPrefs);
1739    mFFmpeg = FFmpegFunctions::Load();
1740    //FFmpegLibsInst()->LoadLibs(NULL,true); //Loaded at startup or from Prefs now
1741 
1742    mPresets = std::make_unique<FFmpegPresets>();
1743    mPresets->GetPresetList(mPresetNames);
1744 
1745    if (mFFmpeg)
1746    {
1747       FetchFormatList();
1748       FetchCodecList();
1749 
1750       PopulateOrExchange(S);
1751 
1752       //Select the format that was selected last time this dialog was closed
1753       mFormatList->Select(mFormatList->FindString(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"))));
1754       DoOnFormatList();
1755 
1756       //Select the codec that was selected last time this dialog was closed
1757       auto codec = mFFmpeg->CreateEncoder(gPrefs->Read(wxT("/FileFormats/FFmpegCodec")).ToUTF8());
1758 
1759       if (codec != nullptr)
1760          mCodecList->Select(mCodecList->FindString(wxString::FromUTF8(codec->GetName())));
1761 
1762       DoOnCodecList();
1763    }
1764 
1765 }
1766 
1767 ///
1768 ///
FetchFormatList()1769 void ExportFFmpegOptions::FetchFormatList()
1770 {
1771    if (!mFFmpeg)
1772       return;
1773 
1774    // Enumerate all output formats
1775    std::unique_ptr<AVOutputFormatWrapper> ofmt;
1776 
1777    while ((ofmt = mFFmpeg->GetNextOutputFormat(ofmt.get()))!=NULL)
1778    {
1779       // Any audio-capable format has default audio codec.
1780       // If it doesn't, then it doesn't supports any audio codecs
1781       if (ofmt->GetAudioCodec() != AUDACITY_AV_CODEC_ID_NONE)
1782       {
1783          mFormatNames.push_back(wxString::FromUTF8(ofmt->GetName()));
1784          mFormatLongNames.push_back(wxString::Format(wxT("%s - %s"),mFormatNames.back(),wxString::FromUTF8(ofmt->GetLongName())));
1785       }
1786    }
1787    // Show all formats
1788    mShownFormatNames = mFormatNames;
1789    mShownFormatLongNames =  mFormatLongNames;
1790 }
1791 
1792 ///
1793 ///
FetchCodecList()1794 void ExportFFmpegOptions::FetchCodecList()
1795 {
1796    if (!mFFmpeg)
1797       return;
1798    // Enumerate all codecs
1799    std::unique_ptr<AVCodecWrapper> codec;
1800    while ((codec = mFFmpeg->GetNextCodec(codec.get()))!=NULL)
1801    {
1802       // We're only interested in audio and only in encoders
1803       if (codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
1804       {
1805          // MP2 Codec is broken.  Don't allow it.
1806          if( codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_MP2))
1807             continue;
1808 
1809          mCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
1810          mCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
1811       }
1812    }
1813    // Show all codecs
1814    mShownCodecNames = mCodecNames;
1815    mShownCodecLongNames = mCodecLongNames;
1816 }
1817 
1818 ///
1819 ///
PopulateOrExchange(ShuttleGui & S)1820 void ExportFFmpegOptions::PopulateOrExchange(ShuttleGui & S)
1821 {
1822    S.StartVerticalLay(1);
1823    S.StartMultiColumn(1, wxEXPAND);
1824    {
1825       S.SetStretchyRow(3);
1826       S.StartMultiColumn(7, wxEXPAND);
1827       {
1828          S.SetStretchyCol(1);
1829          mPresetCombo = S.Id(FEPresetID).AddCombo(XXO("Preset:"), gPrefs->Read(wxT("/FileFormats/FFmpegPreset"),wxEmptyString), mPresetNames);
1830          S.Id(FELoadPresetID).AddButton(XXO("Load Preset"));
1831          S.Id(FESavePresetID).AddButton(XXO("Save Preset"));
1832          S.Id(FEDeletePresetID).AddButton(XXO("Delete Preset"));
1833          S.Id(FEImportPresetsID).AddButton(XXO("Import Presets"));
1834          S.Id(FEExportPresetsID).AddButton(XXO("Export Presets"));
1835       }
1836       S.EndMultiColumn();
1837       S.StartMultiColumn(4, wxALIGN_LEFT);
1838       {
1839          S.SetStretchyCol(1);
1840          S.SetStretchyCol(3);
1841          S.Id(FEFormatLabelID).AddFixedText(XO("Format:"));
1842          mFormatName = S.Id(FEFormatNameID).AddVariableText( {} );
1843          /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1844          S.Id(FECodecLabelID).AddFixedText(XO("Codec:"));
1845          mCodecName = S.Id(FECodecNameID).AddVariableText( {} );
1846       }
1847       S.EndMultiColumn();
1848       S.AddVariableText(XO(
1849 "Not all formats and codecs are compatible. Nor are all option combinations compatible with all codecs."),
1850          false);
1851       S.StartMultiColumn(2, wxEXPAND);
1852       {
1853          S.StartMultiColumn(2, wxEXPAND);
1854          {
1855             S.SetStretchyRow(1);
1856             S.Id(FEAllFormatsID).AddButton(XXO("Show All Formats"));
1857             S.Id(FEAllCodecsID).AddButton(XXO("Show All Codecs"));
1858             mFormatList = S.Id(FEFormatID).AddListBox(mFormatNames);
1859             mFormatList->DeselectAll();
1860             mCodecList = S.Id(FECodecID).AddListBox(mCodecNames);
1861             mCodecList->DeselectAll();
1862          }
1863          S.EndMultiColumn();
1864          S.StartVerticalLay();
1865          {
1866             //S.StartScroller( );
1867             S.SetBorder( 3 );
1868             S.StartStatic(XO("General Options"), 0);
1869             {
1870                S.StartMultiColumn(8, wxEXPAND);
1871                {
1872                   S.Id(FELanguageID)
1873                      .ToolTip(XO("ISO 639 3-letter language code\nOptional\nempty - automatic"))
1874                      .TieTextBox(XXO("Language:"), {wxT("/FileFormats/FFmpegLanguage"), wxEmptyString}, 9);
1875 
1876                   S.AddSpace( 20,0 );
1877                   S.AddVariableText(XO("Bit Reservoir"));
1878                   S.Id(FEBitReservoirID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegBitReservoir"), true});
1879 
1880                   S.AddSpace( 20,0 );
1881                   S.AddVariableText(XO("VBL"));
1882                   S.Id(FEVariableBlockLenID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegVariableBlockLen"), true});
1883                }
1884                S.EndMultiColumn();
1885                S.StartMultiColumn(4, wxALIGN_LEFT);
1886                {
1887                   S.Id(FETagID)
1888                      /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1889                      .ToolTip(XO("Codec tag (FOURCC)\nOptional\nempty - automatic"))
1890                      .TieTextBox(XXO("Tag:"), {wxT("/FileFormats/FFmpegTag"), wxEmptyString}, 4);
1891 
1892                   S.Id(FEBitrateID)
1893                      .ToolTip(XO("Bit Rate (bits/second) - influences the resulting file size and quality\nSome codecs may only accept specific values (128k, 192k, 256k etc)\n0 - automatic\nRecommended - 192000"))
1894                      .TieSpinCtrl(XXO("Bit Rate:"), {wxT("/FileFormats/FFmpegBitRate"), 0}, 1000000, 0);
1895 
1896                   S.Id(FEQualityID)
1897                      .ToolTip(XO("Overall quality, used differently by different codecs\nRequired for vorbis\n0 - automatic\n-1 - off (use bitrate instead)"))
1898                      .TieSpinCtrl(XXO("Quality:"), {wxT("/FileFormats/FFmpegQuality"), 0}, 500, -1);
1899 
1900                   S.Id(FESampleRateID)
1901                      .ToolTip(XO("Sample rate (Hz)\n0 - don't change sample rate"))
1902                      .TieSpinCtrl(XXO("Sample Rate:"), {wxT("/FileFormats/FFmpegSampleRate"), 0}, 200000, 0);
1903 
1904                   S.Id(FECutoffID)
1905                      .ToolTip(XO("Audio cutoff bandwidth (Hz)\nOptional\n0 - automatic"))
1906                      .TieSpinCtrl(XXO("Cutoff:"), {wxT("/FileFormats/FFmpegCutOff"), 0}, 10000000, 0);
1907 
1908                   // PRL:  As commented elsewhere, this preference does nothing
1909                   S.Id(FEProfileID)
1910                      .ToolTip(XO("AAC Profile\nLow Complexity - default\nMost players won't play anything other than LC"))
1911                      .MinSize( { 100, -1 } )
1912                      .TieChoice(XXO("Profile:"), AACProfiles);
1913                }
1914                S.EndMultiColumn();
1915             }
1916             S.EndStatic();
1917             S.StartStatic(XO("FLAC options"),0);
1918             {
1919                S.StartMultiColumn(4, wxALIGN_LEFT);
1920                {
1921                   S
1922                      .ToolTip(XO("Compression level\nRequired for FLAC\n-1 - automatic\nmin - 0 (fast encoding, large output file)\nmax - 10 (slow encoding, small output file)"))
1923                      .Id(FECompLevelID).TieSpinCtrl(XXO("Compression:"), {wxT("/FileFormats/FFmpegCompLevel"), 0}, 10, -1);
1924 
1925                   S.Id(FEFrameSizeID)
1926                      .ToolTip(XO("Frame size\nOptional\n0 - default\nmin - 16\nmax - 65535"))
1927                      .TieSpinCtrl(XXO("Frame:"), {wxT("/FileFormats/FFmpegFrameSize"), 0}, 65535, 0);
1928 
1929                   S.Id(FELPCCoeffsID)
1930                      .ToolTip(XO("LPC coefficients precision\nOptional\n0 - default\nmin - 1\nmax - 15"))
1931                      .TieSpinCtrl(XXO("LPC"), {wxT("/FileFormats/FFmpegLPCCoefPrec"), 0}, 15, 0);
1932 
1933                   S.Id(FEPredOrderID)
1934                      .ToolTip(XO("Prediction Order Method\nEstimate - fastest, lower compression\nLog search - slowest, best compression\nFull search - default"))
1935                      .MinSize( { 100, -1 } )
1936                      .TieNumberAsChoice(
1937                         XXO("PdO Method:"),
1938                         {wxT("/FileFormats/FFmpegPredOrderMethod"),
1939                          4}, // Full search
1940                         PredictionOrderMethodNames
1941                      );
1942 
1943                   S.Id(FEMinPredID)
1944                      .ToolTip(XO("Minimal prediction order\nOptional\n-1 - default\nmin - 0\nmax - 32 (with LPC) or 4 (without LPC)"))
1945                      .TieSpinCtrl(XXO("Min. PdO"), {wxT("/FileFormats/FFmpegMinPredOrder"), -1}, 32, -1);
1946 
1947                   S.Id(FEMaxPredID)
1948                      .ToolTip(XO("Maximal prediction order\nOptional\n-1 - default\nmin - 0\nmax - 32 (with LPC) or 4 (without LPC)"))
1949                      .TieSpinCtrl(XXO("Max. PdO"), {wxT("/FileFormats/FFmpegMaxPredOrder"), -1}, 32, -1);
1950 
1951                   S.Id(FEMinPartOrderID)
1952                      .ToolTip(XO("Minimal partition order\nOptional\n-1 - default\nmin - 0\nmax - 8"))
1953                      .TieSpinCtrl(XXO("Min. PtO"), {wxT("/FileFormats/FFmpegMinPartOrder"), -1}, 8, -1);
1954 
1955                   S.Id(FEMaxPartOrderID)
1956                      .ToolTip(XO("Maximal partition order\nOptional\n-1 - default\nmin - 0\nmax - 8"))
1957                      .TieSpinCtrl(XXO("Max. PtO"), {wxT("/FileFormats/FFmpegMaxPartOrder"), -1}, 8, -1);
1958 
1959                   /* i18n-hint:  Abbreviates "Linear Predictive Coding",
1960                      but this text needs to be kept very short */
1961                   S.AddVariableText(XO("Use LPC"));
1962                   // PRL:  This preference is not used anywhere!
1963                   S.Id(FEUseLPCID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegUseLPC"), true});
1964                }
1965                S.EndMultiColumn();
1966             }
1967             S.EndStatic();
1968             S.StartStatic(XO("MPEG container options"),0);
1969             {
1970                S.StartMultiColumn(4, wxALIGN_LEFT);
1971                {
1972                   S.Id(FEMuxRateID)
1973                      .ToolTip(XO("Maximum bit rate of the multiplexed stream\nOptional\n0 - default"))
1974                      /* i18n-hint: 'mux' is short for multiplexor, a device that selects between several inputs
1975                        'Mux Rate' is a parameter that has some bearing on compression ratio for MPEG
1976                        it has a hard to predict effect on the degree of compression */
1977                      .TieSpinCtrl(XXO("Mux Rate:"), {wxT("/FileFormats/FFmpegMuxRate"), 0}, 10000000, 0);
1978 
1979                   S.Id(FEPacketSizeID)
1980                      /* i18n-hint: 'Packet Size' is a parameter that has some bearing on compression ratio for MPEG
1981                        compression.  It measures how big a chunk of audio is compressed in one piece. */
1982                      .ToolTip(XO("Packet size\nOptional\n0 - default"))
1983                      /* i18n-hint: 'Packet Size' is a parameter that has some bearing on compression ratio for MPEG
1984                        compression.  It measures how big a chunk of audio is compressed in one piece. */
1985                      .TieSpinCtrl(XXO("Packet Size:"), {wxT("/FileFormats/FFmpegPacketSize"), 0}, 10000000, 0);
1986                }
1987                S.EndMultiColumn();
1988             }
1989             S.EndStatic();
1990             //S.EndScroller();
1991             S.SetBorder( 5 );
1992             S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton );
1993          }
1994          S.EndVerticalLay();
1995       }
1996       S.EndMultiColumn();
1997    }
1998    S.EndMultiColumn();
1999    S.EndVerticalLay();
2000 
2001    Layout();
2002    Fit();
2003    SetMinSize(GetSize());
2004    Center();
2005 
2006    return;
2007 }
2008 
2009 ///
2010 ///
FindSelectedFormat(wxString ** name,wxString ** longname)2011 void ExportFFmpegOptions::FindSelectedFormat(wxString **name, wxString **longname)
2012 {
2013    // Get current selection
2014    wxArrayInt selections;
2015    int n = mFormatList->GetSelections(selections);
2016    if (n <= 0) return;
2017 
2018    // Get selected format short name
2019    wxString selfmt = mFormatList->GetString(selections[0]);
2020 
2021    // Find its index
2022    int nFormat = make_iterator_range( mFormatNames ).index( selfmt );
2023    if (nFormat == wxNOT_FOUND) return;
2024 
2025    // Return short name and description
2026    if (name != NULL) *name = &mFormatNames[nFormat];
2027    if (longname != NULL) *longname = &mFormatLongNames[nFormat];
2028    return;
2029 }
2030 ///
2031 ///
FindSelectedCodec(wxString ** name,wxString ** longname)2032 void ExportFFmpegOptions::FindSelectedCodec(wxString **name, wxString **longname)
2033 {
2034    // Get current selection
2035    wxArrayInt selections;
2036    int n = mCodecList->GetSelections(selections);
2037    if (n <= 0) return;
2038 
2039    // Get selected codec short name
2040    wxString selcdc = mCodecList->GetString(selections[0]);
2041 
2042    // Find its index
2043    int nCodec = make_iterator_range( mCodecNames ).index( selcdc );
2044    if (nCodec == wxNOT_FOUND) return;
2045 
2046    // Return short name and description
2047    if (name != NULL) *name = &mCodecNames[nCodec];
2048    if (longname != NULL) *longname = &mCodecLongNames[nCodec];
2049 }
2050 
2051 ///
2052 ///
FetchCompatibleCodecList(const wxChar * fmt,AudacityAVCodecID id)2053 int ExportFFmpegOptions::FetchCompatibleCodecList(const wxChar *fmt, AudacityAVCodecID id)
2054 {
2055    const auto ffmpegId = mFFmpeg->GetAVCodecID(id);
2056 
2057    // By default assume that id is not in the list
2058    int index = -1;
2059    // By default no codecs are compatible (yet)
2060    mShownCodecNames.clear();
2061    mShownCodecLongNames.clear();
2062    // Clear the listbox
2063    mCodecList->Clear();
2064    // Zero - format is not found at all
2065    int found = 0;
2066    wxString str(fmt);
2067    for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2068    {
2069       if (str == CompatibilityList[i].fmt)
2070       {
2071          // Format is found in the list
2072          found = 1;
2073          if (CompatibilityList[i].codec.value == AUDACITY_AV_CODEC_ID_NONE)
2074          {
2075             // Format is found in the list and it is compatible with AUDACITY_AV_CODEC_ID_NONE (means that it is compatible to anything)
2076             found = 2;
2077             break;
2078          }
2079          // Find the codec, that is claimed to be compatible
2080          std::unique_ptr<AVCodecWrapper> codec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(CompatibilityList[i].codec));
2081          // If it exists, is audio and has encoder
2082          if (codec != NULL && codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2083          {
2084             // If it was selected - remember its NEW index
2085             if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2086                index = mShownCodecNames.size();
2087 
2088             mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2089             mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2090          }
2091       }
2092    }
2093    // All codecs are compatible with this format
2094    if (found == 2)
2095    {
2096       std::unique_ptr<AVCodecWrapper> codec;
2097       while ((codec = mFFmpeg->GetNextCodec(codec.get()))!=NULL)
2098       {
2099          if (codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2100          {
2101             // MP2 is broken.
2102             if( codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_MP2) )
2103                continue;
2104 
2105             if (! make_iterator_range( mShownCodecNames )
2106                .contains( wxString::FromUTF8(codec->GetName()) ) )
2107             {
2108                if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2109                   index = mShownCodecNames.size();
2110 
2111                mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2112                mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2113             }
2114          }
2115       }
2116    }
2117    // Format is not found - find format in libavformat and add its default audio codec
2118    // This allows us to provide limited support for NEW formats without modifying the compatibility list
2119    else if (found == 0)
2120    {
2121       wxCharBuffer buf = str.ToUTF8();
2122       auto format = mFFmpeg->GuessOutputFormat(buf, nullptr, nullptr);
2123 
2124       if (format != nullptr)
2125       {
2126          auto codec = mFFmpeg->CreateEncoder(format->GetAudioCodec());
2127 
2128          if (
2129             codec != nullptr && codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2130          {
2131             if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2132                index = mShownCodecNames.size();
2133 
2134             mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2135             mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2136          }
2137       }
2138    }
2139    // Show NEW codec list
2140    mCodecList->Append(mShownCodecNames);
2141 
2142    return index;
2143 }
2144 
2145 ///
2146 ///
FetchCompatibleFormatList(AudacityAVCodecID id,wxString * selfmt)2147 int ExportFFmpegOptions::FetchCompatibleFormatList(
2148    AudacityAVCodecID id, wxString* selfmt)
2149 {
2150    int index = -1;
2151    mShownFormatNames.clear();
2152    mShownFormatLongNames.clear();
2153    mFormatList->Clear();
2154    std::unique_ptr<AVOutputFormatWrapper> ofmt;
2155 
2156    wxArrayString FromList;
2157    // Find all formats compatible to this codec in compatibility list
2158    for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2159    {
2160       if (CompatibilityList[i].codec == id || (CompatibilityList[i].codec.value == AUDACITY_AV_CODEC_ID_NONE) )
2161       {
2162          if ((selfmt != NULL) && (*selfmt == CompatibilityList[i].fmt)) index = mShownFormatNames.size();
2163          FromList.push_back(CompatibilityList[i].fmt);
2164          mShownFormatNames.push_back(CompatibilityList[i].fmt);
2165          auto tofmt = mFFmpeg->GuessOutputFormat(
2166             wxString(CompatibilityList[i].fmt).ToUTF8(), nullptr, nullptr);
2167 
2168          if (tofmt != NULL)
2169          {
2170             mShownFormatLongNames.push_back(wxString::Format(
2171                wxT("%s - %s"), CompatibilityList[i].fmt,
2172                wxString::FromUTF8(tofmt->GetLongName())));
2173          }
2174       }
2175    }
2176    bool found = false;
2177    if (selfmt != NULL)
2178    {
2179       for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2180       {
2181          if (*selfmt == CompatibilityList[i].fmt)
2182          {
2183             found = true;
2184             break;
2185          }
2186       }
2187    }
2188    // Format was in the compatibility list
2189    if (found)
2190    {
2191       // Find all formats which have this codec as default and which are not in the list yet and add them too
2192       while ((ofmt = mFFmpeg->GetNextOutputFormat(ofmt.get()))!=NULL)
2193       {
2194          if (ofmt->GetAudioCodec() == mFFmpeg->GetAVCodecID(id))
2195          {
2196             wxString ofmtname = wxString::FromUTF8(ofmt->GetName());
2197             found = false;
2198             for (unsigned int i = 0; i < FromList.size(); i++)
2199             {
2200                if (ofmtname == FromList[i])
2201                {
2202                   found = true;
2203                   break;
2204                }
2205             }
2206             if (!found)
2207             {
2208                if ((selfmt != NULL) &&
2209                   (*selfmt == wxString::FromUTF8(ofmt->GetName())))
2210                   index = mShownFormatNames.size();
2211 
2212                mShownFormatNames.push_back(wxString::FromUTF8(ofmt->GetName()));
2213 
2214                mShownFormatLongNames.push_back(wxString::Format(
2215                   wxT("%s - %s"), mShownFormatNames.back(),
2216                   wxString::FromUTF8(ofmt->GetLongName())));
2217             }
2218          }
2219       }
2220    }
2221    mFormatList->Append(mShownFormatNames);
2222    return index;
2223 }
2224 
2225 ///
2226 ///
OnDeletePreset(wxCommandEvent & WXUNUSED (event))2227 void ExportFFmpegOptions::OnDeletePreset(wxCommandEvent& WXUNUSED(event))
2228 {
2229    wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2230    wxString presetname = preset->GetValue();
2231    if (presetname.empty())
2232    {
2233       AudacityMessageBox( XO("You can't delete a preset without name") );
2234       return;
2235    }
2236 
2237    auto query = XO("Delete preset '%s'?").Format( presetname );
2238    int action = AudacityMessageBox(
2239       query,
2240       XO("Confirm Deletion"),
2241       wxYES_NO | wxCENTRE);
2242    if (action == wxNO) return;
2243 
2244    mPresets->DeletePreset(presetname);
2245    long index = preset->FindString(presetname);
2246    preset->SetValue(wxEmptyString);
2247    preset->Delete(index);
2248    mPresetNames.erase(
2249       std::find( mPresetNames.begin(), mPresetNames.end(), presetname )
2250    );
2251 }
2252 
2253 ///
2254 ///
OnSavePreset(wxCommandEvent & WXUNUSED (event))2255 void ExportFFmpegOptions::OnSavePreset(wxCommandEvent& WXUNUSED(event))
2256 {  const bool kCheckForOverwrite = true;
2257    SavePreset(kCheckForOverwrite);
2258 }
2259 
2260 // Return false if failed to save.
SavePreset(bool bCheckForOverwrite)2261 bool ExportFFmpegOptions::SavePreset(bool bCheckForOverwrite)
2262 {
2263    wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2264    wxString name = preset->GetValue();
2265    if (name.empty())
2266    {
2267       AudacityMessageBox( XO("You can't save a preset without a name") );
2268       return false;
2269    }
2270    if( bCheckForOverwrite && !mPresets->OverwriteIsOk(name))
2271       return false;
2272    if( !mPresets->SavePreset(this,name) )
2273       return false;
2274    int index = mPresetNames.Index(name,false);
2275    if (index == -1)
2276    {
2277       mPresetNames.push_back(name);
2278       mPresetCombo->Clear();
2279       mPresetCombo->Append(mPresetNames);
2280       mPresetCombo->Select(mPresetNames.Index(name,false));
2281    }
2282    return true;
2283 }
2284 
2285 ///
2286 ///
OnLoadPreset(wxCommandEvent & WXUNUSED (event))2287 void ExportFFmpegOptions::OnLoadPreset(wxCommandEvent& WXUNUSED(event))
2288 {
2289    wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2290    wxString presetname = preset->GetValue();
2291 
2292    mShownFormatNames = mFormatNames;
2293    mShownFormatLongNames = mFormatLongNames;
2294    mFormatList->Clear();
2295    mFormatList->Append(mFormatNames);
2296 
2297    mShownCodecNames = mCodecNames;
2298    mShownCodecLongNames = mCodecLongNames;
2299    mCodecList->Clear();
2300    mCodecList->Append(mCodecNames);
2301 
2302    mPresets->LoadPreset(this,presetname);
2303 
2304    DoOnFormatList();
2305    DoOnCodecList();
2306 }
2307 
FileTypes()2308 static const FileNames::FileTypes &FileTypes()
2309 {
2310    static const FileNames::FileTypes result{
2311       FileNames::XMLFiles, FileNames::AllFiles };
2312    return result;
2313 };
2314 
2315 ///
2316 ///
OnImportPresets(wxCommandEvent & WXUNUSED (event))2317 void ExportFFmpegOptions::OnImportPresets(wxCommandEvent& WXUNUSED(event))
2318 {
2319    wxString path;
2320    FileDialogWrapper dlg(this,
2321       XO("Select xml file with presets to import"),
2322       gPrefs->Read(wxT("/FileFormats/FFmpegPresetDir")),
2323       wxEmptyString,
2324       FileTypes(),
2325       wxFD_OPEN);
2326    if (dlg.ShowModal() == wxID_CANCEL) return;
2327    path = dlg.GetPath();
2328    mPresets->ImportPresets(path);
2329    mPresets->GetPresetList(mPresetNames);
2330    mPresetCombo->Clear();
2331    mPresetCombo->Append(mPresetNames);
2332 }
2333 
2334 ///
2335 ///
OnExportPresets(wxCommandEvent & WXUNUSED (event))2336 void ExportFFmpegOptions::OnExportPresets(wxCommandEvent& WXUNUSED(event))
2337 {
2338    const bool kCheckForOverwrite = true;
2339    // Bug 1180 save any pending preset before exporting the lot.
2340    // If saving fails, don't try to export.
2341    if( !SavePreset(!kCheckForOverwrite) )
2342       return;
2343 
2344    wxArrayString presets;
2345    mPresets->GetPresetList( presets);
2346    if( presets.Count() < 1)
2347    {
2348       AudacityMessageBox( XO("No presets to export") );
2349       return;
2350    }
2351 
2352    wxString path;
2353    FileDialogWrapper dlg(this,
2354       XO("Select xml file to export presets into"),
2355       gPrefs->Read(wxT("/FileFormats/FFmpegPresetDir")),
2356       wxEmptyString,
2357       FileTypes(),
2358       wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
2359    if (dlg.ShowModal() == wxID_CANCEL) return;
2360    path = dlg.GetPath();
2361    mPresets->ExportPresets(path);
2362 }
2363 
2364 ///
2365 ///
OnAllFormats(wxCommandEvent & WXUNUSED (event))2366 void ExportFFmpegOptions::OnAllFormats(wxCommandEvent& WXUNUSED(event))
2367 {
2368    mShownFormatNames = mFormatNames;
2369    mShownFormatLongNames = mFormatLongNames;
2370    mFormatList->Clear();
2371    mFormatList->Append(mFormatNames);
2372 }
2373 
2374 ///
2375 ///
OnAllCodecs(wxCommandEvent & WXUNUSED (event))2376 void ExportFFmpegOptions::OnAllCodecs(wxCommandEvent& WXUNUSED(event))
2377 {
2378    mShownCodecNames = mCodecNames;
2379    mShownCodecLongNames = mCodecLongNames;
2380    mCodecList->Clear();
2381    mCodecList->Append(mCodecNames);
2382 }
2383 
2384 /// ReportIfBadCombination will trap
2385 /// bad combinations of format and codec and report
2386 /// using a message box.
2387 /// We may later extend it to catch bad parameters too.
2388 /// @return true iff a bad combination was reported
2389 /// At the moment we don't trap unrecognised format
2390 /// or codec.  (We do not expect them to happen ever).
ReportIfBadCombination()2391 bool ExportFFmpegOptions::ReportIfBadCombination()
2392 {
2393    wxString *selcdc = nullptr;
2394    wxString* selcdclong = nullptr;
2395 
2396    FindSelectedCodec(&selcdc, &selcdclong);
2397 
2398    if (selcdc == nullptr)
2399       return false; // unrecognised codec. Treated as OK
2400 
2401    auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2402 
2403    if (cdc == nullptr)
2404       return false; // unrecognised codec. Treated as OK
2405 
2406    wxString* selfmt = nullptr;
2407    wxString* selfmtlong = nullptr;
2408 
2409    FindSelectedFormat(&selfmt, &selfmtlong);
2410 
2411    if (selfmt == nullptr)
2412       return false; // unrecognised format; Treated as OK
2413 
2414    // This is intended to test for illegal combinations.
2415    // However, the list updating now seems to be working correctly
2416    // making it impossible to select illegal combinations
2417    bool bFound = false;
2418    for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2419    {
2420       if (*selfmt == CompatibilityList[i].fmt)
2421       {
2422          if (CompatibilityList[i].codec == mFFmpeg->GetAudacityCodecID(cdc->GetId()) || (CompatibilityList[i].codec == AUDACITY_AV_CODEC_ID_NONE) ){
2423             bFound = true;
2424             break;
2425          }
2426       }
2427    }
2428 
2429    // We can put extra code in here, to disallow combinations
2430    // We could also test for illegal parameters, and deliver
2431    // custom error messages in that case.
2432    // The below would make AAC codec disallowed.
2433    //if( cdc->id == AUDACITY_AV_CODEC_ID_AAC)
2434    //   bFound = false;
2435 
2436    // Valid combination was found, so no reporting.
2437    if( bFound )
2438       return false;
2439 
2440    AudacityMessageBox(
2441       /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2442       XO("Format %s is not compatible with codec %s.")
2443          .Format( *selfmt, *selcdc ),
2444       /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2445       XO("Incompatible format and codec"));
2446 
2447    return true;
2448 }
2449 
2450 
2451 
EnableDisableControls(AVCodecWrapper * cdc,wxString * selfmt)2452 void ExportFFmpegOptions::EnableDisableControls(AVCodecWrapper *cdc, wxString *selfmt)
2453 {
2454    int handled = -1;
2455    for (int i = 0; apptable[i].control != 0; i++)
2456    {
2457       if (apptable[i].control != handled)
2458       {
2459          bool codec = false;
2460          bool format = false;
2461          if (apptable[i].codec == AUDACITY_AV_CODEC_ID_NONE)
2462          {
2463             codec = true;
2464          }
2465          else if (
2466             cdc != NULL &&
2467             apptable[i].codec == mFFmpeg->GetAudacityCodecID(cdc->GetId()))
2468          {
2469             codec = true;
2470          }
2471 
2472          if (wxString::FromUTF8(apptable[i].format) == wxT("any")) format = true;
2473          else if (selfmt != NULL &&
2474             *selfmt == wxString::FromUTF8(apptable[i].format)) format = true;
2475          if (codec && format)
2476          {
2477             handled = apptable[i].control;
2478             wxWindow *item = FindWindowById(apptable[i].control,this);
2479             if (item != NULL) item->Enable(apptable[i].enable);
2480          }
2481       }
2482    }
2483 }
2484 
DoOnFormatList()2485 void ExportFFmpegOptions::DoOnFormatList()
2486 {
2487    wxString *selfmt = NULL;
2488    wxString *selfmtlong = NULL;
2489    FindSelectedFormat(&selfmt, &selfmtlong);
2490    if (selfmt == NULL)
2491    {
2492       return;
2493    }
2494 
2495    wxString *selcdc = NULL;
2496    wxString *selcdclong = NULL;
2497    FindSelectedCodec(&selcdc, &selcdclong);
2498 
2499    auto fmt = mFFmpeg->GuessOutputFormat(selfmt->ToUTF8(),NULL,NULL);
2500    if (fmt == NULL)
2501    {
2502       //This shouldn't really happen
2503       mFormatName->SetLabel(wxString(_("Failed to guess format")));
2504       return;
2505    }
2506    mFormatName->SetLabel(wxString::Format(wxT("%s"), *selfmtlong));
2507 
2508    AudacityAVCodecID selcdcid = AUDACITY_AV_CODEC_ID_NONE;
2509 
2510    if (selcdc != nullptr)
2511    {
2512       auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2513 
2514       if (cdc != nullptr)
2515       {
2516          selcdcid = mFFmpeg->GetAudacityCodecID(cdc->GetId());
2517       }
2518    }
2519    int newselcdc =
2520       FetchCompatibleCodecList(*selfmt, selcdcid);
2521 
2522    if (newselcdc >= 0) mCodecList->Select(newselcdc);
2523 
2524    std::unique_ptr<AVCodecWrapper> cdc;
2525 
2526    if (selcdc != nullptr)
2527       cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2528 
2529    EnableDisableControls(cdc.get(), selfmt);
2530    Layout();
2531    Fit();
2532    return;
2533 }
2534 
DoOnCodecList()2535 void ExportFFmpegOptions::DoOnCodecList()
2536 {
2537    wxString *selcdc = nullptr;
2538    wxString* selcdclong = nullptr;
2539 
2540    FindSelectedCodec(&selcdc, &selcdclong);
2541 
2542    if (selcdc == nullptr)
2543    {
2544       return;
2545    }
2546 
2547    wxString* selfmt = nullptr;
2548    wxString* selfmtlong = nullptr;
2549 
2550    FindSelectedFormat(&selfmt, &selfmtlong);
2551 
2552    auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2553    if (cdc == nullptr)
2554    {
2555       //This shouldn't really happen
2556       /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2557       mCodecName->SetLabel(wxString(_("Failed to find the codec")));
2558       return;
2559    }
2560 
2561    mCodecName->SetLabel(wxString::Format(wxT("[%d] %s"), (int) mFFmpeg->GetAudacityCodecID(cdc->GetId()).value, *selcdclong));
2562 
2563    if (selfmt != nullptr)
2564    {
2565       auto fmt = mFFmpeg->GuessOutputFormat(selfmt->ToUTF8(), nullptr, nullptr);
2566       if (fmt == nullptr)
2567       {
2568          selfmt = nullptr;
2569          selfmtlong = nullptr;
2570       }
2571    }
2572 
2573    int newselfmt = FetchCompatibleFormatList(
2574       mFFmpeg->GetAudacityCodecID(cdc->GetId()), selfmt);
2575 
2576    if (newselfmt >= 0)
2577       mFormatList->Select(newselfmt);
2578 
2579    EnableDisableControls(cdc.get(), selfmt);
2580    Layout();
2581    Fit();
2582    return;
2583 }
2584 
2585 ///
2586 ///
OnFormatList(wxCommandEvent & WXUNUSED (event))2587 void ExportFFmpegOptions::OnFormatList(wxCommandEvent& WXUNUSED(event))
2588 {
2589    DoOnFormatList();
2590 }
2591 
2592 ///
2593 ///
OnCodecList(wxCommandEvent & WXUNUSED (event))2594 void ExportFFmpegOptions::OnCodecList(wxCommandEvent& WXUNUSED(event))
2595 {
2596    DoOnCodecList();
2597 }
2598 
2599 
2600 ///
2601 ///
OnOK(wxCommandEvent & WXUNUSED (event))2602 void ExportFFmpegOptions::OnOK(wxCommandEvent& WXUNUSED(event))
2603 {
2604    if( ReportIfBadCombination() )
2605       return;
2606 
2607    int selcdc = mCodecList->GetSelection();
2608    int selfmt = mFormatList->GetSelection();
2609    if (selcdc > -1) gPrefs->Write(wxT("/FileFormats/FFmpegCodec"),mCodecList->GetString(selcdc));
2610    if (selfmt > -1) gPrefs->Write(wxT("/FileFormats/FFmpegFormat"),mFormatList->GetString(selfmt));
2611    gPrefs->Flush();
2612 
2613    ShuttleGui S(this, eIsSavingToPrefs);
2614    PopulateOrExchange(S);
2615 
2616    gPrefs->Flush();
2617 
2618    EndModal(wxID_OK);
2619 
2620    return;
2621 }
2622 
OnGetURL(wxCommandEvent & WXUNUSED (event))2623 void ExportFFmpegOptions::OnGetURL(wxCommandEvent & WXUNUSED(event))
2624 {
2625    HelpSystem::ShowHelp(this, L"Custom_FFmpeg_Export_Options");
2626 }
2627 
2628 
2629 #endif
2630