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 i, ret = 0; | |
130 | |||
131 | 97 | ret = ff_bsf_get_packet(ctx, &in); | |
132 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 48 times.
|
97 | if (ret < 0) |
133 | 49 | return ret; | |
134 | |||
135 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (!s->extradata_parsed) { |
136 | ✗ | av_packet_move_ref(out, in); | |
137 | ✗ | av_packet_free(&in); | |
138 | ✗ | return 0; | |
139 | } | ||
140 | |||
141 | 48 | bytestream2_init(&gb, in->data, in->size); | |
142 | |||
143 |
2/2✓ Branch 1 taken 382 times.
✓ Branch 2 taken 48 times.
|
430 | while (bytestream2_get_bytes_left(&gb)) { |
144 | 382 | uint32_t nalu_size = 0; | |
145 | int nalu_type; | ||
146 | int is_irap, add_extradata, extra_size, prev_size; | ||
147 | |||
148 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 382 times.
|
382 | if (bytestream2_get_bytes_left(&gb) < s->length_size) { |
149 | ✗ | ret = AVERROR_INVALIDDATA; | |
150 | ✗ | goto fail; | |
151 | } | ||
152 |
2/2✓ Branch 0 taken 1528 times.
✓ Branch 1 taken 382 times.
|
1910 | for (i = 0; i < s->length_size; i++) |
153 | 1528 | nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); | |
154 | |||
155 |
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)) { |
156 | ✗ | ret = AVERROR_INVALIDDATA; | |
157 | ✗ | goto fail; | |
158 | } | ||
159 | |||
160 | 382 | nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f; | |
161 | |||
162 | /* prepend extradata to IRAP frames */ | ||
163 |
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 && |
164 | nalu_type <= HEVC_NAL_RSV_IRAP_VCL23; | ||
165 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 381 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
382 | add_extradata = is_irap && !got_irap; |
166 | 382 | extra_size = add_extradata * ctx->par_out->extradata_size; | |
167 | 382 | got_irap |= is_irap; | |
168 | |||
169 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 382 times.
|
382 | if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) { |
170 | ✗ | ret = AVERROR_INVALIDDATA; | |
171 | ✗ | goto fail; | |
172 | } | ||
173 | |||
174 | 382 | prev_size = out->size; | |
175 | |||
176 | 382 | ret = av_grow_packet(out, 4 + nalu_size + extra_size); | |
177 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 382 times.
|
382 | if (ret < 0) |
178 | ✗ | goto fail; | |
179 | |||
180 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 381 times.
|
382 | if (extra_size) |
181 | 1 | memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size); | |
182 | 382 | AV_WB32(out->data + prev_size + extra_size, 1); | |
183 | 382 | bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size); | |
184 | } | ||
185 | |||
186 | 48 | ret = av_packet_copy_props(out, in); | |
187 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | if (ret < 0) |
188 | ✗ | goto fail; | |
189 | |||
190 | 48 | fail: | |
191 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (ret < 0) |
192 | ✗ | av_packet_unref(out); | |
193 | 48 | av_packet_free(&in); | |
194 | |||
195 | 48 | return ret; | |
196 | } | ||
197 | |||
198 | static const enum AVCodecID codec_ids[] = { | ||
199 | AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE, | ||
200 | }; | ||
201 | |||
202 | const FFBitStreamFilter ff_hevc_mp4toannexb_bsf = { | ||
203 | .p.name = "hevc_mp4toannexb", | ||
204 | .p.codec_ids = codec_ids, | ||
205 | .priv_data_size = sizeof(HEVCBSFContext), | ||
206 | .init = hevc_mp4toannexb_init, | ||
207 | .filter = hevc_mp4toannexb_filter, | ||
208 | }; | ||
209 |