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.
This commit is contained in:
Adrien Destugues 2014-12-23 15:37:59 +01:00
parent ca747629f9
commit 5e0e16894d
4 changed files with 67 additions and 32 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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",

View File

@ -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",