FFmpeg
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 "libavformat/mxf.h"
55 #include "libavutil/bprint.h"
56 #include "libavutil/error.h"
57 #include <libxml/parser.h>
58 
59 xmlNodePtr ff_imf_xml_get_child_element_by_name(xmlNodePtr parent, const char *name_utf8)
60 {
61  xmlNodePtr cur_element;
62 
63  cur_element = xmlFirstElementChild(parent);
64  while (cur_element) {
65  if (xmlStrcmp(cur_element->name, name_utf8) == 0)
66  return cur_element;
67 
68  cur_element = xmlNextElementSibling(cur_element);
69  }
70  return NULL;
71 }
72 
73 int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
74 {
75  int ret = 0;
76 
77  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
78  ret = av_uuid_urn_parse(element_text, uuid);
79  if (ret) {
80  av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n");
82  }
83  xmlFree(element_text);
84 
85  return ret;
86 }
87 
88 int 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 (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) {
94  av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n");
96  }
97  xmlFree(element_text);
98 
99  return ret;
100 }
101 
102 int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
103 {
104  int ret = 0;
105 
106  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
107  if (sscanf(element_text, "%" PRIu32, number) != 1) {
108  av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer");
110  }
111  xmlFree(element_text);
112 
113  return ret;
114 }
115 
116 static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
117 {
118  int ret = 0;
119 
120  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
121  if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0)
122  *value = 1;
123  else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0)
124  *value = 0;
125  else
126  ret = 1;
127  xmlFree(element_text);
128 
129  return ret;
130 }
131 
133 {
134  memset(track->id_uuid, 0, sizeof(track->id_uuid));
135 }
136 
138 {
140  track->resource_count = 0;
141  track->resources = NULL;
142 }
143 
145 {
147  track->resource_count = 0;
148  track->resources_alloc_sz = 0;
149  track->resources = NULL;
150 }
151 
153 {
154  rsrc->duration = 0;
155  rsrc->edit_rate = av_make_q(0, 1);
156  rsrc->entry_point = 0;
157  rsrc->repeat_count = 1;
158 }
159 
161 {
163  rsrc->marker_count = 0;
164  rsrc->markers = NULL;
165 }
166 
167 static void imf_marker_init(FFIMFMarker *marker)
168 {
169  marker->label_utf8 = NULL;
170  marker->offset = 0;
171  marker->scope_utf8 = NULL;
172 }
173 
175 {
177  memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
178 }
179 
180 static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
181 {
182  xmlNodePtr element = NULL;
183 
184  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) {
185  av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n");
186  return AVERROR_INVALIDDATA;
187  }
188  cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
189  element->xmlChildrenNode,
190  1);
191 
192  return 0;
193 }
194 
195 static int digit_to_int(char digit)
196 {
197  if (digit >= '0' && digit <= '9')
198  return digit - '0';
199  return -1;
200 }
201 
202 /**
203  * Parses a string that conform to the TimecodeType used in IMF CPL and defined
204  * in SMPTE ST 2067-3.
205  * @param[in] s string to parse
206  * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH,
207  * MM, SS and FF fields of the timecode are returned.
208  * @return 0 on success, < 0 AVERROR code on error.
209  */
210 static int parse_cpl_tc_type(const char *s, int *tc_comps)
211 {
212  if (av_strnlen(s, 11) != 11)
213  return AVERROR(EINVAL);
214 
215  for (int i = 0; i < 4; i++) {
216  int hi;
217  int lo;
218 
219  hi = digit_to_int(s[i * 3]);
220  lo = digit_to_int(s[i * 3 + 1]);
221 
222  if (hi == -1 || lo == -1)
223  return AVERROR(EINVAL);
224 
225  tc_comps[i] = 10 * hi + lo;
226  }
227 
228  return 0;
229 }
230 
231 static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
232 {
233  xmlNodePtr tc_element = NULL;
234  xmlNodePtr element = NULL;
235  xmlChar *tc_str = NULL;
236  int df = 0;
237  int comps[4];
238  int ret = 0;
239 
240  tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode");
241  if (!tc_element)
242  return 0;
243 
244  element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame");
245  if (!element) {
246  av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\
247  a TimecodeDropFrame child element\n");
248  return AVERROR_INVALIDDATA;
249  }
250 
251  if (ff_imf_xml_read_boolean(element, &df)) {
252  av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n");
253  return AVERROR_INVALIDDATA;
254  }
255  element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress");
256  if (!element) {
257  av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\
258  a TimecodeStartAddress child element\n");
259  return AVERROR_INVALIDDATA;
260  }
261 
262  tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
263  ret = parse_cpl_tc_type(tc_str, comps);
264  xmlFree(tc_str);
265  if (ret)
266  return ret;
267 
268  cpl->tc = av_malloc(sizeof(AVTimecode));
269  if (!cpl->tc)
270  return AVERROR(ENOMEM);
273  comps[0], comps[1], comps[2], comps[3],
274  NULL);
275 
276  return ret;
277 }
278 
279 static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
280 {
281  xmlNodePtr element = NULL;
282 
283  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) {
284  av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
285  return AVERROR_INVALIDDATA;
286  }
287 
288  return ff_imf_xml_read_rational(element, &cpl->edit_rate);
289 }
290 
291 static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
292 {
293  xmlNodePtr element = NULL;
294 
295  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) {
296  av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
297  return AVERROR_INVALIDDATA;
298  }
299 
300  return ff_imf_xml_read_uuid(element, cpl->id_uuid);
301 }
302 
303 static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
304 {
305  xmlNodePtr element = NULL;
306  int ret = 0;
307 
308  /* read Offset */
309  if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) {
310  av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n");
311  return AVERROR_INVALIDDATA;
312  }
313  if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
314  return ret;
315 
316  /* read Label and Scope */
317  if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) {
318  av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n");
319  return AVERROR_INVALIDDATA;
320  }
321  if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) {
322  av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n");
323  return AVERROR_INVALIDDATA;
324  }
325  if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
326  marker->scope_utf8
327  = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
328  if (!marker->scope_utf8) {
329  xmlFree(marker->label_utf8);
330  return AVERROR(ENOMEM);
331  }
332  }
333 
334  return ret;
335 }
336 
337 static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
338 {
339  xmlNodePtr element = NULL;
340  int ret = 0;
341 
342  /* read EditRate */
343  if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
344  resource->edit_rate = cpl->edit_rate;
345  } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
346  av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
347  return ret;
348  }
349 
350  /* read EntryPoint */
351  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
352  if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
353  av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
354  return ret;
355  }
356  } else {
357  resource->entry_point = 0;
358  }
359 
360  /* read IntrinsicDuration */
361  if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
362  av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
363  return AVERROR_INVALIDDATA;
364  }
365  if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
366  av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
367  return ret;
368  }
369  resource->duration -= resource->entry_point;
370 
371  /* read SourceDuration */
372  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
373  if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
374  av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
375  return ret;
376  }
377  }
378 
379  /* read RepeatCount */
380  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
381  ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
382 
383  return ret;
384 }
385 
386 static int fill_trackfile_resource(xmlNodePtr tf_resource_elem,
387  FFIMFTrackFileResource *tf_resource,
388  FFIMFCPL *cpl)
389 {
390  xmlNodePtr element = NULL;
391  int ret = 0;
392 
393  if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
394  return ret;
395 
396  /* read TrackFileId */
397  if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
398  if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
399  av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
400  return ret;
401  }
402  } else {
403  av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
404  return AVERROR_INVALIDDATA;
405  }
406 
407  return ret;
408 }
409 
410 static int fill_marker_resource(xmlNodePtr marker_resource_elem,
411  FFIMFMarkerResource *marker_resource,
412  FFIMFCPL *cpl)
413 {
414  xmlNodePtr element = NULL;
415  int ret = 0;
416 
417  if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
418  return ret;
419 
420  /* read markers */
421  element = xmlFirstElementChild(marker_resource_elem);
422  while (element) {
423  if (xmlStrcmp(element->name, "Marker") == 0) {
424  void *tmp;
425 
426  if (marker_resource->marker_count == UINT32_MAX)
427  return AVERROR(ENOMEM);
428  tmp = av_realloc_array(marker_resource->markers,
429  marker_resource->marker_count + 1,
430  sizeof(FFIMFMarker));
431  if (!tmp)
432  return AVERROR(ENOMEM);
433  marker_resource->markers = tmp;
434 
435  imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
436  ret = fill_marker(element,
437  &marker_resource->markers[marker_resource->marker_count]);
438  marker_resource->marker_count++;
439  if (ret)
440  return ret;
441  }
442 
443  element = xmlNextElementSibling(element);
444  }
445 
446  return ret;
447 }
448 
449 static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
450 {
451  int ret = 0;
452  AVUUID uuid;
453  xmlNodePtr resource_list_elem = NULL;
454  xmlNodePtr resource_elem = NULL;
455  xmlNodePtr track_id_elem = NULL;
456  unsigned long resource_elem_count;
457  void *tmp;
458 
459  /* read TrackID element */
460  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
461  av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
462  return AVERROR_INVALIDDATA;
463  }
464  if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
465  av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
466  return AVERROR_INVALIDDATA;
467  }
468  av_log(NULL,
469  AV_LOG_DEBUG,
470  "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
471  AV_UUID_ARG(uuid));
472 
473  /* create main marker virtual track if it does not exist */
474  if (!cpl->main_markers_track) {
476  if (!cpl->main_markers_track)
477  return AVERROR(ENOMEM);
480 
481  } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
482  av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
483  return AVERROR_INVALIDDATA;
484  }
485 
486  /* process resources */
487  resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
488  if (!resource_list_elem)
489  return 0;
490 
491  resource_elem_count = xmlChildElementCount(resource_list_elem);
492  if (resource_elem_count > UINT32_MAX
493  || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
494  return AVERROR(ENOMEM);
496  cpl->main_markers_track->resource_count + resource_elem_count,
497  sizeof(FFIMFMarkerResource));
498  if (!tmp) {
499  av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
500  return AVERROR(ENOMEM);
501  }
503 
504  resource_elem = xmlFirstElementChild(resource_list_elem);
505  while (resource_elem) {
507  ret = fill_marker_resource(resource_elem,
509  cpl);
511  if (ret)
512  return ret;
513 
514  resource_elem = xmlNextElementSibling(resource_elem);
515  }
516 
517  return ret;
518 }
519 
520 static int has_stereo_resources(xmlNodePtr element)
521 {
522  if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
523  return 1;
524 
525  element = xmlFirstElementChild(element);
526  while (element) {
527  if (has_stereo_resources(element))
528  return 1;
529 
530  element = xmlNextElementSibling(element);
531  }
532 
533  return 0;
534 }
535 
536 static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
537 {
538  int ret = 0;
539  AVUUID uuid;
540  xmlNodePtr resource_list_elem = NULL;
541  xmlNodePtr resource_elem = NULL;
542  xmlNodePtr track_id_elem = NULL;
543  unsigned long resource_elem_count;
545  void *tmp;
546 
547  /* read TrackID element */
548  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_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  av_log(NULL,
557  AV_LOG_DEBUG,
558  "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
559  AV_UUID_ARG(uuid));
560 
561  /* get the main audio virtual track corresponding to the sequence */
562  for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
563  if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
564  vt = &cpl->main_audio_tracks[i];
565  break;
566  }
567  }
568 
569  /* create a main audio virtual track if none exists for the sequence */
570  if (!vt) {
571  if (cpl->main_audio_track_count == UINT32_MAX)
572  return AVERROR(ENOMEM);
574  cpl->main_audio_track_count + 1,
576  if (!tmp)
577  return AVERROR(ENOMEM);
578 
579  cpl->main_audio_tracks = tmp;
580  vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
582  cpl->main_audio_track_count++;
583  av_uuid_copy(vt->base.id_uuid, uuid);
584  }
585 
586  /* process resources */
587  resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
588  if (!resource_list_elem)
589  return 0;
590 
591  resource_elem_count = xmlChildElementCount(resource_list_elem);
592  if (resource_elem_count > UINT32_MAX
593  || vt->resource_count > UINT32_MAX - resource_elem_count)
594  return AVERROR(ENOMEM);
596  &vt->resources_alloc_sz,
597  (vt->resource_count + resource_elem_count)
598  * sizeof(FFIMFTrackFileResource));
599  if (!tmp) {
600  av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
601  return AVERROR(ENOMEM);
602  }
603  vt->resources = tmp;
604 
605  resource_elem = xmlFirstElementChild(resource_list_elem);
606  while (resource_elem) {
608  ret = fill_trackfile_resource(resource_elem,
609  &vt->resources[vt->resource_count],
610  cpl);
611  vt->resource_count++;
612  if (ret) {
613  av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
614  continue;
615  }
616 
617  resource_elem = xmlNextElementSibling(resource_elem);
618  }
619 
620  return ret;
621 }
622 
623 static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
624 {
625  int ret = 0;
626  AVUUID uuid;
627  xmlNodePtr resource_list_elem = NULL;
628  xmlNodePtr resource_elem = NULL;
629  xmlNodePtr track_id_elem = NULL;
630  void *tmp;
631  unsigned long resource_elem_count;
632 
633  /* skip stereoscopic resources */
634  if (has_stereo_resources(image_sequence_elem)) {
635  av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
636  return AVERROR_PATCHWELCOME;
637  }
638 
639  /* read TrackId element*/
640  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
641  av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
642  return AVERROR_INVALIDDATA;
643  }
644  if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
645  av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
646  return ret;
647  }
648 
649  /* create main image virtual track if one does not exist */
650  if (!cpl->main_image_2d_track) {
652  if (!cpl->main_image_2d_track)
653  return AVERROR(ENOMEM);
656 
657  } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
658  av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
659  return AVERROR_INVALIDDATA;
660  }
661  av_log(NULL,
662  AV_LOG_DEBUG,
663  "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
664  AV_UUID_ARG(uuid));
665 
666  /* process resources */
667  resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
668  if (!resource_list_elem)
669  return 0;
670 
671  resource_elem_count = xmlChildElementCount(resource_list_elem);
672  if (resource_elem_count > UINT32_MAX
673  || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
674  || (cpl->main_image_2d_track->resource_count + resource_elem_count)
675  > INT_MAX / sizeof(FFIMFTrackFileResource))
676  return AVERROR(ENOMEM);
679  (cpl->main_image_2d_track->resource_count + resource_elem_count)
680  * sizeof(FFIMFTrackFileResource));
681  if (!tmp) {
682  av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
683  return AVERROR(ENOMEM);
684  }
686 
687  resource_elem = xmlFirstElementChild(resource_list_elem);
688  while (resource_elem) {
691  ret = fill_trackfile_resource(resource_elem,
693  cpl);
695  if (ret) {
696  av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n");
697  continue;
698  }
699 
700  resource_elem = xmlNextElementSibling(resource_elem);
701  }
702 
703  return 0;
704 }
705 
706 static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
707 {
708  int ret = 0;
709  xmlNodePtr segment_list_elem = NULL;
710  xmlNodePtr segment_elem = NULL;
711  xmlNodePtr sequence_list_elem = NULL;
712  xmlNodePtr sequence_elem = NULL;
713 
714  if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
715  av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n");
716  return AVERROR_INVALIDDATA;
717  }
718 
719  /* process sequences */
720  segment_elem = xmlFirstElementChild(segment_list_elem);
721  while (segment_elem) {
722  av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
723 
724  sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
725  if (!sequence_list_elem)
726  continue;
727 
728  sequence_elem = xmlFirstElementChild(sequence_list_elem);
729  while (sequence_elem) {
730  if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
731  ret = push_marker_sequence(sequence_elem, cpl);
732 
733  else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
734  ret = push_main_image_2d_sequence(sequence_elem, cpl);
735 
736  else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
737  ret = push_main_audio_sequence(sequence_elem, cpl);
738 
739  else
740  av_log(NULL,
741  AV_LOG_INFO,
742  "The following Sequence is not supported and is ignored: %s\n",
743  sequence_elem->name);
744 
745  /* abort parsing only if memory error occurred */
746  if (ret == AVERROR(ENOMEM))
747  return ret;
748 
749  sequence_elem = xmlNextElementSibling(sequence_elem);
750  }
751 
752  segment_elem = xmlNextElementSibling(segment_elem);
753  }
754 
755  return ret;
756 }
757 
758 int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
759 {
760  int ret = 0;
761  xmlNodePtr cpl_element = NULL;
762 
763  *cpl = ff_imf_cpl_alloc();
764  if (!*cpl) {
765  ret = AVERROR(ENOMEM);
766  goto cleanup;
767  }
768 
769  cpl_element = xmlDocGetRootElement(doc);
770  if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
771  av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
773  goto cleanup;
774  }
775 
776  if ((ret = fill_content_title(cpl_element, *cpl)))
777  goto cleanup;
778  if ((ret = fill_id(cpl_element, *cpl)))
779  goto cleanup;
780  if ((ret = fill_edit_rate(cpl_element, *cpl)))
781  goto cleanup;
782  if ((ret = fill_timecode(cpl_element, *cpl)))
783  goto cleanup;
784  if ((ret = fill_virtual_tracks(cpl_element, *cpl)))
785  goto cleanup;
786 
787 cleanup:
788  if (*cpl && ret) {
789  ff_imf_cpl_free(*cpl);
790  *cpl = NULL;
791  }
792  return ret;
793 }
794 
795 static void imf_marker_free(FFIMFMarker *marker)
796 {
797  if (!marker)
798  return;
799  xmlFree(marker->label_utf8);
800  xmlFree(marker->scope_utf8);
801 }
802 
804 {
805  if (!rsrc)
806  return;
807  for (uint32_t i = 0; i < rsrc->marker_count; i++)
808  imf_marker_free(&rsrc->markers[i]);
809  av_freep(&rsrc->markers);
810 }
811 
813 {
814  if (!vt)
815  return;
816  for (uint32_t i = 0; i < vt->resource_count; i++)
818  av_freep(&vt->resources);
819 }
820 
822 {
823  if (!vt)
824  return;
825  av_freep(&vt->resources);
826 }
827 
828 static void imf_cpl_init(FFIMFCPL *cpl)
829 {
830  av_uuid_nil(cpl->id_uuid);
831  cpl->content_title_utf8 = NULL;
832  cpl->edit_rate = av_make_q(0, 1);
833  cpl->tc = NULL;
834  cpl->main_markers_track = NULL;
835  cpl->main_image_2d_track = NULL;
836  cpl->main_audio_track_count = 0;
837  cpl->main_audio_tracks = NULL;
838 }
839 
841 {
842  FFIMFCPL *cpl;
843 
844  cpl = av_malloc(sizeof(FFIMFCPL));
845  if (!cpl)
846  return NULL;
847  imf_cpl_init(cpl);
848  return cpl;
849 }
850 
852 {
853  if (!cpl)
854  return;
855 
856  if (cpl->tc)
857  av_freep(&cpl->tc);
858 
859  xmlFree(cpl->content_title_utf8);
860 
862 
863  if (cpl->main_markers_track)
865 
867 
868  if (cpl->main_image_2d_track)
870 
871  for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
873 
874  if (cpl->main_audio_tracks)
876 
877  av_freep(&cpl);
878 }
879 
881 {
882  AVBPrint buf;
883  xmlDoc *doc = NULL;
884  int ret = 0;
885 
886  av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
887 
888  ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
889  if (ret < 0 || !avio_feof(in)) {
890  av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n");
891  if (ret == 0)
893  goto clean_up;
894  }
895 
896  LIBXML_TEST_VERSION
897 
898  doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
899  if (!doc) {
900  av_log(NULL,
901  AV_LOG_ERROR,
902  "XML parsing failed when reading the IMF CPL\n");
904  goto clean_up;
905  }
906 
907  if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) {
908  av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
909  } else {
910  av_log(NULL,
911  AV_LOG_INFO,
912  "IMF CPL ContentTitle: %s\n",
913  (*cpl)->content_title_utf8);
914  av_log(NULL,
915  AV_LOG_INFO,
916  "IMF CPL Id: " AV_PRI_UUID "\n",
917  AV_UUID_ARG((*cpl)->id_uuid));
918  }
919 
920  xmlFreeDoc(doc);
921 
922 clean_up:
923  av_bprint_finalize(&buf, NULL);
924 
925  return ret;
926 }
imf_base_virtual_track_init
static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
Definition: imf_cpl.c:132
FFIMFTrackFileVirtualTrack::resources
FFIMFTrackFileResource * resources
Resource elements of the Virtual Track.
Definition: imf.h:114
FFIMFCPL::id_uuid
AVUUID id_uuid
CompositionPlaylist/Id element.
Definition: imf.h:131
parse_cpl_tc_type
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:210
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
AVUUID
uint8_t AVUUID[AV_UUID_LEN]
Definition: uuid.h:60
push_main_audio_sequence
static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:536
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
ff_imf_cpl_free
void ff_imf_cpl_free(FFIMFCPL *cpl)
Deletes an FFIMFCPL data structure previously instantiated with ff_imf_cpl_alloc().
Definition: imf_cpl.c:851
df
#define df(A, B)
Definition: vf_xbr.c:89
AV_UUID_ARG
#define AV_UUID_ARG(x)
Definition: uuid.h:51
imf_marker_free
static void imf_marker_free(FFIMFMarker *marker)
Definition: imf_cpl.c:795
FFIMFMarkerVirtualTrack::resources
FFIMFMarkerResource * resources
Resource elements of the Virtual Track.
Definition: imf.h:124
ff_imf_cpl_alloc
FFIMFCPL * ff_imf_cpl_alloc(void)
Allocates and initializes an FFIMFCPL data structure.
Definition: imf_cpl.c:840
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:28
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:130
ff_imf_parse_cpl_from_xml_dom
int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl)
Parse an IMF CompositionPlaylist element into the FFIMFCPL data structure.
Definition: imf_cpl.c:758
FFIMFTrackFileVirtualTrack::resource_count
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition: imf.h:113
imf_trackfile_virtual_track_init
static void imf_trackfile_virtual_track_init(FFIMFTrackFileVirtualTrack *track)
Definition: imf_cpl.c:144
digit_to_int
static int digit_to_int(char digit)
Definition: imf_cpl.c:195
FFIMFMarkerVirtualTrack
IMF Composition Playlist Virtual Track that consists of Marker Resources.
Definition: imf.h:121
FFIMFCPL::content_title_utf8
xmlChar * content_title_utf8
CompositionPlaylist/ContentTitle element.
Definition: imf.h:132
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
FFIMFCPL::main_audio_tracks
FFIMFTrackFileVirtualTrack * main_audio_tracks
Main Audio Virtual Tracks.
Definition: imf.h:138
mxf.h
imf_cpl_init
static void imf_cpl_init(FFIMFCPL *cpl)
Definition: imf_cpl.c:828
FFIMFBaseResource::duration
uint32_t duration
BaseResourceType/Duration.
Definition: imf.h:71
FFIMFMarker
IMF Marker.
Definition: imf.h:86
FFIMFCPL::main_markers_track
FFIMFMarkerVirtualTrack * main_markers_track
Main Marker Virtual Track.
Definition: imf.h:135
AVRational::num
int num
Numerator.
Definition: rational.h:59
FFIMFCPL::tc
AVTimecode * tc
CompositionPlaylist/CompositionTimecode element.
Definition: imf.h:134
imf_marker_virtual_track_free
static void imf_marker_virtual_track_free(FFIMFMarkerVirtualTrack *vt)
Definition: imf_cpl.c:812
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FFIMFTrackFileVirtualTrack
IMF Composition Playlist Virtual Track that consists of Track File Resources.
Definition: imf.h:111
push_main_image_2d_sequence
static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:623
av_fast_realloc
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
imf_base_resource_init
static void imf_base_resource_init(FFIMFBaseResource *rsrc)
Definition: imf_cpl.c:152
s
#define s(width, name)
Definition: cbs_vp9.c:256
avio_read_to_bprint
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:1347
av_realloc_array
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:215
imf_marker_virtual_track_init
static void imf_marker_virtual_track_init(FFIMFMarkerVirtualTrack *track)
Definition: imf_cpl.c:137
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
imf_trackfile_resource_init
static void imf_trackfile_resource_init(FFIMFTrackFileResource *rsrc)
Definition: imf_cpl.c:174
fill_base_resource
static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:337
ff_imf_xml_read_uint32
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:102
FFIMFCPL::edit_rate
AVRational edit_rate
CompositionPlaylist/EditRate element.
Definition: imf.h:133
fill_trackfile_resource
static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:386
FFIMFTrackFileVirtualTrack::resources_alloc_sz
unsigned int resources_alloc_sz
Size of the resources buffer.
Definition: imf.h:115
NULL
#define NULL
Definition: coverity.c:32
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:64
FFIMFBaseVirtualTrack
IMF Composition Playlist Virtual Track.
Definition: imf.h:104
FFIMFBaseResource
IMF Composition Playlist Base Resource.
Definition: imf.h:68
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_strnlen
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:142
fill_timecode
static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:231
FFIMFMarkerVirtualTrack::base
FFIMFBaseVirtualTrack base
Definition: imf.h:122
FFIMFTrackFileVirtualTrack::base
FFIMFBaseVirtualTrack base
Definition: imf.h:112
error.h
has_stereo_resources
static int has_stereo_resources(xmlNodePtr element)
Definition: imf_cpl.c:520
av_timecode_init_from_components
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:231
FFIMFBaseResource::edit_rate
AVRational edit_rate
BaseResourceType/EditRate.
Definition: imf.h:69
AVIOContext
Bytestream IO Context.
Definition: avio.h:166
fill_content_title
static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:180
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
ff_imf_xml_read_uuid
int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid)
Reads a UUID from an XML element.
Definition: imf_cpl.c:73
fill_marker
static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
Definition: imf_cpl.c:303
av_make_q
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition: rational.h:71
imf_marker_resource_free
static void imf_marker_resource_free(FFIMFMarkerResource *rsrc)
Definition: imf_cpl.c:803
fill_id
static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:291
push_marker_sequence
static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:449
av_uuid_nil
static void av_uuid_nil(AVUUID uu)
Sets a UUID to the nil UUID, i.e.
Definition: uuid.h:141
FFIMFBaseResource::repeat_count
uint32_t repeat_count
BaseResourceType/RepeatCount.
Definition: imf.h:72
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:191
AV_PRI_UUID
#define AV_PRI_UUID
Definition: uuid.h:39
av_uuid_equal
static int av_uuid_equal(const AVUUID uu1, const AVUUID uu2)
Compares two UUIDs for equality.
Definition: uuid.h:119
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
FFIMFBaseVirtualTrack::id_uuid
AVUUID id_uuid
TrackId associated with the Virtual Track.
Definition: imf.h:105
FFIMFBaseResource::entry_point
uint32_t entry_point
BaseResourceType/EntryPoint.
Definition: imf.h:70
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
AV_TIMECODE_FLAG_DROPFRAME
@ AV_TIMECODE_FLAG_DROPFRAME
timecode is drop frame
Definition: timecode.h:36
ret
ret
Definition: filter_design.txt:187
ff_imf_xml_read_boolean
static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
Definition: imf_cpl.c:116
fill_virtual_tracks
static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:706
FFIMFMarkerResource
IMF Composition Playlist Marker Resource.
Definition: imf.h:95
FFIMFMarkerResource::markers
FFIMFMarker * markers
Marker elements.
Definition: imf.h:98
ff_imf_xml_read_rational
int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
Reads an AVRational from an XML element.
Definition: imf_cpl.c:88
FFIMFTrackFileResource
IMF Composition Playlist Track File Resource.
Definition: imf.h:78
FFIMFMarker::offset
uint32_t offset
Marker/Offset.
Definition: imf.h:89
AVRational::den
int den
Denominator.
Definition: rational.h:60
imf_marker_resource_init
static void imf_marker_resource_init(FFIMFMarkerResource *rsrc)
Definition: imf_cpl.c:160
imf_marker_init
static void imf_marker_init(FFIMFMarker *marker)
Definition: imf_cpl.c:167
FFIMFCPL::main_audio_track_count
uint32_t main_audio_track_count
Number of Main Audio Virtual Tracks.
Definition: imf.h:137
FFIMFMarkerVirtualTrack::resource_count
uint32_t resource_count
Number of Resource elements present in the Virtual Track.
Definition: imf.h:123
imf_trackfile_virtual_track_free
static void imf_trackfile_virtual_track_free(FFIMFTrackFileVirtualTrack *vt)
Definition: imf_cpl.c:821
FFIMFCPL::main_image_2d_track
FFIMFTrackFileVirtualTrack * main_image_2d_track
Main Image Virtual Track.
Definition: imf.h:136
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
FFIMFMarkerResource::marker_count
uint32_t marker_count
Number of Marker elements.
Definition: imf.h:97
av_uuid_copy
static void av_uuid_copy(AVUUID dest, const AVUUID src)
Copies the bytes of src into dest.
Definition: uuid.h:130
FFIMFCPL
IMF Composition Playlist.
Definition: imf.h:130
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
imf.h
Public header file for the processing of Interoperable Master Format (IMF) packages.
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
FFIMFMarker::label_utf8
xmlChar * label_utf8
Marker/Label.
Definition: imf.h:87
AVTimecode
Definition: timecode.h:41
ff_imf_parse_cpl
int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl)
Parse an IMF Composition Playlist document into the FFIMFCPL data structure.
Definition: imf_cpl.c:880
FFIMFMarker::scope_utf8
xmlChar * scope_utf8
Marker/Label/@scope.
Definition: imf.h:88
fill_marker_resource
static int fill_marker_resource(xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:410
av_uuid_urn_parse
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:135
ff_imf_xml_get_child_element_by_name
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
fill_edit_rate
static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:279
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:367