1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportFFmpeg.cpp
6
7 Copyright 2008 LRN
8 Based on ImportFLAC.cpp by Sami Liedes and transcode_sample.c by ANYwebcam Pty Ltd
9 Licensed under the GNU General Public License v2 or later
10
11 *//****************************************************************//**
12
13 \class FFmpegImportFileHandle
14 \brief An ImportFileHandle for FFmpeg data
15
16 *//****************************************************************//**
17
18 \class FFmpegImportPlugin
19 \brief An ImportPlugin for FFmpeg data
20
21 *//*******************************************************************/
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)
44 static 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
160 class FFmpegImportFileHandle;
161
162 /// A representative of FFmpeg loader in
163 /// the Audacity import plugin list
164 class FFmpegImportPlugin final : public ImportPlugin
165 {
166 public:
FFmpegImportPlugin()167 FFmpegImportPlugin():
168 ImportPlugin( FileExtensions( exts.begin(), exts.end() ) )
169 {
170 }
171
~FFmpegImportPlugin()172 ~FFmpegImportPlugin() { }
173
GetPluginStringID()174 wxString GetPluginStringID() override { return wxT("libav"); }
175 TranslatableString GetPluginFormatDescription() override;
176
177 ///! Probes the file and opens it if appropriate
178 std::unique_ptr<ImportFileHandle> Open(
179 const FilePath &Filename, AudacityProject*) override;
180 };
181
182 struct StreamContext final
183 {
184 int StreamIndex { -1 };
185
186 std::unique_ptr<AVCodecContextWrapper> CodecContext;
187
188 int InitialChannels { 0 };
189 sampleFormat SampleFormat { floatSample };
190
191 bool Use { true };
192 };
193
194 ///! Does actual import, returned by FFmpegImportPlugin::Open
195 class FFmpegImportFileHandle final : public ImportFileHandle
196 {
197
198 public:
199 FFmpegImportFileHandle(const FilePath & name);
200 ~FFmpegImportFileHandle();
201
202 ///! Format initialization
203 ///\return true if successful, false otherwise
204 bool Init();
205 ///! Codec initialization
206 ///\return true if successful, false otherwise
207 bool InitCodecs();
208
209
210 TranslatableString GetFileDescription() override;
211 ByteCount GetFileUncompressedBytes() override;
212
213 ///! Imports audio
214 ///\return import status (see Import.cpp)
215 ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks,
216 Tags *tags) override;
217
218 ///! Writes decoded data into WaveTracks.
219 ///\param sc - stream context
220 ProgressResult WriteData(StreamContext* sc, const AVPacketWrapper* packet);
221
222 ///! Writes extracted metadata to tags object
223 ///\param avf - file context
224 ///\ tags - Audacity tags object
225 void WriteMetadata(Tags *tags);
226
227 ///! Retrieves metadata from FFmpeg and converts to wxString
228 ///\param avf - file context
229 ///\ tags - Audacity tags object
230 ///\ tag - name of tag to set
231 ///\ name - name of metadata item to retrieve
232 void GetMetadata(Tags &tags, const wxChar *tag, const char *name);
233
234 ///! Called by Import.cpp
235 ///\return number of readable streams in the file
GetStreamCount()236 wxInt32 GetStreamCount() override
237 {
238 return static_cast<wxInt32>(mStreamContexts.size());
239 }
240
241 ///! Called by Import.cpp
242 ///\return array of strings - descriptions of the streams
GetStreamInfo()243 const TranslatableStrings &GetStreamInfo() override
244 {
245 return mStreamInfo;
246 }
247
248 ///! Called by Import.cpp
249 ///\param StreamID - index of the stream in mStreamInfo and mStreamContexts
250 ///\param Use - true if this stream should be imported, false otherwise
SetStreamUsage(wxInt32 StreamID,bool Use)251 void SetStreamUsage(wxInt32 StreamID, bool Use) override
252 {
253 if (StreamID < static_cast<wxInt32>(mStreamContexts.size()))
254 mStreamContexts[StreamID].Use = Use;
255 }
256
257 private:
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
266 TranslatableStrings mStreamInfo; //!< Array of stream descriptions. After Init() and before Import(), same size as mStreamContexts
267
268 wxInt64 mProgressPos = 0; //!< Current timestamp, file position or whatever is used as first argument for Update()
269 wxInt64 mProgressLen = 1; //!< Duration, total length or whatever is used as second argument for Update()
270
271 bool mCancelled = false; //!< True if importing was canceled by user
272 bool mStopped = false; //!< True if importing was stopped by user
273 const FilePath mName;
274 TrackHolders mChannels; //!< 2-dimensional array of WaveTracks.
275 //!< First dimension - streams,
276 //!< After Import(), same size as mStreamContexts;
277 //!< second - channels of a stream.
278 };
279
280
GetPluginFormatDescription()281 TranslatableString FFmpegImportPlugin::GetPluginFormatDescription()
282 {
283 return DESC;
284 }
285
Open(const FilePath & filename,AudacityProject *)286 std::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 {
306 NewImportingSession.Write(false);
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
332 static Importer::RegisteredImportPlugin registered{ "FFmpeg",
333 std::make_unique< FFmpegImportPlugin >()
334 };
335
336
FFmpegImportFileHandle(const FilePath & name)337 FFmpegImportFileHandle::FFmpegImportFileHandle(const FilePath & name)
338 : ImportFileHandle(name)
339 , mName{ name }
340 {
341 }
342
Init()343 bool FFmpegImportFileHandle::Init()
344 {
345 if (!mFFmpeg)
346 return false;
347
348 mAVFormatContext = mFFmpeg->CreateAVFormatContext();
349
350 const auto err = mAVFormatContext->OpenInputContext(mName, nullptr, AVDictionaryWrapper(*mFFmpeg));
351
352 if (err != AVIOContextWrapper::OpenResult::Success)
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
InitCodecs()364 bool FFmpegImportFileHandle::InitCodecs()
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
GetFileDescription()441 TranslatableString FFmpegImportFileHandle::GetFileDescription()
442 {
443 return DESC;
444 }
445
446
GetFileUncompressedBytes()447 auto FFmpegImportFileHandle::GetFileUncompressedBytes() -> ByteCount
448 {
449 // TODO: Get Uncompressed byte count.
450 return 0;
451 }
452
Import(WaveTrackFactory * trackFactory,TrackHolders & outTracks,Tags * tags)453 ProgressResult FFmpegImportFileHandle::Import(WaveTrackFactory *trackFactory,
454 TrackHolders &outTracks,
455 Tags *tags)
456 {
457 outTracks.clear();
458
459 CreateProgress();
460
461 //! This may break the correspondence with mStreamInfo
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.
483 /// TODO: Nag FFmpeg devs about start_time until they finally say WHAT is this and HOW to handle it.
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
495 if (streamStartTime != int64_t(AUDACITY_AV_NOPTS_VALUE) && streamStartTime > 0)
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
543 for (StreamContext& sc : mStreamContexts)
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
WriteData(StreamContext * sc,const AVPacketWrapper * packet)565 ProgressResult FFmpegImportFileHandle::WriteData(StreamContext *sc, const AVPacketWrapper* packet)
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 {
582 return ProgressResult::Success;
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 (
628 packet->GetPresentationTimestamp() != AUDACITY_AV_NOPTS_VALUE &&
629 mAVFormatContext->GetDuration() != AUDACITY_AV_NOPTS_VALUE)
630 {
631 auto timeBase = avStream->GetTimeBase();
632
633 mProgressPos =
634 packet->GetPresentationTimestamp() * timeBase.num / timeBase.den;
635
636 mProgressLen =
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
WriteMetadata(Tags * tags)661 void FFmpegImportFileHandle::WriteMetadata(Tags *tags)
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
GetMetadata(Tags & tags,const wxChar * tag,const char * name)693 void 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
~FFmpegImportFileHandle()702 FFmpegImportFileHandle::~FFmpegImportFileHandle()
703 {
704
705 }
706
707 #endif //USE_FFMPEG
708