Fix FFmpeg 4.4 encoder delay issues and cleanup

This commit is contained in:
bnnm 2021-07-23 15:52:05 +02:00
parent 54f06ba69a
commit 4927761e52
4 changed files with 53 additions and 49 deletions

View File

@ -829,9 +829,7 @@ void xma_fix_raw_samples_ch(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_o
* Somehow also needs to skip 64 extra samples (looks like another FFmpeg bug
* where XMA outputs half a subframe samples late, WMAPRO isn't affected),
* which sometimes makes FFmpeg complain (=reads after end) but doesn't seem audible. */
if (data->skipSamples == 0) {
ffmpeg_set_skip_samples(data, start_skip+64);
}
ffmpeg_set_skip_samples(data, start_skip+64);
}
#endif
}

View File

@ -310,6 +310,8 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
AVStream *stream = data->formatCtx->streams[data->streamIndex];
AVRational tb = {0};
tb.num = 1; tb.den = data->codecCtx->sample_rate;
/* derive info */
data->sampleRate = data->codecCtx->sample_rate;
data->channels = data->codecCtx->channels;
@ -322,40 +324,38 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
#endif
/* try to guess frames/samples (duration isn't always set) */
tb.num = 1; tb.den = data->codecCtx->sample_rate;
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->totalSamples < 0)
data->totalSamples = 0; /* caller must consider this */
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
if (stream->start_skip_samples) /* samples to skip in the first packet */
data->skipSamples = stream->start_skip_samples;
/* read start samples to be skipped (encoder delay), info only.
* Not too reliable though, see ffmpeg_set_skip_samples */
if (stream->start_time > 0 && stream->start_time != AV_NOPTS_VALUE)
data->skip_samples = av_rescale_q(stream->start_time, stream->time_base, tb);
#if 0 //LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
/* exposed before but not too reliable either */
else if (stream->start_skip_samples) /* samples to skip in the first packet */
data->skip_samples = stream->start_skip_samples;
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
data->skipSamples = stream->skip_samples;
#else
if (stream->start_time)
data->skipSamples = av_rescale_q(stream->start_time, stream->time_base, tb);
data->skip_samples = stream->skip_samples;
#endif
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
//VGM_ASSERT(data->codecCtx->internal->skip_samples > 0, ...); /* for codec use, not accessible */
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
#else
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
#endif
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
/* also negative timestamp for formats like OGG/OPUS */
/* not using it: BINK, FLAC, ATRAC3, XMA, MPC, WMA (may use internal skip samples) */
//todo: double check Opus behavior
}
@ -800,18 +800,10 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
data->end_of_stream = 0;
data->end_of_audio = 0;
/* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */
/* consider skip samples (encoder delay), if manually set */
if (data->skip_samples_set) {
AVStream *stream = data->formatCtx->streams[data->streamIndex];
/* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
stream->skip_samples = 0;
stream->start_skip_samples = 0;
#else
stream->start_time = 0;
#endif
data->samples_discard += data->skipSamples;
data->samples_discard += data->skip_samples;
/* internally FFmpeg may skip (skip_samples/start_skip_samples) too */
}
return;
@ -878,34 +870,47 @@ void free_ffmpeg(ffmpeg_codec_data* data) {
/**
* Sets the number of samples to skip at the beginning of the stream, needed by some "gapless" formats.
* (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc to "set up" the decoder).
* Sets the number of samples to skip at the beginning of the stream (encoder delay), needed by some "gapless" formats.
* - should be used at the beginning of the stream
* - should check if there are data->skipSamples before using this, to avoid overwritting FFmpeg's value (ex. AAC).
* - should use only if/when FFmpeg's format is known to botch encoder delay.
*
* This could be added per format in FFmpeg directly, but it's here for flexibility and due to bugs
* (FFmpeg's stream->(start_)skip_samples causes glitches in XMA).
* encoder delay in FFmpeg is handled in multiple ways:
* - avstream/internal->start_skip_samples: skip in the first packet *if* pts=0 (set in MP3 only?)
* - avstream/internal->skip_samples: skip in any packet (set in AAC encoded by libfaac, OPUS, MP3 in SWF, MOV/MP4)
* - avstream->start_time: usually set same as skip_samples but in pts, info only (most of the above but OPUS)
* - codecCtx->delay: seems equivalent to skip_samples, info only (OPUS)
* - negative timestamp: Xiph style (Ogg Vorbis/Opus only?).
* First two are only exposed in FFmpeg v4.4<, meaning you can't override buggy values after that.
* But since FFmpeg only does encoder delay for a handful of formats, shouldn't matter much.
* May need to detect exact versions if they start fixing formats.
*/
void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
AVStream *stream = NULL;
if (!data || !data->formatCtx)
if (!data || !data->formatCtx || !skip_samples)
return;
/* overwrite FFmpeg's skip samples */
stream = data->formatCtx->streams[data->streamIndex];
/* let FFmpeg handle (may need an option to force override?) */
if (data->skip_samples) {
VGM_ASSERT(data->skip_samples != skip_samples,
"FMPEG: ignored skip_samples %i, already set %i\n", skip_samples, (int)data->skip_samples);
return;
}
#if 0
{
AVStream* stream = data->formatCtx->streams[data->streamIndex];
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
stream->start_skip_samples = 0; /* used for the first packet *if* pts=0 */
stream->skip_samples = 0; /* skip_samples can be used for any packet */
stream->start_skip_samples = 0;
stream->skip_samples = 0;
#else
stream->start_time = 0;
//stream->start_time = 0; /* info only = useless */
#endif
}
#endif
/* set skip samples with our internal discard */
data->skip_samples_set = 1;
data->samples_discard = skip_samples;
/* expose (info only) */
data->skipSamples = skip_samples;
data->skip_samples = skip_samples;
}
/* returns channel layout if set */

View File

@ -207,7 +207,7 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
vgmstream->meta_type = meta_MP4;
vgmstream->sample_rate = ffmpeg_data->sampleRate;
vgmstream->num_samples = mp4.num_samples;
if (vgmstream->num_samples == 0)
if (vgmstream->num_samples == 0) /* does this take into account encoder delay? see FFV */
vgmstream->num_samples = ffmpeg_data->totalSamples;
vgmstream->loop_start_sample = mp4.loop_start;
vgmstream->loop_end_sample = mp4.loop_end;
@ -218,8 +218,9 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
vgmstream->num_streams = ffmpeg_data->streamCount; /* may contain N tracks */
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
if (mp4.encoder_delay)
ffmpeg_set_skip_samples(vgmstream->codec_data, mp4.encoder_delay);
/* needed for CRI MP4, otherwise FFmpeg usually reads standard delay */
ffmpeg_set_skip_samples(vgmstream->codec_data, mp4.encoder_delay);
return vgmstream;

View File

@ -1063,7 +1063,7 @@ typedef struct {
int bitrate;
// extra info: 0 if unknown or not fixed
int64_t totalSamples; // estimated count (may not be accurate for some demuxers)
int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
int streamCount; // number of FFmpeg audio streams
/*** internal state ***/