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