FFmpeg
Loading...
Searching...
No Matches
imf_cpl.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19/*
20 *
21 * Copyright (c) Sandflow Consulting LLC
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions are met:
25 *
26 * * Redistributions of source code must retain the above copyright notice, this
27 * list of conditions and the following disclaimer.
28 * * Redistributions in binary form must reproduce the above copyright notice,
29 * this list of conditions and the following disclaimer in the documentation
30 * and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
43 */
44
45/**
46 * Implements IMP CPL processing
47 *
48 * @author Pierre-Anthony Lemieux
49 * @file
50 * @ingroup lavu_imf
51 */
52
53#include "imf.h"
54#include "libavutil/bprint.h"
55#include "libavutil/error.h"
56#include "libavutil/mem.h"
57#include <libxml/parser.h>
58
59xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
60{
61 xmlNodePtr cur_element;
62
63 cur_element = xmlFirstElementChild(parent);
64 while (cur_element) {
65 if (xmlStrcmp(cur_element->name, name_utf8) == 0)
66 return cur_element;
67
68 cur_element = xmlNextElementSibling(cur_element);
69 }
70 return NULL;
71}
72
73int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
74{
75 int ret = 0;
76
77 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
78 if (!element_text)
80 ret = av_uuid_urn_parse(element_text, uuid);
81 if (ret)
83 xmlFree(element_text);
84
85 return ret;
86}
87
88int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
89{
90 int ret = 0;
91
92 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
93 if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2)
95 xmlFree(element_text);
96
97 return ret;
98}
99
100int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
101{
102 int ret = 0;
103
104 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
105 if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1)
107 xmlFree(element_text);
108
109 return ret;
110}
111
112static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
113{
114 int ret = 0;
115
116 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
117 if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0)
118 *value = 1;
119 else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0)
120 *value = 0;
121 else
122 ret = 1;
123 xmlFree(element_text);
124
125 return ret;
126}
127
129{
130 memset(track->id_uuid, 0, sizeof(track->id_uuid));
131}
132
139
147
149{
150 rsrc->duration = 0;
151 rsrc->edit_rate = av_make_q(0, 1);
152 rsrc->entry_point = 0;
153 rsrc->repeat_count = 1;
154}
155
157{
159 rsrc->marker_count = 0;
160 rsrc->markers = NULL;
161}
162
163static void imf_marker_init(FFIMFMarker *marker)
164{
165 marker->label_utf8 = NULL;
166 marker->offset = 0;
167 marker->scope_utf8 = NULL;
168}
169
171{
173 memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
174}
175
176static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
177{
178 xmlNodePtr element = NULL;
179
180 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle")))
181 return AVERROR_INVALIDDATA;
182 cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
183 element->xmlChildrenNode,
184 1);
185 if (!cpl->content_title_utf8)
186 cpl->content_title_utf8 = xmlStrdup("");
187 if (!cpl->content_title_utf8)
188 return AVERROR(ENOMEM);
189
190 return 0;
191}
192
193static int digit_to_int(char digit)
194{
195 if (digit >= '0' && digit <= '9')
196 return digit - '0';
197 return -1;
198}
199
200/**
201 * Parses a string that conform to the TimecodeType used in IMF CPL and defined
202 * in SMPTE ST 2067-3.
203 * @param[in] s string to parse
204 * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH,
205 * MM, SS and FF fields of the timecode are returned.
206 * @return 0 on success, < 0 AVERROR code on error.
207 */
208static int parse_cpl_tc_type(const char *s, int *tc_comps)
209{
210 if (av_strnlen(s, 11) != 11)
211 return AVERROR(EINVAL);
212
213 for (int i = 0; i < 4; i++) {
214 int hi;
215 int lo;
216
217 hi = digit_to_int(s[i * 3]);
218 lo = digit_to_int(s[i * 3 + 1]);
219
220 if (hi == -1 || lo == -1)
221 return AVERROR(EINVAL);
222
223 tc_comps[i] = 10 * hi + lo;
224 }
225
226 return 0;
227}
228
229static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
230{
231 xmlNodePtr tc_element = NULL;
232 xmlNodePtr element = NULL;
233 xmlChar *tc_str = NULL;
234 int df = 0;
235 int comps[4];
236 int ret = 0;
237
238 tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode");
239 if (!tc_element)
240 return 0;
241
242 element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame");
243 if (!element)
244 return AVERROR_INVALIDDATA;
245
246 if (ff_imf_xml_read_boolean(element, &df))
247 return AVERROR_INVALIDDATA;
248
249 element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress");
250 if (!element)
251 return AVERROR_INVALIDDATA;
252
253 tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
254 if (!tc_str)
255 return AVERROR_INVALIDDATA;
256 ret = parse_cpl_tc_type(tc_str, comps);
257 xmlFree(tc_str);
258 if (ret)
259 return ret;
260
261 cpl->tc = av_malloc(sizeof(AVTimecode));
262 if (!cpl->tc)
263 return AVERROR(ENOMEM);
266 comps[0], comps[1], comps[2], comps[3],
267 NULL);
268
269 return ret;
270}
271
272static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
273{
274 xmlNodePtr element = NULL;
275
276 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate")))
277 return AVERROR_INVALIDDATA;
278
279 return ff_imf_xml_read_rational(element, &cpl->edit_rate);
280}
281
282static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
283{
284 xmlNodePtr element = NULL;
285
286 if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id")))
287 return AVERROR_INVALIDDATA;
288
289 return ff_imf_xml_read_uuid(element, cpl->id_uuid);
290}
291
292static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
293{
294 xmlNodePtr element = NULL;
295 int ret = 0;
296
297 /* read Offset */
298 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset")))
299 return AVERROR_INVALIDDATA;
300
301 if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
302 return ret;
303
304 /* read Label and Scope */
305 if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label")))
306 return AVERROR_INVALIDDATA;
307
308 if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1)))
309 return AVERROR_INVALIDDATA;
310
311 if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
312 marker->scope_utf8
313 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
314 if (!marker->scope_utf8) {
315 xmlFree(marker->label_utf8);
316 return AVERROR(ENOMEM);
317 }
318 }
319
320 return ret;
321}
322
323static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem,
324 FFIMFBaseResource *resource, FFIMFCPL *cpl)
325{
326 xmlNodePtr element = NULL;
327 int ret = 0;
328
329 /* read EditRate */
330 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
331 resource->edit_rate = cpl->edit_rate;
332 } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
333 av_log(log_ctx, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
334 return ret;
335 }
336
337 /* read EntryPoint */
338 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
339 if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
340 av_log(log_ctx, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
341 return ret;
342 }
343 } else {
344 resource->entry_point = 0;
345 }
346
347 /* read IntrinsicDuration */
348 if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
349 av_log(log_ctx, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
350 return AVERROR_INVALIDDATA;
351 }
352 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
353 av_log(log_ctx, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
354 return ret;
355 }
356 resource->duration -= resource->entry_point;
357
358 /* read SourceDuration */
359 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
360 if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
361 av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
362 return ret;
363 }
364 }
365
366 /* read RepeatCount */
367 if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
368 ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
369
370 return ret;
371}
372
373static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem,
374 FFIMFTrackFileResource *tf_resource,
375 FFIMFCPL *cpl)
376{
377 xmlNodePtr element = NULL;
378 int ret = 0;
379
380 if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
381 return ret;
382
383 /* read TrackFileId */
384 if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
385 if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
386 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
387 return ret;
388 }
389 } else {
390 av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
391 return AVERROR_INVALIDDATA;
392 }
393
394 return ret;
395}
396
397static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem,
398 FFIMFMarkerResource *marker_resource,
399 FFIMFCPL *cpl)
400{
401 xmlNodePtr element = NULL;
402 int ret = 0;
403
404 if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
405 return ret;
406
407 /* read markers */
408 element = xmlFirstElementChild(marker_resource_elem);
409 while (element) {
410 if (xmlStrcmp(element->name, "Marker") == 0) {
411 void *tmp;
412
413 if (marker_resource->marker_count == UINT32_MAX)
414 return AVERROR(ENOMEM);
415 tmp = av_realloc_array(marker_resource->markers,
416 marker_resource->marker_count + 1,
417 sizeof(FFIMFMarker));
418 if (!tmp)
419 return AVERROR(ENOMEM);
420 marker_resource->markers = tmp;
421
422 imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
423 ret = fill_marker(element,
424 &marker_resource->markers[marker_resource->marker_count]);
425 marker_resource->marker_count++;
426 if (ret) {
427 av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n");
428 return ret;
429 }
430 }
431
432 element = xmlNextElementSibling(element);
433 }
434
435 return ret;
436}
437
438static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
439{
440 int ret = 0;
441 AVUUID uuid;
442 xmlNodePtr resource_list_elem = NULL;
443 xmlNodePtr resource_elem = NULL;
444 xmlNodePtr track_id_elem = NULL;
445 unsigned long resource_elem_count;
446 void *tmp;
447
448 /* read TrackID element */
449 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
450 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
451 return AVERROR_INVALIDDATA;
452 }
453 if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
454 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
455 return AVERROR_INVALIDDATA;
456 }
457 av_log(log_ctx,
459 "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
460 AV_UUID_ARG(uuid));
461
462 /* create main marker virtual track if it does not exist */
463 if (!cpl->main_markers_track) {
465 if (!cpl->main_markers_track)
466 return AVERROR(ENOMEM);
469
470 } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
471 av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
472 return AVERROR_INVALIDDATA;
473 }
474
475 /* process resources */
476 resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
477 if (!resource_list_elem)
478 return 0;
479
480 resource_elem_count = xmlChildElementCount(resource_list_elem);
481 if (resource_elem_count > UINT32_MAX
482 || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
483 return AVERROR(ENOMEM);
485 cpl->main_markers_track->resource_count + resource_elem_count,
486 sizeof(FFIMFMarkerResource));
487 if (!tmp) {
488 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
489 return AVERROR(ENOMEM);
490 }
492
493 resource_elem = xmlFirstElementChild(resource_list_elem);
494 while (resource_elem) {
496 ret = fill_marker_resource(log_ctx, resource_elem,
498 cpl);
500 if (ret)
501 return ret;
502
503 resource_elem = xmlNextElementSibling(resource_elem);
504 }
505
506 return ret;
507}
508
509static int has_stereo_resources(xmlNodePtr element)
510{
511 if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
512 return 1;
513
514 element = xmlFirstElementChild(element);
515 while (element) {
516 if (has_stereo_resources(element))
517 return 1;
518
519 element = xmlNextElementSibling(element);
520 }
521
522 return 0;
523}
524
525static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
526{
527 int ret = 0;
528 AVUUID uuid;
529 xmlNodePtr resource_list_elem = NULL;
530 xmlNodePtr resource_elem = NULL;
531 xmlNodePtr track_id_elem = NULL;
532 unsigned long resource_elem_count;
534 void *tmp;
535
536 /* read TrackID element */
537 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
538 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
539 return AVERROR_INVALIDDATA;
540 }
541 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
542 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
543 return ret;
544 }
545 av_log(log_ctx,
547 "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
548 AV_UUID_ARG(uuid));
549
550 /* get the main audio virtual track corresponding to the sequence */
551 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
552 if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
553 vt = &cpl->main_audio_tracks[i];
554 break;
555 }
556 }
557
558 /* create a main audio virtual track if none exists for the sequence */
559 if (!vt) {
560 if (cpl->main_audio_track_count == UINT32_MAX)
561 return AVERROR(ENOMEM);
563 cpl->main_audio_track_count + 1,
565 if (!tmp)
566 return AVERROR(ENOMEM);
567
568 cpl->main_audio_tracks = tmp;
572 av_uuid_copy(vt->base.id_uuid, uuid);
573 }
574
575 /* process resources */
576 resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
577 if (!resource_list_elem)
578 return 0;
579
580 resource_elem_count = xmlChildElementCount(resource_list_elem);
581 if (resource_elem_count > UINT32_MAX
582 || vt->resource_count > UINT32_MAX - resource_elem_count)
583 return AVERROR(ENOMEM);
586 (vt->resource_count + resource_elem_count)
587 * sizeof(FFIMFTrackFileResource));
588 if (!tmp) {
589 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
590 return AVERROR(ENOMEM);
591 }
592 vt->resources = tmp;
593
594 resource_elem = xmlFirstElementChild(resource_list_elem);
595 while (resource_elem) {
597 ret = fill_trackfile_resource(log_ctx, resource_elem,
598 &vt->resources[vt->resource_count],
599 cpl);
600 if (ret)
601 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
602 else
603 vt->resource_count++;
604
605 resource_elem = xmlNextElementSibling(resource_elem);
606 }
607
608 return ret;
609}
610
611static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
612{
613 int ret = 0;
614 AVUUID uuid;
615 xmlNodePtr resource_list_elem = NULL;
616 xmlNodePtr resource_elem = NULL;
617 xmlNodePtr track_id_elem = NULL;
618 void *tmp;
619 unsigned long resource_elem_count;
620
621 /* skip stereoscopic resources */
622 if (has_stereo_resources(image_sequence_elem)) {
623 av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
625 }
626
627 /* read TrackId element*/
628 if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
629 av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
630 return AVERROR_INVALIDDATA;
631 }
632 if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
633 av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
634 return ret;
635 }
636
637 /* create main image virtual track if one does not exist */
638 if (!cpl->main_image_2d_track) {
640 if (!cpl->main_image_2d_track)
641 return AVERROR(ENOMEM);
644
645 } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
646 av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
647 return AVERROR_INVALIDDATA;
648 }
649 av_log(log_ctx,
651 "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
652 AV_UUID_ARG(uuid));
653
654 /* process resources */
655 resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
656 if (!resource_list_elem)
657 return 0;
658
659 resource_elem_count = xmlChildElementCount(resource_list_elem);
660 if (resource_elem_count > UINT32_MAX
661 || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
662 || (cpl->main_image_2d_track->resource_count + resource_elem_count)
663 > INT_MAX / sizeof(FFIMFTrackFileResource))
664 return AVERROR(ENOMEM);
667 (cpl->main_image_2d_track->resource_count + resource_elem_count)
668 * sizeof(FFIMFTrackFileResource));
669 if (!tmp) {
670 av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
671 return AVERROR(ENOMEM);
672 }
674
675 resource_elem = xmlFirstElementChild(resource_list_elem);
676 while (resource_elem) {
679 ret = fill_trackfile_resource(log_ctx, resource_elem,
681 cpl);
682 if (ret)
683 av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
684 else
686
687 resource_elem = xmlNextElementSibling(resource_elem);
688 }
689
690 return 0;
691}
692
693static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
694{
695 int ret = 0;
696 xmlNodePtr segment_list_elem = NULL;
697 xmlNodePtr segment_elem = NULL;
698 xmlNodePtr sequence_list_elem = NULL;
699 xmlNodePtr sequence_elem = NULL;
700
701 if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
702 av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n");
703 return AVERROR_INVALIDDATA;
704 }
705
706 /* process sequences */
707 segment_elem = xmlFirstElementChild(segment_list_elem);
708 while (segment_elem) {
709 av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
710
711 sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
712 if (sequence_list_elem) {
713
714 sequence_elem = xmlFirstElementChild(sequence_list_elem);
715 while (sequence_elem) {
716 if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
717 ret = push_marker_sequence(log_ctx, sequence_elem, cpl);
718
719 else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
720 ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl);
721
722 else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
723 ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl);
724
725 else
726 av_log(log_ctx,
728 "The following Sequence is not supported and is ignored: %s\n",
729 sequence_elem->name);
730
731 /* abort parsing only if memory error occurred */
732 if (ret == AVERROR(ENOMEM))
733 return ret;
734
735 sequence_elem = xmlNextElementSibling(sequence_elem);
736 }
737 }
738
739 segment_elem = xmlNextElementSibling(segment_elem);
740 }
741
742 return ret;
743}
744
745int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
746{
747 int ret = 0;
748 xmlNodePtr cpl_element = NULL;
749
750 *cpl = ff_imf_cpl_alloc();
751 if (!*cpl) {
752 ret = AVERROR(ENOMEM);
753 goto cleanup;
754 }
755
756 cpl_element = xmlDocGetRootElement(doc);
757 if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
758 av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
760 goto cleanup;
761 }
762
763 if ((ret = fill_content_title(cpl_element, *cpl))) {
764 av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n");
765 goto cleanup;
766 }
767 if ((ret = fill_id(cpl_element, *cpl))) {
768 av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
769 goto cleanup;
770 }
771 if ((ret = fill_edit_rate(cpl_element, *cpl))) {
772 av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
773 goto cleanup;
774 }
775 if ((ret = fill_timecode(cpl_element, *cpl))) {
776 av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n");
777 goto cleanup;
778 }
779 if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl)))
780 goto cleanup;
781
782cleanup:
783 if (*cpl && ret) {
784 ff_imf_cpl_free(*cpl);
785 *cpl = NULL;
786 }
787 return ret;
788}
789
790static void imf_marker_free(FFIMFMarker *marker)
791{
792 if (!marker)
793 return;
794 xmlFree(marker->label_utf8);
795 xmlFree(marker->scope_utf8);
796}
797
799{
800 if (!rsrc)
801 return;
802 for (uint32_t i = 0; i < rsrc->marker_count; i++)
803 imf_marker_free(&rsrc->markers[i]);
804 av_freep(&rsrc->markers);
805}
806
808{
809 if (!vt)
810 return;
811 for (uint32_t i = 0; i < vt->resource_count; i++)
813 av_freep(&vt->resources);
814}
815
817{
818 if (!vt)
819 return;
820 av_freep(&vt->resources);
821}
822
823static void imf_cpl_init(FFIMFCPL *cpl)
824{
825 av_uuid_nil(cpl->id_uuid);
827 cpl->edit_rate = av_make_q(0, 1);
828 cpl->tc = NULL;
831 cpl->main_audio_track_count = 0;
832 cpl->main_audio_tracks = NULL;
833}
834
836{
837 FFIMFCPL *cpl;
838
839 cpl = av_malloc(sizeof(FFIMFCPL));
840 if (!cpl)
841 return NULL;
842 imf_cpl_init(cpl);
843 return cpl;
844}
845
847{
848 if (!cpl)
849 return;
850
851 if (cpl->tc)
852 av_freep(&cpl->tc);
853
854 xmlFree(cpl->content_title_utf8);
855
857
858 if (cpl->main_markers_track)
860
862
863 if (cpl->main_image_2d_track)
865
866 for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
868
869 if (cpl->main_audio_tracks)
871
872 av_freep(&cpl);
873}
874
875int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
876{
877 AVBPrint buf;
878 xmlDoc *doc = NULL;
879 int ret = 0;
880
881 av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
882
883 ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
884 if (ret < 0 || !avio_feof(in)) {
885 av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n");
886 if (ret == 0)
888 goto clean_up;
889 }
890
891 LIBXML_TEST_VERSION
892
893 doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
894 if (!doc) {
895 av_log(log_ctx,
897 "XML parsing failed when reading the IMF CPL\n");
899 goto clean_up;
900 }
901
902 if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) {
903 av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
904 } else {
905 av_log(log_ctx,
907 "IMF CPL ContentTitle: %s\n",
908 (*cpl)->content_title_utf8);
909 av_log(log_ctx,
911 "IMF CPL Id: " AV_PRI_UUID "\n",
912 AV_UUID_ARG((*cpl)->id_uuid));
913 }
914
915 xmlFreeDoc(doc);
916
917clean_up:
919
920 return ret;
921}
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition aviobuf.c:349
int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size)
Read contents of h into print buffer, up to max_size bytes, or up to EOF.
Definition aviobuf.c:1254
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:69
AVBPrint public header.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
error code definitions
double value
Definition eval.c:102
static av_cold void cleanup(FlashSV2Context *s)
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:235
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition error.h:64
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition rational.h:71
void * av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
Reallocate the given buffer if it is not large enough, otherwise do nothing.
Definition mem.c:495
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition mem.c:217
size_t static size_t av_strnlen(const char *s, size_t len)
Get the count of continuous non zero chars starting from the beginning.
Definition avstring.h:141
Public header file for the processing of Interoperable Master Format (IMF) packages.
xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
Returns the first child element with the specified local name.
Definition imf_cpl.c:59
static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
Definition imf_cpl.c:112
static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
Definition imf_cpl.c:156
static void imf_marker_free(FFIMFMarker *marker)
Definition imf_cpl.c:790
FFIMFCPL * ff_imf_cpl_alloc(void)
Allocates and initializes an FFIMFCPL data structure.
Definition imf_cpl.c:835
static void imf_cpl_init(FFIMFCPL *cpl)
Definition imf_cpl.c:823
static void imf_marker_init(FFIMFMarker *marker)
Definition imf_cpl.c:163
int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
Parse an IMF Composition Playlist document into the FFIMFCPL data structure.
Definition imf_cpl.c:875
static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:373
static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition imf_cpl.c:397
static int has_stereo_resources(xmlNodePtr element)
Definition imf_cpl.c:509
static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
Definition imf_cpl.c:170
static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
Definition imf_cpl.c:798
static void imf_base_resource_init(FFIMFBaseResource *rsrc)
Definition imf_cpl.c:148
static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:611
static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:282
static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
Definition imf_cpl.c:133
int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
Parse an IMF CompositionPlaylist element into the FFIMFCPL data structure.
Definition imf_cpl.c:745
static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
Definition imf_cpl.c:140
int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
Reads an AVRational from an XML element.
Definition imf_cpl.c:88
static int parse_cpl_tc_type(const char *s, int *tc_comps)
Parses a string that conform to the TimecodeType used in IMF CPL and defined in SMPTE ST 2067-3.
Definition imf_cpl.c:208
static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
Definition imf_cpl.c:292
static int digit_to_int(char digit)
Definition imf_cpl.c:193
void ff_imf_cpl_free(FFIMFCPL *cpl)
Deletes an FFIMFCPL data structure previously instantiated with ff_imf_cpl_alloc().
Definition imf_cpl.c:846
static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:438
static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
Definition imf_cpl.c:816
static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
Definition imf_cpl.c:807
static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:176
static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
Definition imf_cpl.c:323
static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
Definition imf_cpl.c:525
int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
Reads an unsigned 32-bit integer from an XML element.
Definition imf_cpl.c:100
int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
Reads a UUID from an XML element.
Definition imf_cpl.c:73
static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
Definition imf_cpl.c:128
static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:229
static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:272
static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition imf_cpl.c:693
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
Bytestream IO Context.
Definition avio.h:160
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
IMF Composition Playlist Base Resource.
Definition imf.h:68
AVRational edit_rate
BaseResourceType/EditRate.
Definition imf.h:69
uint32_t duration
BaseResourceType/Duration.
Definition imf.h:71
uint32_t repeat_count
BaseResourceType/RepeatCount.
Definition imf.h:72
uint32_t entry_point
BaseResourceType/EntryPoint.
Definition imf.h:70
IMF Composition Playlist Virtual Track.
Definition imf.h:104
AVUUID id_uuid
TrackId associated with the Virtual Track.
Definition imf.h:105
IMF Composition Playlist.
Definition imf.h:130
FFIMFMarkerVirtualTrack * main_markers_track
Main Marker Virtual Track.
Definition imf.h:135
AVRational edit_rate
CompositionPlaylist/EditRate element.
Definition imf.h:133
FFIMFTrackFileVirtualTrack * main_audio_tracks
Main Audio Virtual Tracks.
Definition imf.h:138
xmlChar * content_title_utf8
CompositionPlaylist/ContentTitle element.
Definition imf.h:132
uint32_t main_audio_track_count
Number of Main Audio Virtual Tracks.
Definition imf.h:137
FFIMFTrackFileVirtualTrack * main_image_2d_track
Main Image Virtual Track.
Definition imf.h:136
AVUUID id_uuid
CompositionPlaylist/Id element.
Definition imf.h:131
AVTimecode * tc
CompositionPlaylist/CompositionTimecode element.
Definition imf.h:134
IMF Composition Playlist Marker Resource.
Definition imf.h:95
uint32_t marker_count
Number of Marker elements.
Definition imf.h:97
FFIMFMarker * markers
Marker elements.
Definition imf.h:98
IMF Composition Playlist Virtual Track that consists of Marker Resources.
Definition imf.h:121
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition imf.h:123
FFIMFBaseVirtualTrack base
Definition imf.h:122
FFIMFMarkerResource * resources
Resource elements of the Virtual Track.
Definition imf.h:124
IMF Marker.
Definition imf.h:86
xmlChar * label_utf8
Marker/Label.
Definition imf.h:87
uint32_t offset
Marker/Offset.
Definition imf.h:89
xmlChar * scope_utf8
Marker/Label/@scope.
Definition imf.h:88
IMF Composition Playlist Track File Resource.
Definition imf.h:78
AVUUID track_file_uuid
TrackFileResourceType/TrackFileId.
Definition imf.h:80
IMF Composition Playlist Virtual Track that consists of Track File Resources.
Definition imf.h:111
FFIMFTrackFileResource * resources
Resource elements of the Virtual Track.
Definition imf.h:114
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition imf.h:113
unsigned int resources_alloc_sz
Size of the resources buffer.
Definition imf.h:115
FFIMFBaseVirtualTrack base
Definition imf.h:112
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
int av_timecode_init_from_components(AVTimecode *tc, AVRational rate, int flags, int hh, int mm, int ss, int ff, void *log_ctx)
Init a timecode struct from the passed timecode components.
Definition timecode.c:211
@ AV_TIMECODE_FLAG_DROPFRAME
timecode is drop frame
Definition timecode.h:36
int av_uuid_urn_parse(const char *in, AVUUID uu)
Parses a URN representation of a UUID, as specified at IETF RFC 4122, into an AVUUID.
Definition uuid.c:136
static void av_uuid_copy(AVUUID dest, const AVUUID src)
Copies the bytes of src into dest.
Definition uuid.h:130
static int av_uuid_equal(const AVUUID uu1, const AVUUID uu2)
Compares two UUIDs for equality.
Definition uuid.h:119
static void av_uuid_nil(AVUUID uu)
Sets a UUID to the nil UUID, i.e.
Definition uuid.h:141
#define AV_UUID_ARG(x)
Definition uuid.h:51
uint8_t AVUUID[AV_UUID_LEN]
Definition uuid.h:60
#define AV_PRI_UUID
Definition uuid.h:39
#define df(A, B)
Definition vf_xbr.c:91