FFmpeg
Loading...
Searching...
No Matches
imf_cpl.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19/*
20 *
21 * Copyright (c) Sandflow Consulting LLC
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions are met:
25 *
26 * * Redistributions of source code must retain the above copyright notice, this
27 * list of conditions and the following disclaimer.
28 * * Redistributions in binary form must reproduce the above copyright notice,
29 * this list of conditions and the following disclaimer in the documentation
30 * and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
43 */
44
45/**
46 * Implements IMP CPL processing
47 *
48 * @author Pierre-Anthony Lemieux
49 * @file
50 * @ingroup lavu_imf
51 */
52
53#include "imf.h"
54#include "libavutil/bprint.h"
55#include "libavutil/error.h"
56#include "libavutil/mem.h"
57#include <libxml/parser.h>
58
59xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
60{
61 xmlNodePtr cur_element;
62
63 cur_element = xmlFirstElementChild(parent);
64 while (cur_element) {
65 if (xmlStrcmp(cur_element->name, name_utf8) == 0)
66 return cur_element;
67
68 cur_element = xmlNextElementSibling(cur_element);
69 }
70 return NULL;
71}
72
73int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
74{
75 int ret = 0;
76
77 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
78 if (!element_text)
80 ret = av_uuid_urn_parse(element_text, uuid);
81 if (ret)
83 xmlFree(element_text);
84
85 return ret;
86}
87
88int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
89{
90 int ret = 0;
91
92 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
93 if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2)
95 xmlFree(element_text);
96
97 return ret;
98}
99
100int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
101{
102 int ret = 0;
103
104 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
105 if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1)
107 xmlFree(element_text);
108
109 return ret;
110}
111
112static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
113{
114 int ret = 0;
115
116 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
117 if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0)
118 *value = 1;
119 else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0)
120 *value = 0;
121 else
122 ret = 1;
123 xmlFree(element_text);
124
125 return ret;
126}
127
129{
130 memset(track->id_uuid, 0, sizeof(track->id_uuid));
131}
132
139
147
149{
150 rsrc->duration = 0;
151 rsrc->edit_rate = av_make_q(0, 1);
152 rsrc->entry_point = 0;
153 rsrc->repeat_count = 1;
154}
155
157{
159 rsrc->marker_count = 0;
160 rsrc->markers = NULL;
161}
162
163static void imf_marker_init(FFIMFMarker *marker)
164{
165 marker->label_utf8 = NULL;
166 marker->offset = 0;
167 marker->scope_utf8 = NULL;
168}
169
171{
173 memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
174}
175
176static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
177{
178 xmlNodePtr element = NULL;
179
180 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle")))
181 return AVERROR_INVALIDDATA;
182 cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
183 element->xmlChildrenNode,
184 1);
185 if (!cpl->content_title_utf8)
186 cpl->content_title_utf8 = xmlStrdup("");
187 if (!cpl->content_title_utf8)
188 return AVERROR(ENOMEM);
189
190 return 0;
191}
192
193static int digit_to_int(char digit)
194{
195 if (digit >= '0' && digit <= '9')
196 return digit - '0';
197 return -1;
198}
199
200/**
201 * Parses a string that conform to the TimecodeType used in IMF CPL and defined
202 * in SMPTE ST 2067-3.
203 * @param[in] s string to parse
204 * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH,
205 * MM, SS and FF fields of the timecode are returned.
206 * @return 0 on success, < 0 AVERROR code on error.
207 */
208static int parse_cpl_tc_type(const char *s, int *tc_comps)
209{
210 if (av_strnlen(s, 11) != 11)
211 return AVERROR(EINVAL);
212
213 for (int i = 0; i < 4; i++) {
214 int hi;
215 int lo;
216
217 hi = digit_to_int(s[i * 3]);
218 lo = digit_to_int(s[i * 3 + 1]);
219
220 if (hi == -1 || lo == -1)
221 return AVERROR(EINVAL);
222
223 tc_comps[i] = 10 * hi + lo;
224 }
225
226 return 0;
227}
228
229static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
230{
231 xmlNodePtr tc_element = NULL;
232 xmlNodePtr element = NULL;
233 xmlChar *tc_str = NULL;
234 int df = 0;
235 int comps[4];
236 int ret = 0;
237
238 tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode");
239 if (!tc_element)
240 return 0;
241
242 element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame");
243 if (!element)
244 return AVERROR_INVALIDDATA;
245
246 if (ff_imf_xml_read_boolean(element, &df))
247 return AVERROR_INVALIDDATA;
248
249 element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress");
250 if (!element)
251 return AVERROR_INVALIDDATA;
252
253 tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
254 if (!tc_str)
255 return AVERROR_INVALIDDATA;
256 ret = parse_cpl_tc_type(tc_str, comps);
257 xmlFree(tc_str);
258 if (ret)
259 return ret;
260
261 cpl->tc = av_malloc(sizeof(AVTimecode));
262 if (!cpl->tc)
263 return AVERROR(ENOMEM);
266 comps[0], comps[1], comps[2], comps[3],
267 NULL);
268
269 return ret;
270}
271
272static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
273{
274 xmlNodePtr element = NULL;
275
276 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate")))
277 return AVERROR_INVALIDDATA;
278
279 return ff_imf_xml_read_rational(element, &cpl->edit_rate);
280}
281
282static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
283{
284 xmlNodePtr element = NULL;
285
286 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id")))
287 return AVERROR_INVALIDDATA;
288
289 return ff_imf_xml_read_uuid(element, cpl->id_uuid);
290}
291
292static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
293{
294 xmlNodePtr element = NULL;
295 int ret = 0;
296
297 /* read Offset */
298 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset")))
299 return AVERROR_INVALIDDATA;
300
301 if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
302 return ret;
303
304 /* read Label and Scope */
305 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label")))
306 return AVERROR_INVALIDDATA;
307
308 if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1)))
309 return AVERROR_INVALIDDATA;
310
311 if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
312 marker->scope_utf8
313 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
314 if (!marker->scope_utf8)
315 return AVERROR(ENOMEM);
316 }
317
318 return ret;
319}
320
321static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem,
322 FFIMFBaseResource *resource, FFIMFCPL *cpl)
323{
324 xmlNodePtr element = NULL;
325 int ret = 0;
326
327 /* read EditRate */
328 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
329 resource->edit_rate = cpl->edit_rate;
330 } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
331 av_log(log_ctx, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
332 return ret;
333 }
334
335 /* read EntryPoint */
336 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
337 if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
338 av_log(log_ctx, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
339 return ret;
340 }
341 } else {
342 resource->entry_point = 0;
343 }
344
345 /* read IntrinsicDuration */
346 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
347 av_log(log_ctx, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
348 return AVERROR_INVALIDDATA;
349 }
350 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
351 av_log(log_ctx, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
352 return ret;
353 }
354 resource->duration -= resource->entry_point;
355
356 /* read SourceDuration */
357 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
358 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
359 av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
360 return ret;
361 }
362 }
363
364 /* read RepeatCount */
365 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
366 ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
367
368 return ret;
369}
370
371static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem,
372 FFIMFTrackFileResource *tf_resource,
373 FFIMFCPL *cpl)
374{
375 xmlNodePtr element = NULL;
376 int ret = 0;
377
378 if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
379 return ret;
380
381 /* read TrackFileId */
382 if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
383 if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
384 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
385 return ret;
386 }
387 } else {
388 av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
389 return AVERROR_INVALIDDATA;
390 }
391
392 return ret;
393}
394
395static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem,
396 FFIMFMarkerResource *marker_resource,
397 FFIMFCPL *cpl)
398{
399 xmlNodePtr element = NULL;
400 int ret = 0;
401
402 if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
403 return ret;
404
405 /* read markers */
406 element = xmlFirstElementChild(marker_resource_elem);
407 while (element) {
408 if (xmlStrcmp(element->name, "Marker") == 0) {
409 void *tmp;
410
411 if (marker_resource->marker_count == UINT32_MAX)
412 return AVERROR(ENOMEM);
413 tmp = av_realloc_array(marker_resource->markers,
414 marker_resource->marker_count + 1,
415 sizeof(FFIMFMarker));
416 if (!tmp)
417 return AVERROR(ENOMEM);
418 marker_resource->markers = tmp;
419
420 imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
421 ret = fill_marker(element,
422 &marker_resource->markers[marker_resource->marker_count]);
423 marker_resource->marker_count++;
424 if (ret) {
425 av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n");
426 return ret;
427 }
428 }
429
430 element = xmlNextElementSibling(element);
431 }
432
433 return ret;
434}
435
436static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
437{
438 int ret = 0;
439 AVUUID uuid;
440 xmlNodePtr resource_list_elem = NULL;
441 xmlNodePtr resource_elem = NULL;
442 xmlNodePtr track_id_elem = NULL;
443 unsigned long resource_elem_count;
444 void *tmp;
445
446 /* read TrackID element */
447 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
448 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
449 return AVERROR_INVALIDDATA;
450 }
451 if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
452 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
453 return AVERROR_INVALIDDATA;
454 }
455 av_log(log_ctx,
457 "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
458 AV_UUID_ARG(uuid));
459
460 /* create main marker virtual track if it does not exist */
461 if (!cpl->main_markers_track) {
463 if (!cpl->main_markers_track)
464 return AVERROR(ENOMEM);
467
468 } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
469 av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
470 return AVERROR_INVALIDDATA;
471 }
472
473 /* process resources */
474 resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
475 if (!resource_list_elem)
476 return 0;
477
478 resource_elem_count = xmlChildElementCount(resource_list_elem);
479 if (resource_elem_count > UINT32_MAX
480 || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
481 return AVERROR(ENOMEM);
483 cpl->main_markers_track->resource_count + resource_elem_count,
484 sizeof(FFIMFMarkerResource));
485 if (!tmp) {
486 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
487 return AVERROR(ENOMEM);
488 }
490
491 resource_elem = xmlFirstElementChild(resource_list_elem);
492 while (resource_elem) {
494 ret = fill_marker_resource(log_ctx, resource_elem,
496 cpl);
498 if (ret)
499 return ret;
500
501 resource_elem = xmlNextElementSibling(resource_elem);
502 }
503
504 return ret;
505}
506
507static int has_stereo_resources(xmlNodePtr element)
508{
509 if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
510 return 1;
511
512 element = xmlFirstElementChild(element);
513 while (element) {
514 if (has_stereo_resources(element))
515 return 1;
516
517 element = xmlNextElementSibling(element);
518 }
519
520 return 0;
521}
522
523static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
524{
525 int ret = 0;
526 AVUUID uuid;
527 xmlNodePtr resource_list_elem = NULL;
528 xmlNodePtr resource_elem = NULL;
529 xmlNodePtr track_id_elem = NULL;
530 unsigned long resource_elem_count;
532 void *tmp;
533
534 /* read TrackID element */
535 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
536 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
537 return AVERROR_INVALIDDATA;
538 }
539 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
540 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
541 return ret;
542 }
543 av_log(log_ctx,
545 "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
546 AV_UUID_ARG(uuid));
547
548 /* get the main audio virtual track corresponding to the sequence */
549 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
550 if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
551 vt = &cpl->main_audio_tracks[i];
552 break;
553 }
554 }
555
556 /* create a main audio virtual track if none exists for the sequence */
557 if (!vt) {
558 if (cpl->main_audio_track_count == UINT32_MAX)
559 return AVERROR(ENOMEM);
561 cpl->main_audio_track_count + 1,
563 if (!tmp)
564 return AVERROR(ENOMEM);
565
566 cpl->main_audio_tracks = tmp;
570 av_uuid_copy(vt->base.id_uuid, uuid);
571 }
572
573 /* process resources */
574 resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
575 if (!resource_list_elem)
576 return 0;
577
578 resource_elem_count = xmlChildElementCount(resource_list_elem);
579 if (resource_elem_count > UINT32_MAX
580 || vt->resource_count > UINT32_MAX - resource_elem_count)
581 return AVERROR(ENOMEM);
584 (vt->resource_count + resource_elem_count)
585 * sizeof(FFIMFTrackFileResource));
586 if (!tmp) {
587 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
588 return AVERROR(ENOMEM);
589 }
590 vt->resources = tmp;
591
592 resource_elem = xmlFirstElementChild(resource_list_elem);
593 while (resource_elem) {
595 ret = fill_trackfile_resource(log_ctx, resource_elem,
596 &vt->resources[vt->resource_count],
597 cpl);
598 if (ret)
599 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
600 else
601 vt->resource_count++;
602
603 resource_elem = xmlNextElementSibling(resource_elem);
604 }
605
606 return ret;
607}
608
609static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
610{
611 int ret = 0;
612 AVUUID uuid;
613 xmlNodePtr resource_list_elem = NULL;
614 xmlNodePtr resource_elem = NULL;
615 xmlNodePtr track_id_elem = NULL;
616 void *tmp;
617 unsigned long resource_elem_count;
618
619 /* skip stereoscopic resources */
620 if (has_stereo_resources(image_sequence_elem)) {
621 av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
623 }
624
625 /* read TrackId element*/
626 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
627 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
628 return AVERROR_INVALIDDATA;
629 }
630 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
631 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
632 return ret;
633 }
634
635 /* create main image virtual track if one does not exist */
636 if (!cpl->main_image_2d_track) {
638 if (!cpl->main_image_2d_track)
639 return AVERROR(ENOMEM);
642
643 } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
644 av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
645 return AVERROR_INVALIDDATA;
646 }
647 av_log(log_ctx,
649 "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
650 AV_UUID_ARG(uuid));
651
652 /* process resources */
653 resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
654 if (!resource_list_elem)
655 return 0;
656
657 resource_elem_count = xmlChildElementCount(resource_list_elem);
658 if (resource_elem_count > UINT32_MAX
659 || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
660 || (cpl->main_image_2d_track->resource_count + resource_elem_count)
661 > INT_MAX / sizeof(FFIMFTrackFileResource))
662 return AVERROR(ENOMEM);
665 (cpl->main_image_2d_track->resource_count + resource_elem_count)
666 * sizeof(FFIMFTrackFileResource));
667 if (!tmp) {
668 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
669 return AVERROR(ENOMEM);
670 }
672
673 resource_elem = xmlFirstElementChild(resource_list_elem);
674 while (resource_elem) {
677 ret = fill_trackfile_resource(log_ctx, resource_elem,
679 cpl);
680 if (ret)
681 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
682 else
684
685 resource_elem = xmlNextElementSibling(resource_elem);
686 }
687
688 return 0;
689}
690
691static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
692{
693 int ret = 0;
694 xmlNodePtr segment_list_elem = NULL;
695 xmlNodePtr segment_elem = NULL;
696 xmlNodePtr sequence_list_elem = NULL;
697 xmlNodePtr sequence_elem = NULL;
698
699 if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
700 av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n");
701 return AVERROR_INVALIDDATA;
702 }
703
704 /* process sequences */
705 segment_elem = xmlFirstElementChild(segment_list_elem);
706 while (segment_elem) {
707 av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
708
709 sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
710 if (sequence_list_elem) {
711
712 sequence_elem = xmlFirstElementChild(sequence_list_elem);
713 while (sequence_elem) {
714 if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
715 ret = push_marker_sequence(log_ctx, sequence_elem, cpl);
716
717 else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
718 ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl);
719
720 else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
721 ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl);
722
723 else
724 av_log(log_ctx,
726 "The following Sequence is not supported and is ignored: %s\n",
727 sequence_elem->name);
728
729 /* abort parsing only if memory error occurred */
730 if (ret == AVERROR(ENOMEM))
731 return ret;
732
733 sequence_elem = xmlNextElementSibling(sequence_elem);
734 }
735 }
736
737 segment_elem = xmlNextElementSibling(segment_elem);
738 }
739
740 return ret;
741}
742
743int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
744{
745 int ret = 0;
746 xmlNodePtr cpl_element = NULL;
747
748 *cpl = ff_imf_cpl_alloc();
749 if (!*cpl) {
750 ret = AVERROR(ENOMEM);
751 goto cleanup;
752 }
753
754 cpl_element = xmlDocGetRootElement(doc);
755 if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
756 av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
758 goto cleanup;
759 }
760
761 if ((ret = fill_content_title(cpl_element, *cpl))) {
762 av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n");
763 goto cleanup;
764 }
765 if ((ret = fill_id(cpl_element, *cpl))) {
766 av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
767 goto cleanup;
768 }
769 if ((ret = fill_edit_rate(cpl_element, *cpl))) {
770 av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
771 goto cleanup;
772 }
773 if ((ret = fill_timecode(cpl_element, *cpl))) {
774 av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n");
775 goto cleanup;
776 }
777 if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl)))
778 goto cleanup;
779
780cleanup:
781 if (*cpl && ret) {
782 ff_imf_cpl_free(*cpl);
783 *cpl = NULL;
784 }
785 return ret;
786}
787
788static void imf_marker_free(FFIMFMarker *marker)
789{
790 if (!marker)
791 return;
792 xmlFree(marker->label_utf8);
793 xmlFree(marker->scope_utf8);
794}
795
797{
798 if (!rsrc)
799 return;
800 for (uint32_t i = 0; i < rsrc->marker_count; i++)
801 imf_marker_free(&rsrc->markers[i]);
802 av_freep(&rsrc->markers);
803}
804
806{
807 if (!vt)
808 return;
809 for (uint32_t i = 0; i < vt->resource_count; i++)
811 av_freep(&vt->resources);
812}
813
815{
816 if (!vt)
817 return;
818 av_freep(&vt->resources);
819}
820
821static void imf_cpl_init(FFIMFCPL *cpl)
822{
823 av_uuid_nil(cpl->id_uuid);
825 cpl->edit_rate = av_make_q(0, 1);
826 cpl->tc = NULL;
829 cpl->main_audio_track_count = 0;
830 cpl->main_audio_tracks = NULL;
831}
832
834{
835 FFIMFCPL *cpl;
836
837 cpl = av_malloc(sizeof(FFIMFCPL));
838 if (!cpl)
839 return NULL;
840 imf_cpl_init(cpl);
841 return cpl;
842}
843
845{
846 if (!cpl)
847 return;
848
849 if (cpl->tc)
850 av_freep(&cpl->tc);
851
852 xmlFree(cpl->content_title_utf8);
853
855
856 if (cpl->main_markers_track)
858
860
861 if (cpl->main_image_2d_track)
863
864 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
866
867 if (cpl->main_audio_tracks)
869
870 av_freep(&cpl);
871}
872
873int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
874{
875 AVBPrint buf;
876 xmlDoc *doc = NULL;
877 int ret = 0;
878
879 av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
880
881 ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
882 if (ret < 0 || !avio_feof(in)) {
883 av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n");
884 if (ret == 0)
886 goto clean_up;
887 }
888
889 LIBXML_TEST_VERSION
890
891 doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
892 if (!doc) {
893 av_log(log_ctx,
895 "XML parsing failed when reading the IMF CPL\n");
897 goto clean_up;
898 }
899
900 if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) {
901 av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
902 } else {
903 av_log(log_ctx,
905 "IMF CPL ContentTitle: %s\n",
906 (*cpl)->content_title_utf8);
907 av_log(log_ctx,
909 "IMF CPL Id: " AV_PRI_UUID "\n",
910 AV_UUID_ARG((*cpl)->id_uuid));
911 }
912
913 xmlFreeDoc(doc);
914
915clean_up:
917
918 return ret;
919}
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition aviobuf.c:349
int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size)
Read contents of h into print buffer, up to max_size bytes, or up to EOF.
Definition aviobuf.c:1213
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:68
AVBPrint public header.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
error code definitions
double value
Definition eval.c:102
static av_cold void cleanup(FlashSV2Context *s)
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:234
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition error.h:64
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition rational.h:71
void * av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
Reallocate the given buffer if it is not large enough, otherwise do nothing.
Definition mem.c:601
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition mem.c:318
size_t static size_t av_strnlen(const char *s, size_t len)
Get the count of continuous non zero chars starting from the beginning.
Definition avstring.h:141
Public header file for the processing of Interoperable Master Format (IMF) packages.
xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
Returns the first child element with the specified local name.
Definition imf_cpl.c:59
static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
Definition imf_cpl.c:112
static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
Definition imf_cpl.c:156
static void imf_marker_free(FFIMFMarker *marker)
Definition imf_cpl.c:788
FFIMFCPL * ff_imf_cpl_alloc(void)
Allocates and initializes an FFIMFCPL data structure.
Definition imf_cpl.c:833
static void imf_cpl_init(FFIMFCPL *cpl)
Definition imf_cpl.c:821
static void imf_marker_init(FFIMFMarker *marker)
Definition imf_cpl.c:163
int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
Parse an IMF Composition Playlist document into the FFIMFCPL data structure.
Definition imf_cpl.c:873
static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:371
static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:395
static int has_stereo_resources(xmlNodePtr element)
Definition imf_cpl.c:507
static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
Definition imf_cpl.c:170
static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
Definition imf_cpl.c:796
static void imf_base_resource_init(FFIMFBaseResource *rsrc)
Definition imf_cpl.c:148
static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:609
static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:282
static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
Definition imf_cpl.c:133
int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
Parse an IMF CompositionPlaylist element into the FFIMFCPL data structure.
Definition imf_cpl.c:743
static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
Definition imf_cpl.c:140
int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
Reads an AVRational from an XML element.
Definition imf_cpl.c:88
static int parse_cpl_tc_type(const char *s, int *tc_comps)
Parses a string that conform to the TimecodeType used in IMF CPL and defined in SMPTE ST 2067-3.
Definition imf_cpl.c:208
static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
Definition imf_cpl.c:292
static int digit_to_int(char digit)
Definition imf_cpl.c:193
void ff_imf_cpl_free(FFIMFCPL *cpl)
Deletes an FFIMFCPL data structure previously instantiated with ff_imf_cpl_alloc().
Definition imf_cpl.c:844
static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:436
static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
Definition imf_cpl.c:814
static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
Definition imf_cpl.c:805
static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:176
static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
Definition imf_cpl.c:321
static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:523
int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
Reads an unsigned 32-bit integer from an XML element.
Definition imf_cpl.c:100
int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
Reads a UUID from an XML element.
Definition imf_cpl.c:73
static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
Definition imf_cpl.c:128
static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:229
static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:272
static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:691
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
Bytestream IO Context.
Definition avio.h:160
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
IMF Composition Playlist Base Resource.
Definition imf.h:68
AVRational edit_rate
BaseResourceType/EditRate.
Definition imf.h:69
uint32_t duration
BaseResourceType/Duration.
Definition imf.h:71
uint32_t repeat_count
BaseResourceType/RepeatCount.
Definition imf.h:72
uint32_t entry_point
BaseResourceType/EntryPoint.
Definition imf.h:70
IMF Composition Playlist Virtual Track.
Definition imf.h:104
AVUUID id_uuid
TrackId associated with the Virtual Track.
Definition imf.h:105
IMF Composition Playlist.
Definition imf.h:130
FFIMFMarkerVirtualTrack * main_markers_track
Main Marker Virtual Track.
Definition imf.h:135
AVRational edit_rate
CompositionPlaylist/EditRate element.
Definition imf.h:133
FFIMFTrackFileVirtualTrack * main_audio_tracks
Main Audio Virtual Tracks.
Definition imf.h:138
xmlChar * content_title_utf8
CompositionPlaylist/ContentTitle element.
Definition imf.h:132
uint32_t main_audio_track_count
Number of Main Audio Virtual Tracks.
Definition imf.h:137
FFIMFTrackFileVirtualTrack * main_image_2d_track
Main Image Virtual Track.
Definition imf.h:136
AVUUID id_uuid
CompositionPlaylist/Id element.
Definition imf.h:131
AVTimecode * tc
CompositionPlaylist/CompositionTimecode element.
Definition imf.h:134
IMF Composition Playlist Marker Resource.
Definition imf.h:95
uint32_t marker_count
Number of Marker elements.
Definition imf.h:97
FFIMFMarker * markers
Marker elements.
Definition imf.h:98
IMF Composition Playlist Virtual Track that consists of Marker Resources.
Definition imf.h:121
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition imf.h:123
FFIMFBaseVirtualTrack base
Definition imf.h:122
FFIMFMarkerResource * resources
Resource elements of the Virtual Track.
Definition imf.h:124
IMF Marker.
Definition imf.h:86
xmlChar * label_utf8
Marker/Label.
Definition imf.h:87
uint32_t offset
Marker/Offset.
Definition imf.h:89
xmlChar * scope_utf8
Marker/Label/@scope.
Definition imf.h:88
IMF Composition Playlist Track File Resource.
Definition imf.h:78
AVUUID track_file_uuid
TrackFileResourceType/TrackFileId.
Definition imf.h:80
IMF Composition Playlist Virtual Track that consists of Track File Resources.
Definition imf.h:111
FFIMFTrackFileResource * resources
Resource elements of the Virtual Track.
Definition imf.h:114
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition imf.h:113
unsigned int resources_alloc_sz
Size of the resources buffer.
Definition imf.h:115
FFIMFBaseVirtualTrack base
Definition imf.h:112
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
int av_timecode_init_from_components(AVTimecode *tc, AVRational rate, int flags, int hh, int mm, int ss, int ff, void *log_ctx)
Init a timecode struct from the passed timecode components.
Definition timecode.c:211
@ AV_TIMECODE_FLAG_DROPFRAME
timecode is drop frame
Definition timecode.h:36
int av_uuid_urn_parse(const char *in, AVUUID uu)
Parses a URN representation of a UUID, as specified at IETF RFC 4122, into an AVUUID.
Definition uuid.c:136
static void av_uuid_copy(AVUUID dest, const AVUUID src)
Copies the bytes of src into dest.
Definition uuid.h:130
static int av_uuid_equal(const AVUUID uu1, const AVUUID uu2)
Compares two UUIDs for equality.
Definition uuid.h:119
static void av_uuid_nil(AVUUID uu)
Sets a UUID to the nil UUID, i.e.
Definition uuid.h:141
#define AV_UUID_ARG(x)
Definition uuid.h:51
uint8_t AVUUID[AV_UUID_LEN]
Definition uuid.h:60
#define AV_PRI_UUID
Definition uuid.h:39
#define df(A, B)
Definition vf_xbr.c:91