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
57 #include <libxml/parser.h>
58
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 }
71 }
72
74 {
76
77 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
78 if (!element_text)
83 xmlFree(element_text);
84
86 }
87
89 {
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
98 }
99
101 {
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
110 }
111
113 {
115
116 xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
117 if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0)
119 else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0)
121 else
123 xmlFree(element_text);
124
126 }
127
129 {
131 }
132
134 {
136 track->resource_count = 0;
137 track->resources =
NULL;
138 }
139
141 {
143 track->resource_count = 0;
144 track->resources_alloc_sz = 0;
145 track->resources =
NULL;
146 }
147
149 {
154 }
155
157 {
159 rsrc->marker_count = 0;
160 rsrc->markers =
NULL;
161 }
162
164 {
168 }
169
171 {
173 memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
174 }
175
177 {
178 xmlNodePtr element =
NULL;
179
183 element->xmlChildrenNode,
184 1);
189
190 return 0;
191 }
192
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 */
209 {
212
213 for (
int i = 0;
i < 4;
i++) {
214 int hi;
215 int lo;
216
219
220 if (hi == -1 || lo == -1)
222
223 tc_comps[
i] = 10 * hi + lo;
224 }
225
226 return 0;
227 }
228
230 {
231 xmlNodePtr tc_element =
NULL;
232 xmlNodePtr element =
NULL;
233 xmlChar *tc_str =
NULL;
235 int comps[4];
237
239 if (!tc_element)
240 return 0;
241
243 if (!element)
245
248
250 if (!element)
252
253 tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
254 if (!tc_str)
257 xmlFree(tc_str);
260
266 comps[0], comps[1], comps[2], comps[3],
268
270 }
271
273 {
274 xmlNodePtr element =
NULL;
275
278
280 }
281
283 {
284 xmlNodePtr element =
NULL;
285
288
290 }
291
293 {
294 xmlNodePtr element =
NULL;
296
297 /* read Offset */
300
303
304 /* read Label and Scope */
307
308 if (!(marker->
label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1)))
310
311 if (!(marker->
scope_utf8 = xmlGetNoNsProp(element,
"scope"))) {
313 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
317 }
318 }
319
321 }
322
325 {
326 xmlNodePtr element =
NULL;
328
329 /* read EditRate */
335 }
336
337 /* read EntryPoint */
342 }
343 } else {
345 }
346
347 /* read IntrinsicDuration */
351 }
353 av_log(log_ctx,
AV_LOG_ERROR,
"Invalid IntrinsicDuration element found in a Resource\n");
355 }
357
358 /* read SourceDuration */
363 }
364 }
365
366 /* read RepeatCount */
369
371 }
372
376 {
377 xmlNodePtr element =
NULL;
379
382
383 /* read TrackFileId */
388 }
389 } else {
392 }
393
395 }
396
400 {
401 xmlNodePtr element =
NULL;
403
406
407 /* read markers */
408 element = xmlFirstElementChild(marker_resource_elem);
409 while (element) {
410 if (xmlStrcmp(element->name, "Marker") == 0) {
412
413 if (marker_resource->marker_count == UINT32_MAX)
416 marker_resource->marker_count + 1,
420 marker_resource->markers =
tmp;
421
422 imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
424 &marker_resource->markers[marker_resource->marker_count]);
425 marker_resource->marker_count++;
429 }
430 }
431
432 element = xmlNextElementSibling(element);
433 }
434
436 }
437
439 {
442 xmlNodePtr resource_list_elem =
NULL;
443 xmlNodePtr resource_elem =
NULL;
444 xmlNodePtr track_id_elem =
NULL;
445 unsigned long resource_elem_count;
447
448 /* read TrackID element */
452 }
456 }
459 "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
461
462 /* create main marker virtual track if it does not exist */
469
473 }
474
475 /* process resources */
477 if (!resource_list_elem)
478 return 0;
479
480 resource_elem_count = xmlChildElementCount(resource_list_elem);
481 if (resource_elem_count > UINT32_MAX
490 }
492
493 resource_elem = xmlFirstElementChild(resource_list_elem);
494 while (resource_elem) {
498 cpl);
502
503 resource_elem = xmlNextElementSibling(resource_elem);
504 }
505
507 }
508
510 {
511 if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
512 return 1;
513
514 element = xmlFirstElementChild(element);
515 while (element) {
517 return 1;
518
519 element = xmlNextElementSibling(element);
520 }
521
522 return 0;
523 }
524
526 {
529 xmlNodePtr resource_list_elem =
NULL;
530 xmlNodePtr resource_elem =
NULL;
531 xmlNodePtr track_id_elem =
NULL;
532 unsigned long resource_elem_count;
535
536 /* read TrackID element */
540 }
544 }
547 "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
549
550 /* get the main audio virtual track corresponding to the sequence */
554 break;
555 }
556 }
557
558 /* create a main audio virtual track if none exists for the sequence */
559 if (!vt) {
567
573 }
574
575 /* process resources */
577 if (!resource_list_elem)
578 return 0;
579
580 resource_elem_count = xmlChildElementCount(resource_list_elem);
581 if (resource_elem_count > UINT32_MAX
591 }
593
594 resource_elem = xmlFirstElementChild(resource_list_elem);
595 while (resource_elem) {
599 cpl);
602 else
604
605 resource_elem = xmlNextElementSibling(resource_elem);
606 }
607
609 }
610
612 {
615 xmlNodePtr resource_list_elem =
NULL;
616 xmlNodePtr resource_elem =
NULL;
617 xmlNodePtr track_id_elem =
NULL;
619 unsigned long resource_elem_count;
620
621 /* skip stereoscopic resources */
623 av_log(log_ctx,
AV_LOG_ERROR,
"Stereoscopic 3D image virtual tracks not supported\n");
625 }
626
627 /* read TrackId element*/
631 }
635 }
636
637 /* create main image virtual track if one does not exist */
644
648 }
651 "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
653
654 /* process resources */
656 if (!resource_list_elem)
657 return 0;
658
659 resource_elem_count = xmlChildElementCount(resource_list_elem);
660 if (resource_elem_count > UINT32_MAX
672 }
674
675 resource_elem = xmlFirstElementChild(resource_list_elem);
676 while (resource_elem) {
681 cpl);
684 else
686
687 resource_elem = xmlNextElementSibling(resource_elem);
688 }
689
690 return 0;
691 }
692
694 {
696 xmlNodePtr segment_list_elem =
NULL;
697 xmlNodePtr segment_elem =
NULL;
698 xmlNodePtr sequence_list_elem =
NULL;
699 xmlNodePtr sequence_elem =
NULL;
700
704 }
705
706 /* process sequences */
707 segment_elem = xmlFirstElementChild(segment_list_elem);
708 while (segment_elem) {
710
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)
718
719 else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
721
722 else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
724
725 else
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 */
734
735 sequence_elem = xmlNextElementSibling(sequence_elem);
736 }
737 }
738
739 segment_elem = xmlNextElementSibling(segment_elem);
740 }
741
743 }
744
746 {
748 xmlNodePtr cpl_element =
NULL;
749
751 if (!*cpl) {
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");
761 }
762
764 av_log(log_ctx,
AV_LOG_ERROR,
"Cannot read the ContentTitle element from the IMF CPL\n");
766 }
770 }
774 }
776 av_log(log_ctx,
AV_LOG_ERROR,
"Invalid CompositionTimecode element found in the IMF CPL\n");
778 }
781
786 }
788 }
789
791 {
792 if (!marker)
793 return;
796 }
797
799 {
800 if (!rsrc)
801 return;
805 }
806
808 {
809 if (!vt)
810 return;
814 }
815
817 {
818 if (!vt)
819 return;
821 }
822
824 {
833 }
834
836 {
838
840 if (!cpl)
843 return cpl;
844 }
845
847 {
848 if (!cpl)
849 return;
850
853
855
857
860
862
865
868
871
873 }
874
876 {
877 AVBPrint buf;
880
881 av_bprint_init(&buf, 0, INT_MAX);
// xmlReadMemory uses integer length
882
888 goto clean_up;
889 }
890
891 LIBXML_TEST_VERSION
892
893 doc = xmlReadMemory(buf.str, buf.len,
NULL,
NULL, 0);
894 if (!doc) {
897 "XML parsing failed when reading the IMF CPL\n");
899 goto clean_up;
900 }
901
904 } else {
907 "IMF CPL ContentTitle: %s\n",
908 (*cpl)->content_title_utf8);
913 }
914
915 xmlFreeDoc(doc);
916
917 clean_up:
919
921 }