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  if (!element_text)
79  return AVERROR_INVALIDDATA;
80  ret = av_uuid_urn_parse(element_text, uuid);
81  if (ret)
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 (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2)
95  xmlFree(element_text);
96 
97  return ret;
98 }
99 
100 int 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 
112 static 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 
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 {
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 
163 static 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 
176 static 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 
193 static 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  */
208 static 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 
229 static 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 
272 static 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 
282 static 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 
292 static 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 
323 static 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 
373 static 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 
397 static 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 
438 static 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,
458  AV_LOG_DEBUG,
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 
509 static 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 
525 static 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,
546  AV_LOG_DEBUG,
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;
569  vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
571  cpl->main_audio_track_count++;
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);
585  &vt->resources_alloc_sz,
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 
611 static 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");
624  return AVERROR_PATCHWELCOME;
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,
650  AV_LOG_DEBUG,
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 
693 static 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  continue;
714 
715  sequence_elem = xmlFirstElementChild(sequence_list_elem);
716  while (sequence_elem) {
717  if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
718  ret = push_marker_sequence(log_ctx, sequence_elem, cpl);
719 
720  else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
721  ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl);
722 
723  else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
724  ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl);
725 
726  else
727  av_log(log_ctx,
728  AV_LOG_INFO,
729  "The following Sequence is not supported and is ignored: %s\n",
730  sequence_elem->name);
731 
732  /* abort parsing only if memory error occurred */
733  if (ret == AVERROR(ENOMEM))
734  return ret;
735 
736  sequence_elem = xmlNextElementSibling(sequence_elem);
737  }
738 
739  segment_elem = xmlNextElementSibling(segment_elem);
740  }
741 
742  return ret;
743 }
744 
745 int 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 
782 cleanup:
783  if (*cpl && ret) {
784  ff_imf_cpl_free(*cpl);
785  *cpl = NULL;
786  }
787  return ret;
788 }
789 
790 static 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 
823 static void imf_cpl_init(FFIMFCPL *cpl)
824 {
825  av_uuid_nil(cpl->id_uuid);
826  cpl->content_title_utf8 = NULL;
827  cpl->edit_rate = av_make_q(0, 1);
828  cpl->tc = NULL;
829  cpl->main_markers_track = NULL;
830  cpl->main_image_2d_track = 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 
875 int 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,
896  AV_LOG_ERROR,
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,
906  AV_LOG_INFO,
907  "IMF CPL ContentTitle: %s\n",
908  (*cpl)->content_title_utf8);
909  av_log(log_ctx,
910  AV_LOG_INFO,
911  "IMF CPL Id: " AV_PRI_UUID "\n",
912  AV_UUID_ARG((*cpl)->id_uuid));
913  }
914 
915  xmlFreeDoc(doc);
916 
917 clean_up:
918  av_bprint_finalize(&buf, NULL);
919 
920  return ret;
921 }
imf_base_virtual_track_init
static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
Definition: imf_cpl.c:128
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:208
ff_imf_parse_cpl
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
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
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:846
df
#define df(A, B)
Definition: vf_xbr.c:90
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:790
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:835
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:28
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:130
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:140
ff_imf_parse_cpl_from_xml_dom
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
digit_to_int
static int digit_to_int(char digit)
Definition: imf_cpl.c:193
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:823
fill_marker_resource
static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:397
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:807
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
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:148
s
#define s(width, name)
Definition: cbs_vp9.c:198
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:1250
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:133
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:170
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:100
FFIMFCPL::edit_rate
AVRational edit_rate
CompositionPlaylist/EditRate element.
Definition: imf.h:133
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:141
fill_timecode
static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:229
FFIMFMarkerVirtualTrack::base
FFIMFBaseVirtualTrack base
Definition: imf.h:122
fill_trackfile_resource
static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:373
push_main_image_2d_sequence
static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:611
FFIMFTrackFileVirtualTrack::base
FFIMFBaseVirtualTrack base
Definition: imf.h:112
error.h
has_stereo_resources
static int has_stereo_resources(xmlNodePtr element)
Definition: imf_cpl.c:509
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:232
FFIMFBaseResource::edit_rate
AVRational edit_rate
BaseResourceType/EditRate.
Definition: imf.h:69
push_marker_sequence
static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:438
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
fill_content_title
static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:176
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:240
fill_virtual_tracks
static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:693
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:292
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:798
push_main_audio_sequence
static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:525
fill_base_resource
static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:323
fill_id
static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:282
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:255
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:112
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:156
imf_marker_init
static void imf_marker_init(FFIMFMarker *marker)
Definition: imf_cpl.c:163
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:816
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
FFIMFMarker::scope_utf8
xmlChar * scope_utf8
Marker/Label/@scope.
Definition: imf.h:88
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:272
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:345