Imported Upstream version 5.1.2
[platform/upstream/ffmpeg.git] / libavformat / imf_cpl.c
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 "libavformat/mxf.h"
55 #include "libavutil/bprint.h"
56 #include "libavutil/error.h"
57 #include <libxml/parser.h>
58
59 xmlNodePtr 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
73 int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
74 {
75     xmlChar *element_text = NULL;
76     int ret = 0;
77
78     element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
79     ret = av_uuid_urn_parse(element_text, uuid);
80     if (ret) {
81         av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n");
82         ret = AVERROR_INVALIDDATA;
83     }
84     xmlFree(element_text);
85
86     return ret;
87 }
88
89 int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
90 {
91     xmlChar *element_text = NULL;
92     int ret = 0;
93
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;
98     }
99     xmlFree(element_text);
100
101     return ret;
102 }
103
104 int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
105 {
106     xmlChar *element_text = NULL;
107     int ret = 0;
108
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;
113     }
114     xmlFree(element_text);
115
116     return ret;
117 }
118
119 static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
120 {
121     memset(track->id_uuid, 0, sizeof(track->id_uuid));
122 }
123
124 static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
125 {
126     imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
127     track->resource_count = 0;
128     track->resources = NULL;
129 }
130
131 static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
132 {
133     imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
134     track->resource_count = 0;
135     track->resources_alloc_sz = 0;
136     track->resources = NULL;
137 }
138
139 static void imf_base_resource_init(FFIMFBaseResource *rsrc)
140 {
141     rsrc->duration = 0;
142     rsrc->edit_rate = av_make_q(0, 1);
143     rsrc->entry_point = 0;
144     rsrc->repeat_count = 1;
145 }
146
147 static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
148 {
149     imf_base_resource_init((FFIMFBaseResource *)rsrc);
150     rsrc->marker_count = 0;
151     rsrc->markers = NULL;
152 }
153
154 static void imf_marker_init(FFIMFMarker *marker)
155 {
156     marker->label_utf8 = NULL;
157     marker->offset = 0;
158     marker->scope_utf8 = NULL;
159 }
160
161 static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
162 {
163     imf_base_resource_init((FFIMFBaseResource *)rsrc);
164     memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
165 }
166
167 static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
168 {
169     xmlNodePtr element = NULL;
170
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;
174     }
175     cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
176                                                    element->xmlChildrenNode,
177                                                    1);
178
179     return 0;
180 }
181
182 static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
183 {
184     xmlNodePtr element = NULL;
185
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;
189     }
190
191     return ff_imf_xml_read_rational(element, &cpl->edit_rate);
192 }
193
194 static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
195 {
196     xmlNodePtr element = NULL;
197
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;
201     }
202
203     return ff_imf_xml_read_uuid(element, cpl->id_uuid);
204 }
205
206 static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
207 {
208     xmlNodePtr element = NULL;
209     int ret = 0;
210
211     /* read Offset */
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;
215     }
216     if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
217         return ret;
218
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;
223     }
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;
227     }
228     if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
229         marker->scope_utf8
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);
234         }
235     }
236
237     return ret;
238 }
239
240 static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
241 {
242     xmlNodePtr element = NULL;
243     int ret = 0;
244
245     /* read EditRate */
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");
250         return ret;
251     }
252
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");
257             return ret;
258         }
259     } else {
260         resource->entry_point = 0;
261     }
262
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;
267     }
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");
270         return ret;
271     }
272     resource->duration -= resource->entry_point;
273
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");
278             return ret;
279         }
280     }
281
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);
285
286     return ret;
287 }
288
289 static int fill_trackfile_resource(xmlNodePtr tf_resource_elem,
290                                    FFIMFTrackFileResource *tf_resource,
291                                    FFIMFCPL *cpl)
292 {
293     xmlNodePtr element = NULL;
294     int ret = 0;
295
296     if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
297         return ret;
298
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");
303             return ret;
304         }
305     } else {
306         av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
307         return AVERROR_INVALIDDATA;
308     }
309
310     return ret;
311 }
312
313 static int fill_marker_resource(xmlNodePtr marker_resource_elem,
314                                 FFIMFMarkerResource *marker_resource,
315                                 FFIMFCPL *cpl)
316 {
317     xmlNodePtr element = NULL;
318     int ret = 0;
319
320     if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
321         return ret;
322
323     /* read markers */
324     element = xmlFirstElementChild(marker_resource_elem);
325     while (element) {
326         if (xmlStrcmp(element->name, "Marker") == 0) {
327             void *tmp;
328
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));
334             if (!tmp)
335                 return AVERROR(ENOMEM);
336             marker_resource->markers = tmp;
337
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++;
342             if (ret)
343                 return ret;
344         }
345
346         element = xmlNextElementSibling(element);
347     }
348
349     return ret;
350 }
351
352 static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
353 {
354     int ret = 0;
355     AVUUID uuid;
356     xmlNodePtr resource_list_elem = NULL;
357     xmlNodePtr resource_elem = NULL;
358     xmlNodePtr track_id_elem = NULL;
359     unsigned long resource_elem_count;
360     void *tmp;
361
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;
366     }
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;
370     }
371     av_log(NULL,
372            AV_LOG_DEBUG,
373            "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
374            AV_UUID_ARG(uuid));
375
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);
383
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;
387     }
388
389     /* process resources */
390     resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
391     if (!resource_list_elem)
392         return 0;
393
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));
401     if (!tmp) {
402         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
403         return AVERROR(ENOMEM);
404     }
405     cpl->main_markers_track->resources = tmp;
406
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],
412                                    cpl);
413         cpl->main_markers_track->resource_count++;
414         if (ret)
415             return ret;
416
417         resource_elem = xmlNextElementSibling(resource_elem);
418     }
419
420     return ret;
421 }
422
423 static int has_stereo_resources(xmlNodePtr element)
424 {
425     if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
426         return 1;
427
428     element = xmlFirstElementChild(element);
429     while (element) {
430         if (has_stereo_resources(element))
431             return 1;
432
433         element = xmlNextElementSibling(element);
434     }
435
436     return 0;
437 }
438
439 static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
440 {
441     int ret = 0;
442     AVUUID uuid;
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;
448     void *tmp;
449
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;
454     }
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");
457         return ret;
458     }
459     av_log(NULL,
460            AV_LOG_DEBUG,
461            "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
462            AV_UUID_ARG(uuid));
463
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];
468             break;
469         }
470     }
471
472     /* create a main audio virtual track if none exists for the sequence */
473     if (!vt) {
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));
479         if (!tmp)
480             return AVERROR(ENOMEM);
481
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);
487     }
488
489     /* process resources */
490     resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
491     if (!resource_list_elem)
492         return 0;
493
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));
502     if (!tmp) {
503         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
504         return AVERROR(ENOMEM);
505     }
506     vt->resources = tmp;
507
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],
513                                       cpl);
514         vt->resource_count++;
515         if (ret) {
516             av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
517             continue;
518         }
519
520         resource_elem = xmlNextElementSibling(resource_elem);
521     }
522
523     return ret;
524 }
525
526 static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
527 {
528     int ret = 0;
529     AVUUID uuid;
530     xmlNodePtr resource_list_elem = NULL;
531     xmlNodePtr resource_elem = NULL;
532     xmlNodePtr track_id_elem = NULL;
533     void *tmp;
534     unsigned long resource_elem_count;
535
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;
540     }
541
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;
546     }
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");
549         return ret;
550     }
551
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);
559
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;
563     }
564     av_log(NULL,
565            AV_LOG_DEBUG,
566            "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
567            AV_UUID_ARG(uuid));
568
569     /* process resources */
570     resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
571     if (!resource_list_elem)
572         return 0;
573
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));
584     if (!tmp) {
585         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
586         return AVERROR(ENOMEM);
587     }
588     cpl->main_image_2d_track->resources = tmp;
589
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],
596                                       cpl);
597         cpl->main_image_2d_track->resource_count++;
598         if (ret) {
599             av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
600             continue;
601         }
602
603         resource_elem = xmlNextElementSibling(resource_elem);
604     }
605
606     return 0;
607 }
608
609 static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
610 {
611     int ret = 0;
612     xmlNodePtr segment_list_elem = NULL;
613     xmlNodePtr segment_elem = NULL;
614     xmlNodePtr sequence_list_elem = NULL;
615     xmlNodePtr sequence_elem = NULL;
616
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;
620     }
621
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");
626
627         sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
628         if (!segment_list_elem)
629             continue;
630
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);
635
636             else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
637                 ret = push_main_image_2d_sequence(sequence_elem, cpl);
638
639             else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
640                 ret = push_main_audio_sequence(sequence_elem, cpl);
641
642             else
643                 av_log(NULL,
644                        AV_LOG_INFO,
645                        "The following Sequence is not supported and is ignored: %s\n",
646                        sequence_elem->name);
647
648             /* abort parsing only if memory error occurred */
649             if (ret == AVERROR(ENOMEM))
650                 return ret;
651
652             sequence_elem = xmlNextElementSibling(sequence_elem);
653         }
654
655         segment_elem = xmlNextElementSibling(segment_elem);
656     }
657
658     return ret;
659 }
660
661 int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
662 {
663     int ret = 0;
664     xmlNodePtr cpl_element = NULL;
665
666     *cpl = ff_imf_cpl_alloc();
667     if (!*cpl) {
668         ret = AVERROR(ENOMEM);
669         goto cleanup;
670     }
671
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;
676         goto cleanup;
677     }
678
679     if ((ret = fill_content_title(cpl_element, *cpl)))
680         goto cleanup;
681     if ((ret = fill_id(cpl_element, *cpl)))
682         goto cleanup;
683     if ((ret = fill_edit_rate(cpl_element, *cpl)))
684         goto cleanup;
685     if ((ret = fill_virtual_tracks(cpl_element, *cpl)))
686         goto cleanup;
687
688 cleanup:
689     if (*cpl && ret) {
690         ff_imf_cpl_free(*cpl);
691         *cpl = NULL;
692     }
693     return ret;
694 }
695
696 static void imf_marker_free(FFIMFMarker *marker)
697 {
698     if (!marker)
699         return;
700     xmlFree(marker->label_utf8);
701     xmlFree(marker->scope_utf8);
702 }
703
704 static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
705 {
706     if (!rsrc)
707         return;
708     for (uint32_t i = 0; i < rsrc->marker_count; i++)
709         imf_marker_free(&rsrc->markers[i]);
710     av_freep(&rsrc->markers);
711 }
712
713 static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
714 {
715     if (!vt)
716         return;
717     for (uint32_t i = 0; i < vt->resource_count; i++)
718         imf_marker_resource_free(&vt->resources[i]);
719     av_freep(&vt->resources);
720 }
721
722 static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
723 {
724     if (!vt)
725         return;
726     av_freep(&vt->resources);
727 }
728
729 static void imf_cpl_init(FFIMFCPL *cpl)
730 {
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;
738 }
739
740 FFIMFCPL *ff_imf_cpl_alloc(void)
741 {
742     FFIMFCPL *cpl;
743
744     cpl = av_malloc(sizeof(FFIMFCPL));
745     if (!cpl)
746         return NULL;
747     imf_cpl_init(cpl);
748     return cpl;
749 }
750
751 void ff_imf_cpl_free(FFIMFCPL *cpl)
752 {
753     if (!cpl)
754         return;
755
756     xmlFree(cpl->content_title_utf8);
757
758     imf_marker_virtual_track_free(cpl->main_markers_track);
759
760     if (cpl->main_markers_track)
761         av_freep(&cpl->main_markers_track);
762
763     imf_trackfile_virtual_track_free(cpl->main_image_2d_track);
764
765     if (cpl->main_image_2d_track)
766         av_freep(&cpl->main_image_2d_track);
767
768     for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
769         imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]);
770
771     if (cpl->main_audio_tracks)
772         av_freep(&cpl->main_audio_tracks);
773
774     av_freep(&cpl);
775 }
776
777 int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl)
778 {
779     AVBPrint buf;
780     xmlDoc *doc = NULL;
781     int ret = 0;
782
783     av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
784
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");
788         if (ret == 0)
789             ret = AVERROR_INVALIDDATA;
790         goto clean_up;
791     }
792
793     LIBXML_TEST_VERSION
794
795     doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
796     if (!doc) {
797         av_log(NULL,
798                 AV_LOG_ERROR,
799                 "XML parsing failed when reading the IMF CPL\n");
800         ret = AVERROR_INVALIDDATA;
801         goto clean_up;
802     }
803
804     if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) {
805         av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
806     } else {
807         av_log(NULL,
808                 AV_LOG_INFO,
809                 "IMF CPL ContentTitle: %s\n",
810                 (*cpl)->content_title_utf8);
811         av_log(NULL,
812                 AV_LOG_INFO,
813                 "IMF CPL Id: " AV_PRI_UUID "\n",
814                 AV_UUID_ARG((*cpl)->id_uuid));
815     }
816
817     xmlFreeDoc(doc);
818
819 clean_up:
820     av_bprint_finalize(&buf, NULL);
821
822     return ret;
823 }