| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * HEVC MP4 to Annex B byte stream format filter | ||
| 3 | * copyright (c) 2015 Anton Khirnov | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <string.h> | ||
| 23 | |||
| 24 | #include "libavutil/intreadwrite.h" | ||
| 25 | #include "libavutil/mem.h" | ||
| 26 | |||
| 27 | #include "bsf.h" | ||
| 28 | #include "bsf_internal.h" | ||
| 29 | #include "bytestream.h" | ||
| 30 | #include "defs.h" | ||
| 31 | |||
| 32 | #include "hevc/hevc.h" | ||
| 33 | |||
| 34 | #define MIN_HEVCC_LENGTH 23 | ||
| 35 | |||
| 36 | typedef struct HEVCBSFContext { | ||
| 37 | uint8_t length_size; | ||
| 38 | int extradata_parsed; | ||
| 39 | } HEVCBSFContext; | ||
| 40 | |||
| 41 | 1 | static int hevc_extradata_to_annexb(AVBSFContext *ctx) | |
| 42 | { | ||
| 43 | GetByteContext gb; | ||
| 44 | int length_size, num_arrays, i, j; | ||
| 45 | 1 | int ret = 0; | |
| 46 | |||
| 47 | 1 | uint8_t *new_extradata = NULL; | |
| 48 | 1 | size_t new_extradata_size = 0; | |
| 49 | |||
| 50 | 1 | bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size); | |
| 51 | |||
| 52 | 1 | bytestream2_skip(&gb, 21); | |
| 53 | 1 | length_size = (bytestream2_get_byte(&gb) & 3) + 1; | |
| 54 | 1 | num_arrays = bytestream2_get_byte(&gb); | |
| 55 | |||
| 56 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (i = 0; i < num_arrays; i++) { |
| 57 | 3 | int type = bytestream2_get_byte(&gb) & 0x3f; | |
| 58 | 3 | int cnt = bytestream2_get_be16(&gb); | |
| 59 | |||
| 60 |
5/10✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
3 | if (!(type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS || |
| 61 | type == HEVC_NAL_SEI_PREFIX || type == HEVC_NAL_SEI_SUFFIX)) { | ||
| 62 | ✗ | av_log(ctx, AV_LOG_ERROR, "Invalid NAL unit type in extradata: %d\n", | |
| 63 | type); | ||
| 64 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 65 | ✗ | goto fail; | |
| 66 | } | ||
| 67 | |||
| 68 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | for (j = 0; j < cnt; j++) { |
| 69 | 3 | const int nalu_len = bytestream2_get_be16(&gb); | |
| 70 | |||
| 71 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | if (!nalu_len || |
| 72 | 3 | nalu_len > bytestream2_get_bytes_left(&gb) || | |
| 73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | 4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) { |
| 74 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 75 | ✗ | goto fail; | |
| 76 | } | ||
| 77 | 3 | ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + AV_INPUT_BUFFER_PADDING_SIZE); | |
| 78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret < 0) |
| 79 | ✗ | goto fail; | |
| 80 | |||
| 81 | 3 | AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode | |
| 82 | 3 | bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, nalu_len); | |
| 83 | 3 | new_extradata_size += 4 + nalu_len; | |
| 84 | 3 | memset(new_extradata + new_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); | |
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | 1 | av_freep(&ctx->par_out->extradata); | |
| 89 | 1 | ctx->par_out->extradata = new_extradata; | |
| 90 | 1 | ctx->par_out->extradata_size = new_extradata_size; | |
| 91 | |||
| 92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!new_extradata_size) |
| 93 | ✗ | av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n"); | |
| 94 | |||
| 95 | 1 | return length_size; | |
| 96 | ✗ | fail: | |
| 97 | ✗ | av_freep(&new_extradata); | |
| 98 | ✗ | return ret; | |
| 99 | } | ||
| 100 | |||
| 101 | 1 | static int hevc_mp4toannexb_init(AVBSFContext *ctx) | |
| 102 | { | ||
| 103 | 1 | HEVCBSFContext *s = ctx->priv_data; | |
| 104 | int ret; | ||
| 105 | |||
| 106 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH || |
| 107 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | AV_RB24(ctx->par_in->extradata) == 1 || |
| 108 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | AV_RB32(ctx->par_in->extradata) == 1) { |
| 109 | ✗ | av_log(ctx, AV_LOG_VERBOSE, | |
| 110 | "The input looks like it is Annex B already\n"); | ||
| 111 | } else { | ||
| 112 | 1 | ret = hevc_extradata_to_annexb(ctx); | |
| 113 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (ret < 0) |
| 114 | ✗ | return ret; | |
| 115 | 1 | s->length_size = ret; | |
| 116 | 1 | s->extradata_parsed = 1; | |
| 117 | } | ||
| 118 | |||
| 119 | 1 | return 0; | |
| 120 | } | ||
| 121 | |||
| 122 | 97 | static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) | |
| 123 | { | ||
| 124 | 97 | HEVCBSFContext *s = ctx->priv_data; | |
| 125 | AVPacket *in; | ||
| 126 | GetByteContext gb; | ||
| 127 | |||
| 128 | 97 | int got_irap = 0; | |
| 129 | 97 | int got_ps = 0, seen_irap_ps = 0; | |
| 130 | 97 | int i, ret = 0; | |
| 131 | |||
| 132 | 97 | ret = ff_bsf_get_packet(ctx, &in); | |
| 133 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 48 times.
|
97 | if (ret < 0) |
| 134 | 49 | return ret; | |
| 135 | |||
| 136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (!s->extradata_parsed) { |
| 137 | ✗ | av_packet_move_ref(out, in); | |
| 138 | ✗ | av_packet_free(&in); | |
| 139 | ✗ | return 0; | |
| 140 | } | ||
| 141 | |||
| 142 | 48 | bytestream2_init(&gb, in->data, in->size); | |
| 143 | |||
| 144 |
4/4✓ Branch 0 taken 428 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 381 times.
✓ Branch 4 taken 47 times.
|
429 | while (!got_irap && bytestream2_get_bytes_left(&gb)) { |
| 145 | 381 | uint32_t nalu_size = 0; | |
| 146 | int nalu_type; | ||
| 147 | |||
| 148 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 381 times.
|
381 | if (bytestream2_get_bytes_left(&gb) < s->length_size) { |
| 149 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 150 | ✗ | goto fail; | |
| 151 | } | ||
| 152 |
2/2✓ Branch 0 taken 1524 times.
✓ Branch 1 taken 381 times.
|
1905 | for (i = 0; i < s->length_size; i++) |
| 153 | 1524 | nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); | |
| 154 | |||
| 155 |
2/4✓ Branch 0 taken 381 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 381 times.
|
381 | if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { |
| 156 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 157 | ✗ | goto fail; | |
| 158 | } | ||
| 159 | |||
| 160 | 381 | nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f; | |
| 161 | 381 | bytestream2_skip(&gb, nalu_size); | |
| 162 |
4/4✓ Branch 0 taken 51 times.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 50 times.
|
381 | got_irap |= nalu_type >= HEVC_NAL_BLA_W_LP && |
| 163 | nalu_type <= HEVC_NAL_RSV_IRAP_VCL23; | ||
| 164 |
4/4✓ Branch 0 taken 50 times.
✓ Branch 1 taken 331 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 47 times.
|
381 | got_ps |= nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS; |
| 165 | } | ||
| 166 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
48 | seen_irap_ps = got_irap && got_ps; |
| 167 | 48 | got_irap = got_ps = 0; | |
| 168 | |||
| 169 | 48 | bytestream2_init(&gb, in->data, in->size); | |
| 170 | |||
| 171 |
2/2✓ Branch 1 taken 382 times.
✓ Branch 2 taken 48 times.
|
430 | while (bytestream2_get_bytes_left(&gb)) { |
| 172 | 382 | uint32_t nalu_size = 0; | |
| 173 | int nalu_type; | ||
| 174 | int is_irap, is_ps, add_extradata, extra_size, prev_size; | ||
| 175 | |||
| 176 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 382 times.
|
382 | if (bytestream2_get_bytes_left(&gb) < s->length_size) { |
| 177 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 178 | ✗ | goto fail; | |
| 179 | } | ||
| 180 |
2/2✓ Branch 0 taken 1528 times.
✓ Branch 1 taken 382 times.
|
1910 | for (i = 0; i < s->length_size; i++) |
| 181 | 1528 | nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); | |
| 182 | |||
| 183 |
2/4✓ Branch 0 taken 382 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 382 times.
|
382 | if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { |
| 184 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 185 | ✗ | goto fail; | |
| 186 | } | ||
| 187 | |||
| 188 | 382 | nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f; | |
| 189 | |||
| 190 | /* prepend extradata to IRAP frames */ | ||
| 191 |
4/4✓ Branch 0 taken 52 times.
✓ Branch 1 taken 330 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 51 times.
|
382 | is_irap = nalu_type >= HEVC_NAL_BLA_W_LP && |
| 192 | nalu_type <= HEVC_NAL_RSV_IRAP_VCL23; | ||
| 193 |
5/6✓ Branch 0 taken 51 times.
✓ Branch 1 taken 331 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
382 | is_ps = nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS && seen_irap_ps; |
| 194 |
7/8✓ Branch 0 taken 379 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 378 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
382 | add_extradata = (is_ps || is_irap) && !got_ps && !got_irap; |
| 195 | 382 | extra_size = add_extradata * ctx->par_out->extradata_size; | |
| 196 | 382 | got_irap |= is_irap; | |
| 197 | 382 | got_ps |= is_ps; | |
| 198 | |||
| 199 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 382 times.
|
382 | if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) { |
| 200 | ✗ | ret = AVERROR_INVALIDDATA; | |
| 201 | ✗ | goto fail; | |
| 202 | } | ||
| 203 | |||
| 204 | 382 | prev_size = out->size; | |
| 205 | |||
| 206 | 382 | ret = av_grow_packet(out, 4 + nalu_size + extra_size); | |
| 207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 382 times.
|
382 | if (ret < 0) |
| 208 | ✗ | goto fail; | |
| 209 | |||
| 210 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 381 times.
|
382 | if (extra_size) |
| 211 | 1 | memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size); | |
| 212 | 382 | AV_WB32(out->data + prev_size + extra_size, 1); | |
| 213 | 382 | bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size); | |
| 214 | } | ||
| 215 | |||
| 216 | 48 | ret = av_packet_copy_props(out, in); | |
| 217 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | if (ret < 0) |
| 218 | ✗ | goto fail; | |
| 219 | |||
| 220 | 48 | fail: | |
| 221 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (ret < 0) |
| 222 | ✗ | av_packet_unref(out); | |
| 223 | 48 | av_packet_free(&in); | |
| 224 | |||
| 225 | 48 | return ret; | |
| 226 | } | ||
| 227 | |||
| 228 | static const enum AVCodecID codec_ids[] = { | ||
| 229 | AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE, | ||
| 230 | }; | ||
| 231 | |||
| 232 | const FFBitStreamFilter ff_hevc_mp4toannexb_bsf = { | ||
| 233 | .p.name = "hevc_mp4toannexb", | ||
| 234 | .p.codec_ids = codec_ids, | ||
| 235 | .priv_data_size = sizeof(HEVCBSFContext), | ||
| 236 | .init = hevc_mp4toannexb_init, | ||
| 237 | .filter = hevc_mp4toannexb_filter, | ||
| 238 | }; | ||
| 239 |