From 5e0e16894d67e220b4684809b154b85db4229381 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Tue, 23 Dec 2014 15:37:59 +0100 Subject: [PATCH] ffmpeg plugin: improve encoding format negociation The ffmpeg encoders are restricted on which raw formats they accept (each with different constraints). When asked to encode something, the ffmpeg encoder now checks the codec capabilities and if the suggested input format does not match, modifies it to something the codec can actually handle. Enable "experimental" codecs, as this is needed for AAC support in ffmpeg 0.10. This allows format negociation to work properly, and as a result we can enable several new audio formats: AAC (for MPEG family only), AC3, and raw audio. Declare MKV as B_ANY_FORMAT_FAMILY since it is designed to be a generic container (so it is possible to put AAC in MKV). Also remove duplicate entries for AC3. --- .../media/plugins/ffmpeg/AVCodecEncoder.cpp | 6 ++- .../media/plugins/ffmpeg/AVFormatWriter.cpp | 47 +++++++++++++++++++ .../media/plugins/ffmpeg/EncoderTable.cpp | 44 ++++++----------- .../media/plugins/ffmpeg/MuxerTable.cpp | 2 +- 4 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp index 79db210898..910b30b7fc 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp @@ -462,12 +462,12 @@ AVCodecEncoder::_Setup() if (fContext->codec_id == CODEC_ID_MPEG2VIDEO) { // Just for testing, we also add B frames */ fContext->max_b_frames = 2; - } else if (fContext->codec_id == CODEC_ID_MPEG1VIDEO){ + } else if (fContext->codec_id == CODEC_ID_MPEG1VIDEO) { // Needed to avoid using macroblocks in which some coeffs overflow. // This does not happen with normal video, it just happens here as // the motion of the chroma plane does not match the luma plane. fContext->mb_decision = 2; - } + } // Unfortunately, we may fail later, when we try to open the codec // for real... but we need to delay this because we still allow @@ -491,6 +491,8 @@ AVCodecEncoder::_OpenCodecIfNeeded() if (fCodecInitStatus == CODEC_INIT_FAILED) return false; + fContext->strict_std_compliance = -2; + // Open the codec int result = avcodec_open(fContext, fCodec); if (result >= 0) diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp index 11e1a96c04..1095a7d7d6 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp @@ -171,6 +171,9 @@ AVFormatWriter::StreamCookie::Init(media_format* format, // channels fStream->codec->channels = format->u.raw_audio.channel_count; + + // set fStream to the audio format we want to use. This is only a hint + // (each encoder has a different set of accepted formats) switch (format->u.raw_audio.format) { case media_raw_audio_format::B_AUDIO_FLOAT: fStream->codec->sample_fmt = AV_SAMPLE_FMT_FLT; @@ -193,6 +196,49 @@ AVFormatWriter::StreamCookie::Init(media_format* format, return B_MEDIA_BAD_FORMAT; break; } + + // Now negociate the actual format with the encoder + // First check if the requested format is acceptable + AVCodec* codec = avcodec_find_encoder(fStream->codec->codec_id); + const enum AVSampleFormat *p = codec->sample_fmts; + for (; *p != -1; p++) { + if (*p == fStream->codec->sample_fmt) + break; + } + // If not, force one of the acceptable ones + if (*p == -1) { + fStream->codec->sample_fmt = codec->sample_fmts[0]; + + // And finally set the format struct to the accepted format. It is + // then up to the caller to make sure we get data matching that + // format. + switch (fStream->codec->sample_fmt) { + case AV_SAMPLE_FMT_FLT: + format->u.raw_audio.format + = media_raw_audio_format::B_AUDIO_FLOAT; + break; + case AV_SAMPLE_FMT_DBL: + format->u.raw_audio.format + = media_raw_audio_format::B_AUDIO_DOUBLE; + break; + case AV_SAMPLE_FMT_S32: + format->u.raw_audio.format + = media_raw_audio_format::B_AUDIO_INT; + break; + case AV_SAMPLE_FMT_S16: + format->u.raw_audio.format + = media_raw_audio_format::B_AUDIO_SHORT; + break; + case AV_SAMPLE_FMT_U8: + format->u.raw_audio.format + = media_raw_audio_format::B_AUDIO_UCHAR; + break; + default: + return B_MEDIA_BAD_FORMAT; + break; + } + } + if (format->u.raw_audio.channel_mask == 0) { // guess the channel mask... switch (format->u.raw_audio.channel_count) { @@ -418,6 +464,7 @@ AVFormatWriter::CommitHeader() // we have no idea (in the future) what CodecID some encoder uses, // it may be an encoder from a different plugin. AVCodecContext* codecContext = stream->codec; + codecContext->strict_std_compliance = -2; AVCodec* codec = avcodec_find_encoder(codecContext->codec_id); if (codec == NULL || avcodec_open(codecContext, codec) < 0) { TRACE(" stream[%u] - failed to open AVCodecContext\n", i); diff --git a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp index 69525eb85b..1b3445a7ef 100644 --- a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp +++ b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp @@ -102,6 +102,19 @@ const EncoderDescription gEncoderTable[] = { B_MEDIA_ENCODED_AUDIO, 10 }, + { + { + "Advanced Audio Coding (AAC)", + "aac", + 0, + CODEC_ID_AAC, + { 0 } + }, + B_MPEG_FORMAT_FAMILY, + B_MEDIA_RAW_AUDIO, + B_MEDIA_ENCODED_AUDIO, + 10 + }, { { "Raw audio", @@ -115,24 +128,9 @@ const EncoderDescription gEncoderTable[] = { B_MEDIA_ENCODED_AUDIO, 1 }, - // All of those crash ffmpeg in one way or another... -#if 0 { { - "Advanced Audio Coding (AAC)", - "aac", - 0, - CODEC_ID_AAC, - { 0 } - }, - B_ANY_FORMAT_FAMILY, - B_MEDIA_RAW_AUDIO, - B_MEDIA_ENCODED_AUDIO, - 10 - }, - { - { - "Dolby AC-3", + "Dolby Digital (AC-3)", "ac3", 0, CODEC_ID_AC3, @@ -143,6 +141,7 @@ const EncoderDescription gEncoderTable[] = { B_MEDIA_ENCODED_AUDIO, 10 }, +#if 0 { { "Apple Lossless Audio Codec", @@ -221,19 +220,6 @@ const EncoderDescription gEncoderTable[] = { B_MEDIA_ENCODED_AUDIO, 10 }, - { - { - "Dolby Digital (AC-3)", - "ac3", - 0, - CODEC_ID_AC3, - { 0 } - }, - B_ANY_FORMAT_FAMILY, - B_MEDIA_RAW_AUDIO, - B_MEDIA_ENCODED_AUDIO, - 10 - }, { { "Vorbis audio", diff --git a/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp b/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp index b759229adb..2856694b98 100644 --- a/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp +++ b/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp @@ -121,7 +121,7 @@ const media_file_format gMuxerTable[] = { | media_file_format::B_KNOWS_ENCODED_VIDEO | media_file_format::B_KNOWS_ENCODED_AUDIO, { 0 }, - B_QUICKTIME_FORMAT_FAMILY, + B_ANY_FORMAT_FAMILY, 100, { 0 }, "video/x-matroska",