Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / AV1 OBU rewrite filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/bitstream.h>
29 : #include <gpac/internal/media_dev.h>
30 :
31 : #ifndef GPAC_DISABLE_AV_PARSERS
32 :
33 : typedef struct
34 : {
35 : //opts
36 : Bool rcfg, tsep;
37 :
38 : //only one input pid declared
39 : GF_FilterPid *ipid;
40 : //only one output pid declared
41 : GF_FilterPid *opid;
42 :
43 : u32 crc;
44 :
45 : Bool ivf_hdr;
46 : u32 mode;
47 : GF_BitStream *bs_w;
48 : GF_BitStream *bs_r;
49 : u32 w, h;
50 : GF_Fraction fps;
51 : GF_AV1Config *av1c;
52 : u32 av1b_cfg_size;
53 : u32 codec_id;
54 : } GF_OBUMxCtx;
55 :
56 4 : GF_Err obumx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
57 : {
58 : u32 crc;
59 : const GF_PropertyValue *p, *dcd;
60 4 : GF_OBUMxCtx *ctx = gf_filter_get_udta(filter);
61 :
62 4 : if (is_remove) {
63 0 : ctx->ipid = NULL;
64 0 : if (ctx->opid) {
65 0 : gf_filter_pid_remove(ctx->opid);
66 0 : ctx->opid = NULL;
67 : }
68 : return GF_OK;
69 : }
70 4 : if (! gf_filter_pid_check_caps(pid))
71 : return GF_NOT_SUPPORTED;
72 :
73 4 : dcd = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
74 4 : if (!dcd) return GF_NON_COMPLIANT_BITSTREAM;
75 :
76 4 : crc = gf_crc_32(dcd->value.data.ptr, dcd->value.data.size);
77 4 : if (ctx->crc == crc) return GF_OK;
78 4 : ctx->crc = crc;
79 :
80 4 : if (!ctx->opid) {
81 4 : ctx->opid = gf_filter_pid_new(filter);
82 : }
83 : //copy properties at init or reconfig
84 4 : gf_filter_pid_copy_properties(ctx->opid, pid);
85 :
86 4 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, &PROP_BOOL(GF_TRUE) );
87 :
88 4 : ctx->ipid = pid;
89 :
90 4 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
91 :
92 4 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_CODECID);
93 4 : ctx->codec_id = p ? p->value.uint : 0;
94 4 : switch (ctx->codec_id) {
95 4 : case GF_CODECID_AV1:
96 : //check output type OBU vs av1b
97 4 : p = gf_filter_pid_caps_query(ctx->opid, GF_PROP_PID_FILE_EXT);
98 4 : if (p) {
99 4 : if (!strcmp(p->value.string, "obu")) ctx->mode = 0;
100 3 : else if (!strcmp(p->value.string, "av1b") || !strcmp(p->value.string, "av1")) ctx->mode = 1;
101 : //we might want to add a generic IVF read/write at some point
102 1 : else if (!strcmp(p->value.string, "ivf")) {
103 1 : ctx->mode = 2;
104 1 : ctx->ivf_hdr = 1;
105 : }
106 : } else {
107 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[OBUWrite] Couldn't guess desired output format type, assuming plain OBU\n"));
108 : }
109 : break;
110 0 : case GF_CODECID_VP8:
111 : case GF_CODECID_VP9:
112 : case GF_CODECID_VP10:
113 0 : ctx->mode = 2; //IVF only
114 0 : ctx->ivf_hdr = 1;
115 0 : break;
116 : }
117 :
118 4 : if (ctx->av1c) gf_odf_av1_cfg_del(ctx->av1c);
119 4 : ctx->av1c = NULL;
120 4 : if (ctx->mode==1) {
121 2 : u32 i=0;
122 : GF_AV1_OBUArrayEntry *obu;
123 2 : ctx->av1c = gf_odf_av1_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
124 2 : if (!ctx->av1c) {
125 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[OBUWrite] Invalid av1 config\n"));
126 0 : return GF_NON_COMPLIANT_BITSTREAM;
127 : }
128 2 : ctx->av1b_cfg_size = 0;
129 :
130 6 : while ((obu = gf_list_enum(ctx->av1c->obu_array, &i))) {
131 : //we don't output sequence header since it shall be present in sync sample
132 : //this avoids creating duplicate of the seqeunce header in the output stream
133 2 : if (obu->obu_type==OBU_SEQUENCE_HEADER) {
134 2 : i--;
135 2 : gf_list_rem(ctx->av1c->obu_array, i);
136 2 : gf_free(obu->obu);
137 2 : gf_free(obu);
138 2 : continue;
139 : }
140 0 : ctx->av1b_cfg_size += (u32) obu->obu_length;
141 0 : ctx->av1b_cfg_size += gf_av1_leb128_size(obu->obu_length);
142 : }
143 : }
144 :
145 :
146 4 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_WIDTH);
147 4 : if (p) ctx->w = p->value.uint;
148 4 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_HEIGHT);
149 4 : if (p) ctx->h = p->value.uint;
150 4 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FPS);
151 4 : if (p) ctx->fps = p->value.frac;
152 4 : if (!ctx->fps.num || !ctx->fps.den) {
153 1 : ctx->fps.num = 25;
154 1 : ctx->fps.den = 1;
155 : }
156 4 : gf_filter_pid_set_property_str(ctx->opid, "obu:mode", &PROP_UINT(ctx->mode) );
157 4 : gf_filter_pid_set_framing_mode(ctx->ipid, GF_TRUE);
158 :
159 4 : return GF_OK;
160 : }
161 :
162 :
163 524 : GF_Err obumx_process(GF_Filter *filter)
164 : {
165 : u32 i;
166 : u32 frame_sizes[128], max_frames;
167 524 : GF_OBUMxCtx *ctx = gf_filter_get_udta(filter);
168 : GF_FilterPacket *pck, *dst_pck;
169 : u8 *data, *output;
170 : u32 pck_size, size, sap_type, hdr_size, av1b_frame_size=0;
171 :
172 :
173 524 : pck = gf_filter_pid_get_packet(ctx->ipid);
174 524 : if (!pck) {
175 4 : if (gf_filter_pid_is_eos(ctx->ipid)) {
176 4 : gf_filter_pid_set_eos(ctx->opid);
177 4 : return GF_EOS;
178 : }
179 : return GF_OK;
180 : }
181 :
182 520 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
183 : hdr_size = 0;
184 520 : size = pck_size;
185 : //always add temporal delim
186 520 : if (ctx->codec_id==GF_CODECID_AV1)
187 520 : size += 2;
188 :
189 520 : sap_type = gf_filter_pck_get_sap(pck);
190 520 : if (!sap_type) {
191 516 : u8 flags = gf_filter_pck_get_dependency_flags(pck);
192 516 : if (flags) {
193 : //get dependsOn
194 0 : flags>>=4;
195 0 : flags &= 0x3;
196 0 : if (flags==2) sap_type = 3; //could be 1, 2 or 3
197 : }
198 : }
199 :
200 : memset(frame_sizes, 0, sizeof(u32)*128);
201 520 : if (ctx->mode==2) {
202 173 : if (ctx->ivf_hdr) hdr_size += 32;
203 173 : hdr_size += 12;
204 : }
205 520 : if (ctx->mode==1) {
206 : u32 obu_sizes=0;
207 : u32 frame_idx=0;
208 :
209 174 : obu_sizes = frame_sizes[0] = 3;
210 174 : if (sap_type && ctx->av1b_cfg_size) {
211 0 : frame_sizes[0] += ctx->av1b_cfg_size;
212 : obu_sizes += ctx->av1b_cfg_size;
213 : }
214 :
215 174 : if (!ctx->bs_r) ctx->bs_r = gf_bs_new(data, pck_size, GF_BITSTREAM_READ);
216 172 : else gf_bs_reassign_buffer(ctx->bs_r, data, pck_size);
217 :
218 367 : while (gf_bs_available(ctx->bs_r)) {
219 : ObuType obu_type;
220 : u32 obu_size;
221 : Bool obu_extension_flag, obu_has_size_field;
222 : u8 temporal_id, spatial_id;
223 193 : u32 obu_hdr_size = (u32) gf_bs_get_position(ctx->bs_r);
224 :
225 193 : gf_av1_parse_obu_header(ctx->bs_r, &obu_type, &obu_extension_flag, &obu_has_size_field, &temporal_id, &spatial_id);
226 :
227 193 : if (!obu_has_size_field) {
228 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[OBUWrite] OBU without size field, bug in demux filter !!\n"));
229 0 : return GF_NON_COMPLIANT_BITSTREAM;
230 : }
231 193 : obu_size = (u32)gf_av1_leb128_read(ctx->bs_r, NULL);
232 193 : obu_hdr_size = (u32) gf_bs_get_position(ctx->bs_r) - obu_hdr_size;
233 193 : gf_bs_skip_bytes(ctx->bs_r, obu_size);
234 :
235 193 : obu_size += obu_hdr_size;
236 193 : obu_sizes += obu_size + gf_av1_leb128_size(obu_size);
237 :
238 193 : if (obu_type==OBU_FRAME) {
239 191 : frame_idx++;
240 191 : if (frame_idx==128) {
241 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[OBUWrite] more than 128 frames in a temporal unit not supported\n"));
242 : return GF_NOT_SUPPORTED;
243 : }
244 191 : if (frame_idx>1)
245 17 : frame_sizes[frame_idx-1] = 0;
246 : }
247 193 : frame_sizes[frame_idx ? (frame_idx-1) : 0] += obu_size + gf_av1_leb128_size(obu_size);
248 : }
249 : max_frames = frame_idx;
250 : size = obu_sizes;
251 : //packet without frame
252 174 : if (!max_frames) {
253 0 : size += gf_av1_leb128_size(frame_sizes[0]);
254 : } else {
255 365 : for (i=0;i<max_frames; i++) {
256 191 : size += gf_av1_leb128_size(frame_sizes[i]);
257 : }
258 : }
259 : av1b_frame_size = size;
260 174 : size += gf_av1_leb128_size(size);
261 : } else {
262 346 : if (sap_type && ctx->av1b_cfg_size) size += ctx->av1b_cfg_size;
263 : }
264 :
265 520 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, hdr_size+size, &output);
266 520 : if (!dst_pck) return GF_OUT_OF_MEM;
267 :
268 520 : gf_filter_pck_merge_properties(pck, dst_pck);
269 :
270 520 : if (!ctx->bs_w) ctx->bs_w = gf_bs_new(output, hdr_size+size, GF_BITSTREAM_WRITE);
271 516 : else gf_bs_reassign_buffer(ctx->bs_w, output, hdr_size+size);
272 :
273 520 : if (ctx->mode==1) {
274 : u32 frame_idx = 0;
275 : //temporal unit
276 174 : gf_av1_leb128_write(ctx->bs_w, av1b_frame_size);
277 : assert(frame_sizes[0]);
278 174 : gf_av1_leb128_write(ctx->bs_w, frame_sizes[0]);
279 :
280 : //write temporal delim with obu size set
281 174 : gf_av1_leb128_write(ctx->bs_w, 2);
282 174 : gf_bs_write_u8(ctx->bs_w, 0x12);
283 174 : gf_bs_write_u8(ctx->bs_w, 0);
284 :
285 174 : if (sap_type && ctx->av1b_cfg_size) {
286 : GF_AV1_OBUArrayEntry *obu;
287 0 : i=0;
288 0 : while ((obu = gf_list_enum(ctx->av1c->obu_array, &i))) {
289 0 : gf_av1_leb128_write(ctx->bs_w, obu->obu_length);
290 0 : gf_bs_write_data(ctx->bs_w, obu->obu, (u32) obu->obu_length);
291 : }
292 : }
293 :
294 174 : gf_bs_reassign_buffer(ctx->bs_r, data, pck_size);
295 :
296 541 : while (gf_bs_available(ctx->bs_r)) {
297 : ObuType obu_type;
298 : u32 obu_size;
299 : Bool obu_extension_flag, obu_has_size_field;
300 : u8 temporal_id, spatial_id;
301 193 : u32 obu_hdr_size, start = (u32) gf_bs_get_position(ctx->bs_r);
302 :
303 193 : gf_av1_parse_obu_header(ctx->bs_r, &obu_type, &obu_extension_flag, &obu_has_size_field, &temporal_id, &spatial_id);
304 193 : obu_size = (u32)gf_av1_leb128_read(ctx->bs_r, NULL);
305 :
306 193 : obu_hdr_size = (u32) gf_bs_get_position(ctx->bs_r) - start;
307 193 : gf_bs_skip_bytes(ctx->bs_r, obu_size);
308 :
309 193 : if (obu_type==OBU_FRAME) {
310 191 : frame_idx++;
311 191 : if (frame_idx>1) {
312 17 : gf_av1_leb128_write(ctx->bs_w, frame_sizes[frame_idx-1] );
313 : }
314 : }
315 :
316 193 : obu_size += obu_hdr_size;
317 193 : gf_av1_leb128_write(ctx->bs_w, obu_size);
318 193 : gf_bs_write_data(ctx->bs_w, data+start, obu_size);
319 :
320 : }
321 : assert(gf_bs_get_position(ctx->bs_w) == size);
322 : } else {
323 :
324 : //write IVF headers
325 346 : if (ctx->ivf_hdr) {
326 1 : gf_bs_write_u32(ctx->bs_w, GF_4CC('D', 'K', 'I', 'F'));
327 1 : gf_bs_write_u16_le(ctx->bs_w, 0);
328 1 : gf_bs_write_u16_le(ctx->bs_w, 32);
329 :
330 1 : gf_bs_write_u32(ctx->bs_w, GF_4CC('A', 'V', '0', '1') ); //codec_fourcc
331 1 : gf_bs_write_u16_le(ctx->bs_w, ctx->w);
332 1 : gf_bs_write_u16_le(ctx->bs_w, ctx->h);
333 1 : gf_bs_write_u32_le(ctx->bs_w, ctx->fps.num);
334 1 : gf_bs_write_u32_le(ctx->bs_w, ctx->fps.den);
335 1 : gf_bs_write_u32_le(ctx->bs_w, 0); //nb frames
336 1 : gf_bs_write_u32_le(ctx->bs_w, 0);
337 1 : ctx->ivf_hdr = 0;
338 : }
339 346 : if (ctx->mode==2) {
340 173 : u64 cts = gf_filter_pck_get_cts(pck);
341 173 : cts *= ctx->fps.den;
342 173 : cts /= ctx->fps.num;
343 173 : cts /= gf_filter_pck_get_timescale(pck);
344 173 : gf_bs_write_u32_le(ctx->bs_w, size);
345 173 : gf_bs_write_u64(ctx->bs_w, cts);
346 : }
347 346 : if (ctx->codec_id==GF_CODECID_AV1) {
348 : //write temporal delim with obu size set
349 346 : gf_bs_write_u8(ctx->bs_w, 0x12);
350 346 : gf_bs_write_u8(ctx->bs_w, 0);
351 :
352 346 : if (sap_type && ctx->av1b_cfg_size) {
353 : GF_AV1_OBUArrayEntry *obu;
354 0 : i=0;
355 0 : while ((obu = gf_list_enum(ctx->av1c->obu_array, &i))) {
356 0 : gf_av1_leb128_write(ctx->bs_w, obu->obu_length);
357 0 : gf_bs_write_data(ctx->bs_w, obu->obu, (u32) obu->obu_length);
358 : }
359 : }
360 : }
361 346 : gf_bs_write_data(ctx->bs_w, data, pck_size);
362 : }
363 :
364 520 : if (!ctx->rcfg) {
365 0 : ctx->av1b_cfg_size = 0;
366 : }
367 520 : gf_filter_pck_send(dst_pck);
368 520 : gf_filter_pid_drop_packet(ctx->ipid);
369 :
370 520 : return GF_OK;
371 : }
372 4 : static void obumx_finalize(GF_Filter *filter)
373 : {
374 4 : GF_OBUMxCtx *ctx = gf_filter_get_udta(filter);
375 4 : if (ctx->bs_r) gf_bs_del(ctx->bs_r);
376 4 : if (ctx->bs_w) gf_bs_del(ctx->bs_w);
377 4 : if (ctx->av1c) gf_odf_av1_cfg_del(ctx->av1c);
378 4 : }
379 :
380 : static const GF_FilterCapability OBUMxCaps[] =
381 : {
382 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
383 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_AV1),
384 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_VP8),
385 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_VP9),
386 : CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_VP10),
387 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
388 : CAP_BOOL(GF_CAPS_OUTPUT, GF_PROP_PID_UNFRAMED, GF_TRUE),
389 : };
390 :
391 :
392 :
393 : #define OFFS(_n) #_n, offsetof(GF_OBUMxCtx, _n)
394 : static const GF_FilterArgs OBUMxArgs[] =
395 : {
396 : { OFFS(rcfg), "force repeating decoder config at each I-frame", GF_PROP_BOOL, "true", NULL, 0},
397 : {0}
398 : };
399 :
400 :
401 : GF_FilterRegister OBUMxRegister = {
402 : .name = "ufobu",
403 : GF_FS_SET_DESCRIPTION("IVF/OBU/annexB writer")
404 : GF_FS_SET_HELP("This filter is used to rewrite AV1 OBU bitstream into IVF, annex B or OBU sequence, reinserting the temporal delimiter OBU.")
405 : .private_size = sizeof(GF_OBUMxCtx),
406 : .args = OBUMxArgs,
407 : SETCAPS(OBUMxCaps),
408 : .finalize = obumx_finalize,
409 : .configure_pid = obumx_configure_pid,
410 : .process = obumx_process
411 : };
412 :
413 :
414 2877 : const GF_FilterRegister *obumx_register(GF_FilterSession *session)
415 : {
416 2877 : return &OBUMxRegister;
417 : }
418 :
419 : #else
420 : const GF_FilterRegister *obumx_register(GF_FilterSession *session)
421 : {
422 : return NULL;
423 : }
424 : #endif // GPAC_DISABLE_AV_PARSERS
425 :
|