Audacity 3.2.0
ImportFFmpeg.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ImportFFmpeg.cpp
6
7Copyright 2008 LRN
8Based on ImportFLAC.cpp by Sami Liedes and transcode_sample.c by ANYwebcam Pty Ltd
9Licensed under the GNU General Public License v2 or later
10
11*//****************************************************************//****************************************************************//*******************************************************************/
22
23
24
25// For compilers that support precompilation, includes "wx/wx.h".
26#include <wx/wxprec.h>
27
28#include "../FFmpeg.h"
29#include "FFmpegFunctions.h"
30
31#ifndef WX_PRECOMP
32// Include your minimal set of headers here, or wx.h
33#include <wx/log.h>
34#include <wx/window.h>
35#endif
36
37#include "../widgets/ProgressDialog.h"
38
39
40#define DESC XO("FFmpeg-compatible files")
41
42//TODO: remove non-audio extensions
43#if defined(USE_FFMPEG)
44static const auto exts = {
45 wxT("4xm"),
46 wxT("MTV"),
47 wxT("roq"),
48 wxT("aac"),
49 wxT("ac3"),
50 wxT("aif"),
51 wxT("aiff"),
52 wxT("afc"),
53 wxT("aifc"),
54 wxT("al"),
55 wxT("amr"),
56 wxT("apc"),
57 wxT("ape"),
58 wxT("apl"),
59 wxT("mac"),
60 wxT("asf"),
61 wxT("wmv"),
62 wxT("wma"),
63 wxT("au"),
64 wxT("avi"),
65 wxT("avs"),
66 wxT("bethsoftvid"),
67 wxT("c93"),
68 wxT("302"),
69 wxT("daud"),
70 wxT("dsicin"),
71 wxT("dts"),
72 wxT("dv"),
73 wxT("dxa"),
74 wxT("ea"),
75 wxT("cdata"),
76 wxT("ffm"),
77 wxT("film_cpk"),
78 wxT("flac"),
79 wxT("flic"),
80 wxT("flv"),
81 wxT("gif"),
82 wxT("gxf"),
83 wxT("idcin"),
84 wxT("image2"),
85 wxT("image2pipe"),
86 wxT("cgi"),
87 wxT("ipmovie"),
88 wxT("nut"),
89 wxT("lmlm4"),
90 wxT("m4v"),
91 wxT("mkv"),
92 wxT("mm"),
93 wxT("mmf"),
94 wxT("mov"),
95 wxT("mp4"),
96 wxT("m4a"),
97 wxT("m4r"),
98 wxT("3gp"),
99 wxT("3g2"),
100 wxT("mj2"),
101 wxT("mp3"),
102 wxT("mpc"),
103 wxT("mpc8"),
104 wxT("mpg"),
105 wxT("mpeg"),
106 wxT("ts"),
107 wxT("mpegtsraw"),
108 wxT("mpegvideo"),
109 wxT("msnwctcp"),
110 wxT("ul"),
111 wxT("mxf"),
112 wxT("nsv"),
113 wxT("nuv"),
114 wxT("ogg"),
115 wxT("opus"),
116 wxT("psxstr"),
117 wxT("pva"),
118 wxT("redir"),
119 wxT("rl2"),
120 wxT("rm"),
121 wxT("ra"),
122 wxT("rv"),
123 wxT("rtsp"),
124 wxT("s16be"),
125 wxT("sw"),
126 wxT("s8"),
127 wxT("sb"),
128 wxT("sdp"),
129 wxT("shn"),
130 wxT("siff"),
131 wxT("vb"),
132 wxT("son"),
133 wxT("smk"),
134 wxT("sol"),
135 wxT("swf"),
136 wxT("thp"),
137 wxT("tiertexseq"),
138 wxT("tta"),
139 wxT("txd"),
140 wxT("u16be"),
141 wxT("uw"),
142 wxT("ub"),
143 wxT("u8"),
144 wxT("vfwcap"),
145 wxT("vmd"),
146 wxT("voc"),
147 wxT("wav"),
148 wxT("wc3movie"),
149 wxT("wsaud"),
150 wxT("wsvqa"),
151 wxT("wv")
152};
153
154// all the includes live here by default
155#include "Import.h"
156#include "../Tags.h"
157#include "../WaveTrack.h"
158#include "ImportPlugin.h"
159
161
165{
166public:
169 {
170 }
171
173
174 wxString GetPluginStringID() override { return wxT("libav"); }
176
178 std::unique_ptr<ImportFileHandle> Open(
179 const FilePath &Filename, AudacityProject*) override;
180};
181
182struct StreamContext final
183{
184 int StreamIndex { -1 };
185
186 std::unique_ptr<AVCodecContextWrapper> CodecContext;
187
190
191 bool Use { true };
192};
193
196{
197
198public:
201
204 bool Init();
207 bool InitCodecs();
208
209
212
215 ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks,
216 Tags *tags) override;
217
221
225 void WriteMetadata(Tags *tags);
226
232 void GetMetadata(Tags &tags, const wxChar *tag, const char *name);
233
236 wxInt32 GetStreamCount() override
237 {
238 return static_cast<wxInt32>(mStreamContexts.size());
239 }
240
244 {
245 return mStreamInfo;
246 }
247
251 void SetStreamUsage(wxInt32 StreamID, bool Use) override
252 {
253 if (StreamID < static_cast<wxInt32>(mStreamContexts.size()))
254 mStreamContexts[StreamID].Use = Use;
255 }
256
257private:
258 // Construct this member first, so it is destroyed last, so the functions
259 // remain loaded while other members are destroyed
260 const std::shared_ptr<FFmpegFunctions> mFFmpeg = FFmpegFunctions::Load();
261
262 std::vector<StreamContext> mStreamContexts;
263
264 std::unique_ptr<AVFormatContextWrapper> mAVFormatContext;
265
267
268 wxInt64 mProgressPos = 0;
269 wxInt64 mProgressLen = 1;
270
271 bool mCancelled = false;
272 bool mStopped = false;
278};
279
280
282{
283 return DESC;
284}
285
286std::unique_ptr<ImportFileHandle> FFmpegImportPlugin::Open(
287 const FilePath &filename, AudacityProject*)
288{
289 auto ffmpeg = FFmpegFunctions::Load();
290
291 //Check if we're loading explicitly supported format
292 wxString extension = filename.AfterLast(wxT('.'));
293 if (SupportsExtension(extension))
294 {
295 //Audacity is trying to load something that is declared as
296 //officially supported by this plugin.
297 //If we don't have FFmpeg configured - tell the user about it.
298 //Since this will be happening often, use disableable "FFmpeg not found" dialog
299 //insdead of usual AudacityMessageBox()
300 bool newsession = NewImportingSession.Read();
301 if (!ffmpeg)
302 {
303 auto dontShowDlg = FFmpegNotFoundDontShow.Read();
304 if (dontShowDlg == 0 && newsession)
305 {
307 gPrefs->Flush();
308 FFmpegNotFoundDialog{ nullptr }.ShowModal();
309
310 ffmpeg = FFmpegFunctions::Load();
311 }
312 }
313 }
314 if (!ffmpeg)
315 {
316 return nullptr;
317 }
318
319 // Construct the handle only after any reloading of ffmpeg functions
320 auto handle = std::make_unique<FFmpegImportFileHandle>(filename);
321
322 // Open the file for import
323 bool success = handle->Init();
324
325 if (!success) {
326 return nullptr;
327 }
328
329 return handle;
330}
331
333 std::make_unique< FFmpegImportPlugin >()
334};
335
336
339, mName{ name }
340{
341}
342
344{
345 if (!mFFmpeg)
346 return false;
347
348 mAVFormatContext = mFFmpeg->CreateAVFormatContext();
349
350 const auto err = mAVFormatContext->OpenInputContext(mName, nullptr, AVDictionaryWrapper(*mFFmpeg));
351
353 {
354 wxLogError(wxT("FFmpeg : AVFormatContextWrapper::OpenInputContext() failed for file %s"), mName);
355 return false;
356 }
357
358 if (!InitCodecs())
359 return false;
360
361 return true;
362}
363
365{
366 for (unsigned int i = 0; i < mAVFormatContext->GetStreamsCount(); i++)
367 {
368 const AVStreamWrapper* stream = mAVFormatContext->GetStream(i);
369
370 if (stream->IsAudio())
371 {
372 const AVCodecIDFwd id = mAVFormatContext->GetStream(i)->GetAVCodecID();
373
374 auto codec = mFFmpeg->CreateDecoder(id);
375 auto name = mFFmpeg->avcodec_get_name(id);
376
377 if (codec == NULL)
378 {
379 wxLogError(
380 wxT("FFmpeg : CreateDecoder() failed. Index[%02d], Codec[%02x - %s]"),
381 i, id, name);
382 //FFmpeg can't decode this stream, skip it
383 continue;
384 }
385
386 auto codecContextPtr = stream->GetAVCodecContext();
387
388 if ( codecContextPtr->Open( codecContextPtr->GetCodec() ) < 0 )
389 {
390 wxLogError(wxT("FFmpeg : Open() failed. Index[%02d], Codec[%02x - %s]"),i,id,name);
391 //Can't open decoder - skip this stream
392 continue;
393 }
394
395 const int channels = codecContextPtr->GetChannels();
396 const sampleFormat preferredFormat =
397 codecContextPtr->GetPreferredAudacitySampleFormat();
398
399 auto codecContext = codecContextPtr.get();
400
401 mStreamContexts.emplace_back(
402 StreamContext { stream->GetIndex(), std::move(codecContextPtr),
403 channels, preferredFormat, true });
404
405 // Stream is decodeable and it is audio. Add it and its description to the arrays
406 int duration = 0;
407 if (stream->GetDuration() > 0)
408 duration = stream->GetDuration() * stream->GetTimeBase().num / stream->GetTimeBase().den;
409 else
410 duration = mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE;
411
412 wxString bitrate;
413 if (codecContext->GetBitRate() > 0)
414 bitrate.Printf(wxT("%d"),(int)codecContext->GetBitRate());
415 else
416 bitrate.Printf(wxT("?"));
417
418 AVDictionaryWrapper streamMetadata = stream->GetMetadata();
419
420 auto lang = std::string(streamMetadata.Get("language", {}));
421
422 auto strinfo = XO(
423/* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
424"Index[%02x] Codec[%s], Language[%s], Bitrate[%s], Channels[%d], Duration[%d]")
425 .Format(
426 stream->GetIndex(),
427 name,
428 lang,
429 bitrate,
430 (int)codecContext->GetChannels(),
431 (int)duration);
432
433 mStreamInfo.push_back(strinfo);
434 }
435 //for video and unknown streams do nothing
436 }
437 //It doesn't really returns false, but GetStreamCount() will return 0 if file is composed entirely of unreadable streams
438 return true;
439}
440
442{
443 return DESC;
444}
445
446
448{
449 // TODO: Get Uncompressed byte count.
450 return 0;
451}
452
454 TrackHolders &outTracks,
455 Tags *tags)
456{
457 outTracks.clear();
458
460
462 mStreamContexts.erase (std::remove_if (mStreamContexts.begin (), mStreamContexts.end (), [](const StreamContext& ctx) {
463 return !ctx.Use;
464 }), mStreamContexts.end());
465
466 mChannels.resize(mStreamContexts.size());
467
468 int s = -1;
469 for (auto &stream : mChannels)
470 {
471 ++s;
472
473 const StreamContext& sc = mStreamContexts[s];
474
475 stream.resize(sc.InitialChannels);
476
477 for (auto &channel : stream)
478 channel = NewWaveTrack(*trackFactory, sc.SampleFormat, sc.CodecContext->GetSampleRate());
479 }
480
481 // Handles the start_time by creating silence. This may or may not be correct.
482 // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
484 s = -1;
485 for (auto &stream : mChannels)
486 {
487 ++s;
488
489 int64_t stream_delay = 0;
490 const auto& sc = mStreamContexts[s];
491
492 const int64_t streamStartTime =
493 mAVFormatContext->GetStream(sc.StreamIndex)->GetStartTime();
494
496 {
497 stream_delay = streamStartTime;
498
499 wxLogDebug(
500 wxT("Stream %d start_time = %lld, that would be %f milliseconds."),
501 s, (long long)streamStartTime, double(streamStartTime) / 1000);
502 }
503
504 if (stream_delay > 0)
505 {
506 int c = -1;
507 for (auto &channel : stream)
508 {
509 ++c;
510
511 WaveTrack *t = channel.get();
512 t->InsertSilence(0,double(stream_delay)/AUDACITY_AV_TIME_BASE);
513 }
514 }
515 }
516 // This is the heart of the importing process
517 // The result of Import() to be returned. It will be something other than zero if user canceled or some error appears.
518 auto res = ProgressResult::Success;
519
520 // Read frames.
521 for (std::unique_ptr<AVPacketWrapper> packet;
522 (packet = mAVFormatContext->ReadNextPacket()) != nullptr &&
523 (res == ProgressResult::Success);)
524 {
525 // Find a matching StreamContext
526 auto streamContextIt = std::find_if(
527 mStreamContexts.begin(), mStreamContexts.end(),
528 [index = packet->GetStreamIndex()](const StreamContext& ctx)
529 { return ctx.StreamIndex == index;
530 });
531
532 if (streamContextIt == mStreamContexts.end())
533 continue;
534
535 res = WriteData(&(*streamContextIt), packet.get());
536 }
537
538 // Flush the decoders.
539 if (!mStreamContexts.empty() && (res == ProgressResult::Success || res == ProgressResult::Stopped))
540 {
541 auto emptyPacket = mFFmpeg->CreateAVPacketWrapper();
542
544 WriteData(&sc, emptyPacket.get());
545 }
546
547 // Something bad happened - destroy everything!
548 if (res == ProgressResult::Cancelled || res == ProgressResult::Failed)
549 return res;
550 //else if (res == 2), we just stop the decoding as if the file has ended
551
552 // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
553 for (auto &stream : mChannels)
554 for(auto &channel : stream)
555 channel->Flush();
556
557 outTracks.swap(mChannels);
558
559 // Save metadata
560 WriteMetadata(tags);
561
562 return res;
563}
564
566{
567 // Find the stream index in mStreamContexts array
568 int streamid = -1;
569 auto iter = mChannels.begin();
570
571 for (int i = 0; i < static_cast<int>(mStreamContexts.size()); ++iter, ++i)
572 {
573 if (&mStreamContexts[i] == sc)
574 {
575 streamid = i;
576 break;
577 }
578 }
579 // Stream is not found. This should not really happen
580 if (streamid == -1)
581 {
583 }
584
585 size_t nChannels = std::min(sc->CodecContext->GetChannels(), sc->InitialChannels);
586
587 if (sc->SampleFormat == int16Sample)
588 {
589 auto data = sc->CodecContext->DecodeAudioPacketInt16(packet);
590
591 const int channelsCount = sc->CodecContext->GetChannels();
592 const int samplesPerChannel = data.size() / channelsCount;
593
594 // Write audio into WaveTracks
595 auto iter2 = iter->begin();
596 for (size_t chn = 0; chn < nChannels; ++iter2, ++chn)
597 {
598 iter2->get()->Append(
599 reinterpret_cast<samplePtr>(data.data() + chn), sc->SampleFormat,
600 samplesPerChannel,
601 sc->CodecContext->GetChannels());
602 }
603 }
604 else if (sc->SampleFormat == floatSample)
605 {
606 auto data = sc->CodecContext->DecodeAudioPacketFloat(packet);
607
608 const int channelsCount = sc->CodecContext->GetChannels();
609 const int samplesPerChannel = data.size() / channelsCount;
610
611 // Write audio into WaveTracks
612 auto iter2 = iter->begin();
613 for (size_t chn = 0; chn < nChannels; ++iter2, ++chn)
614 {
615 iter2->get()->Append(
616 reinterpret_cast<samplePtr>(data.data() + chn), sc->SampleFormat,
617 samplesPerChannel, sc->CodecContext->GetChannels());
618 }
619 }
620
621 const AVStreamWrapper* avStream = mAVFormatContext->GetStream(sc->StreamIndex);
622
623 // Try to update the progress indicator (and see if user wants to cancel)
624 auto updateResult = ProgressResult::Success;
625 int64_t filesize = mFFmpeg->avio_size(mAVFormatContext->GetAVIOContext()->GetWrappedValue());
626 // PTS (presentation time) is the proper way of getting current position
627 if (
630 {
631 auto timeBase = avStream->GetTimeBase();
632
634 packet->GetPresentationTimestamp() * timeBase.num / timeBase.den;
635
637 (mAVFormatContext->GetDuration() > 0 ?
638 mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE :
639 1);
640 }
641 // When PTS is not set, use number of frames and number of current frame
642 else if (
643 avStream->GetFramesCount() > 0 && sc->CodecContext->GetFrameNumber() > 0 &&
644 sc->CodecContext->GetFrameNumber() <= avStream->GetFramesCount())
645 {
646 mProgressPos = sc->CodecContext->GetFrameNumber();
647 mProgressLen = avStream->GetFramesCount();
648 }
649 // When number of frames is unknown, use position in file
650 else if (
651 filesize > 0 && packet->GetPos() > 0 && packet->GetPos() <= filesize)
652 {
653 mProgressPos = packet->GetPos();
654 mProgressLen = filesize;
655 }
656 updateResult = mProgress->Update(mProgressPos, mProgressLen != 0 ? mProgressLen : 1);
657
658 return updateResult;
659}
660
662{
663 Tags temp;
664
665 GetMetadata(temp, TAG_TITLE, "title");
666 GetMetadata(temp, TAG_COMMENTS, "comment");
667 GetMetadata(temp, TAG_ALBUM, "album");
668 GetMetadata(temp, TAG_TRACK, "track");
669 GetMetadata(temp, TAG_GENRE, "genre");
670
671 if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("m4a"))
672 {
673 GetMetadata(temp, TAG_ARTIST, "artist");
674 GetMetadata(temp, TAG_YEAR, "date");
675 }
676 else if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("asf")) /* wma */
677 {
678 GetMetadata(temp, TAG_ARTIST, "artist");
679 GetMetadata(temp, TAG_YEAR, "year");
680 }
681 else
682 {
683 GetMetadata(temp, TAG_ARTIST, "author");
684 GetMetadata(temp, TAG_YEAR, "year");
685 }
686
687 if (!temp.IsEmpty())
688 {
689 *tags = temp;
690 }
691}
692
693void FFmpegImportFileHandle::GetMetadata(Tags &tags, const wxChar *tag, const char *name)
694{
695 auto metadata = mAVFormatContext->GetMetadata();
696
697 if (metadata.HasValue(name, DICT_IGNORE_SUFFIX))
698 tags.SetTag(tag, wxString::FromUTF8(std::string(metadata.Get(name, {}, DICT_IGNORE_SUFFIX))));
699}
700
701
703{
704
705}
706
707#endif //USE_FFMPEG
int AVCodecIDFwd
Definition: AVCodecID.h:407
#define DICT_IGNORE_SUFFIX
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:82
BoolSetting FFmpegNotFoundDontShow
Definition: FFmpeg.cpp:362
#define AUDACITY_AV_TIME_BASE
Definition: FFmpegTypes.h:80
#define AUDACITY_AV_NOPTS_VALUE
Definition: FFmpegTypes.h:74
BoolSetting NewImportingSession
Definition: Import.cpp:895
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
static const auto exts
#define DESC
static Importer::RegisteredImportPlugin registered
The interface that all file import "plugins" (if you want to call them that) must implement....
#define XO(s)
Definition: Internat.h:31
FileConfig * gPrefs
Definition: Prefs.cpp:71
wxString FilePath
Definition: Project.h:20
sampleFormat
Definition: SampleFormat.h:29
@ floatSample
Definition: SampleFormat.h:34
@ int16Sample
Definition: SampleFormat.h:32
char * samplePtr
Definition: SampleFormat.h:49
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
std::vector< TranslatableString > TranslatableStrings
std::string_view Get(const std::string_view &key, const std::string_view &defaultValue, int flags=0) const
virtual int64_t GetPos() const noexcept=0
virtual int64_t GetPresentationTimestamp() const noexcept=0
virtual int64_t GetDuration() const noexcept=0
virtual int GetIndex() const noexcept=0
virtual bool IsAudio() const noexcept=0
virtual AVDictionaryWrapper GetMetadata() const noexcept=0
virtual AudacityAVRational GetTimeBase() const noexcept=0
virtual int64_t GetFramesCount() const noexcept=0
virtual std::unique_ptr< AVCodecContextWrapper > GetAVCodecContext() const noexcept=0
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
! Does actual import, returned by FFmpegImportPlugin::Open
wxInt64 mProgressPos
Current timestamp, file position or whatever is used as first argument for Update()
std::unique_ptr< AVFormatContextWrapper > mAVFormatContext
FFmpegImportFileHandle(const FilePath &name)
void GetMetadata(Tags &tags, const wxChar *tag, const char *name)
TranslatableStrings mStreamInfo
Array of stream descriptions. After Init() and before Import(), same size as mStreamContexts.
void SetStreamUsage(wxInt32 StreamID, bool Use) override
const TranslatableStrings & GetStreamInfo() override
ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
const std::shared_ptr< FFmpegFunctions > mFFmpeg
TranslatableString GetFileDescription() override
void WriteMetadata(Tags *tags)
bool mStopped
True if importing was stopped by user.
ByteCount GetFileUncompressedBytes() override
bool mCancelled
True if importing was canceled by user.
wxInt32 GetStreamCount() override
std::vector< StreamContext > mStreamContexts
ProgressResult WriteData(StreamContext *sc, const AVPacketWrapper *packet)
wxInt64 mProgressLen
Duration, total length or whatever is used as second argument for Update()
An ImportPlugin for FFmpeg data.
wxString GetPluginStringID() override
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
! Probes the file and opens it if appropriate
TranslatableString GetPluginFormatDescription() override
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
unsigned long long ByteCount
Definition: ImportPlugin.h:127
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:159
std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
Build a wave track with appropriate format, which will not be narrower than the specified one.
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
bool SupportsExtension(const FileExtension &extension)
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
ID3 Tags (for MP3)
Definition: Tags.h:73
bool IsEmpty()
Definition: Tags.cpp:297
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:441
Holds a msgid for the translation catalog; may also bind format arguments.
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:613
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
void InsertSilence(double t, double len) override
Definition: WaveTrack.cpp:1597
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
ProgressResult
Definition: BasicUI.h:145
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
int den
denominator
Definition: FFmpegTypes.h:139
int num
numerator
Definition: FFmpegTypes.h:138
static std::shared_ptr< FFmpegFunctions > Load(bool fromUserPathOnly=false)
std::unique_ptr< AVCodecContextWrapper > CodecContext
sampleFormat SampleFormat