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#include "FFmpeg.h"
24#include "FFmpegFunctions.h"
25
26#include <wx/log.h>
27#include <wx/window.h>
28
29#define DESC XO("FFmpeg-compatible files")
30
31//TODO: remove non-audio extensions
32static const auto exts = {
33 wxT("4xm"),
34 wxT("MTV"),
35 wxT("roq"),
36 wxT("aac"),
37 wxT("ac3"),
38 wxT("aif"),
39 wxT("aiff"),
40 wxT("afc"),
41 wxT("aifc"),
42 wxT("al"),
43 wxT("amr"),
44 wxT("apc"),
45 wxT("ape"),
46 wxT("apl"),
47 wxT("mac"),
48 wxT("asf"),
49 wxT("wmv"),
50 wxT("wma"),
51 wxT("au"),
52 wxT("avi"),
53 wxT("avs"),
54 wxT("bethsoftvid"),
55 wxT("c93"),
56 wxT("302"),
57 wxT("daud"),
58 wxT("dsicin"),
59 wxT("dts"),
60 wxT("dv"),
61 wxT("dxa"),
62 wxT("ea"),
63 wxT("cdata"),
64 wxT("ffm"),
65 wxT("film_cpk"),
66 wxT("flac"),
67 wxT("flic"),
68 wxT("flv"),
69 wxT("gif"),
70 wxT("gxf"),
71 wxT("idcin"),
72 wxT("image2"),
73 wxT("image2pipe"),
74 wxT("cgi"),
75 wxT("ipmovie"),
76 wxT("nut"),
77 wxT("lmlm4"),
78 wxT("m4v"),
79 wxT("mkv"),
80 wxT("mm"),
81 wxT("mmf"),
82 wxT("mov"),
83 wxT("mp4"),
84 wxT("m4a"),
85 wxT("m4r"),
86 wxT("3gp"),
87 wxT("3g2"),
88 wxT("mj2"),
89 wxT("mp3"),
90 wxT("mpc"),
91 wxT("mpc8"),
92 wxT("mpg"),
93 wxT("mpeg"),
94 wxT("ts"),
95 wxT("mpegtsraw"),
96 wxT("mpegvideo"),
97 wxT("msnwctcp"),
98 wxT("ul"),
99 wxT("mxf"),
100 wxT("nsv"),
101 wxT("nuv"),
102 wxT("ogg"),
103 wxT("opus"),
104 wxT("psxstr"),
105 wxT("pva"),
106 wxT("redir"),
107 wxT("rl2"),
108 wxT("rm"),
109 wxT("ra"),
110 wxT("rv"),
111 wxT("rtsp"),
112 wxT("s16be"),
113 wxT("sw"),
114 wxT("s8"),
115 wxT("sb"),
116 wxT("sdp"),
117 wxT("shn"),
118 wxT("siff"),
119 wxT("vb"),
120 wxT("son"),
121 wxT("smk"),
122 wxT("sol"),
123 wxT("swf"),
124 wxT("thp"),
125 wxT("tiertexseq"),
126 wxT("tta"),
127 wxT("txd"),
128 wxT("u16be"),
129 wxT("uw"),
130 wxT("ub"),
131 wxT("u8"),
132 wxT("vfwcap"),
133 wxT("vmd"),
134 wxT("voc"),
135 wxT("wav"),
136 wxT("wc3movie"),
137 wxT("wsaud"),
138 wxT("wsvqa"),
139 wxT("wv")
140};
141
142// all the includes live here by default
143#include "Import.h"
144#include "Tags.h"
145#include "WaveTrack.h"
146#include "ImportPlugin.h"
147#include "ImportUtils.h"
149
151
155{
156public:
159 {
160 }
161
163
164 wxString GetPluginStringID() override { return wxT("libav"); }
166
168 {
169 return !FFmpegFunctions::Load()
170 ? XO("Try installing FFmpeg.\n") : TranslatableString{};
171 }
172
174 std::unique_ptr<ImportFileHandle> Open(
175 const FilePath &Filename, AudacityProject*) override;
176};
177
178struct StreamContext final
179{
180 int StreamIndex { -1 };
181
182 std::unique_ptr<AVCodecContextWrapper> CodecContext;
183
186
187 bool Use { true };
188};
189
192{
193
194public:
197
200 bool Init();
203 bool InitCodecs();
204
205
208
209 void Import(ImportProgressListener& progressListener,
210 WaveTrackFactory *trackFactory,
211 TrackHolders &outTracks,
212 Tags *tags) override;
213
214 FilePath GetFilename() const override;
215
216 void Cancel() override;
217
218 void Stop() override;
219
222 void WriteData(StreamContext* sc, const AVPacketWrapper* packet);
223
227 void WriteMetadata(Tags *tags);
228
234 void GetMetadata(Tags &tags, const wxChar *tag, const char *name);
235
238 wxInt32 GetStreamCount() override
239 {
240 return static_cast<wxInt32>(mStreamContexts.size());
241 }
242
246 {
247 return mStreamInfo;
248 }
249
253 void SetStreamUsage(wxInt32 StreamID, bool Use) override
254 {
255 if (StreamID < static_cast<wxInt32>(mStreamContexts.size()))
256 mStreamContexts[StreamID].Use = Use;
257 }
258
259private:
260 // Construct this member first, so it is destroyed last, so the functions
261 // remain loaded while other members are destroyed
262 const std::shared_ptr<FFmpegFunctions> mFFmpeg = FFmpegFunctions::Load();
263
264 std::vector<StreamContext> mStreamContexts;
265
266 std::unique_ptr<AVFormatContextWrapper> mAVFormatContext;
267
269
270 wxInt64 mProgressPos = 0;
271 wxInt64 mProgressLen = 1;
272
273 bool mCancelled = false;
274 bool mStopped = false;
276 std::vector<TrackListHolder> mStreams;
277};
278
279
281{
282 return DESC;
283}
284
285std::unique_ptr<ImportFileHandle> FFmpegImportPlugin::Open(
286 const FilePath &filename, AudacityProject*)
287{
288 auto ffmpeg = FFmpegFunctions::Load();
289
290 //Check if we're loading explicitly supported format
291 wxString extension = filename.AfterLast(wxT('.'));
292 if (SupportsExtension(extension))
293 {
294 //Audacity is trying to load something that is declared as
295 //officially supported by this plugin.
296 //If we don't have FFmpeg configured - tell the user about it.
297 //Since this will be happening often, use disableable "FFmpeg not found" dialog
298 //insdead of usual AudacityMessageBox()
299 bool newsession = NewImportingSession.Read();
300 if (!ffmpeg)
301 {
302 auto dontShowDlg = FFmpegNotFoundDontShow.Read();
303 if (dontShowDlg == 0 && newsession)
304 {
306 gPrefs->Flush();
307 FFmpegNotFoundDialog{ nullptr }.ShowModal();
308
309 ffmpeg = FFmpegFunctions::Load();
310 }
311 }
312 }
313 if (!ffmpeg)
314 {
315 return nullptr;
316 }
317
318 // Construct the handle only after any reloading of ffmpeg functions
319 auto handle = std::make_unique<FFmpegImportFileHandle>(filename);
320
321 // Open the file for import
322 bool success = handle->Init();
323
324 if (!success) {
325 return nullptr;
326 }
327
328 return handle;
329}
330
332 std::make_unique< FFmpegImportPlugin >()
333};
334
335
337 : mName{ name }
338{
339}
340
342{
343 if (!mFFmpeg)
344 return false;
345
346 mAVFormatContext = mFFmpeg->CreateAVFormatContext();
347
348 const auto err = mAVFormatContext->OpenInputContext(mName, nullptr, AVDictionaryWrapper(*mFFmpeg));
349
351 {
352 wxLogError(wxT("FFmpeg : AVFormatContextWrapper::OpenInputContext() failed for file %s"), mName);
353 return false;
354 }
355
356 if (!InitCodecs())
357 return false;
358
359 return true;
360}
361
363{
364 for (unsigned int i = 0; i < mAVFormatContext->GetStreamsCount(); i++)
365 {
366 const AVStreamWrapper* stream = mAVFormatContext->GetStream(i);
367
368 if (stream->IsAudio())
369 {
370 const AVCodecIDFwd id = mAVFormatContext->GetStream(i)->GetAVCodecID();
371
372 auto codec = mFFmpeg->CreateDecoder(id);
373 auto name = mFFmpeg->avcodec_get_name(id);
374
375 if (codec == NULL)
376 {
377 wxLogError(
378 wxT("FFmpeg : CreateDecoder() failed. Index[%02d], Codec[%02x - %s]"),
379 i, id, name);
380 //FFmpeg can't decode this stream, skip it
381 continue;
382 }
383
384 auto codecContextPtr = stream->GetAVCodecContext();
385
386 if ( codecContextPtr->Open( codecContextPtr->GetCodec() ) < 0 )
387 {
388 wxLogError(wxT("FFmpeg : Open() failed. Index[%02d], Codec[%02x - %s]"),i,id,name);
389 //Can't open decoder - skip this stream
390 continue;
391 }
392
393 const int channels = codecContextPtr->GetChannels();
394 const sampleFormat preferredFormat =
395 codecContextPtr->GetPreferredAudacitySampleFormat();
396
397 auto codecContext = codecContextPtr.get();
398
399 mStreamContexts.emplace_back(
400 StreamContext { stream->GetIndex(), std::move(codecContextPtr),
401 channels, preferredFormat, true });
402
403 // Stream is decodeable and it is audio. Add it and its description to the arrays
404 int duration = 0;
405 if (stream->GetDuration() > 0)
406 duration = stream->GetDuration() * stream->GetTimeBase().num / stream->GetTimeBase().den;
407 else
408 duration = mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE;
409
410 wxString bitrate;
411 if (codecContext->GetBitRate() > 0)
412 bitrate.Printf(wxT("%d"),(int)codecContext->GetBitRate());
413 else
414 bitrate.Printf(wxT("?"));
415
416 AVDictionaryWrapper streamMetadata = stream->GetMetadata();
417
418 auto lang = std::string(streamMetadata.Get("language", {}));
419
420 auto strinfo = XO(
421/* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
422"Index[%02x] Codec[%s], Language[%s], Bitrate[%s], Channels[%d], Duration[%d]")
423 .Format(
424 stream->GetIndex(),
425 name,
426 lang,
427 bitrate,
428 (int)codecContext->GetChannels(),
429 (int)duration);
430
431 mStreamInfo.push_back(strinfo);
432 }
433 //for video and unknown streams do nothing
434 }
435 //It doesn't really returns false, but GetStreamCount() will return 0 if file is composed entirely of unreadable streams
436 return true;
437}
438
440{
441 return DESC;
442}
443
444
446{
447 // TODO: Get Uncompressed byte count.
448 return 0;
449}
450
452 WaveTrackFactory *trackFactory,
453 TrackHolders &outTracks,
454 Tags *tags)
455{
456 outTracks.clear();
457 mCancelled = false;
458 mStopped = false;
459
461 mStreamContexts.erase (std::remove_if (mStreamContexts.begin (), mStreamContexts.end (), [](const StreamContext& ctx) {
462 return !ctx.Use;
463 }), mStreamContexts.end());
464
465 for(unsigned s = 0; s < mStreamContexts.size(); ++s)
466 {
467 const StreamContext& sc = mStreamContexts[s];
468
469 auto stream = ImportUtils::NewWaveTrack(
470 *trackFactory,
472 sc.SampleFormat,
473 sc.CodecContext->GetSampleRate()
474 );
475
476 // Handles the start_time by creating silence. This may or may not be correct.
477 // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
479
480 int64_t stream_delay = 0;
481
482 const int64_t streamStartTime =
483 mAVFormatContext->GetStream(sc.StreamIndex)->GetStartTime();
484
486 {
487 stream_delay = streamStartTime;
488
489 wxLogDebug(
490 wxT("Stream %d start_time = %lld, that would be %f milliseconds."),
491 s, (long long)streamStartTime, double(streamStartTime) / 1000);
492 }
493
494 if (stream_delay > 0) {
495 for (auto track : *stream)
496 track->InsertSilence(0, double(stream_delay) / AUDACITY_AV_TIME_BASE);
497 }
498
499 mStreams.push_back(std::move(stream));
500 }
501
502 // This is the heart of the importing process
503
504 // Read frames.
505 for (std::unique_ptr<AVPacketWrapper> packet;
506 (packet = mAVFormatContext->ReadNextPacket()) != nullptr &&
507 !mCancelled && !mStopped;)
508 {
509 // Find a matching StreamContext
510 auto streamContextIt = std::find_if(
511 mStreamContexts.begin(), mStreamContexts.end(),
512 [index = packet->GetStreamIndex()](const StreamContext& ctx)
513 { return ctx.StreamIndex == index;
514 });
515
516 if (streamContextIt == mStreamContexts.end())
517 continue;
518
519 WriteData(&(*streamContextIt), packet.get());
520 if(mProgressLen > 0)
521 progressListener.OnImportProgress(static_cast<double>(mProgressPos) /
522 static_cast<double>(mProgressLen));
523 }
524
525 // Flush the decoders.
526 if (!mStreamContexts.empty() && !mCancelled)
527 {
528 auto emptyPacket = mFFmpeg->CreateAVPacketWrapper();
529
531 WriteData(&sc, emptyPacket.get());
532 }
533
534 if(mCancelled)
535 {
537 return;
538 }
539
540 // Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
542
543 // Save metadata
544 WriteMetadata(tags);
545 progressListener.OnImportResult(mStopped
548}
549
551{
552 return mName;
553}
554
556{
557 if(!mStopped)
558 mCancelled = true;
559}
560
562{
563 if(!mCancelled)
564 mStopped = true;
565}
566
568{
569 // Find the stream in mStreamContexts array
570 auto streamIt = std::find_if(
571 mStreamContexts.begin(),
572 mStreamContexts.end(),
573 [&](StreamContext& context) { return sc == &context; }
574 );
575
576 // Stream is not found. This should not really happen
577 if (streamIt == mStreamContexts.end())
578 {
579 //VS: Shouldn't this mean import failure?
580 return;
581 }
582 auto stream = mStreams[std::distance(mStreamContexts.begin(), streamIt)];
583
584 const auto nChannels = std::min(sc->CodecContext->GetChannels(), sc->InitialChannels);
585
586 // Write audio into WaveTracks
587 if (sc->SampleFormat == int16Sample)
588 {
589 auto data = sc->CodecContext->DecodeAudioPacketInt16(packet);
590 const auto channelsCount = sc->CodecContext->GetChannels();
591 const auto samplesPerChannel = data.size() / channelsCount;
592
593 unsigned chn = 0;
594 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
595 {
596 if(chn >= nChannels)
597 return;
598
599 channel.AppendBuffer(
600 reinterpret_cast<samplePtr>(data.data() + chn),
601 sc->SampleFormat,
602 samplesPerChannel,
603 sc->CodecContext->GetChannels(),
604 sc->SampleFormat
605 );
606 ++chn;
607 });
608 }
609 else if (sc->SampleFormat == floatSample)
610 {
611 auto data = sc->CodecContext->DecodeAudioPacketFloat(packet);
612 const auto channelsCount = sc->CodecContext->GetChannels();
613 const auto samplesPerChannel = data.size() / channelsCount;
614
615 auto channelIndex = 0;
616 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
617 {
618 if(channelIndex >= nChannels)
619 return;
620
621 channel.AppendBuffer(
622 reinterpret_cast<samplePtr>(data.data() + channelIndex),
623 sc->SampleFormat,
624 samplesPerChannel,
625 sc->CodecContext->GetChannels(),
626 sc->SampleFormat
627 );
628 ++channelIndex;
629 });
630 }
631 const AVStreamWrapper* avStream = mAVFormatContext->GetStream(sc->StreamIndex);
632
633 int64_t filesize = mFFmpeg->avio_size(mAVFormatContext->GetAVIOContext()->GetWrappedValue());
634 // PTS (presentation time) is the proper way of getting current position
635 if (
638 {
639 auto timeBase = avStream->GetTimeBase();
640
642 packet->GetPresentationTimestamp() * timeBase.num / timeBase.den;
643
645 (mAVFormatContext->GetDuration() > 0 ?
646 mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE :
647 1);
648 }
649 // When PTS is not set, use number of frames and number of current frame
650 else if (
651 avStream->GetFramesCount() > 0 && sc->CodecContext->GetFrameNumber() > 0 &&
652 sc->CodecContext->GetFrameNumber() <= avStream->GetFramesCount())
653 {
654 mProgressPos = sc->CodecContext->GetFrameNumber();
655 mProgressLen = avStream->GetFramesCount();
656 }
657 // When number of frames is unknown, use position in file
658 else if (
659 filesize > 0 && packet->GetPos() > 0 && packet->GetPos() <= filesize)
660 {
661 mProgressPos = packet->GetPos();
662 mProgressLen = filesize;
663 }
664}
665
667{
668 Tags temp;
669
670 GetMetadata(temp, TAG_TITLE, "title");
671 GetMetadata(temp, TAG_COMMENTS, "comment");
672 GetMetadata(temp, TAG_ALBUM, "album");
673 GetMetadata(temp, TAG_TRACK, "track");
674 GetMetadata(temp, TAG_GENRE, "genre");
675
676 if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("m4a"))
677 {
678 GetMetadata(temp, TAG_ARTIST, "artist");
679 GetMetadata(temp, TAG_YEAR, "date");
680 }
681 else if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("asf")) /* wma */
682 {
683 GetMetadata(temp, TAG_ARTIST, "artist");
684 GetMetadata(temp, TAG_YEAR, "year");
685 }
686 else
687 {
688 GetMetadata(temp, TAG_ARTIST, "author");
689 GetMetadata(temp, TAG_YEAR, "year");
690 }
691
692 if (!temp.IsEmpty())
693 {
694 *tags = temp;
695 }
696}
697
698void FFmpegImportFileHandle::GetMetadata(Tags &tags, const wxChar *tag, const char *name)
699{
700 auto metadata = mAVFormatContext->GetMetadata();
701
702 if (metadata.HasValue(name, DICT_IGNORE_SUFFIX))
703 tags.SetTag(tag, wxString::FromUTF8(std::string(metadata.Get(name, {}, DICT_IGNORE_SUFFIX))));
704}
705
706
708{
709
710}
int AVCodecIDFwd
Definition: AVCodecID.h:407
#define DICT_IGNORE_SUFFIX
wxT("CloseDown"))
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
BoolSetting FFmpegNotFoundDontShow
Definition: FFmpeg.cpp:355
#define AUDACITY_AV_TIME_BASE
Definition: FFmpegTypes.h:80
#define AUDACITY_AV_NOPTS_VALUE
Definition: FFmpegTypes.h:74
XO("Cut/Copy/Paste")
BoolSetting NewImportingSession
Definition: Import.cpp:846
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....
std::vector< std::shared_ptr< TrackList > > TrackHolders
Definition: ImportRaw.h:24
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
wxString FilePath
Definition: Project.h:21
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
char * samplePtr
Definition: SampleFormat.h:57
#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:90
! Does actual import, returned by FFmpegImportPlugin::Open
void WriteData(StreamContext *sc, const AVPacketWrapper *packet)
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)
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
TranslatableStrings mStreamInfo
Array of stream descriptions. After Init() and before Import(), same size as mStreamContexts.
std::vector< TrackListHolder > mStreams
void SetStreamUsage(wxInt32 StreamID, bool Use) override
FilePath GetFilename() const override
void Stop() override
const TranslatableStrings & GetStreamInfo() override
void Cancel() 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
wxInt64 mProgressLen
Duration, total length or whatever is used as second argument for Update()
An ImportPlugin for FFmpeg data.
TranslatableString FailureHint() const override
User visible message suggesting what to do when a file type isn't recognized; default empty string.
wxString GetPluginStringID() override
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
! Probes the file and opens it if appropriate
TranslatableString GetPluginFormatDescription() override
Base class for FlacImportFileHandle, LOFImportFileHandle, MP3ImportFileHandle, OggImportFileHandle an...
Definition: ImportPlugin.h:111
unsigned long long ByteCount
Definition: ImportPlugin.h:114
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
bool SupportsExtension(const FileExtension &extension)
Interface used to report on import state and progress.
virtual void OnImportResult(ImportResult result)=0
Used to report on import result for file handle passed as argument to OnImportFileOpened.
virtual void OnImportProgress(double progress)=0
static TrackListHolder NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:66
static void FinalizeImport(TrackHolders &outTracks, const std::vector< TrackListHolder > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:257
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
ID3 Tags (for MP3)
Definition: Tags.h:73
bool IsEmpty()
Definition: Tags.cpp:287
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:436
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:1251
virtual bool Flush() noexcept=0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
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