This is the result of me screwing around to see how complex of a task this would be; the answer is evidently "not particularly complex". I seem to have at least a few issues to work out, since the .ts files I'm generating don't always seek correctly, but it's a start. Would appreciate it if anyone who knows a bit more about how AVC and HEVC differ here could provide some guidance on what's left to do. --- libavcodec/h264_mp4toannexb_bsf.c | 126 ++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 45 deletions(-)
diff --git a/libavcodec/h264_mp4toannexb_bsf.c b/libavcodec/h264_mp4toannexb_bsf.c index ae96ee9..7cf8bc2 100644 --- a/libavcodec/h264_mp4toannexb_bsf.c +++ b/libavcodec/h264_mp4toannexb_bsf.c @@ -32,6 +32,7 @@ typedef struct H264BSFContext { uint8_t new_idr; uint8_t idr_sps_seen; uint8_t idr_pps_seen; + uint8_t idr_vps_seen; int extradata_parsed; } H264BSFContext; @@ -63,56 +64,83 @@ static int alloc_and_copy(uint8_t **poutbuf, int *poutbuf_size, return 0; } +#define NAL_SPS 33 +#define NAL_PPS 34 + static int h264_extradata_to_annexb(H264BSFContext *ctx, AVCodecContext *avctx, const int padding) { - uint16_t unit_size; + int hevc = avctx->codec_id == AV_CODEC_ID_HEVC; + uint16_t unit_size, unit_nb; uint64_t total_size = 0; - uint8_t *out = NULL, unit_nb, sps_done = 0, - sps_seen = 0, pps_seen = 0; - const uint8_t *extradata = avctx->extradata + 4; + uint8_t *out = NULL, sps_done = 0, + sps_seen = 0, pps_seen = 0, set_nb; + const uint8_t *extradata = avctx->extradata + (hevc ? 21 : 4); static const uint8_t nalu_header[4] = { 0, 0, 0, 1 }; int length_size = (*extradata++ & 0x3) + 1; // retrieve length coded size ctx->sps_offset = ctx->pps_offset = -1; - /* retrieve sps and pps unit(s) */ - unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */ - if (!unit_nb) { - goto pps; - } else { - ctx->sps_offset = 0; - sps_seen = 1; - } - - while (unit_nb--) { - int err; + set_nb = hevc ? *extradata++ : 1; - unit_size = AV_RB16(extradata); - total_size += unit_size + 4; - if (total_size > INT_MAX - padding) { - av_log(avctx, 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 > avctx->extradata + avctx->extradata_size) { - av_log(avctx, AV_LOG_ERROR, "Packet header is not contained in global extradata, " - "corrupted stream or invalid MP4/AVCC bitstream\n"); - av_free(out); - return AVERROR(EINVAL); - } - 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; -pps: - if (!unit_nb && !sps_done++) { - unit_nb = *extradata++; /* number of pps unit(s) */ - if (unit_nb) { - ctx->pps_offset = (extradata - 1) - (avctx->extradata + 4); + while (set_nb--) { + int type = hevc ? *extradata++ & 0x3f : 0; + /* retrieve sps and pps unit(s) */ + if (hevc) { + unit_nb = AV_RB16(extradata); + extradata += 2; + if (type == NAL_SPS) { + ctx->sps_offset = total_size; + sps_seen = 1; + } else if (type == NAL_PPS) { + ctx->pps_offset = total_size; pps_seen = 1; } + } else { + unit_nb = *extradata++ & 0x1f; /* number of units */ + if (!unit_nb) { + type = NAL_PPS; + goto pps; + } else { + type = NAL_SPS; + ctx->sps_offset = 0; + sps_seen = 1; + } + } + + while (unit_nb--) { + int err; + + unit_size = AV_RB16(extradata); + if (type == NAL_SPS || type == NAL_PPS || !hevc) { + if (!hevc) + ctx->sps_offset = total_size; + total_size += unit_size + 4; + if (total_size > INT_MAX - padding) { + av_log(avctx, 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 > avctx->extradata + avctx->extradata_size) { + av_log(avctx, AV_LOG_ERROR, "Packet header is not contained in global extradata, " + "corrupted stream or invalid MP4/AVCC bitstream\n"); + av_free(out); + return AVERROR(EINVAL); + } + 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; + pps: + if (!hevc && !unit_nb && !sps_done++) { + unit_nb = *extradata++; /* number of pps unit(s) */ + if (unit_nb) { + ctx->pps_offset = (extradata - 1) - (avctx->extradata + 4); + pps_seen = 1; + } + } } } @@ -143,12 +171,18 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, int keyframe) { H264BSFContext *ctx = bsfc->priv_data; + int hevc = avctx->codec_id == AV_CODEC_ID_HEVC; int i; uint8_t unit_type; int32_t nal_size; uint32_t cumul_size = 0; const uint8_t *buf_end = buf + buf_size; int ret = 0; + uint8_t vps_type = hevc ? 32 : 0; + uint8_t sps_type = hevc ? 33 : 7; + uint8_t pps_type = hevc ? 34 : 8; + uint8_t idr_type = hevc ? 19 : 5; + uint8_t idr_type2 = hevc ? 20 : 5; /* nothing to filter */ if (!avctx->extradata || avctx->extradata_size < 6) { @@ -180,14 +214,16 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, nal_size = (nal_size << 8) | buf[i]; buf += ctx->length_size; - unit_type = *buf & 0x1f; + unit_type = hevc ? (*buf >> 1) & 0x3f : *buf & 0x1f; if (buf + nal_size > buf_end || nal_size < 0) goto fail; - if (unit_type == 7) + if (unit_type == vps_type) + ctx->idr_vps_seen = ctx->new_idr = 1; + else if (unit_type == sps_type) ctx->idr_sps_seen = ctx->new_idr = 1; - else if (unit_type == 8) { + else if (unit_type == pps_type) { ctx->idr_pps_seen = ctx->new_idr = 1; /* if SPS has not been seen yet, prepend the AVCC one to PPS */ if (!ctx->idr_sps_seen) { @@ -208,18 +244,18 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, /* 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 (!ctx->new_idr && unit_type == 5 && (buf[1] & 0x80)) + if (!ctx->new_idr && (unit_type == idr_type || unit_type == idr_type2) && (buf[1] & 0x80)) ctx->new_idr = 1; /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ - if (ctx->new_idr && unit_type == 5 && !ctx->idr_sps_seen && !ctx->idr_pps_seen) { + if (ctx->new_idr && (unit_type == idr_type || unit_type == idr_type2) && !ctx->idr_sps_seen && !ctx->idr_pps_seen) { if ((ret=alloc_and_copy(poutbuf, poutbuf_size, avctx->extradata, avctx->extradata_size, buf, nal_size)) < 0) goto fail; ctx->new_idr = 0; /* if only SPS has been seen, also insert PPS */ - } else if (ctx->new_idr && unit_type == 5 && ctx->idr_sps_seen && !ctx->idr_pps_seen) { + } else if (ctx->new_idr && (unit_type == idr_type || unit_type == idr_type2) && ctx->idr_sps_seen && !ctx->idr_pps_seen) { if (ctx->pps_offset == -1) { av_log(avctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); if ((ret = alloc_and_copy(poutbuf, poutbuf_size, -- 2.4.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel