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 if (resource->entry_point > resource->duration) {
355 av_log(log_ctx, AV_LOG_ERROR, "EntryPoint is beyond the IntrinsicDuration of a Resource\n");
356 return AVERROR_INVALIDDATA;
357 }
358 resource->duration -= resource->entry_point;
359
360 /* read SourceDuration */
361 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
362 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
363 av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
364 return ret;
365 }
366 }
367
368 /* read RepeatCount */
369 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
370 ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
371
372 return ret;
373}
374
375static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem,
376 FFIMFTrackFileResource *tf_resource,
377 FFIMFCPL *cpl)
378{
379 xmlNodePtr element = NULL;
380 int ret = 0;
381
382 if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
383 return ret;
384
385 /* read TrackFileId */
386 if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
387 if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
388 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
389 return ret;
390 }
391 } else {
392 av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
393 return AVERROR_INVALIDDATA;
394 }
395
396 return ret;
397}
398
399static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem,
400 FFIMFMarkerResource *marker_resource,
401 FFIMFCPL *cpl)
402{
403 xmlNodePtr element = NULL;
404 int ret = 0;
405
406 if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
407 return ret;
408
409 /* read markers */
410 element = xmlFirstElementChild(marker_resource_elem);
411 while (element) {
412 if (xmlStrcmp(element->name, "Marker") == 0) {
413 void *tmp;
414
415 if (marker_resource->marker_count == UINT32_MAX)
416 return AVERROR(ENOMEM);
417 tmp = av_realloc_array(marker_resource->markers,
418 marker_resource->marker_count + 1,
419 sizeof(FFIMFMarker));
420 if (!tmp)
421 return AVERROR(ENOMEM);
422 marker_resource->markers = tmp;
423
424 imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
425 ret = fill_marker(element,
426 &marker_resource->markers[marker_resource->marker_count]);
427 marker_resource->marker_count++;
428 if (ret) {
429 av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n");
430 return ret;
431 }
432 }
433
434 element = xmlNextElementSibling(element);
435 }
436
437 return ret;
438}
439
440static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
441{
442 int ret = 0;
443 AVUUID uuid;
444 xmlNodePtr resource_list_elem = NULL;
445 xmlNodePtr resource_elem = NULL;
446 xmlNodePtr track_id_elem = NULL;
447 unsigned long resource_elem_count;
448 void *tmp;
449
450 /* read TrackID element */
451 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
452 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
453 return AVERROR_INVALIDDATA;
454 }
455 if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
456 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
457 return AVERROR_INVALIDDATA;
458 }
459 av_log(log_ctx,
461 "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
462 AV_UUID_ARG(uuid));
463
464 /* create main marker virtual track if it does not exist */
465 if (!cpl->main_markers_track) {
467 if (!cpl->main_markers_track)
468 return AVERROR(ENOMEM);
471
472 } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
473 av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
474 return AVERROR_INVALIDDATA;
475 }
476
477 /* process resources */
478 resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
479 if (!resource_list_elem)
480 return 0;
481
482 resource_elem_count = xmlChildElementCount(resource_list_elem);
483 if (resource_elem_count > UINT32_MAX
484 || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
485 return AVERROR(ENOMEM);
487 cpl->main_markers_track->resource_count + resource_elem_count,
488 sizeof(FFIMFMarkerResource));
489 if (!tmp) {
490 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
491 return AVERROR(ENOMEM);
492 }
494
495 resource_elem = xmlFirstElementChild(resource_list_elem);
496 while (resource_elem) {
498 ret = fill_marker_resource(log_ctx, resource_elem,
500 cpl);
502 if (ret)
503 return ret;
504
505 resource_elem = xmlNextElementSibling(resource_elem);
506 }
507
508 return ret;
509}
510
511static int has_stereo_resources(xmlNodePtr element)
512{
513 if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
514 return 1;
515
516 element = xmlFirstElementChild(element);
517 while (element) {
518 if (has_stereo_resources(element))
519 return 1;
520
521 element = xmlNextElementSibling(element);
522 }
523
524 return 0;
525}
526
527static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
528{
529 int ret = 0;
530 AVUUID uuid;
531 xmlNodePtr resource_list_elem = NULL;
532 xmlNodePtr resource_elem = NULL;
533 xmlNodePtr track_id_elem = NULL;
534 unsigned long resource_elem_count;
536 void *tmp;
537
538 /* read TrackID element */
539 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
540 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
541 return AVERROR_INVALIDDATA;
542 }
543 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
544 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
545 return ret;
546 }
547 av_log(log_ctx,
549 "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
550 AV_UUID_ARG(uuid));
551
552 /* get the main audio virtual track corresponding to the sequence */
553 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
554 if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
555 vt = &cpl->main_audio_tracks[i];
556 break;
557 }
558 }
559
560 /* create a main audio virtual track if none exists for the sequence */
561 if (!vt) {
562 if (cpl->main_audio_track_count == UINT32_MAX)
563 return AVERROR(ENOMEM);
565 cpl->main_audio_track_count + 1,
567 if (!tmp)
568 return AVERROR(ENOMEM);
569
570 cpl->main_audio_tracks = tmp;
574 av_uuid_copy(vt->base.id_uuid, uuid);
575 }
576
577 /* process resources */
578 resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
579 if (!resource_list_elem)
580 return 0;
581
582 resource_elem_count = xmlChildElementCount(resource_list_elem);
583 if (resource_elem_count > UINT32_MAX
584 || vt->resource_count > UINT32_MAX - resource_elem_count)
585 return AVERROR(ENOMEM);
588 (vt->resource_count + resource_elem_count)
589 * sizeof(FFIMFTrackFileResource));
590 if (!tmp) {
591 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
592 return AVERROR(ENOMEM);
593 }
594 vt->resources = tmp;
595
596 resource_elem = xmlFirstElementChild(resource_list_elem);
597 while (resource_elem) {
599 ret = fill_trackfile_resource(log_ctx, resource_elem,
600 &vt->resources[vt->resource_count],
601 cpl);
602 if (ret)
603 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
604 else
605 vt->resource_count++;
606
607 resource_elem = xmlNextElementSibling(resource_elem);
608 }
609
610 return ret;
611}
612
613static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
614{
615 int ret = 0;
616 AVUUID uuid;
617 xmlNodePtr resource_list_elem = NULL;
618 xmlNodePtr resource_elem = NULL;
619 xmlNodePtr track_id_elem = NULL;
620 void *tmp;
621 unsigned long resource_elem_count;
622
623 /* skip stereoscopic resources */
624 if (has_stereo_resources(image_sequence_elem)) {
625 av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
627 }
628
629 /* read TrackId element*/
630 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
631 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
632 return AVERROR_INVALIDDATA;
633 }
634 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
635 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
636 return ret;
637 }
638
639 /* create main image virtual track if one does not exist */
640 if (!cpl->main_image_2d_track) {
642 if (!cpl->main_image_2d_track)
643 return AVERROR(ENOMEM);
646
647 } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
648 av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
649 return AVERROR_INVALIDDATA;
650 }
651 av_log(log_ctx,
653 "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
654 AV_UUID_ARG(uuid));
655
656 /* process resources */
657 resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
658 if (!resource_list_elem)
659 return 0;
660
661 resource_elem_count = xmlChildElementCount(resource_list_elem);
662 if (resource_elem_count > UINT32_MAX
663 || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
664 || (cpl->main_image_2d_track->resource_count + resource_elem_count)
665 > INT_MAX / sizeof(FFIMFTrackFileResource))
666 return AVERROR(ENOMEM);
669 (cpl->main_image_2d_track->resource_count + resource_elem_count)
670 * sizeof(FFIMFTrackFileResource));
671 if (!tmp) {
672 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
673 return AVERROR(ENOMEM);
674 }
676
677 resource_elem = xmlFirstElementChild(resource_list_elem);
678 while (resource_elem) {
681 ret = fill_trackfile_resource(log_ctx, resource_elem,
683 cpl);
684 if (ret)
685 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
686 else
688
689 resource_elem = xmlNextElementSibling(resource_elem);
690 }
691
692 return 0;
693}
694
695static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
696{
697 int ret = 0;
698 xmlNodePtr segment_list_elem = NULL;
699 xmlNodePtr segment_elem = NULL;
700 xmlNodePtr sequence_list_elem = NULL;
701 xmlNodePtr sequence_elem = NULL;
702
703 if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
704 av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n");
705 return AVERROR_INVALIDDATA;
706 }
707
708 /* process sequences */
709 segment_elem = xmlFirstElementChild(segment_list_elem);
710 while (segment_elem) {
711 av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
712
713 sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
714 if (sequence_list_elem) {
715
716 sequence_elem = xmlFirstElementChild(sequence_list_elem);
717 while (sequence_elem) {
718 if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
719 ret = push_marker_sequence(log_ctx, sequence_elem, cpl);
720
721 else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
722 ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl);
723
724 else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
725 ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl);
726
727 else
728 av_log(log_ctx,
730 "The following Sequence is not supported and is ignored: %s\n",
731 sequence_elem->name);
732
733 /* abort parsing only if memory error occurred */
734 if (ret == AVERROR(ENOMEM))
735 return ret;
736
737 sequence_elem = xmlNextElementSibling(sequence_elem);
738 }
739 }
740
741 segment_elem = xmlNextElementSibling(segment_elem);
742 }
743
744 return ret;
745}
746
747int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
748{
749 int ret = 0;
750 xmlNodePtr cpl_element = NULL;
751
752 *cpl = ff_imf_cpl_alloc();
753 if (!*cpl) {
754 ret = AVERROR(ENOMEM);
755 goto cleanup;
756 }
757
758 cpl_element = xmlDocGetRootElement(doc);
759 if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
760 av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
762 goto cleanup;
763 }
764
765 if ((ret = fill_content_title(cpl_element, *cpl))) {
766 av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n");
767 goto cleanup;
768 }
769 if ((ret = fill_id(cpl_element, *cpl))) {
770 av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
771 goto cleanup;
772 }
773 if ((ret = fill_edit_rate(cpl_element, *cpl))) {
774 av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
775 goto cleanup;
776 }
777 if ((ret = fill_timecode(cpl_element, *cpl))) {
778 av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n");
779 goto cleanup;
780 }
781 if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl)))
782 goto cleanup;
783
784cleanup:
785 if (*cpl && ret) {
786 ff_imf_cpl_free(*cpl);
787 *cpl = NULL;
788 }
789 return ret;
790}
791
792static void imf_marker_free(FFIMFMarker *marker)
793{
794 if (!marker)
795 return;
796 xmlFree(marker->label_utf8);
797 xmlFree(marker->scope_utf8);
798}
799
801{
802 if (!rsrc)
803 return;
804 for (uint32_t i = 0; i < rsrc->marker_count; i++)
805 imf_marker_free(&rsrc->markers[i]);
806 av_freep(&rsrc->markers);
807}
808
810{
811 if (!vt)
812 return;
813 for (uint32_t i = 0; i < vt->resource_count; i++)
815 av_freep(&vt->resources);
816}
817
819{
820 if (!vt)
821 return;
822 av_freep(&vt->resources);
823}
824
825static void imf_cpl_init(FFIMFCPL *cpl)
826{
827 av_uuid_nil(cpl->id_uuid);
829 cpl->edit_rate = av_make_q(0, 1);
830 cpl->tc = NULL;
833 cpl->main_audio_track_count = 0;
834 cpl->main_audio_tracks = NULL;
835}
836
838{
839 FFIMFCPL *cpl;
840
841 cpl = av_malloc(sizeof(FFIMFCPL));
842 if (!cpl)
843 return NULL;
844 imf_cpl_init(cpl);
845 return cpl;
846}
847
849{
850 if (!cpl)
851 return;
852
853 if (cpl->tc)
854 av_freep(&cpl->tc);
855
856 xmlFree(cpl->content_title_utf8);
857
859
860 if (cpl->main_markers_track)
862
864
865 if (cpl->main_image_2d_track)
867
868 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
870
871 if (cpl->main_audio_tracks)
873
874 av_freep(&cpl);
875}
876
877int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
878{
879 AVBPrint buf;
880 xmlDoc *doc = NULL;
881 int ret = 0;
882
883 av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
884
885 ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
886 if (ret < 0 || !avio_feof(in)) {
887 av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n");
888 if (ret == 0)
890 goto clean_up;
891 }
892
893 LIBXML_TEST_VERSION
894
895 doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
896 if (!doc) {
897 av_log(log_ctx,
899 "XML parsing failed when reading the IMF CPL\n");
901 goto clean_up;
902 }
903
904 if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) {
905 av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
906 } else {
907 av_log(log_ctx,
909 "IMF CPL ContentTitle: %s\n",
910 (*cpl)->content_title_utf8);
911 av_log(log_ctx,
913 "IMF CPL Id: " AV_PRI_UUID "\n",
914 AV_UUID_ARG((*cpl)->id_uuid));
915 }
916
917 xmlFreeDoc(doc);
918
919clean_up:
921
922 return ret;
923}
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:1222
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
int ret
Definition ffplay.c:391
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:792
FFIMFCPL * ff_imf_cpl_alloc(void)
Allocates and initializes an FFIMFCPL data structure.
Definition imf_cpl.c:837
static void imf_cpl_init(FFIMFCPL *cpl)
Definition imf_cpl.c:825
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:877
static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:375
static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:399
static int has_stereo_resources(xmlNodePtr element)
Definition imf_cpl.c:511
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:800
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:613
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:747
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:848
static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:440
static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
Definition imf_cpl.c:818
static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
Definition imf_cpl.c:809
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:527
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:695
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