2 * This file is part of FFmpeg.
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.
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.
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
21 * Copyright (c) Sandflow Consulting LLC
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions are met:
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.
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.
46 * Implements IMP CPL processing
48 * @author Pierre-Anthony Lemieux
54 #include "libavformat/mxf.h"
55 #include "libavutil/bprint.h"
56 #include "libavutil/error.h"
57 #include <libxml/parser.h>
59 xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
61 xmlNodePtr cur_element;
63 cur_element = xmlFirstElementChild(parent);
65 if (xmlStrcmp(cur_element->name, name_utf8) == 0)
68 cur_element = xmlNextElementSibling(cur_element);
73 int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
75 xmlChar *element_text = NULL;
78 element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
79 ret = av_uuid_urn_parse(element_text, uuid);
81 av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n");
82 ret = AVERROR_INVALIDDATA;
84 xmlFree(element_text);
89 int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
91 xmlChar *element_text = NULL;
94 element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
95 if (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) {
96 av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n");
97 ret = AVERROR_INVALIDDATA;
99 xmlFree(element_text);
104 int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
106 xmlChar *element_text = NULL;
109 element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
110 if (sscanf(element_text, "%" PRIu32, number) != 1) {
111 av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer");
112 ret = AVERROR_INVALIDDATA;
114 xmlFree(element_text);
119 static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
121 memset(track->id_uuid, 0, sizeof(track->id_uuid));
124 static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
126 imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
127 track->resource_count = 0;
128 track->resources = NULL;
131 static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
133 imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
134 track->resource_count = 0;
135 track->resources_alloc_sz = 0;
136 track->resources = NULL;
139 static void imf_base_resource_init(FFIMFBaseResource *rsrc)
142 rsrc->edit_rate = av_make_q(0, 1);
143 rsrc->entry_point = 0;
144 rsrc->repeat_count = 1;
147 static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
149 imf_base_resource_init((FFIMFBaseResource *)rsrc);
150 rsrc->marker_count = 0;
151 rsrc->markers = NULL;
154 static void imf_marker_init(FFIMFMarker *marker)
156 marker->label_utf8 = NULL;
158 marker->scope_utf8 = NULL;
161 static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
163 imf_base_resource_init((FFIMFBaseResource *)rsrc);
164 memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
167 static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
169 xmlNodePtr element = NULL;
171 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) {
172 av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n");
173 return AVERROR_INVALIDDATA;
175 cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
176 element->xmlChildrenNode,
182 static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
184 xmlNodePtr element = NULL;
186 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) {
187 av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
188 return AVERROR_INVALIDDATA;
191 return ff_imf_xml_read_rational(element, &cpl->edit_rate);
194 static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
196 xmlNodePtr element = NULL;
198 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) {
199 av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
200 return AVERROR_INVALIDDATA;
203 return ff_imf_xml_read_uuid(element, cpl->id_uuid);
206 static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
208 xmlNodePtr element = NULL;
212 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) {
213 av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n");
214 return AVERROR_INVALIDDATA;
216 if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
219 /* read Label and Scope */
220 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) {
221 av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n");
222 return AVERROR_INVALIDDATA;
224 if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) {
225 av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n");
226 return AVERROR_INVALIDDATA;
228 if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
230 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
231 if (!marker->scope_utf8) {
232 xmlFree(marker->label_utf8);
233 return AVERROR(ENOMEM);
240 static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
242 xmlNodePtr element = NULL;
246 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
247 resource->edit_rate = cpl->edit_rate;
248 } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
249 av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
253 /* read EntryPoint */
254 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
255 if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
256 av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
260 resource->entry_point = 0;
263 /* read IntrinsicDuration */
264 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
265 av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
266 return AVERROR_INVALIDDATA;
268 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
269 av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
272 resource->duration -= resource->entry_point;
274 /* read SourceDuration */
275 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
276 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
277 av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
282 /* read RepeatCount */
283 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
284 ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
289 static int fill_trackfile_resource(xmlNodePtr tf_resource_elem,
290 FFIMFTrackFileResource *tf_resource,
293 xmlNodePtr element = NULL;
296 if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
299 /* read TrackFileId */
300 if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
301 if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
302 av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
306 av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
307 return AVERROR_INVALIDDATA;
313 static int fill_marker_resource(xmlNodePtr marker_resource_elem,
314 FFIMFMarkerResource *marker_resource,
317 xmlNodePtr element = NULL;
320 if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
324 element = xmlFirstElementChild(marker_resource_elem);
326 if (xmlStrcmp(element->name, "Marker") == 0) {
329 if (marker_resource->marker_count == UINT32_MAX)
330 return AVERROR(ENOMEM);
331 tmp = av_realloc_array(marker_resource->markers,
332 marker_resource->marker_count + 1,
333 sizeof(FFIMFMarker));
335 return AVERROR(ENOMEM);
336 marker_resource->markers = tmp;
338 imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
339 ret = fill_marker(element,
340 &marker_resource->markers[marker_resource->marker_count]);
341 marker_resource->marker_count++;
346 element = xmlNextElementSibling(element);
352 static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
356 xmlNodePtr resource_list_elem = NULL;
357 xmlNodePtr resource_elem = NULL;
358 xmlNodePtr track_id_elem = NULL;
359 unsigned long resource_elem_count;
362 /* read TrackID element */
363 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
364 av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
365 return AVERROR_INVALIDDATA;
367 if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
368 av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
369 return AVERROR_INVALIDDATA;
373 "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
376 /* create main marker virtual track if it does not exist */
377 if (!cpl->main_markers_track) {
378 cpl->main_markers_track = av_malloc(sizeof(FFIMFMarkerVirtualTrack));
379 if (!cpl->main_markers_track)
380 return AVERROR(ENOMEM);
381 imf_marker_virtual_track_init(cpl->main_markers_track);
382 av_uuid_copy(cpl->main_markers_track->base.id_uuid, uuid);
384 } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
385 av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
386 return AVERROR_INVALIDDATA;
389 /* process resources */
390 resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
391 if (!resource_list_elem)
394 resource_elem_count = xmlChildElementCount(resource_list_elem);
395 if (resource_elem_count > UINT32_MAX
396 || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
397 return AVERROR(ENOMEM);
398 tmp = av_realloc_array(cpl->main_markers_track->resources,
399 cpl->main_markers_track->resource_count + resource_elem_count,
400 sizeof(FFIMFMarkerResource));
402 av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
403 return AVERROR(ENOMEM);
405 cpl->main_markers_track->resources = tmp;
407 resource_elem = xmlFirstElementChild(resource_list_elem);
408 while (resource_elem) {
409 imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]);
410 ret = fill_marker_resource(resource_elem,
411 &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count],
413 cpl->main_markers_track->resource_count++;
417 resource_elem = xmlNextElementSibling(resource_elem);
423 static int has_stereo_resources(xmlNodePtr element)
425 if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
428 element = xmlFirstElementChild(element);
430 if (has_stereo_resources(element))
433 element = xmlNextElementSibling(element);
439 static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
443 xmlNodePtr resource_list_elem = NULL;
444 xmlNodePtr resource_elem = NULL;
445 xmlNodePtr track_id_elem = NULL;
446 unsigned long resource_elem_count;
447 FFIMFTrackFileVirtualTrack *vt = NULL;
450 /* read TrackID element */
451 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
452 av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
453 return AVERROR_INVALIDDATA;
455 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
456 av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
461 "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
464 /* get the main audio virtual track corresponding to the sequence */
465 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
466 if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
467 vt = &cpl->main_audio_tracks[i];
472 /* create a main audio virtual track if none exists for the sequence */
474 if (cpl->main_audio_track_count == UINT32_MAX)
475 return AVERROR(ENOMEM);
476 tmp = av_realloc_array(cpl->main_audio_tracks,
477 cpl->main_audio_track_count + 1,
478 sizeof(FFIMFTrackFileVirtualTrack));
480 return AVERROR(ENOMEM);
482 cpl->main_audio_tracks = tmp;
483 vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
484 imf_trackfile_virtual_track_init(vt);
485 cpl->main_audio_track_count++;
486 av_uuid_copy(vt->base.id_uuid, uuid);
489 /* process resources */
490 resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
491 if (!resource_list_elem)
494 resource_elem_count = xmlChildElementCount(resource_list_elem);
495 if (resource_elem_count > UINT32_MAX
496 || vt->resource_count > UINT32_MAX - resource_elem_count)
497 return AVERROR(ENOMEM);
498 tmp = av_fast_realloc(vt->resources,
499 &vt->resources_alloc_sz,
500 (vt->resource_count + resource_elem_count)
501 * sizeof(FFIMFTrackFileResource));
503 av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
504 return AVERROR(ENOMEM);
508 resource_elem = xmlFirstElementChild(resource_list_elem);
509 while (resource_elem) {
510 imf_trackfile_resource_init(&vt->resources[vt->resource_count]);
511 ret = fill_trackfile_resource(resource_elem,
512 &vt->resources[vt->resource_count],
514 vt->resource_count++;
516 av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
520 resource_elem = xmlNextElementSibling(resource_elem);
526 static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
530 xmlNodePtr resource_list_elem = NULL;
531 xmlNodePtr resource_elem = NULL;
532 xmlNodePtr track_id_elem = NULL;
534 unsigned long resource_elem_count;
536 /* skip stereoscopic resources */
537 if (has_stereo_resources(image_sequence_elem)) {
538 av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
539 return AVERROR_PATCHWELCOME;
542 /* read TrackId element*/
543 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
544 av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
545 return AVERROR_INVALIDDATA;
547 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
548 av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
552 /* create main image virtual track if one does not exist */
553 if (!cpl->main_image_2d_track) {
554 cpl->main_image_2d_track = av_malloc(sizeof(FFIMFTrackFileVirtualTrack));
555 if (!cpl->main_image_2d_track)
556 return AVERROR(ENOMEM);
557 imf_trackfile_virtual_track_init(cpl->main_image_2d_track);
558 av_uuid_copy(cpl->main_image_2d_track->base.id_uuid, uuid);
560 } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
561 av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
562 return AVERROR_INVALIDDATA;
566 "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
569 /* process resources */
570 resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
571 if (!resource_list_elem)
574 resource_elem_count = xmlChildElementCount(resource_list_elem);
575 if (resource_elem_count > UINT32_MAX
576 || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
577 || (cpl->main_image_2d_track->resource_count + resource_elem_count)
578 > INT_MAX / sizeof(FFIMFTrackFileResource))
579 return AVERROR(ENOMEM);
580 tmp = av_fast_realloc(cpl->main_image_2d_track->resources,
581 &cpl->main_image_2d_track->resources_alloc_sz,
582 (cpl->main_image_2d_track->resource_count + resource_elem_count)
583 * sizeof(FFIMFTrackFileResource));
585 av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
586 return AVERROR(ENOMEM);
588 cpl->main_image_2d_track->resources = tmp;
590 resource_elem = xmlFirstElementChild(resource_list_elem);
591 while (resource_elem) {
592 imf_trackfile_resource_init(
593 &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]);
594 ret = fill_trackfile_resource(resource_elem,
595 &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count],
597 cpl->main_image_2d_track->resource_count++;
599 av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
603 resource_elem = xmlNextElementSibling(resource_elem);
609 static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
612 xmlNodePtr segment_list_elem = NULL;
613 xmlNodePtr segment_elem = NULL;
614 xmlNodePtr sequence_list_elem = NULL;
615 xmlNodePtr sequence_elem = NULL;
617 if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
618 av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n");
619 return AVERROR_INVALIDDATA;
622 /* process sequences */
623 segment_elem = xmlFirstElementChild(segment_list_elem);
624 while (segment_elem) {
625 av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
627 sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
628 if (!segment_list_elem)
631 sequence_elem = xmlFirstElementChild(sequence_list_elem);
632 while (sequence_elem) {
633 if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
634 ret = push_marker_sequence(sequence_elem, cpl);
636 else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
637 ret = push_main_image_2d_sequence(sequence_elem, cpl);
639 else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
640 ret = push_main_audio_sequence(sequence_elem, cpl);
645 "The following Sequence is not supported and is ignored: %s\n",
646 sequence_elem->name);
648 /* abort parsing only if memory error occurred */
649 if (ret == AVERROR(ENOMEM))
652 sequence_elem = xmlNextElementSibling(sequence_elem);
655 segment_elem = xmlNextElementSibling(segment_elem);
661 int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
664 xmlNodePtr cpl_element = NULL;
666 *cpl = ff_imf_cpl_alloc();
668 ret = AVERROR(ENOMEM);
672 cpl_element = xmlDocGetRootElement(doc);
673 if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
674 av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
675 ret = AVERROR_INVALIDDATA;
679 if ((ret = fill_content_title(cpl_element, *cpl)))
681 if ((ret = fill_id(cpl_element, *cpl)))
683 if ((ret = fill_edit_rate(cpl_element, *cpl)))
685 if ((ret = fill_virtual_tracks(cpl_element, *cpl)))
690 ff_imf_cpl_free(*cpl);
696 static void imf_marker_free(FFIMFMarker *marker)
700 xmlFree(marker->label_utf8);
701 xmlFree(marker->scope_utf8);
704 static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
708 for (uint32_t i = 0; i < rsrc->marker_count; i++)
709 imf_marker_free(&rsrc->markers[i]);
710 av_freep(&rsrc->markers);
713 static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
717 for (uint32_t i = 0; i < vt->resource_count; i++)
718 imf_marker_resource_free(&vt->resources[i]);
719 av_freep(&vt->resources);
722 static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
726 av_freep(&vt->resources);
729 static void imf_cpl_init(FFIMFCPL *cpl)
731 av_uuid_nil(cpl->id_uuid);
732 cpl->content_title_utf8 = NULL;
733 cpl->edit_rate = av_make_q(0, 1);
734 cpl->main_markers_track = NULL;
735 cpl->main_image_2d_track = NULL;
736 cpl->main_audio_track_count = 0;
737 cpl->main_audio_tracks = NULL;
740 FFIMFCPL *ff_imf_cpl_alloc(void)
744 cpl = av_malloc(sizeof(FFIMFCPL));
751 void ff_imf_cpl_free(FFIMFCPL *cpl)
756 xmlFree(cpl->content_title_utf8);
758 imf_marker_virtual_track_free(cpl->main_markers_track);
760 if (cpl->main_markers_track)
761 av_freep(&cpl->main_markers_track);
763 imf_trackfile_virtual_track_free(cpl->main_image_2d_track);
765 if (cpl->main_image_2d_track)
766 av_freep(&cpl->main_image_2d_track);
768 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
769 imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]);
771 if (cpl->main_audio_tracks)
772 av_freep(&cpl->main_audio_tracks);
777 int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl)
783 av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
785 ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
786 if (ret < 0 || !avio_feof(in)) {
787 av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n");
789 ret = AVERROR_INVALIDDATA;
795 doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
799 "XML parsing failed when reading the IMF CPL\n");
800 ret = AVERROR_INVALIDDATA;
804 if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) {
805 av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
809 "IMF CPL ContentTitle: %s\n",
810 (*cpl)->content_title_utf8);
813 "IMF CPL Id: " AV_PRI_UUID "\n",
814 AV_UUID_ARG((*cpl)->id_uuid));
820 av_bprint_finalize(&buf, NULL);