[FFmpeg-devel] [PATCH 2/2] avcodec/videotoolbox: fix decoding of some h264 bitstreams
Hendrik Leppkes
h.leppkes at gmail.com
Thu Oct 1 18:29:00 CEST 2015
On Thu, Oct 1, 2015 at 6:13 PM, wm4 <nfxjfg at googlemail.com> wrote:
> This affects Annex B streams (such as demuxed from .ts and others). It
> also handles the format change in reinit-large_420_8-to-small_420_8.h264
> correctly.
>
> Instead of passing through the extradata, create it on the fly it from
> the currently active SPS and PPS. Since reconstructing the PPS and SPS
> NALs would be very complicated and verbose, we use the NALs as they
> originally appeared in the bitstream.
>
> The code for writing the extradata is somewhat derived from
> libavformat/avc.c, but it's small and different enough that sharing it
> is not really worth it.
> ---
> Even though it requires changes in the general h264 decoder (previous
> patch), this solution is much cleaner and more robust than my patch
> from yesterday.
> ---
> libavcodec/videotoolbox.c | 48 +++++++++++++++++++++++++++++------------------
> 1 file changed, 30 insertions(+), 18 deletions(-)
>
> diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c
> index 9dec5fc..cc1e592 100644
> --- a/libavcodec/videotoolbox.c
> +++ b/libavcodec/videotoolbox.c
> @@ -77,28 +77,40 @@ int ff_videotoolbox_alloc_frame(AVCodecContext *avctx, AVFrame *frame)
> return 0;
> }
>
> +#define AV_W8(p, v) *(p) = (v)
> +
> CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx)
> {
> + H264Context *h = avctx->priv_data;
> CFDataRef data = NULL;
> + uint8_t *p;
> + int vt_extradata_size = 6 + 3 + h->sps.data_size + 4 + h->pps.data_size;
> + uint8_t *vt_extradata = av_malloc(vt_extradata_size);
> + if (!vt_extradata)
> + return NULL;
>
> - /* Each VCL NAL in the bitstream sent to the decoder
> - * is preceded by a 4 bytes length header.
> - * Change the avcC atom header if needed, to signal headers of 4 bytes. */
> - if (avctx->extradata_size >= 4 && (avctx->extradata[4] & 0x03) != 0x03) {
> - uint8_t *rw_extradata = av_memdup(avctx->extradata, avctx->extradata_size);
> -
> - if (!rw_extradata)
> - return NULL;
> -
> - rw_extradata[4] |= 0x03;
> -
> - data = CFDataCreate(kCFAllocatorDefault, rw_extradata, avctx->extradata_size);
> -
> - av_freep(&rw_extradata);
> - } else {
> - data = CFDataCreate(kCFAllocatorDefault, avctx->extradata, avctx->extradata_size);
> - }
> -
> + p = vt_extradata;
> +
> + AV_W8(p + 0, 1); /* version */
> + AV_W8(p + 1, h->sps.data[0]); /* profile */
> + AV_W8(p + 2, h->sps.data[1]); /* profile compat */
> + AV_W8(p + 3, h->sps.data[2]); /* level */
> + AV_W8(p + 4, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 3 (11) */
> + AV_W8(p + 5, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */
> + AV_WB16(p + 6, h->sps.data_size + 1);
> + AV_W8(p + 8, NAL_SPS | (3 << 5)); // NAL unit header
> + memcpy(p + 9, h->sps.data, h->sps.data_size);
> + p += 9 + h->sps.data_size;
> + AV_W8(p + 0, 1); /* number of pps */
> + AV_WB16(p + 1, h->pps.data_size + 1);
> + AV_W8(p + 3, NAL_PPS | (3 << 5)); // NAL unit header
> + memcpy(p + 4, h->pps.data, h->pps.data_size);
> +
> + p += 4 + h->pps.data_size;
> + av_assert0(p - vt_extradata == vt_extradata_size);
> +
> + data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size);
> + av_free(vt_extradata);
> return data;
> }
>
This will still fail spectacularly with a SPS/PPS change mid-stream. I
don't suppose it somehow accepts the SPS/PPS data in-band as well?
- Hendrik
More information about the ffmpeg-devel
mailing list