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