• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ff_imf_xml_get_child_element_by_name(xmlNodePtr parent,const char * name_utf8)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 
ff_imf_xml_read_uuid(xmlNodePtr element,AVUUID uuid)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     if (!element_text)
80         return AVERROR_INVALIDDATA;
81     ret = av_uuid_urn_parse(element_text, uuid);
82     if (ret) {
83         av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n");
84         ret = AVERROR_INVALIDDATA;
85     }
86     xmlFree(element_text);
87 
88     return ret;
89 }
90 
ff_imf_xml_read_rational(xmlNodePtr element,AVRational * rational)91 int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
92 {
93     xmlChar *element_text = NULL;
94     int ret = 0;
95 
96     element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
97     if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) {
98         av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n");
99         ret = AVERROR_INVALIDDATA;
100     }
101     xmlFree(element_text);
102 
103     return ret;
104 }
105 
ff_imf_xml_read_uint32(xmlNodePtr element,uint32_t * number)106 int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
107 {
108     xmlChar *element_text = NULL;
109     int ret = 0;
110 
111     element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
112     if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1) {
113         av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer");
114         ret = AVERROR_INVALIDDATA;
115     }
116     xmlFree(element_text);
117 
118     return ret;
119 }
120 
imf_base_virtual_track_init(FFIMFBaseVirtualTrack * track)121 static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
122 {
123     memset(track->id_uuid, 0, sizeof(track->id_uuid));
124 }
125 
imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack * track)126 static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
127 {
128     imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
129     track->resource_count = 0;
130     track->resources = NULL;
131 }
132 
imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack * track)133 static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
134 {
135     imf_base_virtual_track_init((FFIMFBaseVirtualTrack *)track);
136     track->resource_count = 0;
137     track->resources_alloc_sz = 0;
138     track->resources = NULL;
139 }
140 
imf_base_resource_init(FFIMFBaseResource * rsrc)141 static void imf_base_resource_init(FFIMFBaseResource *rsrc)
142 {
143     rsrc->duration = 0;
144     rsrc->edit_rate = av_make_q(0, 1);
145     rsrc->entry_point = 0;
146     rsrc->repeat_count = 1;
147 }
148 
imf_marker_resource_init(FFIMFMarkerResource * rsrc)149 static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
150 {
151     imf_base_resource_init((FFIMFBaseResource *)rsrc);
152     rsrc->marker_count = 0;
153     rsrc->markers = NULL;
154 }
155 
imf_marker_init(FFIMFMarker * marker)156 static void imf_marker_init(FFIMFMarker *marker)
157 {
158     marker->label_utf8 = NULL;
159     marker->offset = 0;
160     marker->scope_utf8 = NULL;
161 }
162 
imf_trackfile_resource_init(FFIMFTrackFileResource * rsrc)163 static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
164 {
165     imf_base_resource_init((FFIMFBaseResource *)rsrc);
166     memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
167 }
168 
fill_content_title(xmlNodePtr cpl_element,FFIMFCPL * cpl)169 static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
170 {
171     xmlNodePtr element = NULL;
172 
173     if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) {
174         av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n");
175         return AVERROR_INVALIDDATA;
176     }
177     cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
178                                                    element->xmlChildrenNode,
179                                                    1);
180     if (!cpl->content_title_utf8)
181         cpl->content_title_utf8 = xmlStrdup("");
182     if (!cpl->content_title_utf8)
183         return AVERROR(ENOMEM);
184 
185     return 0;
186 }
187 
fill_edit_rate(xmlNodePtr cpl_element,FFIMFCPL * cpl)188 static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
189 {
190     xmlNodePtr element = NULL;
191 
192     if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) {
193         av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
194         return AVERROR_INVALIDDATA;
195     }
196 
197     return ff_imf_xml_read_rational(element, &cpl->edit_rate);
198 }
199 
fill_id(xmlNodePtr cpl_element,FFIMFCPL * cpl)200 static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
201 {
202     xmlNodePtr element = NULL;
203 
204     if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) {
205         av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
206         return AVERROR_INVALIDDATA;
207     }
208 
209     return ff_imf_xml_read_uuid(element, cpl->id_uuid);
210 }
211 
fill_marker(xmlNodePtr marker_elem,FFIMFMarker * marker)212 static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
213 {
214     xmlNodePtr element = NULL;
215     int ret = 0;
216 
217     /* read Offset */
218     if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) {
219         av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n");
220         return AVERROR_INVALIDDATA;
221     }
222     if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
223         return ret;
224 
225     /* read Label and Scope */
226     if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) {
227         av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n");
228         return AVERROR_INVALIDDATA;
229     }
230     if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) {
231         av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n");
232         return AVERROR_INVALIDDATA;
233     }
234     if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
235         marker->scope_utf8
236             = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
237         if (!marker->scope_utf8) {
238             xmlFree(marker->label_utf8);
239             return AVERROR(ENOMEM);
240         }
241     }
242 
243     return ret;
244 }
245 
fill_base_resource(xmlNodePtr resource_elem,FFIMFBaseResource * resource,FFIMFCPL * cpl)246 static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
247 {
248     xmlNodePtr element = NULL;
249     int ret = 0;
250 
251     /* read EditRate */
252     if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
253         resource->edit_rate = cpl->edit_rate;
254     } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
255         av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
256         return ret;
257     }
258 
259     /* read EntryPoint */
260     if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
261         if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
262             av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
263             return ret;
264         }
265     } else {
266         resource->entry_point = 0;
267     }
268 
269     /* read IntrinsicDuration */
270     if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
271         av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
272         return AVERROR_INVALIDDATA;
273     }
274     if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
275         av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
276         return ret;
277     }
278     resource->duration -= resource->entry_point;
279 
280     /* read SourceDuration */
281     if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
282         if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
283             av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
284             return ret;
285         }
286     }
287 
288     /* read RepeatCount */
289     if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
290         ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
291 
292     return ret;
293 }
294 
fill_trackfile_resource(xmlNodePtr tf_resource_elem,FFIMFTrackFileResource * tf_resource,FFIMFCPL * cpl)295 static int fill_trackfile_resource(xmlNodePtr tf_resource_elem,
296                                    FFIMFTrackFileResource *tf_resource,
297                                    FFIMFCPL *cpl)
298 {
299     xmlNodePtr element = NULL;
300     int ret = 0;
301 
302     if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
303         return ret;
304 
305     /* read TrackFileId */
306     if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
307         if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
308             av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
309             return ret;
310         }
311     } else {
312         av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
313         return AVERROR_INVALIDDATA;
314     }
315 
316     return ret;
317 }
318 
fill_marker_resource(xmlNodePtr marker_resource_elem,FFIMFMarkerResource * marker_resource,FFIMFCPL * cpl)319 static int fill_marker_resource(xmlNodePtr marker_resource_elem,
320                                 FFIMFMarkerResource *marker_resource,
321                                 FFIMFCPL *cpl)
322 {
323     xmlNodePtr element = NULL;
324     int ret = 0;
325 
326     if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
327         return ret;
328 
329     /* read markers */
330     element = xmlFirstElementChild(marker_resource_elem);
331     while (element) {
332         if (xmlStrcmp(element->name, "Marker") == 0) {
333             void *tmp;
334 
335             if (marker_resource->marker_count == UINT32_MAX)
336                 return AVERROR(ENOMEM);
337             tmp = av_realloc_array(marker_resource->markers,
338                                    marker_resource->marker_count + 1,
339                                    sizeof(FFIMFMarker));
340             if (!tmp)
341                 return AVERROR(ENOMEM);
342             marker_resource->markers = tmp;
343 
344             imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
345             ret = fill_marker(element,
346                               &marker_resource->markers[marker_resource->marker_count]);
347             marker_resource->marker_count++;
348             if (ret)
349                 return ret;
350         }
351 
352         element = xmlNextElementSibling(element);
353     }
354 
355     return ret;
356 }
357 
push_marker_sequence(xmlNodePtr marker_sequence_elem,FFIMFCPL * cpl)358 static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
359 {
360     int ret = 0;
361     AVUUID uuid;
362     xmlNodePtr resource_list_elem = NULL;
363     xmlNodePtr resource_elem = NULL;
364     xmlNodePtr track_id_elem = NULL;
365     unsigned long resource_elem_count;
366     void *tmp;
367 
368     /* read TrackID element */
369     if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
370         av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
371         return AVERROR_INVALIDDATA;
372     }
373     if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
374         av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
375         return AVERROR_INVALIDDATA;
376     }
377     av_log(NULL,
378            AV_LOG_DEBUG,
379            "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
380            AV_UUID_ARG(uuid));
381 
382     /* create main marker virtual track if it does not exist */
383     if (!cpl->main_markers_track) {
384         cpl->main_markers_track = av_malloc(sizeof(FFIMFMarkerVirtualTrack));
385         if (!cpl->main_markers_track)
386             return AVERROR(ENOMEM);
387         imf_marker_virtual_track_init(cpl->main_markers_track);
388         av_uuid_copy(cpl->main_markers_track->base.id_uuid, uuid);
389 
390     } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
391         av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
392         return AVERROR_INVALIDDATA;
393     }
394 
395     /* process resources */
396     resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
397     if (!resource_list_elem)
398         return 0;
399 
400     resource_elem_count = xmlChildElementCount(resource_list_elem);
401     if (resource_elem_count > UINT32_MAX
402         || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
403         return AVERROR(ENOMEM);
404     tmp = av_realloc_array(cpl->main_markers_track->resources,
405                            cpl->main_markers_track->resource_count + resource_elem_count,
406                            sizeof(FFIMFMarkerResource));
407     if (!tmp) {
408         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
409         return AVERROR(ENOMEM);
410     }
411     cpl->main_markers_track->resources = tmp;
412 
413     resource_elem = xmlFirstElementChild(resource_list_elem);
414     while (resource_elem) {
415         imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]);
416         ret = fill_marker_resource(resource_elem,
417                                    &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count],
418                                    cpl);
419         cpl->main_markers_track->resource_count++;
420         if (ret)
421             return ret;
422 
423         resource_elem = xmlNextElementSibling(resource_elem);
424     }
425 
426     return ret;
427 }
428 
has_stereo_resources(xmlNodePtr element)429 static int has_stereo_resources(xmlNodePtr element)
430 {
431     if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
432         return 1;
433 
434     element = xmlFirstElementChild(element);
435     while (element) {
436         if (has_stereo_resources(element))
437             return 1;
438 
439         element = xmlNextElementSibling(element);
440     }
441 
442     return 0;
443 }
444 
push_main_audio_sequence(xmlNodePtr audio_sequence_elem,FFIMFCPL * cpl)445 static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
446 {
447     int ret = 0;
448     AVUUID uuid;
449     xmlNodePtr resource_list_elem = NULL;
450     xmlNodePtr resource_elem = NULL;
451     xmlNodePtr track_id_elem = NULL;
452     unsigned long resource_elem_count;
453     FFIMFTrackFileVirtualTrack *vt = NULL;
454     void *tmp;
455 
456     /* read TrackID element */
457     if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
458         av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
459         return AVERROR_INVALIDDATA;
460     }
461     if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
462         av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
463         return ret;
464     }
465     av_log(NULL,
466            AV_LOG_DEBUG,
467            "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
468            AV_UUID_ARG(uuid));
469 
470     /* get the main audio virtual track corresponding to the sequence */
471     for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
472         if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
473             vt = &cpl->main_audio_tracks[i];
474             break;
475         }
476     }
477 
478     /* create a main audio virtual track if none exists for the sequence */
479     if (!vt) {
480         if (cpl->main_audio_track_count == UINT32_MAX)
481             return AVERROR(ENOMEM);
482         tmp = av_realloc_array(cpl->main_audio_tracks,
483                                cpl->main_audio_track_count + 1,
484                                sizeof(FFIMFTrackFileVirtualTrack));
485         if (!tmp)
486             return AVERROR(ENOMEM);
487 
488         cpl->main_audio_tracks = tmp;
489         vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
490         imf_trackfile_virtual_track_init(vt);
491         cpl->main_audio_track_count++;
492         av_uuid_copy(vt->base.id_uuid, uuid);
493     }
494 
495     /* process resources */
496     resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
497     if (!resource_list_elem)
498         return 0;
499 
500     resource_elem_count = xmlChildElementCount(resource_list_elem);
501     if (resource_elem_count > UINT32_MAX
502         || vt->resource_count > UINT32_MAX - resource_elem_count)
503         return AVERROR(ENOMEM);
504     tmp = av_fast_realloc(vt->resources,
505                           &vt->resources_alloc_sz,
506                           (vt->resource_count + resource_elem_count)
507                               * sizeof(FFIMFTrackFileResource));
508     if (!tmp) {
509         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
510         return AVERROR(ENOMEM);
511     }
512     vt->resources = tmp;
513 
514     resource_elem = xmlFirstElementChild(resource_list_elem);
515     while (resource_elem) {
516         imf_trackfile_resource_init(&vt->resources[vt->resource_count]);
517         ret = fill_trackfile_resource(resource_elem,
518                                       &vt->resources[vt->resource_count],
519                                       cpl);
520         if (ret)
521             av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
522         else
523             vt->resource_count++;
524 
525         resource_elem = xmlNextElementSibling(resource_elem);
526     }
527 
528     return ret;
529 }
530 
push_main_image_2d_sequence(xmlNodePtr image_sequence_elem,FFIMFCPL * cpl)531 static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
532 {
533     int ret = 0;
534     AVUUID uuid;
535     xmlNodePtr resource_list_elem = NULL;
536     xmlNodePtr resource_elem = NULL;
537     xmlNodePtr track_id_elem = NULL;
538     void *tmp;
539     unsigned long resource_elem_count;
540 
541     /* skip stereoscopic resources */
542     if (has_stereo_resources(image_sequence_elem)) {
543         av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
544         return AVERROR_PATCHWELCOME;
545     }
546 
547     /* read TrackId element*/
548     if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
549         av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
550         return AVERROR_INVALIDDATA;
551     }
552     if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
553         av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
554         return ret;
555     }
556 
557     /* create main image virtual track if one does not exist */
558     if (!cpl->main_image_2d_track) {
559         cpl->main_image_2d_track = av_malloc(sizeof(FFIMFTrackFileVirtualTrack));
560         if (!cpl->main_image_2d_track)
561             return AVERROR(ENOMEM);
562         imf_trackfile_virtual_track_init(cpl->main_image_2d_track);
563         av_uuid_copy(cpl->main_image_2d_track->base.id_uuid, uuid);
564 
565     } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
566         av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
567         return AVERROR_INVALIDDATA;
568     }
569     av_log(NULL,
570            AV_LOG_DEBUG,
571            "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
572            AV_UUID_ARG(uuid));
573 
574     /* process resources */
575     resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
576     if (!resource_list_elem)
577         return 0;
578 
579     resource_elem_count = xmlChildElementCount(resource_list_elem);
580     if (resource_elem_count > UINT32_MAX
581         || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
582         || (cpl->main_image_2d_track->resource_count + resource_elem_count)
583             > INT_MAX / sizeof(FFIMFTrackFileResource))
584         return AVERROR(ENOMEM);
585     tmp = av_fast_realloc(cpl->main_image_2d_track->resources,
586                           &cpl->main_image_2d_track->resources_alloc_sz,
587                           (cpl->main_image_2d_track->resource_count + resource_elem_count)
588                               * sizeof(FFIMFTrackFileResource));
589     if (!tmp) {
590         av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
591         return AVERROR(ENOMEM);
592     }
593     cpl->main_image_2d_track->resources = tmp;
594 
595     resource_elem = xmlFirstElementChild(resource_list_elem);
596     while (resource_elem) {
597         imf_trackfile_resource_init(
598             &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]);
599         ret = fill_trackfile_resource(resource_elem,
600                                       &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count],
601                                       cpl);
602         if (ret)
603             av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
604         else
605             cpl->main_image_2d_track->resource_count++;
606 
607         resource_elem = xmlNextElementSibling(resource_elem);
608     }
609 
610     return 0;
611 }
612 
fill_virtual_tracks(xmlNodePtr cpl_element,FFIMFCPL * cpl)613 static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
614 {
615     int ret = 0;
616     xmlNodePtr segment_list_elem = NULL;
617     xmlNodePtr segment_elem = NULL;
618     xmlNodePtr sequence_list_elem = NULL;
619     xmlNodePtr sequence_elem = NULL;
620 
621     if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
622         av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n");
623         return AVERROR_INVALIDDATA;
624     }
625 
626     /* process sequences */
627     segment_elem = xmlFirstElementChild(segment_list_elem);
628     while (segment_elem) {
629         av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
630 
631         sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
632         if (!segment_list_elem)
633             continue;
634 
635         sequence_elem = xmlFirstElementChild(sequence_list_elem);
636         while (sequence_elem) {
637             if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
638                 ret = push_marker_sequence(sequence_elem, cpl);
639 
640             else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
641                 ret = push_main_image_2d_sequence(sequence_elem, cpl);
642 
643             else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
644                 ret = push_main_audio_sequence(sequence_elem, cpl);
645 
646             else
647                 av_log(NULL,
648                        AV_LOG_INFO,
649                        "The following Sequence is not supported and is ignored: %s\n",
650                        sequence_elem->name);
651 
652             /* abort parsing only if memory error occurred */
653             if (ret == AVERROR(ENOMEM))
654                 return ret;
655 
656             sequence_elem = xmlNextElementSibling(sequence_elem);
657         }
658 
659         segment_elem = xmlNextElementSibling(segment_elem);
660     }
661 
662     return ret;
663 }
664 
ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc,FFIMFCPL ** cpl)665 int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
666 {
667     int ret = 0;
668     xmlNodePtr cpl_element = NULL;
669 
670     *cpl = ff_imf_cpl_alloc();
671     if (!*cpl) {
672         ret = AVERROR(ENOMEM);
673         goto cleanup;
674     }
675 
676     cpl_element = xmlDocGetRootElement(doc);
677     if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
678         av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
679         ret = AVERROR_INVALIDDATA;
680         goto cleanup;
681     }
682 
683     if ((ret = fill_content_title(cpl_element, *cpl)))
684         goto cleanup;
685     if ((ret = fill_id(cpl_element, *cpl)))
686         goto cleanup;
687     if ((ret = fill_edit_rate(cpl_element, *cpl)))
688         goto cleanup;
689     if ((ret = fill_virtual_tracks(cpl_element, *cpl)))
690         goto cleanup;
691 
692 cleanup:
693     if (*cpl && ret) {
694         ff_imf_cpl_free(*cpl);
695         *cpl = NULL;
696     }
697     return ret;
698 }
699 
imf_marker_free(FFIMFMarker * marker)700 static void imf_marker_free(FFIMFMarker *marker)
701 {
702     if (!marker)
703         return;
704     xmlFree(marker->label_utf8);
705     xmlFree(marker->scope_utf8);
706 }
707 
imf_marker_resource_free(FFIMFMarkerResource * rsrc)708 static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
709 {
710     if (!rsrc)
711         return;
712     for (uint32_t i = 0; i < rsrc->marker_count; i++)
713         imf_marker_free(&rsrc->markers[i]);
714     av_freep(&rsrc->markers);
715 }
716 
imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack * vt)717 static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
718 {
719     if (!vt)
720         return;
721     for (uint32_t i = 0; i < vt->resource_count; i++)
722         imf_marker_resource_free(&vt->resources[i]);
723     av_freep(&vt->resources);
724 }
725 
imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack * vt)726 static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
727 {
728     if (!vt)
729         return;
730     av_freep(&vt->resources);
731 }
732 
imf_cpl_init(FFIMFCPL * cpl)733 static void imf_cpl_init(FFIMFCPL *cpl)
734 {
735     av_uuid_nil(cpl->id_uuid);
736     cpl->content_title_utf8 = NULL;
737     cpl->edit_rate = av_make_q(0, 1);
738     cpl->main_markers_track = NULL;
739     cpl->main_image_2d_track = NULL;
740     cpl->main_audio_track_count = 0;
741     cpl->main_audio_tracks = NULL;
742 }
743 
ff_imf_cpl_alloc(void)744 FFIMFCPL *ff_imf_cpl_alloc(void)
745 {
746     FFIMFCPL *cpl;
747 
748     cpl = av_malloc(sizeof(FFIMFCPL));
749     if (!cpl)
750         return NULL;
751     imf_cpl_init(cpl);
752     return cpl;
753 }
754 
ff_imf_cpl_free(FFIMFCPL * cpl)755 void ff_imf_cpl_free(FFIMFCPL *cpl)
756 {
757     if (!cpl)
758         return;
759 
760     xmlFree(cpl->content_title_utf8);
761 
762     imf_marker_virtual_track_free(cpl->main_markers_track);
763 
764     if (cpl->main_markers_track)
765         av_freep(&cpl->main_markers_track);
766 
767     imf_trackfile_virtual_track_free(cpl->main_image_2d_track);
768 
769     if (cpl->main_image_2d_track)
770         av_freep(&cpl->main_image_2d_track);
771 
772     for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
773         imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]);
774 
775     if (cpl->main_audio_tracks)
776         av_freep(&cpl->main_audio_tracks);
777 
778     av_freep(&cpl);
779 }
780 
ff_imf_parse_cpl(AVIOContext * in,FFIMFCPL ** cpl)781 int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl)
782 {
783     AVBPrint buf;
784     xmlDoc *doc = NULL;
785     int ret = 0;
786 
787     av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
788 
789     ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
790     if (ret < 0 || !avio_feof(in)) {
791         av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n");
792         if (ret == 0)
793             ret = AVERROR_INVALIDDATA;
794         goto clean_up;
795     }
796 
797     LIBXML_TEST_VERSION
798 
799     doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
800     if (!doc) {
801         av_log(NULL,
802                 AV_LOG_ERROR,
803                 "XML parsing failed when reading the IMF CPL\n");
804         ret = AVERROR_INVALIDDATA;
805         goto clean_up;
806     }
807 
808     if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) {
809         av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
810     } else {
811         av_log(NULL,
812                 AV_LOG_INFO,
813                 "IMF CPL ContentTitle: %s\n",
814                 (*cpl)->content_title_utf8);
815         av_log(NULL,
816                 AV_LOG_INFO,
817                 "IMF CPL Id: " AV_PRI_UUID "\n",
818                 AV_UUID_ARG((*cpl)->id_uuid));
819     }
820 
821     xmlFreeDoc(doc);
822 
823 clean_up:
824     av_bprint_finalize(&buf, NULL);
825 
826     return ret;
827 }
828