aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder/plugins/FfmpegDecoderPlugin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/plugins/FfmpegDecoderPlugin.cxx')
-rw-r--r--src/decoder/plugins/FfmpegDecoderPlugin.cxx138
1 files changed, 123 insertions, 15 deletions
diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
index e150fa3bf..7e164e5bd 100644
--- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
@@ -25,7 +25,10 @@
#include "lib/ffmpeg/Domain.hxx"
#include "../DecoderAPI.hxx"
#include "FfmpegMetaData.hxx"
+#include "tag/TagBuilder.hxx"
#include "tag/TagHandler.hxx"
+#include "tag/ReplayGain.hxx"
+#include "tag/MixRamp.hxx"
#include "input/InputStream.hxx"
#include "CheckAudioFormat.hxx"
#include "util/Error.hxx"
@@ -320,16 +323,22 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
AVPacket packet2 = *packet;
- uint8_t *output_buffer;
-
DecoderCommand cmd = DecoderCommand::NONE;
while (packet2.size > 0 && cmd == DecoderCommand::NONE) {
- int audio_size = 0;
int got_frame = 0;
int len = avcodec_decode_audio4(codec_context,
frame, &got_frame,
&packet2);
- if (len >= 0 && got_frame) {
+ if (len < 0) {
+ /* if error, we skip the frame */
+ LogDefault(ffmpeg_domain,
+ "decoding failed, frame skipped");
+ break;
+ }
+
+ uint8_t *output_buffer;
+ int audio_size = 0;
+ if (got_frame) {
audio_size = copy_interleave_frame(codec_context,
frame,
&output_buffer,
@@ -338,13 +347,6 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
len = audio_size;
}
- if (len < 0) {
- /* if error, we skip the frame */
- LogDefault(ffmpeg_domain,
- "decoding failed, frame skipped");
- break;
- }
-
packet2.data += len;
packet2.size -= len;
@@ -438,6 +440,109 @@ ffmpeg_probe(Decoder *decoder, InputStream &is)
}
static void
+FfmpegParseMetaData(AVDictionary &dict, ReplayGainInfo &rg, MixRampInfo &mr)
+{
+ AVDictionaryEntry *i = nullptr;
+
+ while ((i = av_dict_get(&dict, "", i,
+ AV_DICT_IGNORE_SUFFIX)) != nullptr) {
+ const char *name = i->key;
+ const char *value = i->value;
+
+ if (!ParseReplayGainTag(rg, name, value))
+ ParseMixRampTag(mr, name, value);
+ }
+}
+
+static void
+FfmpegParseMetaData(const AVStream &stream,
+ ReplayGainInfo &rg, MixRampInfo &mr)
+{
+ FfmpegParseMetaData(*stream.metadata, rg, mr);
+}
+
+static void
+FfmpegParseMetaData(const AVFormatContext &format_context, int audio_stream,
+ ReplayGainInfo &rg, MixRampInfo &mr)
+{
+ FfmpegParseMetaData(*format_context.metadata, rg, mr);
+
+ if (audio_stream >= 0)
+ FfmpegParseMetaData(*format_context.streams[audio_stream],
+ rg, mr);
+}
+
+static void
+FfmpegParseMetaData(Decoder &decoder,
+ const AVFormatContext &format_context, int audio_stream)
+{
+ ReplayGainInfo rg;
+ rg.Clear();
+
+ MixRampInfo mr;
+ mr.Clear();
+
+ FfmpegParseMetaData(format_context, audio_stream, rg, mr);
+
+ if (rg.IsDefined())
+ decoder_replay_gain(decoder, &rg);
+
+ if (mr.IsDefined())
+ decoder_mixramp(decoder, std::move(mr));
+}
+
+static void
+FfmpegScanMetadata(const AVStream &stream,
+ const tag_handler &handler, void *handler_ctx)
+{
+ FfmpegScanDictionary(stream.metadata, &handler, handler_ctx);
+}
+
+static void
+FfmpegScanMetadata(const AVFormatContext &format_context, int audio_stream,
+ const tag_handler &handler, void *handler_ctx)
+{
+ FfmpegScanDictionary(format_context.metadata, &handler, handler_ctx);
+ if (audio_stream >= 0)
+ FfmpegScanMetadata(*format_context.streams[audio_stream],
+ handler, handler_ctx);
+}
+
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
+
+static void
+FfmpegScanTag(const AVFormatContext &format_context, int audio_stream,
+ TagBuilder &tag)
+{
+ FfmpegScanMetadata(format_context, audio_stream,
+ full_tag_handler, &tag);
+}
+
+/**
+ * Check if a new stream tag was received and pass it to
+ * decoder_tag().
+ */
+static void
+FfmpegCheckTag(Decoder &decoder, InputStream &is,
+ AVFormatContext &format_context, int audio_stream)
+{
+ AVStream &stream = *format_context.streams[audio_stream];
+ if ((stream.event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) == 0)
+ /* no new metadata */
+ return;
+
+ /* clear the flag */
+ stream.event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+
+ TagBuilder tag;
+ FfmpegScanTag(format_context, audio_stream, tag);
+ if (!tag.IsEmpty())
+ decoder_tag(decoder, is, tag.Commit());
+}
+
+#endif
+
+static void
ffmpeg_decode(Decoder &decoder, InputStream &input)
{
AVInputFormat *input_format = ffmpeg_probe(&decoder, input);
@@ -541,6 +646,8 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
decoder_initialized(decoder, audio_format,
input.IsSeekable(), total_time);
+ FfmpegParseMetaData(decoder, *format_context, audio_stream);
+
#if LIBAVUTIL_VERSION_MAJOR >= 53
AVFrame *frame = av_frame_alloc();
#else
@@ -562,6 +669,10 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
/* end of file */
break;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
+ FfmpegCheckTag(decoder, input, *format_context, audio_stream);
+#endif
+
if (packet.stream_index == audio_stream)
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
@@ -634,11 +745,8 @@ ffmpeg_scan_stream(InputStream &is,
tag_handler_invoke_duration(handler, handler_ctx, duration);
}
- ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
int idx = ffmpeg_find_audio_stream(f);
- if (idx >= 0)
- ffmpeg_scan_dictionary(f->streams[idx]->metadata,
- handler, handler_ctx);
+ FfmpegScanMetadata(*f, idx, *handler, handler_ctx);
avformat_close_input(&f);
return true;