diff mbox series

[FFmpeg-devel,2/5] avformat/imf: CPL processor

Message ID 20211020145336.19725-2-pal@sandflow.com
State New
Headers show
Series [FFmpeg-devel,1/5] avformat/imf: Headers
Related show

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Pierre-Anthony Lemieux Oct. 20, 2021, 2:53 p.m. UTC
From: Pierre-Anthony Lemieux <pal@sandflow.com>

Signed-off-by: Pierre-Anthony Lemieux <pal@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

Comments

Paul B Mahol Oct. 27, 2021, 7:57 a.m. UTC | #1
On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:

> From: Pierre-Anthony Lemieux <pal@sandflow.com>
>
> Signed-off-by: Pierre-Anthony Lemieux <pal@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.


> +            }
> +
> 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-request@ffmpeg.org with subject "unsubscribe".
>
Pierre-Anthony Lemieux Oct. 28, 2021, 4:34 a.m. UTC | #2
On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>
>
> On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:
>>
>> From: Pierre-Anthony Lemieux <pal@sandflow.com>
>>
>> Signed-off-by: Pierre-Anthony Lemieux <pal@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?

>
>>
>> +            }
>> +            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-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol Oct. 28, 2021, 7:56 a.m. UTC | #3
On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com>
wrote:

> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote:
> >
> >
> >
> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:
> >>
> >> From: Pierre-Anthony Lemieux <pal@sandflow.com>
> >>
> >> Signed-off-by: Pierre-Anthony Lemieux <pal@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.

>
> >
> >>
> >> +            }
> >> +
> 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-request@ffmpeg.org with subject "unsubscribe".
>
Pierre-Anthony Lemieux Oct. 31, 2021, 4:47 p.m. UTC | #4
On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>
>
> On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> wrote:
>>
>> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote:
>> >
>> >
>> >
>> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:
>> >>
>> >> From: Pierre-Anthony Lemieux <pal@sandflow.com>
>> >>
>> >> Signed-off-by: Pierre-Anthony Lemieux <pal@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?

>>
>>
>> >
>> >>
>> >> +            }
>> >> +            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-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol Oct. 31, 2021, 6:35 p.m. UTC | #5
On Sun, Oct 31, 2021 at 5:48 PM Pierre-Anthony Lemieux <pal@sandflow.com>
wrote:

> On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote:
> >
> >
> >
> > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com>
> wrote:
> >>
> >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote:
> >> >
> >> >
> >> >
> >> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:
> >> >>
> >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com>
> >> >>
> >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@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.


> >>
> >>
> >> >
> >> >>
> >> >> +            }
> >> >> +
> 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-request@ffmpeg.org with subject "unsubscribe".
>
Pierre-Anthony Lemieux Oct. 31, 2021, 6:37 p.m. UTC | #6
On Sun, Oct 31, 2021 at 11:35 AM Paul B Mahol <onemda@gmail.com> wrote:
>
>
>
> On Sun, Oct 31, 2021 at 5:48 PM Pierre-Anthony Lemieux <pal@sandflow.com> wrote:
>>
>> On Thu, Oct 28, 2021 at 12:56 AM Paul B Mahol <onemda@gmail.com> wrote:
>> >
>> >
>> >
>> > On Thu, Oct 28, 2021 at 6:34 AM Pierre-Anthony Lemieux <pal@sandflow.com> wrote:
>> >>
>> >> On Wed, Oct 27, 2021 at 12:57 AM Paul B Mahol <onemda@gmail.com> wrote:
>> >> >
>> >> >
>> >> >
>> >> > On Wed, Oct 20, 2021 at 4:55 PM <pal@sandflow.com> wrote:
>> >> >>
>> >> >> From: Pierre-Anthony Lemieux <pal@sandflow.com>
>> >> >>
>> >> >> Signed-off-by: Pierre-Anthony Lemieux <pal@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-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

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);
+            }
+            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;
+}