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)
81  xmlFree(element_text);
82 
83  return ret;
84 }
85 
86 int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational)
87 {
88  int ret = 0;
89 
90  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
91  if (sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2)
93  xmlFree(element_text);
94 
95  return ret;
96 }
97 
98 int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number)
99 {
100  int ret = 0;
101 
102  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
103  if (sscanf(element_text, "%" PRIu32, number) != 1)
105  xmlFree(element_text);
106 
107  return ret;
108 }
109 
110 static int ff_imf_xml_read_boolean(xmlNodePtr element, int *value)
111 {
112  int ret = 0;
113 
114  xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
115  if (xmlStrcmp(element_text, "true") == 0 || xmlStrcmp(element_text, "1") == 0)
116  *value = 1;
117  else if (xmlStrcmp(element_text, "false") == 0 || xmlStrcmp(element_text, "0") == 0)
118  *value = 0;
119  else
120  ret = 1;
121  xmlFree(element_text);
122 
123  return ret;
124 }
125 
127 {
128  memset(track->id_uuid, 0, sizeof(track->id_uuid));
129 }
130 
132 {
134  track->resource_count = 0;
135  track->resources = NULL;
136 }
137 
139 {
141  track->resource_count = 0;
142  track->resources_alloc_sz = 0;
143  track->resources = NULL;
144 }
145 
147 {
148  rsrc->duration = 0;
149  rsrc->edit_rate = av_make_q(0, 1);
150  rsrc->entry_point = 0;
151  rsrc->repeat_count = 1;
152 }
153 
155 {
157  rsrc->marker_count = 0;
158  rsrc->markers = NULL;
159 }
160 
161 static void imf_marker_init(FFIMFMarker *marker)
162 {
163  marker->label_utf8 = NULL;
164  marker->offset = 0;
165  marker->scope_utf8 = NULL;
166 }
167 
169 {
171  memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid));
172 }
173 
174 static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl)
175 {
176  xmlNodePtr element = NULL;
177 
178  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle")))
179  return AVERROR_INVALIDDATA;
180  cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc,
181  element->xmlChildrenNode,
182  1);
183 
184  return 0;
185 }
186 
187 static int digit_to_int(char digit)
188 {
189  if (digit >= '0' && digit <= '9')
190  return digit - '0';
191  return -1;
192 }
193 
194 /**
195  * Parses a string that conform to the TimecodeType used in IMF CPL and defined
196  * in SMPTE ST 2067-3.
197  * @param[in] s string to parse
198  * @param[out] tc_comps pointer to an array of 4 integers where the parsed HH,
199  * MM, SS and FF fields of the timecode are returned.
200  * @return 0 on success, < 0 AVERROR code on error.
201  */
202 static int parse_cpl_tc_type(const char *s, int *tc_comps)
203 {
204  if (av_strnlen(s, 11) != 11)
205  return AVERROR(EINVAL);
206 
207  for (int i = 0; i < 4; i++) {
208  int hi;
209  int lo;
210 
211  hi = digit_to_int(s[i * 3]);
212  lo = digit_to_int(s[i * 3 + 1]);
213 
214  if (hi == -1 || lo == -1)
215  return AVERROR(EINVAL);
216 
217  tc_comps[i] = 10 * hi + lo;
218  }
219 
220  return 0;
221 }
222 
223 static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
224 {
225  xmlNodePtr tc_element = NULL;
226  xmlNodePtr element = NULL;
227  xmlChar *tc_str = NULL;
228  int df = 0;
229  int comps[4];
230  int ret = 0;
231 
232  tc_element = ff_imf_xml_get_child_element_by_name(cpl_element, "CompositionTimecode");
233  if (!tc_element)
234  return 0;
235 
236  element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame");
237  if (!element)
238  return AVERROR_INVALIDDATA;
239 
240  if (ff_imf_xml_read_boolean(element, &df))
241  return AVERROR_INVALIDDATA;
242 
243  element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress");
244  if (!element)
245  return AVERROR_INVALIDDATA;
246 
247  tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1);
248  ret = parse_cpl_tc_type(tc_str, comps);
249  xmlFree(tc_str);
250  if (ret)
251  return ret;
252 
253  cpl->tc = av_malloc(sizeof(AVTimecode));
254  if (!cpl->tc)
255  return AVERROR(ENOMEM);
258  comps[0], comps[1], comps[2], comps[3],
259  NULL);
260 
261  return ret;
262 }
263 
264 static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl)
265 {
266  xmlNodePtr element = NULL;
267 
268  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate")))
269  return AVERROR_INVALIDDATA;
270 
271  return ff_imf_xml_read_rational(element, &cpl->edit_rate);
272 }
273 
274 static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
275 {
276  xmlNodePtr element = NULL;
277 
278  if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id")))
279  return AVERROR_INVALIDDATA;
280 
281  return ff_imf_xml_read_uuid(element, cpl->id_uuid);
282 }
283 
284 static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker)
285 {
286  xmlNodePtr element = NULL;
287  int ret = 0;
288 
289  /* read Offset */
290  if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset")))
291  return AVERROR_INVALIDDATA;
292 
293  if ((ret = ff_imf_xml_read_uint32(element, &marker->offset)))
294  return ret;
295 
296  /* read Label and Scope */
297  if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label")))
298  return AVERROR_INVALIDDATA;
299 
300  if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1)))
301  return AVERROR_INVALIDDATA;
302 
303  if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) {
304  marker->scope_utf8
305  = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers");
306  if (!marker->scope_utf8) {
307  xmlFree(marker->label_utf8);
308  return AVERROR(ENOMEM);
309  }
310  }
311 
312  return ret;
313 }
314 
315 static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem,
316  FFIMFBaseResource *resource, FFIMFCPL *cpl)
317 {
318  xmlNodePtr element = NULL;
319  int ret = 0;
320 
321  /* read EditRate */
322  if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) {
323  resource->edit_rate = cpl->edit_rate;
324  } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) {
325  av_log(log_ctx, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n");
326  return ret;
327  }
328 
329  /* read EntryPoint */
330  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) {
331  if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) {
332  av_log(log_ctx, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n");
333  return ret;
334  }
335  } else {
336  resource->entry_point = 0;
337  }
338 
339  /* read IntrinsicDuration */
340  if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) {
341  av_log(log_ctx, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n");
342  return AVERROR_INVALIDDATA;
343  }
344  if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
345  av_log(log_ctx, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n");
346  return ret;
347  }
348  resource->duration -= resource->entry_point;
349 
350  /* read SourceDuration */
351  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) {
352  if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) {
353  av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n");
354  return ret;
355  }
356  }
357 
358  /* read RepeatCount */
359  if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "RepeatCount")))
360  ret = ff_imf_xml_read_uint32(element, &resource->repeat_count);
361 
362  return ret;
363 }
364 
365 static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem,
366  FFIMFTrackFileResource *tf_resource,
367  FFIMFCPL *cpl)
368 {
369  xmlNodePtr element = NULL;
370  int ret = 0;
371 
372  if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl)))
373  return ret;
374 
375  /* read TrackFileId */
376  if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) {
377  if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) {
378  av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n");
379  return ret;
380  }
381  } else {
382  av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n");
383  return AVERROR_INVALIDDATA;
384  }
385 
386  return ret;
387 }
388 
389 static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem,
390  FFIMFMarkerResource *marker_resource,
391  FFIMFCPL *cpl)
392 {
393  xmlNodePtr element = NULL;
394  int ret = 0;
395 
396  if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl)))
397  return ret;
398 
399  /* read markers */
400  element = xmlFirstElementChild(marker_resource_elem);
401  while (element) {
402  if (xmlStrcmp(element->name, "Marker") == 0) {
403  void *tmp;
404 
405  if (marker_resource->marker_count == UINT32_MAX)
406  return AVERROR(ENOMEM);
407  tmp = av_realloc_array(marker_resource->markers,
408  marker_resource->marker_count + 1,
409  sizeof(FFIMFMarker));
410  if (!tmp)
411  return AVERROR(ENOMEM);
412  marker_resource->markers = tmp;
413 
414  imf_marker_init(&marker_resource->markers[marker_resource->marker_count]);
415  ret = fill_marker(element,
416  &marker_resource->markers[marker_resource->marker_count]);
417  marker_resource->marker_count++;
418  if (ret) {
419  av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n");
420  return ret;
421  }
422  }
423 
424  element = xmlNextElementSibling(element);
425  }
426 
427  return ret;
428 }
429 
430 static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
431 {
432  int ret = 0;
433  AVUUID uuid;
434  xmlNodePtr resource_list_elem = NULL;
435  xmlNodePtr resource_elem = NULL;
436  xmlNodePtr track_id_elem = NULL;
437  unsigned long resource_elem_count;
438  void *tmp;
439 
440  /* read TrackID element */
441  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) {
442  av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n");
443  return AVERROR_INVALIDDATA;
444  }
445  if (ff_imf_xml_read_uuid(track_id_elem, uuid)) {
446  av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n");
447  return AVERROR_INVALIDDATA;
448  }
449  av_log(log_ctx,
450  AV_LOG_DEBUG,
451  "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n",
452  AV_UUID_ARG(uuid));
453 
454  /* create main marker virtual track if it does not exist */
455  if (!cpl->main_markers_track) {
457  if (!cpl->main_markers_track)
458  return AVERROR(ENOMEM);
461 
462  } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) {
463  av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n");
464  return AVERROR_INVALIDDATA;
465  }
466 
467  /* process resources */
468  resource_list_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "ResourceList");
469  if (!resource_list_elem)
470  return 0;
471 
472  resource_elem_count = xmlChildElementCount(resource_list_elem);
473  if (resource_elem_count > UINT32_MAX
474  || cpl->main_markers_track->resource_count > UINT32_MAX - resource_elem_count)
475  return AVERROR(ENOMEM);
477  cpl->main_markers_track->resource_count + resource_elem_count,
478  sizeof(FFIMFMarkerResource));
479  if (!tmp) {
480  av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n");
481  return AVERROR(ENOMEM);
482  }
484 
485  resource_elem = xmlFirstElementChild(resource_list_elem);
486  while (resource_elem) {
488  ret = fill_marker_resource(log_ctx, resource_elem,
490  cpl);
492  if (ret)
493  return ret;
494 
495  resource_elem = xmlNextElementSibling(resource_elem);
496  }
497 
498  return ret;
499 }
500 
501 static int has_stereo_resources(xmlNodePtr element)
502 {
503  if (xmlStrcmp(element->name, "Left") == 0 || xmlStrcmp(element->name, "Right") == 0)
504  return 1;
505 
506  element = xmlFirstElementChild(element);
507  while (element) {
508  if (has_stereo_resources(element))
509  return 1;
510 
511  element = xmlNextElementSibling(element);
512  }
513 
514  return 0;
515 }
516 
517 static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
518 {
519  int ret = 0;
520  AVUUID uuid;
521  xmlNodePtr resource_list_elem = NULL;
522  xmlNodePtr resource_elem = NULL;
523  xmlNodePtr track_id_elem = NULL;
524  unsigned long resource_elem_count;
526  void *tmp;
527 
528  /* read TrackID element */
529  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) {
530  av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
531  return AVERROR_INVALIDDATA;
532  }
533  if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
534  av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
535  return ret;
536  }
537  av_log(log_ctx,
538  AV_LOG_DEBUG,
539  "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n",
540  AV_UUID_ARG(uuid));
541 
542  /* get the main audio virtual track corresponding to the sequence */
543  for (uint32_t i = 0; i < cpl->main_audio_track_count; i++) {
544  if (av_uuid_equal(cpl->main_audio_tracks[i].base.id_uuid, uuid)) {
545  vt = &cpl->main_audio_tracks[i];
546  break;
547  }
548  }
549 
550  /* create a main audio virtual track if none exists for the sequence */
551  if (!vt) {
552  if (cpl->main_audio_track_count == UINT32_MAX)
553  return AVERROR(ENOMEM);
555  cpl->main_audio_track_count + 1,
557  if (!tmp)
558  return AVERROR(ENOMEM);
559 
560  cpl->main_audio_tracks = tmp;
561  vt = &cpl->main_audio_tracks[cpl->main_audio_track_count];
563  cpl->main_audio_track_count++;
564  av_uuid_copy(vt->base.id_uuid, uuid);
565  }
566 
567  /* process resources */
568  resource_list_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "ResourceList");
569  if (!resource_list_elem)
570  return 0;
571 
572  resource_elem_count = xmlChildElementCount(resource_list_elem);
573  if (resource_elem_count > UINT32_MAX
574  || vt->resource_count > UINT32_MAX - resource_elem_count)
575  return AVERROR(ENOMEM);
577  &vt->resources_alloc_sz,
578  (vt->resource_count + resource_elem_count)
579  * sizeof(FFIMFTrackFileResource));
580  if (!tmp) {
581  av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n");
582  return AVERROR(ENOMEM);
583  }
584  vt->resources = tmp;
585 
586  resource_elem = xmlFirstElementChild(resource_list_elem);
587  while (resource_elem) {
589  ret = fill_trackfile_resource(log_ctx, resource_elem,
590  &vt->resources[vt->resource_count],
591  cpl);
592  if (ret)
593  av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
594  else
595  vt->resource_count++;
596 
597  resource_elem = xmlNextElementSibling(resource_elem);
598  }
599 
600  return ret;
601 }
602 
603 static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl)
604 {
605  int ret = 0;
606  AVUUID uuid;
607  xmlNodePtr resource_list_elem = NULL;
608  xmlNodePtr resource_elem = NULL;
609  xmlNodePtr track_id_elem = NULL;
610  void *tmp;
611  unsigned long resource_elem_count;
612 
613  /* skip stereoscopic resources */
614  if (has_stereo_resources(image_sequence_elem)) {
615  av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n");
616  return AVERROR_PATCHWELCOME;
617  }
618 
619  /* read TrackId element*/
620  if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) {
621  av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n");
622  return AVERROR_INVALIDDATA;
623  }
624  if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) {
625  av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n");
626  return ret;
627  }
628 
629  /* create main image virtual track if one does not exist */
630  if (!cpl->main_image_2d_track) {
632  if (!cpl->main_image_2d_track)
633  return AVERROR(ENOMEM);
636 
637  } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) {
638  av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n");
639  return AVERROR_INVALIDDATA;
640  }
641  av_log(log_ctx,
642  AV_LOG_DEBUG,
643  "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n",
644  AV_UUID_ARG(uuid));
645 
646  /* process resources */
647  resource_list_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "ResourceList");
648  if (!resource_list_elem)
649  return 0;
650 
651  resource_elem_count = xmlChildElementCount(resource_list_elem);
652  if (resource_elem_count > UINT32_MAX
653  || cpl->main_image_2d_track->resource_count > UINT32_MAX - resource_elem_count
654  || (cpl->main_image_2d_track->resource_count + resource_elem_count)
655  > INT_MAX / sizeof(FFIMFTrackFileResource))
656  return AVERROR(ENOMEM);
659  (cpl->main_image_2d_track->resource_count + resource_elem_count)
660  * sizeof(FFIMFTrackFileResource));
661  if (!tmp) {
662  av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n");
663  return AVERROR(ENOMEM);
664  }
666 
667  resource_elem = xmlFirstElementChild(resource_list_elem);
668  while (resource_elem) {
671  ret = fill_trackfile_resource(log_ctx, resource_elem,
673  cpl);
674  if (ret)
675  av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n");
676  else
678 
679  resource_elem = xmlNextElementSibling(resource_elem);
680  }
681 
682  return 0;
683 }
684 
685 static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
686 {
687  int ret = 0;
688  xmlNodePtr segment_list_elem = NULL;
689  xmlNodePtr segment_elem = NULL;
690  xmlNodePtr sequence_list_elem = NULL;
691  xmlNodePtr sequence_elem = NULL;
692 
693  if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) {
694  av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n");
695  return AVERROR_INVALIDDATA;
696  }
697 
698  /* process sequences */
699  segment_elem = xmlFirstElementChild(segment_list_elem);
700  while (segment_elem) {
701  av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n");
702 
703  sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList");
704  if (!sequence_list_elem)
705  continue;
706 
707  sequence_elem = xmlFirstElementChild(sequence_list_elem);
708  while (sequence_elem) {
709  if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0)
710  ret = push_marker_sequence(log_ctx, sequence_elem, cpl);
711 
712  else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0)
713  ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl);
714 
715  else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0)
716  ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl);
717 
718  else
719  av_log(log_ctx,
720  AV_LOG_INFO,
721  "The following Sequence is not supported and is ignored: %s\n",
722  sequence_elem->name);
723 
724  /* abort parsing only if memory error occurred */
725  if (ret == AVERROR(ENOMEM))
726  return ret;
727 
728  sequence_elem = xmlNextElementSibling(sequence_elem);
729  }
730 
731  segment_elem = xmlNextElementSibling(segment_elem);
732  }
733 
734  return ret;
735 }
736 
737 int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl)
738 {
739  int ret = 0;
740  xmlNodePtr cpl_element = NULL;
741 
742  *cpl = ff_imf_cpl_alloc();
743  if (!*cpl) {
744  ret = AVERROR(ENOMEM);
745  goto cleanup;
746  }
747 
748  cpl_element = xmlDocGetRootElement(doc);
749  if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) {
750  av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n");
752  goto cleanup;
753  }
754 
755  if ((ret = fill_content_title(cpl_element, *cpl))) {
756  av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n");
757  goto cleanup;
758  }
759  if ((ret = fill_id(cpl_element, *cpl))) {
760  av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n");
761  goto cleanup;
762  }
763  if ((ret = fill_edit_rate(cpl_element, *cpl))) {
764  av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n");
765  goto cleanup;
766  }
767  if ((ret = fill_timecode(cpl_element, *cpl))) {
768  av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n");
769  goto cleanup;
770  }
771  if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl)))
772  goto cleanup;
773 
774 cleanup:
775  if (*cpl && ret) {
776  ff_imf_cpl_free(*cpl);
777  *cpl = NULL;
778  }
779  return ret;
780 }
781 
782 static void imf_marker_free(FFIMFMarker *marker)
783 {
784  if (!marker)
785  return;
786  xmlFree(marker->label_utf8);
787  xmlFree(marker->scope_utf8);
788 }
789 
791 {
792  if (!rsrc)
793  return;
794  for (uint32_t i = 0; i < rsrc->marker_count; i++)
795  imf_marker_free(&rsrc->markers[i]);
796  av_freep(&rsrc->markers);
797 }
798 
800 {
801  if (!vt)
802  return;
803  for (uint32_t i = 0; i < vt->resource_count; i++)
805  av_freep(&vt->resources);
806 }
807 
809 {
810  if (!vt)
811  return;
812  av_freep(&vt->resources);
813 }
814 
815 static void imf_cpl_init(FFIMFCPL *cpl)
816 {
817  av_uuid_nil(cpl->id_uuid);
818  cpl->content_title_utf8 = NULL;
819  cpl->edit_rate = av_make_q(0, 1);
820  cpl->tc = NULL;
821  cpl->main_markers_track = NULL;
822  cpl->main_image_2d_track = NULL;
823  cpl->main_audio_track_count = 0;
824  cpl->main_audio_tracks = NULL;
825 }
826 
828 {
829  FFIMFCPL *cpl;
830 
831  cpl = av_malloc(sizeof(FFIMFCPL));
832  if (!cpl)
833  return NULL;
834  imf_cpl_init(cpl);
835  return cpl;
836 }
837 
839 {
840  if (!cpl)
841  return;
842 
843  if (cpl->tc)
844  av_freep(&cpl->tc);
845 
846  xmlFree(cpl->content_title_utf8);
847 
849 
850  if (cpl->main_markers_track)
852 
854 
855  if (cpl->main_image_2d_track)
857 
858  for (uint32_t i = 0; i < cpl->main_audio_track_count; i++)
860 
861  if (cpl->main_audio_tracks)
863 
864  av_freep(&cpl);
865 }
866 
867 int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl)
868 {
869  AVBPrint buf;
870  xmlDoc *doc = NULL;
871  int ret = 0;
872 
873  av_bprint_init(&buf, 0, INT_MAX); // xmlReadMemory uses integer length
874 
875  ret = avio_read_to_bprint(in, &buf, SIZE_MAX);
876  if (ret < 0 || !avio_feof(in)) {
877  av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n");
878  if (ret == 0)
880  goto clean_up;
881  }
882 
883  LIBXML_TEST_VERSION
884 
885  doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0);
886  if (!doc) {
887  av_log(log_ctx,
888  AV_LOG_ERROR,
889  "XML parsing failed when reading the IMF CPL\n");
891  goto clean_up;
892  }
893 
894  if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) {
895  av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n");
896  } else {
897  av_log(log_ctx,
898  AV_LOG_INFO,
899  "IMF CPL ContentTitle: %s\n",
900  (*cpl)->content_title_utf8);
901  av_log(log_ctx,
902  AV_LOG_INFO,
903  "IMF CPL Id: " AV_PRI_UUID "\n",
904  AV_UUID_ARG((*cpl)->id_uuid));
905  }
906 
907  xmlFreeDoc(doc);
908 
909 clean_up:
910  av_bprint_finalize(&buf, NULL);
911 
912  return ret;
913 }
imf_base_virtual_track_init
static void imf_base_virtual_track_init(FFIMFBaseVirtualTrack *track)
Definition: imf_cpl.c:126
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:202
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:867
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:838
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:782
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:827
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:138
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:737
digit_to_int
static int digit_to_int(char digit)
Definition: imf_cpl.c:187
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:815
fill_marker_resource
static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:389
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:799
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:146
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:131
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:168
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:98
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:142
fill_timecode
static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:223
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:365
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:603
FFIMFTrackFileVirtualTrack::base
FFIMFBaseVirtualTrack base
Definition: imf.h:112
error.h
has_stereo_resources
static int has_stereo_resources(xmlNodePtr element)
Definition: imf_cpl.c:501
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
push_marker_sequence
static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:430
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:174
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
fill_virtual_tracks
static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:685
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:284
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:790
push_main_audio_sequence
static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl)
Definition: imf_cpl.c:517
fill_base_resource
static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl)
Definition: imf_cpl.c:315
fill_id
static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl)
Definition: imf_cpl.c:274
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:110
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:86
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:154
imf_marker_init
static void imf_marker_init(FFIMFMarker *marker)
Definition: imf_cpl.c:161
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:808
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:264
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:367