X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fh264_mp4toannexb_bsf.c;h=2822644b105784c385c6bf7464b642595e9c1e99;hb=d92f38c179591a608390ffa9fee59c309142e79d;hp=fb3f24ea4063bc6d552a83867dca79a40a912b2a;hpb=4f9a8d3fe2f9485ee08848d336ee96f15ec0e7e6;p=ffmpeg diff --git a/libavcodec/h264_mp4toannexb_bsf.c b/libavcodec/h264_mp4toannexb_bsf.c index fb3f24ea406..2822644b105 100644 --- a/libavcodec/h264_mp4toannexb_bsf.c +++ b/libavcodec/h264_mp4toannexb_bsf.c @@ -21,16 +21,21 @@ #include +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" #include "h264.h" typedef struct H264BSFContext { - int32_t sps_offset; - int32_t pps_offset; + uint8_t *sps; + uint8_t *pps; + int sps_size; + int pps_size; uint8_t length_size; uint8_t new_idr; uint8_t idr_sps_seen; @@ -38,98 +43,91 @@ typedef struct H264BSFContext { int extradata_parsed; } H264BSFContext; -static int alloc_and_copy(AVPacket *out, - const uint8_t *sps_pps, uint32_t sps_pps_size, - const uint8_t *in, uint32_t in_size, int ps) +static void count_or_copy(uint8_t **out, uint64_t *out_size, + const uint8_t *in, int in_size, int ps, int copy) { - uint32_t offset = out->size; - uint8_t start_code_size = offset == 0 || ps ? 4 : 3; - int err; - - err = av_grow_packet(out, sps_pps_size + in_size + start_code_size); - if (err < 0) - return err; - - if (sps_pps) - memcpy(out->data + offset, sps_pps, sps_pps_size); - memcpy(out->data + sps_pps_size + start_code_size + offset, in, in_size); - if (start_code_size == 4) { - AV_WB32(out->data + offset + sps_pps_size, 1); - } else { - (out->data + offset + sps_pps_size)[0] = - (out->data + offset + sps_pps_size)[1] = 0; - (out->data + offset + sps_pps_size)[2] = 1; + uint8_t start_code_size = ps < 0 ? 0 : *out_size == 0 || ps ? 4 : 3; + + if (copy) { + memcpy(*out + start_code_size, in, in_size); + if (start_code_size == 4) { + AV_WB32(*out, 1); + } else if (start_code_size) { + (*out)[0] = + (*out)[1] = 0; + (*out)[2] = 1; + } + *out += start_code_size + in_size; } - - return 0; + *out_size += start_code_size + in_size; } static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) { H264BSFContext *s = ctx->priv_data; + GetByteContext ogb, *gb = &ogb; uint16_t unit_size; - uint64_t total_size = 0; - uint8_t *out = NULL, unit_nb, sps_done = 0, - sps_seen = 0, pps_seen = 0; - const uint8_t *extradata = ctx->par_in->extradata + 4; + uint32_t total_size = 0; + uint8_t *out = NULL, unit_nb, sps_done = 0; static const uint8_t nalu_header[4] = { 0, 0, 0, 1 }; - int length_size = (*extradata++ & 0x3) + 1; // retrieve length coded size + int length_size, pps_offset = 0; - s->sps_offset = s->pps_offset = -1; + bytestream2_init(gb, ctx->par_in->extradata, ctx->par_in->extradata_size); + + bytestream2_skipu(gb, 4); + + /* retrieve length coded size */ + length_size = (bytestream2_get_byteu(gb) & 0x3) + 1; /* retrieve sps and pps unit(s) */ - unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */ + unit_nb = bytestream2_get_byteu(gb) & 0x1f; /* number of sps unit(s) */ if (!unit_nb) { goto pps; - } else { - s->sps_offset = 0; - sps_seen = 1; } while (unit_nb--) { int err; - unit_size = AV_RB16(extradata); + /* possible overread ok due to padding */ + unit_size = bytestream2_get_be16u(gb); total_size += unit_size + 4; - if (total_size > INT_MAX - padding) { - av_log(ctx, AV_LOG_ERROR, - "Too big extradata size, corrupted stream or invalid MP4/AVCC bitstream\n"); - av_free(out); - return AVERROR(EINVAL); - } - if (extradata + 2 + unit_size > ctx->par_in->extradata + ctx->par_in->extradata_size) { - av_log(ctx, AV_LOG_ERROR, "Packet header is not contained in global extradata, " + av_assert1(total_size <= INT_MAX - padding); + if (bytestream2_get_bytes_left(gb) < unit_size + !sps_done) { + av_log(ctx, AV_LOG_ERROR, "Global extradata truncated, " "corrupted stream or invalid MP4/AVCC bitstream\n"); av_free(out); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } if ((err = av_reallocp(&out, total_size + padding)) < 0) return err; memcpy(out + total_size - unit_size - 4, nalu_header, 4); - memcpy(out + total_size - unit_size, extradata + 2, unit_size); - extradata += 2 + unit_size; + bytestream2_get_bufferu(gb, out + total_size - unit_size, unit_size); pps: if (!unit_nb && !sps_done++) { - unit_nb = *extradata++; /* number of pps unit(s) */ - if (unit_nb) { - s->pps_offset = total_size; - pps_seen = 1; - } + unit_nb = bytestream2_get_byteu(gb); /* number of pps unit(s) */ + pps_offset = total_size; } } if (out) memset(out + total_size, 0, padding); - if (!sps_seen) + if (pps_offset) { + s->sps = out; + s->sps_size = pps_offset; + } else { av_log(ctx, AV_LOG_WARNING, "Warning: SPS NALU missing or invalid. " "The resulting stream may not play.\n"); - - if (!pps_seen) + } + if (pps_offset < total_size) { + s->pps = out + pps_offset; + s->pps_size = total_size - pps_offset; + } else { av_log(ctx, AV_LOG_WARNING, "Warning: PPS NALU missing or invalid. " "The resulting stream may not play.\n"); + } av_freep(&ctx->par_out->extradata); ctx->par_out->extradata = out; @@ -150,7 +148,7 @@ static int h264_mp4toannexb_init(AVBSFContext *ctx) (extra_size >= 4 && AV_RB32(ctx->par_in->extradata) == 1)) { av_log(ctx, AV_LOG_VERBOSE, "The input looks like it is Annex B already\n"); - } else if (extra_size >= 6) { + } else if (extra_size >= 7) { ret = h264_extradata_to_annexb(ctx, AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; @@ -168,18 +166,16 @@ static int h264_mp4toannexb_init(AVBSFContext *ctx) return 0; } -static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) +static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) { H264BSFContext *s = ctx->priv_data; - AVPacket *in; - uint8_t unit_type; - int32_t nal_size; - uint32_t cumul_size = 0; + uint8_t unit_type, new_idr, sps_seen, pps_seen; const uint8_t *buf; const uint8_t *buf_end; - int buf_size; - int ret = 0, i; + uint8_t *out; + uint64_t out_size; + int ret; ret = ff_bsf_get_packet(ctx, &in); if (ret < 0) @@ -187,94 +183,117 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) /* nothing to filter */ if (!s->extradata_parsed) { - av_packet_move_ref(out, in); + av_packet_move_ref(opkt, in); av_packet_free(&in); return 0; } - buf = in->data; - buf_size = in->size; buf_end = in->data + in->size; - do { - ret= AVERROR(EINVAL); - if (buf + s->length_size > buf_end) - goto fail; - - for (nal_size = 0, i = 0; ilength_size; i++) - nal_size = (nal_size << 8) | buf[i]; - - buf += s->length_size; - unit_type = *buf & 0x1f; - - if (nal_size > buf_end - buf || nal_size < 0) - goto fail; - - if (unit_type == H264_NAL_SPS) - s->idr_sps_seen = s->new_idr = 1; - else if (unit_type == H264_NAL_PPS) { - s->idr_pps_seen = s->new_idr = 1; - /* if SPS has not been seen yet, prepend the AVCC one to PPS */ - if (!s->idr_sps_seen) { - if (s->sps_offset == -1) - av_log(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n"); - else { - if ((ret = alloc_and_copy(out, - ctx->par_out->extradata + s->sps_offset, - s->pps_offset != -1 ? s->pps_offset : ctx->par_out->extradata_size - s->sps_offset, - buf, nal_size, 1)) < 0) - goto fail; - s->idr_sps_seen = 1; - goto next_nal; +#define LOG_ONCE(...) \ + if (j) \ + av_log(__VA_ARGS__) + for (int j = 0; j < 2; j++) { + buf = in->data; + new_idr = s->new_idr; + sps_seen = s->idr_sps_seen; + pps_seen = s->idr_pps_seen; + out_size = 0; + + do { + uint32_t nal_size = 0; + + /* possible overread ok due to padding */ + for (int i = 0; i < s->length_size; i++) + nal_size = (nal_size << 8) | buf[i]; + + buf += s->length_size; + + /* This check requires the cast as the right side might + * otherwise be promoted to an unsigned value. */ + if ((int64_t)nal_size > buf_end - buf) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (!nal_size) + continue; + + unit_type = *buf & 0x1f; + + if (unit_type == H264_NAL_SPS) { + sps_seen = new_idr = 1; + } else if (unit_type == H264_NAL_PPS) { + pps_seen = new_idr = 1; + /* if SPS has not been seen yet, prepend the AVCC one to PPS */ + if (!sps_seen) { + if (!s->sps_size) { + LOG_ONCE(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n"); + } else { + count_or_copy(&out, &out_size, s->sps, s->sps_size, -1, j); + sps_seen = 1; + } } } - } - /* if this is a new IDR picture following an IDR picture, reset the idr flag. - * Just check first_mb_in_slice to be 0 as this is the simplest solution. - * This could be checking idr_pic_id instead, but would complexify the parsing. */ - if (!s->new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80)) - s->new_idr = 1; - - /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ - if (s->new_idr && unit_type == H264_NAL_IDR_SLICE && !s->idr_sps_seen && !s->idr_pps_seen) { - if ((ret=alloc_and_copy(out, - ctx->par_out->extradata, ctx->par_out->extradata_size, - buf, nal_size, 1)) < 0) - goto fail; - s->new_idr = 0; - /* if only SPS has been seen, also insert PPS */ - } else if (s->new_idr && unit_type == H264_NAL_IDR_SLICE && s->idr_sps_seen && !s->idr_pps_seen) { - if (s->pps_offset == -1) { - av_log(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); - if ((ret = alloc_and_copy(out, NULL, 0, buf, nal_size, 0)) < 0) - goto fail; - } else if ((ret = alloc_and_copy(out, - ctx->par_out->extradata + s->pps_offset, ctx->par_out->extradata_size - s->pps_offset, - buf, nal_size, 1)) < 0) - goto fail; - } else { - if ((ret=alloc_and_copy(out, NULL, 0, buf, nal_size, unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS)) < 0) + /* If this is a new IDR picture following an IDR picture, reset the idr flag. + * Just check first_mb_in_slice to be 0 as this is the simplest solution. + * This could be checking idr_pic_id instead, but would complexify the parsing. */ + if (!new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80)) + new_idr = 1; + + /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ + if (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) { + if (ctx->par_out->extradata) + count_or_copy(&out, &out_size, ctx->par_out->extradata, + ctx->par_out->extradata_size, -1, j); + new_idr = 0; + /* if only SPS has been seen, also insert PPS */ + } else if (new_idr && unit_type == H264_NAL_IDR_SLICE && sps_seen && !pps_seen) { + if (!s->pps_size) { + LOG_ONCE(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); + } else { + count_or_copy(&out, &out_size, s->pps, s->pps_size, -1, j); + } + } + + count_or_copy(&out, &out_size, buf, nal_size, + unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS, j); + if (!new_idr && unit_type == H264_NAL_SLICE) { + new_idr = 1; + sps_seen = 0; + pps_seen = 0; + } + + buf += nal_size; + } while (buf < buf_end); + + if (!j) { + if (out_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + ret = AVERROR_INVALIDDATA; goto fail; - if (!s->new_idr && unit_type == H264_NAL_SLICE) { - s->new_idr = 1; - s->idr_sps_seen = 0; - s->idr_pps_seen = 0; } + ret = av_new_packet(opkt, out_size); + if (ret < 0) + goto fail; + out = opkt->data; } + } +#undef LOG_ONCE + + av_assert1(out_size == opkt->size); -next_nal: - buf += nal_size; - cumul_size += nal_size + s->length_size; - } while (cumul_size < buf_size); + s->new_idr = new_idr; + s->idr_sps_seen = sps_seen; + s->idr_pps_seen = pps_seen; - ret = av_packet_copy_props(out, in); + ret = av_packet_copy_props(opkt, in); if (ret < 0) goto fail; fail: if (ret < 0) - av_packet_unref(out); + av_packet_unref(opkt); av_packet_free(&in); return ret;