On Sun, Oct 31, 2021 at 11:35 AM Paul B Mahol <one...@gmail.com> wrote: > > > > On Sun, Oct 31, 2021 at 5:48 PM Pierre-Anthony Lemieux <p...@sandflow.com> > wrote: >> >> On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <one...@gmail.com> wrote: >> > >> > >> > >> > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <p...@sandflow.com> >> > wrote: >> >> >> >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <one...@gmail.com> wrote: >> >> > >> >> > >> >> > >> >> > On Wed, Oct 20, 2021 at 4:55 PM <p...@sandflow.com> wrote: >> >> >> >> >> >> From: Pierre-Anthony Lemieux <p...@sandflow.com> >> >> >> >> >> >> Signed-off-by: Pierre-Anthony Lemieux <p...@sandflow.com> >> >> >> --- >> >> >> >> >> >> Notes: >> >> >> Implements IMF Composition Playlist (CPL) parsing. >> >> >> >> >> >> libavformat/imf_cpl.c | 666 ++++++++++++++++++++++++++++++++++++++++++ >> >> >> 1 file changed, 666 insertions(+) >> >> >> create mode 100644 libavformat/imf_cpl.c >> >> >> >> >> >> diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c >> >> >> new file mode 100644 >> >> >> index 0000000000..8ef574ad78 >> >> >> --- /dev/null >> >> >> +++ b/libavformat/imf_cpl.c >> >> >> @@ -0,0 +1,666 @@ >> >> >> +/* >> >> >> + * Copyright (c) Sandflow Consulting LLC >> >> >> + * >> >> >> + * Redistribution and use in source and binary forms, with or without >> >> >> + * modification, are permitted provided that the following conditions >> >> >> are met: >> >> >> + * >> >> >> + * * Redistributions of source code must retain the above copyright >> >> >> notice, this >> >> >> + * list of conditions and the following disclaimer. >> >> >> + * * Redistributions in binary form must reproduce the above >> >> >> copyright notice, >> >> >> + * this list of conditions and the following disclaimer in the >> >> >> documentation >> >> >> + * and/or other materials provided with the distribution. >> >> >> + * >> >> >> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND >> >> >> CONTRIBUTORS "AS IS" >> >> >> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED >> >> >> TO, THE >> >> >> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >> >> >> PURPOSE >> >> >> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR >> >> >> CONTRIBUTORS BE >> >> >> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >> >> >> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT >> >> >> OF >> >> >> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR >> >> >> BUSINESS >> >> >> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, >> >> >> WHETHER IN >> >> >> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR >> >> >> OTHERWISE) >> >> >> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF >> >> >> ADVISED OF THE >> >> >> + * POSSIBILITY OF SUCH DAMAGE. >> >> >> + * >> >> >> + * This file is part of FFmpeg. >> >> >> + * >> >> >> + * FFmpeg is free software; you can redistribute it and/or >> >> >> + * modify it under the terms of the GNU Lesser General Public >> >> >> + * License as published by the Free Software Foundation; either >> >> >> + * version 2.1 of the License, or (at your option) any later version. >> >> >> + * >> >> >> + * FFmpeg is distributed in the hope that it will be useful, >> >> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> >> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> >> >> + * Lesser General Public License for more details. >> >> >> + * >> >> >> + * You should have received a copy of the GNU Lesser General Public >> >> >> + * License along with FFmpeg; if not, write to the Free Software >> >> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >> >> >> 02110-1301 USA >> >> >> + */ >> >> >> + >> >> >> +/** >> >> >> + * Implements IMP CPL processing >> >> >> + * >> >> >> + * @author Pierre-Anthony Lemieux >> >> >> + * @file >> >> >> + * @ingroup lavu_imf >> >> >> + */ >> >> >> + >> >> >> +#include "imf.h" >> >> >> +#include "imf_internal.h" >> >> >> +#include "libavformat/mxf.h" >> >> >> +#include "libavutil/bprint.h" >> >> >> +#include "libavutil/error.h" >> >> >> +#include <libxml/parser.h> >> >> >> + >> >> >> +xmlNodePtr xml_get_child_element_by_name(xmlNodePtr parent, const >> >> >> char *name_utf8) { >> >> >> + xmlNodePtr cur_element; >> >> >> + >> >> >> + cur_element = xmlFirstElementChild(parent); >> >> >> + while (cur_element) { >> >> >> + if (xmlStrcmp(cur_element->name, name_utf8) == 0) >> >> >> + return cur_element; >> >> >> + cur_element = xmlNextElementSibling(cur_element); >> >> >> + } >> >> >> + return NULL; >> >> >> +} >> >> >> + >> >> >> +int xml_read_UUID(xmlNodePtr element, uint8_t uuid[16]) { >> >> >> + xmlChar *element_text = NULL; >> >> >> + int scanf_ret; >> >> >> + int ret = 0; >> >> >> + >> >> >> + element_text = xmlNodeListGetString(element->doc, >> >> >> element->xmlChildrenNode, 1); >> >> >> + scanf_ret = sscanf(element_text, >> >> >> + UUID_FORMAT, >> >> >> + &uuid[0], >> >> >> + &uuid[1], >> >> >> + &uuid[2], >> >> >> + &uuid[3], >> >> >> + &uuid[4], >> >> >> + &uuid[5], >> >> >> + &uuid[6], >> >> >> + &uuid[7], >> >> >> + &uuid[8], >> >> >> + &uuid[9], >> >> >> + &uuid[10], >> >> >> + &uuid[11], >> >> >> + &uuid[12], >> >> >> + &uuid[13], >> >> >> + &uuid[14], >> >> >> + &uuid[15]); >> >> >> + if (scanf_ret != 16) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + xmlFree(element_text); >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +int xml_read_rational(xmlNodePtr element, AVRational *rational) { >> >> >> + xmlChar *element_text = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + element_text = xmlNodeListGetString(element->doc, >> >> >> element->xmlChildrenNode, 1); >> >> >> + if (sscanf(element_text, "%i %i", &rational->num, &rational->den) >> >> >> != 2) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + xmlFree(element_text); >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +int xml_read_ulong(xmlNodePtr element, unsigned long *number) { >> >> >> + xmlChar *element_text = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + element_text = xmlNodeListGetString(element->doc, >> >> >> element->xmlChildrenNode, 1); >> >> >> + if (sscanf(element_text, "%lu", number) != 1) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid unsigned long"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + xmlFree(element_text); >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static void imf_base_virtual_track_init(IMFBaseVirtualTrack *track) { >> >> >> + memset(track->id_uuid, 0, sizeof(track->id_uuid)); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_virtual_track_init(IMFMarkerVirtualTrack >> >> >> *track) { >> >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> >> + track->resource_count = 0; >> >> >> + track->resources = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_virtual_track_init(IMFTrackFileVirtualTrack >> >> >> *track) { >> >> >> + imf_base_virtual_track_init((IMFBaseVirtualTrack *)track); >> >> >> + track->resource_count = 0; >> >> >> + track->resources = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_base_resource_init(IMFBaseResource *rsrc) { >> >> >> + rsrc->duration = 0; >> >> >> + rsrc->edit_rate = av_make_q(0, 0); >> >> >> + rsrc->entry_point = 0; >> >> >> + rsrc->repeat_count = 1; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_resource_init(IMFMarkerResource *rsrc) { >> >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> >> + rsrc->marker_count = 0; >> >> >> + rsrc->markers = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_init(IMFMarker *marker) { >> >> >> + marker->label_utf8 = NULL; >> >> >> + marker->offset = 0; >> >> >> + marker->scope_utf8 = NULL; >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_resource_init(IMFTrackFileResource *rsrc) { >> >> >> + imf_base_resource_init((IMFBaseResource *)rsrc); >> >> >> + memset(rsrc->track_file_uuid, 0, sizeof(rsrc->track_file_uuid)); >> >> >> +} >> >> >> + >> >> >> +static int fill_content_title(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = xml_get_child_element_by_name(cpl_element, >> >> >> "ContentTitle"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in >> >> >> the IMF CPL\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc, >> >> >> element->xmlChildrenNode, 1); >> >> >> + >> >> >> + return 0; >> >> >> +} >> >> >> + >> >> >> +static int fill_edit_rate(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = xml_get_child_element_by_name(cpl_element, >> >> >> "EditRate"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the >> >> >> IMF CPL\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + >> >> >> + return xml_read_rational(element, &cpl->edit_rate); >> >> >> +} >> >> >> + >> >> >> +static int fill_id(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + >> >> >> + if (!(element = xml_get_child_element_by_name(cpl_element, >> >> >> "Id"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF >> >> >> CPL\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + >> >> >> + return xml_read_UUID(element, cpl->id_uuid); >> >> >> +} >> >> >> + >> >> >> +static int fill_marker(xmlNodePtr marker_elem, IMFMarker *marker) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + /* read Offset */ >> >> >> + if (!(element = xml_get_child_element_by_name(marker_elem, >> >> >> "Offset"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Offset element not found in a >> >> >> Marker\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if ((ret = xml_read_ulong(element, &marker->offset))) >> >> >> + return ret; >> >> >> + >> >> >> + /* read Label and Scope */ >> >> >> + if (!(element = xml_get_child_element_by_name(marker_elem, >> >> >> "Label"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Label element not found in a >> >> >> Marker\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, >> >> >> element->xmlChildrenNode, 1))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a >> >> >> Marker\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) { >> >> >> + marker->scope_utf8 = >> >> >> xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers"); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int fill_base_resource(xmlNodePtr resource_elem, >> >> >> IMFBaseResource *resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + /* read EditRate */ >> >> >> + if (!(element = xml_get_child_element_by_name(resource_elem, >> >> >> "EditRate"))) { >> >> >> + resource->edit_rate = cpl->edit_rate; >> >> >> + } else if (ret = xml_read_rational(element, >> >> >> &resource->edit_rate)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in >> >> >> a Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + >> >> >> + /* read EntryPoint */ >> >> >> + if (element = xml_get_child_element_by_name(resource_elem, >> >> >> "EntryPoint")) { >> >> >> + if (ret = xml_read_ulong(element, &resource->entry_point)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element >> >> >> found in a Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + } else >> >> >> + resource->entry_point = 0; >> >> >> + >> >> >> + /* read IntrinsicDuration */ >> >> >> + if (!(element = xml_get_child_element_by_name(resource_elem, >> >> >> "IntrinsicDuration"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing >> >> >> from Resource\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (ret = xml_read_ulong(element, &resource->duration)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element >> >> >> found in a Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + resource->duration -= resource->entry_point; >> >> >> + >> >> >> + /* read SourceDuration */ >> >> >> + if (element = xml_get_child_element_by_name(resource_elem, >> >> >> "SourceDuration")) { >> >> >> + if (ret = xml_read_ulong(element, &resource->duration)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "SourceDuration element >> >> >> missing from Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + } >> >> >> + >> >> >> + /* read RepeatCount */ >> >> >> + if (element = xml_get_child_element_by_name(resource_elem, >> >> >> "RepeatCount")) { >> >> >> + ret = xml_read_ulong(element, &resource->repeat_count); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, >> >> >> IMFTrackFileResource *tf_resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + if (ret = fill_base_resource(tf_resource_elem, (IMFBaseResource >> >> >> *)tf_resource, cpl)) >> >> >> + return ret; >> >> >> + >> >> >> + /* read TrackFileId */ >> >> >> + if (element = xml_get_child_element_by_name(tf_resource_elem, >> >> >> "TrackFileId")) { >> >> >> + if (ret = xml_read_UUID(element, >> >> >> tf_resource->track_file_uuid)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element >> >> >> found in Resource\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + } else { >> >> >> + av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from >> >> >> Resource\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int fill_marker_resource(xmlNodePtr marker_resource_elem, >> >> >> IMFMarkerResource *marker_resource, IMFCPL *cpl) { >> >> >> + xmlNodePtr element = NULL; >> >> >> + int ret = 0; >> >> >> + >> >> >> + if (ret = fill_base_resource(marker_resource_elem, >> >> >> (IMFBaseResource *)marker_resource, cpl)) >> >> >> + return ret; >> >> >> + >> >> >> + /* read markers */ >> >> >> + element = xmlFirstElementChild(marker_resource_elem); >> >> >> + while (element) { >> >> >> + if (xmlStrcmp(element->name, "Marker") == 0) { >> >> >> + marker_resource->markers = >> >> >> av_realloc(marker_resource->markers, (++marker_resource->marker_count) >> >> >> * sizeof(IMFMarker)); >> >> >> + if (!marker_resource->markers) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate >> >> >> Marker\n"); >> >> >> + exit(1); >> >> > >> >> > >> >> > exit() should not be used. >> >> >> >> Is `return AVERROR_EXIT;` an acceptable substitute? >> > >> > >> > Nope. >> > >> > Caller should make proper use of proper return values. >> >> Ok, so `fill_marker_resource()` returns AVERROR(ENOMEM) on a memory >> allocation error (like ffmpeg.c) and the caller handles it? >> > Exactly.
Thanks. Will do. > >> >> >> >> >> >> >> > >> >> >> >> >> >> + } >> >> >> + >> >> >> imf_marker_init(&marker_resource->markers[marker_resource->marker_count >> >> >> - 1]); >> >> >> + fill_marker(element, >> >> >> &marker_resource->markers[marker_resource->marker_count - 1]); >> >> >> + } >> >> >> + element = xmlNextElementSibling(element); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int push_marker_sequence(xmlNodePtr marker_sequence_elem, >> >> >> IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + >> >> >> + /* read TrackID element */ >> >> >> + if (!(track_id_elem = >> >> >> xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from >> >> >> Sequence\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (ret = xml_read_UUID(track_id_elem, uuid)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in >> >> >> Sequence\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Marker Sequence >> >> >> for Virtual Track " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> >> + >> >> >> + /* create main marker virtual track if it does not exist */ >> >> >> + if (!cpl->main_markers_track) { >> >> >> + cpl->main_markers_track = >> >> >> av_malloc(sizeof(IMFMarkerVirtualTrack)); >> >> >> + if (!cpl->main_markers_track) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Marker >> >> >> Virtual Track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_marker_virtual_track_init(cpl->main_markers_track); >> >> >> + memcpy(cpl->main_markers_track->base.id_uuid, uuid, >> >> >> sizeof(uuid)); >> >> >> + } else if (memcmp(cpl->main_markers_track->base.id_uuid, uuid, >> >> >> sizeof(uuid)) != 0) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks >> >> >> were found\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + >> >> >> + /* process resources */ >> >> >> + resource_list_elem = >> >> >> xml_get_child_element_by_name(marker_sequence_elem, "ResourceList"); >> >> >> + if (!resource_list_elem) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + cpl->main_markers_track->resources = >> >> >> av_realloc(cpl->main_markers_track->resources, >> >> >> (++cpl->main_markers_track->resource_count) * >> >> >> sizeof(IMFMarkerResource)); >> >> >> + if (!cpl->main_markers_track->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + >> >> >> imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count >> >> >> - 1]); >> >> >> + fill_marker_resource(resource_elem, >> >> >> &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count >> >> >> - 1], cpl); >> >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int has_stereo_resources(xmlNodePtr element) { >> >> >> + if (xmlStrcmp(element->name, "Left") == 0 || >> >> >> xmlStrcmp(element->name, "Right") == 0) >> >> >> + return 1; >> >> >> + element = xmlFirstElementChild(element); >> >> >> + while (element) { >> >> >> + if (has_stereo_resources(element)) >> >> >> + return 1; >> >> >> + element = xmlNextElementSibling(element); >> >> >> + } >> >> >> + return 0; >> >> >> +} >> >> >> + >> >> >> +static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, >> >> >> IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + IMFTrackFileVirtualTrack *vt = NULL; >> >> >> + >> >> >> + /* read TrackID element */ >> >> >> + if (!(track_id_elem = >> >> >> xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from >> >> >> audio sequence\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (ret = xml_read_UUID(track_id_elem, uuid)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in >> >> >> audio sequence\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Audio Sequence for >> >> >> Virtual Track " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> >> + >> >> >> + /* get the main audio virtual track corresponding to the sequence >> >> >> */ >> >> >> + for (int i = 0; i < cpl->main_audio_track_count; i++) >> >> >> + if (memcmp(cpl->main_audio_tracks[i].base.id_uuid, uuid, >> >> >> sizeof(uuid)) == 0) { >> >> >> + vt = &cpl->main_audio_tracks[i]; >> >> >> + break; >> >> >> + } >> >> >> + >> >> >> + /* create a main audio virtual track if none exists for the >> >> >> sequence */ >> >> >> + if (!vt) { >> >> >> + cpl->main_audio_tracks = av_realloc(cpl->main_audio_tracks, >> >> >> sizeof(IMFTrackFileVirtualTrack) * (++cpl->main_audio_track_count)); >> >> >> + if (!cpl->main_audio_tracks) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainAudio >> >> >> virtual track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + vt = &cpl->main_audio_tracks[cpl->main_audio_track_count - 1]; >> >> >> + imf_trackfile_virtual_track_init(vt); >> >> >> + memcpy(vt->base.id_uuid, uuid, sizeof(uuid)); >> >> >> + } >> >> >> + >> >> >> + /* process resources */ >> >> >> + resource_list_elem = >> >> >> xml_get_child_element_by_name(audio_sequence_elem, "ResourceList"); >> >> >> + if (!resource_list_elem) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + vt->resources = av_realloc(vt->resources, >> >> >> (++vt->resource_count) * sizeof(IMFTrackFileResource)); >> >> >> + if (!vt->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_trackfile_resource_init(&vt->resources[vt->resource_count >> >> >> - 1]); >> >> >> + fill_trackfile_resource(resource_elem, >> >> >> &vt->resources[vt->resource_count - 1], cpl); >> >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static int push_main_image_2d_sequence(xmlNodePtr >> >> >> image_sequence_elem, IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + uint8_t uuid[16]; >> >> >> + xmlNodePtr resource_list_elem = NULL; >> >> >> + xmlNodePtr resource_elem = NULL; >> >> >> + xmlNodePtr track_id_elem = NULL; >> >> >> + >> >> >> + /* skip stereoscopic resources */ >> >> >> + if (has_stereo_resources(image_sequence_elem)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual >> >> >> tracks not supported\n"); >> >> >> + return AVERROR_PATCHWELCOME; >> >> >> + } >> >> >> + >> >> >> + /* read TrackId element*/ >> >> >> + if (!(track_id_elem = >> >> >> xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "TrackId element missing from >> >> >> audio sequence\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (ret = xml_read_UUID(track_id_elem, uuid)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in >> >> >> audio sequence\n"); >> >> >> + return ret; >> >> >> + } >> >> >> + >> >> >> + /* create main image virtual track if one does not exist */ >> >> >> + if (!cpl->main_image_2d_track) { >> >> >> + cpl->main_image_2d_track = >> >> >> av_malloc(sizeof(IMFTrackFileVirtualTrack)); >> >> >> + if (!cpl->main_image_2d_track) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate MainImage >> >> >> virtual track\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + imf_trackfile_virtual_track_init(cpl->main_image_2d_track); >> >> >> + memcpy(cpl->main_image_2d_track->base.id_uuid, uuid, >> >> >> sizeof(uuid)); >> >> >> + } else if (memcmp(cpl->main_image_2d_track->base.id_uuid, uuid, >> >> >> sizeof(uuid)) != 0) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks >> >> >> found\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Main Image >> >> >> Sequence for Virtual Track " UUID_FORMAT "\n", UID_ARG(uuid)); >> >> >> + >> >> >> + /* process resources */ >> >> >> + if (!(resource_list_elem = >> >> >> xml_get_child_element_by_name(image_sequence_elem, "ResourceList"))) >> >> >> + return 0; >> >> >> + resource_elem = xmlFirstElementChild(resource_list_elem); >> >> >> + while (resource_elem) { >> >> >> + cpl->main_image_2d_track->resources = >> >> >> av_realloc(cpl->main_image_2d_track->resources, >> >> >> (++cpl->main_image_2d_track->resource_count) * >> >> >> sizeof(IMFTrackFileResource)); >> >> >> + if (!cpl->main_image_2d_track->resources) { >> >> >> + av_log(NULL, AV_LOG_PANIC, "Cannot allocate Resource\n"); >> >> >> + exit(1); >> >> >> + } >> >> >> + >> >> >> imf_trackfile_resource_init(&cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count >> >> >> - 1]); >> >> >> + fill_trackfile_resource(resource_elem, >> >> >> &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count >> >> >> - 1], cpl); >> >> >> + resource_elem = xmlNextElementSibling(resource_elem); >> >> >> + } >> >> >> + >> >> >> + return 0; >> >> >> +} >> >> >> + >> >> >> +static int fill_virtual_tracks(xmlNodePtr cpl_element, IMFCPL *cpl) { >> >> >> + int ret = 0; >> >> >> + xmlNodePtr segment_list_elem = NULL; >> >> >> + xmlNodePtr segment_elem = NULL; >> >> >> + xmlNodePtr sequence_list_elem = NULL; >> >> >> + xmlNodePtr sequence_elem = NULL; >> >> >> + >> >> >> + if (!(segment_list_elem = >> >> >> xml_get_child_element_by_name(cpl_element, "SegmentList"))) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + >> >> >> + /* process sequences */ >> >> >> + segment_elem = xmlFirstElementChild(segment_list_elem); >> >> >> + while (segment_elem) { >> >> >> + av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); >> >> >> + sequence_list_elem = >> >> >> xml_get_child_element_by_name(segment_elem, "SequenceList"); >> >> >> + if (!segment_list_elem) >> >> >> + continue; >> >> >> + sequence_elem = xmlFirstElementChild(sequence_list_elem); >> >> >> + while (sequence_elem) { >> >> >> + if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0) >> >> >> + push_marker_sequence(sequence_elem, cpl); >> >> >> + else if (xmlStrcmp(sequence_elem->name, >> >> >> "MainImageSequence") == 0) >> >> >> + push_main_image_2d_sequence(sequence_elem, cpl); >> >> >> + else if (xmlStrcmp(sequence_elem->name, >> >> >> "MainAudioSequence") == 0) >> >> >> + push_main_audio_sequence(sequence_elem, cpl); >> >> >> + else { >> >> >> + av_log(NULL, AV_LOG_INFO, "The following Sequence is >> >> >> not supported and is ignored: %s\n", sequence_elem->name); >> >> >> + } >> >> >> + sequence_elem = xmlNextElementSibling(sequence_elem); >> >> >> + } >> >> >> + segment_elem = xmlNextElementSibling(segment_elem); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +int parse_imf_cpl_from_xml_dom(xmlDocPtr doc, IMFCPL **cpl) { >> >> >> + int ret = 0; >> >> >> + xmlNodePtr cpl_element = NULL; >> >> >> + >> >> >> + *cpl = imf_cpl_alloc(); >> >> >> + if (!*cpl) { >> >> >> + av_log(NULL, AV_LOG_FATAL, "Cannot allocate CPL\n"); >> >> >> + ret = AVERROR_BUG; >> >> >> + goto cleanup; >> >> >> + } >> >> >> + cpl_element = xmlDocGetRootElement(doc); >> >> >> + if (xmlStrcmp(cpl_element->name, "CompositionPlaylist")) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is >> >> >> not CompositionPlaylist\n"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + goto cleanup; >> >> >> + } >> >> >> + if (ret = fill_content_title(cpl_element, *cpl)) >> >> >> + goto cleanup; >> >> >> + if (ret = fill_id(cpl_element, *cpl)) >> >> >> + goto cleanup; >> >> >> + if (ret = fill_edit_rate(cpl_element, *cpl)) >> >> >> + goto cleanup; >> >> >> + if (ret = fill_virtual_tracks(cpl_element, *cpl)) >> >> >> + goto cleanup; >> >> >> + >> >> >> +cleanup: >> >> >> + if (*cpl && ret) { >> >> >> + imf_cpl_free(*cpl); >> >> >> + *cpl = NULL; >> >> >> + } >> >> >> + return ret; >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_free(IMFMarker *marker) { >> >> >> + if (!marker) >> >> >> + return; >> >> >> + xmlFree(marker->label_utf8); >> >> >> + xmlFree(marker->scope_utf8); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_resource_free(IMFMarkerResource *rsrc) { >> >> >> + if (!rsrc) >> >> >> + return; >> >> >> + for (unsigned long i = 0; i < rsrc->marker_count; i++) >> >> >> + imf_marker_free(&rsrc->markers[i]); >> >> >> + av_free(rsrc->markers); >> >> >> +} >> >> >> + >> >> >> +static void imf_marker_virtual_track_free(IMFMarkerVirtualTrack *vt) { >> >> >> + if (!vt) >> >> >> + return; >> >> >> + for (unsigned long i = 0; i < vt->resource_count; i++) >> >> >> + imf_marker_resource_free(&vt->resources[i]); >> >> >> + av_free(vt->resources); >> >> >> +} >> >> >> + >> >> >> +static void imf_trackfile_virtual_track_free(IMFTrackFileVirtualTrack >> >> >> *vt) { >> >> >> + if (!vt) >> >> >> + return; >> >> >> + av_free(vt->resources); >> >> >> +} >> >> >> + >> >> >> +static void imf_cpl_init(IMFCPL *cpl) { >> >> >> + memset(cpl->id_uuid, 0, sizeof(cpl->id_uuid)); >> >> >> + cpl->content_title_utf8 = NULL; >> >> >> + cpl->edit_rate = av_make_q(0, 0); >> >> >> + cpl->main_markers_track = NULL; >> >> >> + cpl->main_image_2d_track = NULL; >> >> >> + cpl->main_audio_track_count = 0; >> >> >> + cpl->main_audio_tracks = NULL; >> >> >> +} >> >> >> + >> >> >> +IMFCPL *imf_cpl_alloc(void) { >> >> >> + IMFCPL *cpl; >> >> >> + >> >> >> + cpl = av_malloc(sizeof(IMFCPL)); >> >> >> + if (!cpl) >> >> >> + return NULL; >> >> >> + imf_cpl_init(cpl); >> >> >> + return cpl; >> >> >> +} >> >> >> + >> >> >> +void imf_cpl_free(IMFCPL *cpl) { >> >> >> + if (cpl) { >> >> >> + xmlFree(cpl->content_title_utf8); >> >> >> + imf_marker_virtual_track_free(cpl->main_markers_track); >> >> >> + imf_trackfile_virtual_track_free(cpl->main_image_2d_track); >> >> >> + for (unsigned long i = 0; i < cpl->main_audio_track_count; >> >> >> i++) >> >> >> + >> >> >> imf_trackfile_virtual_track_free(&cpl->main_audio_tracks[i]); >> >> >> + } >> >> >> + av_free(cpl); >> >> >> + cpl = NULL; >> >> >> +} >> >> >> + >> >> >> +int parse_imf_cpl(AVIOContext *in, IMFCPL **cpl) { >> >> >> + AVBPrint buf; >> >> >> + xmlDoc *doc = NULL; >> >> >> + int ret = 0; >> >> >> + int64_t filesize = 0; >> >> >> + >> >> >> + filesize = avio_size(in); >> >> >> + filesize = filesize > 0 ? filesize : 8192; >> >> >> + av_bprint_init(&buf, filesize + 1, AV_BPRINT_SIZE_UNLIMITED); >> >> >> + if ((ret = avio_read_to_bprint(in, &buf, UINT_MAX - 1)) < 0 || >> >> >> !avio_feof(in) || (filesize = buf.len) == 0) { >> >> >> + if (ret == 0) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); >> >> >> + return AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + } else { >> >> >> + LIBXML_TEST_VERSION >> >> >> + doc = xmlReadMemory(buf.str, filesize, NULL, NULL, 0); >> >> >> + if (!doc) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "XML parsing failed when >> >> >> reading the IMF CPL\n"); >> >> >> + ret = AVERROR_INVALIDDATA; >> >> >> + } >> >> >> + if (ret = parse_imf_cpl_from_xml_dom(doc, cpl)) { >> >> >> + av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); >> >> >> + } else { >> >> >> + av_log(NULL, AV_LOG_INFO, "IMF CPL ContentTitle: %s\n", >> >> >> (*cpl)->content_title_utf8); >> >> >> + av_log(NULL, AV_LOG_INFO, "IMF CPL Id: " UUID_FORMAT >> >> >> "\n", UID_ARG((*cpl)->id_uuid)); >> >> >> + } >> >> >> + xmlFreeDoc(doc); >> >> >> + xmlCleanupParser(); >> >> >> + } >> >> >> + >> >> >> + return ret; >> >> >> +} >> >> >> -- >> >> >> 2.17.1 >> >> >> >> >> >> _______________________________________________ >> >> >> ffmpeg-devel mailing list >> >> >> ffmpeg-devel@ffmpeg.org >> >> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel >> >> >> >> >> >> To unsubscribe, visit link above, or email >> >> >> ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".