[FFmpeg-devel] [PATCH] Adobe HTTP Dynamic Streaming (HDS) demuxer improvements

Gorilla Maguila gorilla.maguila at gmail.com
Tue Apr 28 16:15:29 CEST 2015


I will try to fix all the problems, although I'm not the original author of
most of the code, I just added better fragments/segment calculation for
live streams, and other small changes.

2015-04-28 15:32 GMT+02:00 Clément Bœsch <u at pkh.me>:

> On Tue, Apr 28, 2015 at 02:48:42PM +0200, Gorilla Maguila wrote:
> [...]
> > From 5bb2e85f2e7c4dfeb3569225e263ddc6a4f127cd Mon Sep 17 00:00:00 2001
> > From: Developer Mobdro <developer at mobdro.com>
> > Date: Tue, 28 Apr 2015 14:38:35 +0200
> > Subject: [PATCH] hds demuxer
> >
> > ---
> >  configure                 |   8 +
> >  libavformat/Makefile      |   1 +
> >  libavformat/allformats.c  |   2 +-
> >  libavformat/amfmetadata.c | 248 ++++++++++++++
> >  libavformat/amfmetadata.h |  45 +++
> >  libavformat/f4fbox.c      | 311 +++++++++++++++++
> >  libavformat/f4fbox.h      | 101 ++++++
> >  libavformat/f4mmanifest.c | 340 +++++++++++++++++++
> >  libavformat/f4mmanifest.h |  65 ++++
> >  libavformat/flvtag.c      | 385 +++++++++++++++++++++
> >  libavformat/flvtag.h      |  39 +++
> >  libavformat/hdsdec.c      | 826
> ++++++++++++++++++++++++++++++++++++++++++++++
> >  12 files changed, 2370 insertions(+), 1 deletion(-)
> >  create mode 100644 libavformat/amfmetadata.c
> >  create mode 100644 libavformat/amfmetadata.h
> >  create mode 100644 libavformat/f4fbox.c
> >  create mode 100644 libavformat/f4fbox.h
> >  create mode 100644 libavformat/f4mmanifest.c
> >  create mode 100644 libavformat/f4mmanifest.h
> >  create mode 100644 libavformat/flvtag.c
> >  create mode 100644 libavformat/flvtag.h
> >  create mode 100644 libavformat/hdsdec.c
> >
> > diff --git a/configure b/configure
> > index 88e0d97..185f9bc 100755
> > --- a/configure
> > +++ b/configure
> > @@ -277,6 +277,7 @@ External library support:
> >    --enable-x11grab         enable X11 grabbing (legacy) [no]
> >    --disable-xlib           disable xlib [autodetect]
> >    --disable-zlib           disable zlib [autodetect]
> > +  --disable-xml2           disable XML parsing using the C library
> libxml2 [autodetect]
> >
>
> alphabetical order is welcome
>
> >  Toolchain options:
> >    --arch=ARCH              select architecture [$arch]
> > @@ -1425,6 +1426,7 @@ EXTERNAL_LIBRARY_LIST="
> >      x11grab
> >      xlib
> >      zlib
> > +    xml2
> >  "
> >
> >  DOCUMENT_LIST="
> > @@ -2482,6 +2484,7 @@ dxa_demuxer_select="riffdec"
> >  eac3_demuxer_select="ac3_parser"
> >  f4v_muxer_select="mov_muxer"
> >  flac_demuxer_select="flac_parser"
> > +hds_demuxer_select="xml2"
> >  hds_muxer_select="flv_muxer"
> >  hls_muxer_select="mpegts_muxer"
> >  image2_alias_pix_demuxer_select="image2_demuxer"
> > @@ -5014,6 +5017,11 @@ disabled  zlib || check_lib   zlib.h
> zlibVersion -lz   || disable  zlib
> >  disabled bzlib || check_lib2 bzlib.h BZ2_bzlibVersion -lbz2 || disable
> bzlib
> >  disabled  lzma || check_lib2  lzma.h lzma_version_number -llzma ||
> disable lzma
> >
>
> > +disabled xml2 || {
> > +    check_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h
> xmlCheckVersion &&
> > +    require_pkg_config libxml-2.0 libxml2/libxml/xmlversion.h
> xmlCheckVersion
> > +} || disable xml2
> > +
>
> Please no auto-detect of external libs, and no fallback hack
> (keep require_pkg_config exclusively)
>
> >  check_lib math.h sin -lm && LIBM="-lm"
> >  disabled crystalhd || check_lib libcrystalhd/libcrystalhd_if.h
> DtsCrystalHDVersion -lcrystalhd || disable crystalhd
> >
> > diff --git a/libavformat/Makefile b/libavformat/Makefile
> > index 8d9a770..d2062b7 100644
> > --- a/libavformat/Makefile
> > +++ b/libavformat/Makefile
> > @@ -179,6 +179,7 @@ OBJS-$(CONFIG_H263_DEMUXER)              +=
> h263dec.o rawdec.o
> >  OBJS-$(CONFIG_H263_MUXER)                += rawenc.o
> >  OBJS-$(CONFIG_H264_DEMUXER)              += h264dec.o rawdec.o
> >  OBJS-$(CONFIG_H264_MUXER)                += rawenc.o
> > +OBJS-$(CONFIG_HDS_DEMUXER)               += hdsdec.o amfmetadata.o
> f4mmanifest.o f4fbox.o flvtag.o
> >  OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> >  OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> >  OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> > diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> > index e6a9d01..d1be6d9 100644
> > --- a/libavformat/allformats.c
> > +++ b/libavformat/allformats.c
> > @@ -141,7 +141,7 @@ void av_register_all(void)
> >      REGISTER_MUXDEMUX(H261,             h261);
> >      REGISTER_MUXDEMUX(H263,             h263);
> >      REGISTER_MUXDEMUX(H264,             h264);
> > -    REGISTER_MUXER   (HDS,              hds);
> > +    REGISTER_MUXDEMUX(HDS,              hds);
> >      REGISTER_MUXDEMUX(HEVC,             hevc);
> >      REGISTER_MUXDEMUX(HLS,              hls);
> >      REGISTER_DEMUXER (HNM,              hnm);
> > diff --git a/libavformat/amfmetadata.c b/libavformat/amfmetadata.c
> > new file mode 100644
> > index 0000000..148f7cd
> > --- /dev/null
> > +++ b/libavformat/amfmetadata.c
> > @@ -0,0 +1,248 @@
> > +/*
> > + * Adobe Action Message Format Parser
> > + * Copyright (c) 2013 Cory McCarthy
> > + *
> > + * 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
> > + */
> > +
> > +/**
> > + * @file
> > + * @brief Adobe Action Message Format Parser
>
> > + * @author Cory McCarthy
>
> Copyright holder should be enough
>
> > + * @see
> http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
> > + * @see
> http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf
> > + */
> > +
> > +#include "amfmetadata.h"
> > +#include "avio_internal.h"
> > +#include "flv.h"
> > +#include "libavutil/avstring.h"
> > +#include "libavutil/intfloat.h"
> > +
>
> > +static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata
> *metadata, const char *name);
>
> Can't avoid this forward declaration?
>
> > +
> > +
> > +static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
> > +{
> > +    int length = avio_rb16(ioc);
> > +    if (length >= buffsize) {
> > +        avio_skip(ioc, length);
> > +        return -1;
> > +    }
> > +
> > +    avio_read(ioc, buffer, length);
> > +
> > +    buffer[length] = '\0';
> > +
> > +    return length;
> > +}
> > +
> > +static int amf_metadata_read_string_value(AVIOContext *in, char *str,
> int str_size)
> > +{
> > +    uint8_t type;
> > +
> > +    type = avio_r8(in);
>
> > +    if(type != AMF_DATA_TYPE_STRING) {
>
> style: here and several time later, space after if. See
> http://ffmpeg.org/developer.html#toc-Coding-Rules-1
>
> > +        av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected type 2, type =
> %d \n", type);
> > +        return -1;
> > +    }
> > +
> > +    return amf_get_string(in, str, str_size);
> > +}
> > +
> > +static void amf_metadata_assign_property_number(AMFMetadata *metadata,
> > +    const char *name, double value)
> > +{
> > +    if(!av_strcasecmp("width", name)) {
> > +        metadata->width = (int)value;
> > +    }else if(!av_strcasecmp("height", name)) {
> > +        metadata->height = (int)value;
> > +    }else if(!av_strcasecmp("framerate", name)) {
> > +        metadata->frame_rate = (int)value;
> > +    }else if(!av_strcasecmp("videodatarate", name)) {
> > +        metadata->video_data_rate = (int)value;
> > +    }else if(!av_strcasecmp("audiosamplerate", name)) {
> > +        metadata->audio_sample_rate = (int)value;
> > +    }else if(!av_strcasecmp("audiochannels", name)) {
> > +        metadata->nb_audio_channels = (int)value;
> > +    }else if(!av_strcasecmp("stereo", name)) {
> > +        metadata->nb_audio_channels = ((int)value) ? 2 : 1;
> > +    }else if(!av_strcasecmp("audiodatarate", name)) {
> > +        metadata->audio_data_rate = (int)value;
> > +    }else if(!av_strcasecmp("audiocodecid", name)) {
> > +        if((int)value == 10)
> > +            metadata->audio_codec_id = AV_CODEC_ID_AAC;
> > +    }else if(!av_strcasecmp("videocodecid", name)) {
> > +        if((int)value == 7)
> > +            metadata->video_codec_id = AV_CODEC_ID_H264;
> > +    }
> > +}
> > +
> > +static void amf_metadata_assign_property_string(AMFMetadata *metadata,
> > +    const char *name, const char *value)
> > +{
> > +    if(!av_strcasecmp("audiocodecid", name)) {
> > +        if(!av_strcasecmp("mp4a", value))
> > +            metadata->audio_codec_id = AV_CODEC_ID_AAC;
>
> > +     else if(!av_strcasecmp("aac", value))
>
> Here and later: tabs are not allowed in FFmpeg, it won't be pushable.
>
> > +            metadata->audio_codec_id = AV_CODEC_ID_AAC;
> > +    }
> > +    else
> > +    if(!av_strcasecmp("videocodecid", name)) {
> > +        if(!av_strcasecmp("avc1", value))
> > +            metadata->video_codec_id = AV_CODEC_ID_H264;
> > +     else if(!av_strcasecmp("h264", value))
> > +         metadata->video_codec_id = AV_CODEC_ID_H264;
> > +    }
> > +}
> > +
> > +static int amf_metadata_parse_object_property(AVIOContext *in,
> AMFMetadata *metadata)
> > +{
>
> > +    char name[INT16_MAX];
>
> Here and later, this is way too large for a stack item.
>
> > +    int ret;
> > +
> > +    if((ret = amf_get_string(in, name, sizeof(name))) < 0)
> > +        return ret;
> > +
>
> > +    if(!strlen(name))
> > +        return -1;
>
> No need to call strlen to check only the first character.
>
> > +
> > +    return amf_metadata_parse_value(in, metadata, name);
> > +}
> > +
> > +static int amf_metadata_parse_object(AVIOContext *in, AMFMetadata
> *metadata)
> > +{
> > +    int ret;
> > +
> > +    while(!avio_feof(in)) {
> > +        if((ret = amf_metadata_parse_object_property(in, metadata)) <
> 0) {
> > +            if(avio_r8(in) != AMF_END_OF_OBJECT)
> > +                return ret;
> > +            break;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int amf_metadata_parse_strict_array(AVIOContext *in, AMFMetadata
> *metadata)
> > +{
> > +    int length;
> > +    int ret;
> > +
> > +    length = avio_rb32(in);
> > +    while(!avio_feof(in) && length > 0) {
> > +        if((ret = amf_metadata_parse_value(in, metadata, NULL)) < 0)
> > +            return ret;
> > +        length--;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata
> *metadata, const char *name)
> > +{
> > +    uint8_t type;
> > +    char value_str[INT16_MAX];
> > +    double value_number;
> > +    int ret = 0;
> > +
> > +    type = avio_r8(in);
> > +
>
> > +    if(type == AMF_DATA_TYPE_NUMBER) {
> > +        value_number = av_int2double(avio_rb64(in));
> > +        amf_metadata_assign_property_number(metadata, name,
> value_number);
> > +    }
> > +    else if(type == AMF_DATA_TYPE_BOOL) {
> > +        value_number = avio_r8(in);
> > +        amf_metadata_assign_property_number(metadata, name,
> value_number);
> > +    }
> > +    else if(type == AMF_DATA_TYPE_STRING) {
> > +        if((ret = amf_get_string(in, value_str, sizeof(value_str))) < 0)
> > +            return ret;
> > +        amf_metadata_assign_property_string(metadata, name, value_str);
> > +    }
> > +    else if(type == AMF_DATA_TYPE_OBJECT) {
> > +        ret = amf_metadata_parse_object(in, metadata);
> > +    }
> > +    else if(type == AMF_DATA_TYPE_MIXEDARRAY) {
> > +        avio_skip(in, 4);
> > +        ret = amf_metadata_parse_object(in, metadata);
> > +    }
> > +    else if(type == AMF_DATA_TYPE_ARRAY) {
> > +        ret = amf_metadata_parse_strict_array(in, metadata);
> > +    }
>
> You can use a switch here
>
> > +
> > +    return ret;
> > +}
> > +
> > +static int amf_metadata_parse(AVIOContext *in, AMFMetadata *metadata)
> > +{
> > +    char name[INT16_MAX];
> > +    int ret;
> > +
> > +    if((ret = amf_metadata_read_string_value(in, name, sizeof(name))) <
> 0) {
> > +        av_log(NULL, AV_LOG_ERROR, "amfmetadata Failed to read
> onMetadata string, ret: %d \n", ret);
> > +        return ret;
> > +    }
> > +
> > +    if(av_strcasecmp(name, "onMetaData")) {
> > +        av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected onMetadata,
> str = %s \n", name);
> > +        return -1;
> > +    }
> > +
> > +    return amf_metadata_parse_value(in, metadata, name);
> > +}
> > +
> > +int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata
> *metadata)
> > +{
> > +    AVIOContext *in;
> > +    int ret;
> > +
> > +    if(!buffer)
> > +        return 0;
> > +    if(buffer_size <= 0)
> > +        return 0;
> > +
> > +    in = avio_alloc_context(buffer, buffer_size,
> > +        0, NULL, NULL, NULL, NULL);
> > +    if(!in)
> > +        return AVERROR(ENOMEM);
> > +
> > +    ret = amf_metadata_parse(in, metadata);
> > +    av_freep(&in);
> > +
> > +    return ret;
> > +}
> > diff --git a/libavformat/amfmetadata.h b/libavformat/amfmetadata.h
> > new file mode 100644
> > index 0000000..a9ed998
> > --- /dev/null
> > +++ b/libavformat/amfmetadata.h
> > @@ -0,0 +1,45 @@
> > +/*
> > + * Adobe Action Message Format Parser
> > + * Copyright (c) 2013 Cory McCarthy
> > + *
> > + * 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
> > + */
> > +
> > +/**
> > + * @file
> > + * @brief Adobe Action Message Format Parser
> > + * @author Cory McCarthy
> > + * @see
> http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
> > + * @see
> http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf
> > + */
> > +
> > +#include "libavcodec/avcodec.h"
> > +
> > +typedef struct AMFMetadata {
> > +    int width;
> > +    int height;
> > +    int frame_rate;
> > +    int audio_sample_rate;
> > +    int nb_audio_channels;
> > +    int audio_data_rate;
> > +    int video_data_rate;
> > +
> > +    enum AVCodecID audio_codec_id;
> > +    enum AVCodecID video_codec_id;
> > +} AMFMetadata;
> > +
> > +int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata
> *metadata);
> > diff --git a/libavformat/f4fbox.c b/libavformat/f4fbox.c
> > new file mode 100644
> > index 0000000..37f6912
> > --- /dev/null
> > +++ b/libavformat/f4fbox.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + * Adobe Fragmented F4V File (F4F) Parser
> > + * Copyright (c) 2013 Cory McCarthy
> > + *
> > + * 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
> > + */
> > +
> > +/**
> > + * @file
> > + * @brief Adobe Fragmented F4V File (F4F) Parser for Adobe HDS
> > + * @author Cory McCarthy
> > + * @see
> http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
> > + */
> > +
> > +#include "f4fbox.h"
> > +#include "avformat.h"
> > +
>
> > +static int f4fbox_parse_single_box(AVIOContext *in, void *opaque);
> > +
>
> Again, isn't this avoidable?
>
> > +static int f4fbox_parse_asrt(AVIOContext *in, int64_t data_size, void
> *opaque)
> > +{
> > +    F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opaque;
> > +    F4FSegmentRunTableBox *asrt;
> > +    F4FSegmentRunEntry *entry;
> > +    uint8_t quality_entry_count;
> > +    uint32_t segment_run_entry_count;
> > +    char url[1024];
> > +    int i;
> > +
>
> > +    asrt = av_mallocz(sizeof(F4FSegmentRunTableBox));
>
> asrt = av_mallocz(sizeof(*asrt));
>
> ditto later on
>
> > +    if(!asrt)
> > +        return AVERROR(ENOMEM);
> > +
> > +    parent->segment_run_table_boxes[parent->nb_segment_run_table_boxes]
> = asrt;
> > +    parent->nb_segment_run_table_boxes++;
> > +
> > +    asrt->version = avio_r8(in);
> > +    asrt->flags = avio_rb24(in);
> > +
> > +    quality_entry_count = avio_r8(in);
> > +    for(i = 0; i < quality_entry_count; i++) {
> > +        avio_get_str(in, sizeof(url), url, sizeof(url));
> > +    }
> > +
> > +    segment_run_entry_count = avio_rb32(in);
> > +    for(i = 0; i < segment_run_entry_count; i++) {
> > +        entry = av_mallocz(sizeof(F4FSegmentRunEntry));
> > +        if(!entry)
> > +            return AVERROR(ENOMEM);
> > +
> > +        asrt->segment_run_entries[asrt->nb_segment_run_entries] = entry;
> > +        asrt->nb_segment_run_entries++;
> > +
> > +        entry->first_segment = avio_rb32(in);
>
> > +
>
> trailing whitespace
>
> > +        entry->fragments_per_segment = avio_rb32(in);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int f4fbox_parse_afrt(AVIOContext *in, int64_t data_size, void
> *opaque)
> > +{
> > +    F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opaque;
> > +    F4FFragmentRunTableBox *afrt;
> > +    F4FFragmentRunEntry *entry;
> > +    uint8_t quality_entry_count;
> > +    uint32_t fragment_run_entry_count;
> > +    char url[1024];
> > +    int i;
> > +
> > +    afrt = av_mallocz(sizeof(F4FFragmentRunTableBox));
> > +    if(!afrt)
> > +        return AVERROR(ENOMEM);
> > +
> > +
> parent->fragment_run_table_boxes[parent->nb_fragment_run_table_boxes] =
> afrt;
> > +    parent->nb_fragment_run_table_boxes++;
> > +
> > +    afrt->version = avio_r8(in);
> > +    afrt->flags = avio_rb24(in);
> > +
> > +    afrt->timescale = avio_rb32(in);
> > +
> > +    quality_entry_count = avio_r8(in);
> > +    for(i = 0; i < quality_entry_count; i++) {
> > +        avio_get_str(in, sizeof(url), url, sizeof(url));
> > +    }
> > +
> > +    fragment_run_entry_count = avio_rb32(in);
> > +    for(i = 0; i < fragment_run_entry_count; i++) {
> > +        entry = av_mallocz(sizeof(F4FFragmentRunEntry));
> > +        if(!entry)
> > +            return AVERROR(ENOMEM);
> > +
> > +        afrt->fragment_run_entries[afrt->nb_fragment_run_entries] =
> entry;
> > +        afrt->nb_fragment_run_entries++;
> > +
> > +        entry->first_fragment = avio_rb32(in);
> > +        entry->first_fragment_time_stamp = avio_rb64(in);
> > +        entry->fragment_duration = avio_rb32(in);
> > +        if(entry->fragment_duration == 0) {
> > +            entry->discontinuity_indicator = avio_r8(in);
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +
> > +static int f4fbox_parse_abst(AVIOContext *in, int64_t data_size, void
> *opaque)
> > +{
> > +    F4FBox *parent = (F4FBox*)opaque;
> > +    F4FBootstrapInfoBox *abst = &(parent->abst);
> > +    uint8_t server_entry_count, quality_entry_count;
> > +    uint8_t segment_run_table_count, fragment_run_table_count;
> > +    uint8_t byte;
> > +    char url[1024];
> > +    int i, ret;
> > +
> > +    abst->version = avio_r8(in);
> > +    abst->flags = avio_rb24(in);
> > +    abst->bootstrap_info_version = avio_rb32(in);
> > +
> > +    byte = avio_r8(in);
>
> > +    abst->profile = (byte >> 6) & 0x03;
> > +    abst->is_live = (byte >> 5) & 0x01;
> > +    abst->is_update = (byte >> 4) & 0x01;
>
> nit: parenthesis are useless
>
> > +
> > +    abst->timescale = avio_rb32(in);
> > +    abst->current_media_time = avio_rb64(in);
> > +    abst->smpte_time_code_offset = avio_rb64(in);
> > +
> > +    avio_get_str(in, sizeof(abst->movie_id), abst->movie_id,
> sizeof(abst->movie_id));
> > +
> > +    server_entry_count = avio_r8(in);
> > +    for(i = 0; i < server_entry_count; i++) {
> > +        avio_get_str(in, sizeof(url), url, sizeof(url));
> > +    }
> > +
> > +    quality_entry_count = avio_r8(in);
> > +    for(i = 0; i < quality_entry_count; i++) {
> > +        avio_get_str(in, sizeof(url), url, sizeof(url));
> > +    }
> > +
> > +    avio_get_str(in, sizeof(abst->drm_data), abst->drm_data,
> sizeof(abst->drm_data));
> > +    avio_get_str(in, sizeof(abst->metadata), abst->metadata,
> sizeof(abst->metadata));
> > +
> > +    segment_run_table_count = avio_r8(in);
> > +    for(i = 0; i < segment_run_table_count; i++) {
> > +        if((ret = f4fbox_parse_single_box(in, abst)) < 0) {
> > +            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse asrt
> box, ret: %d \n", ret);
> > +            return ret;
> > +        }
> > +    }
> > +
> > +    fragment_run_table_count = avio_r8(in);
> > +    for(i = 0; i < fragment_run_table_count; i++) {
> > +        if((ret = f4fbox_parse_single_box(in, abst)) < 0) {
> > +            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse afrt
> box, ret: %d \n", ret);
> > +            return ret;
> > +        }
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int f4fbox_parse_mdat(AVIOContext *in, int64_t data_size, void
> *opaque)
> > +{
> > +    F4FBox *parent = (F4FBox*)opaque;
> > +    F4FMediaDataBox *mdat = &(parent->mdat);
> > +
> > +    mdat->data = av_mallocz(sizeof(uint8_t)*data_size);
> > +    if(!mdat->data)
> > +        return AVERROR(ENOMEM);
> > +
> > +    mdat->size = data_size;
> > +    avio_read(in, mdat->data, mdat->size);
> > +
> > +    return 0;
> > +}
> > +
> > +static int f4fbox_parse_single_box(AVIOContext *in, void *opaque)
> > +{
> > +    int64_t bytes_read, bytes_left, start_pos, end_pos;
> > +    uint64_t size;
> > +    uint32_t type;
> > +    int ret = 0;
> > +
> > +    bytes_read = 0;
> > +    start_pos = avio_tell(in);
> > +
> > +    size = avio_rb32(in);
> > +    type = avio_rl32(in);
> > +    bytes_read += 8;
> > +
> > +    if(size == 1) {/* 64 bit extended size */
> > +        size = avio_rb64(in) - 8;
> > +        bytes_read += 8;
> > +    }
> > +
> > +    if(size == 0)
> > +        return -1;
> > +
> > +    if(type == MKTAG('a', 'b', 's', 't')) {
> > +        ret = f4fbox_parse_abst(in, size, opaque);
> > +    }
> > +    if(type == MKTAG('a', 's', 'r', 't')) {
> > +        ret = f4fbox_parse_asrt(in, size, opaque);
> > +    }
> > +    if(type == MKTAG('a', 'f', 'r', 't')) {
> > +        ret = f4fbox_parse_afrt(in, size, opaque);
> > +    }
> > +    if(type == MKTAG('m', 'd', 'a', 't')) {
> > +        ret = f4fbox_parse_mdat(in, size, opaque);
> > +    }
> > +
> > +    if(ret < 0)
> > +        return ret;
> > +
> > +    end_pos = avio_tell(in);
> > +    bytes_left = size - (end_pos - start_pos);
> > +    if(bytes_left > 0)
> > +        avio_skip(in, bytes_left);
> > +
> > +    bytes_read += size;
> > +
> > +    return bytes_read;
> > +}
> > +
> > +static int f4fbox_parse(AVIOContext *in, int64_t data_size, void
> *opaque)
> > +{
> > +    int64_t bytes_read = 0;
> > +    int ret;
> > +
> > +    while(!avio_feof(in) && bytes_read + 8 < data_size) {
> > +        if((ret = f4fbox_parse_single_box(in, opaque)) < 0) {
> > +            av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse box,
> ret: %d \n", ret);
> > +            return ret;
> > +        }
> > +        bytes_read += ret;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +int ff_parse_f4f_box(uint8_t *buffer, int buffer_size, F4FBox *box)
> > +{
> > +    AVIOContext *in;
> > +    int ret;
> > +
> > +    in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL,
> NULL);
> > +    if(!in)
> > +        return AVERROR(ENOMEM);
> > +
> > +    ret = f4fbox_parse(in, buffer_size, box);
> > +    av_freep(&in);
> > +
> > +    return ret;
> > +}
> > +
> > +int ff_free_f4f_box(F4FBox *box)
> > +{
> > +    F4FBootstrapInfoBox *abst;
> > +    F4FSegmentRunTableBox *asrt;
> > +    F4FSegmentRunEntry *sre;
> > +    F4FFragmentRunTableBox *afrt;
> > +    F4FFragmentRunEntry *fre;
> > +    F4FMediaDataBox *mdat;
> > +    int i, j;
> > +
> > +    abst = &(box->abst);
> > +    for(i = 0; i < abst->nb_segment_run_table_boxes; i++) {
> > +        asrt = abst->segment_run_table_boxes[i];
> > +        for(j = 0; j < asrt->nb_segment_run_entries; j++) {
> > +            sre = asrt->segment_run_entries[j];
> > +            av_freep(&sre);
> > +        }
> > +        av_freep(&asrt);
> > +    }
> > +
> > +    for(i = 0; i < abst->nb_fragment_run_table_boxes; i++) {
> > +        afrt = abst->fragment_run_table_boxes[i];
> > +        for(j = 0; j < afrt->nb_fragment_run_entries; j++) {
> > +            fre = afrt->fragment_run_entries[j];
> > +            av_freep(&fre);
> > +        }
> > +        av_freep(&afrt);
> > +    }
> > +
> > +    mdat = &(box->mdat);
> > +    if(mdat->size > 0)
> > +        av_freep(&mdat->data);
> > +
>
> > +    memset(box, 0x00, sizeof(F4FBox));
> > +
>
> sizeof(*box);
>
> [...]
> > +#include "f4mmanifest.h"
> > +#include "libavutil/avstring.h"
> > +#include "libavutil/base64.h"
>
> > +#include <libxml/parser.h>
> > +#include <libxml/tree.h>
>
> system headers before local ones
>
> > +
> > +#define XML_FORMATIC_TAB 0x0a
> > +#define XML_FORMATIC_LF 0x09
> > +#define XML_FORMATIC_CR 0x0d
> > +#define XML_FORMATIC_WHITE 0x20
> > +
> > +
> > +static int f4m_get_xml_content_offset(xmlChar *p){
> > +
> > +    int result = 0;
> > +    int len = strlen(p);
> > +    int i;
> > +
> > +    for(i = 0; i < len; i++){
> > +        if(p[i] == XML_FORMATIC_LF || p[i] == XML_FORMATIC_TAB || p[i]
> == XML_FORMATIC_CR || p[i] == XML_FORMATIC_WHITE)
> > +            result++;
> > +        else
> > +       break;
> > +    }
> > +
> > +    if(result > len)
> > +        result = 0;
> > +
> > +    return result;
> > +}
> > +
> > +static int f4m_get_content_length(xmlChar *p){
> > +
> > +    int result = 0;
> > +    int len = strlen(p);
> > +    int i;
> > +
> > +    for(i = 0; i < len; i++){
> > +        if(p[i] != XML_FORMATIC_LF && p[i] != XML_FORMATIC_TAB && p[i]
> != XML_FORMATIC_CR && p[i] != XML_FORMATIC_WHITE)
> > +      result++;
> > +
> > +    }
> > +
> > +    result++;
> > +
> > +    if(result > MAX_URL_SIZE)
> > +        result = MAX_URL_SIZE;
> > +
> > +    return result;
> > +
> > +}
>
> Many trailing whitespaces (and tab) here. git or your configured editor
> should show them
>
> [...]
>
> > + * @note Test streams are below:
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/april11/hdworld/hdworld_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/april11/cctv/cctv_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/april11/sintel/sintel-hd_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,1280x720_1900_m,1280x720_2500_m,1280x720_3500_m,.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/akamai10year/Akamai_10_Year_,200,300,600,800,1000,1500,2500,4000,k.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/seeker/LegendofSeeker_16x9_24fps_H264_,400K,650K,1Mbps,1.4Mbps,1.8Mbps,2.5Mbps,.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,1280x720_2000,1280x720_3000,.f4v.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/companion/nba_game/nba_game.mov_,300,600,800,1000,2500,4000,9000,k.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/companion/big_bang_theory/big_bang_theory.mov_,300,600,800,1000,2500,4000,9000,k.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/shuttle/shuttle_,300,600,800,1000,k.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiplatform-f.akamaihd.net/z/multi/up_trailer/up_trailer_720p_,300,600,800,1000,k.mp4.csmil/manifest.f4m?hdcore
> > + * @test
> http://multiformatlive-f.akamaihd.net/z/demostream_1@2131/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/darkknight/darkknight.smil/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/amours/amours.smil/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/robinhood/robinhood.smil/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/wallstreet/wallstreet.smil/manifest.f4m?hdcore
> > + * @test
> http://zerihdndemo-f.akamaihd.net/z/h264/rockandroll/rockandroll.smil/manifest.f4m?hdcore
> > + * @test http://184.72.239.149/vod/smil:bigbuckbunny.smil/manifest.f4m
> > + *
> > + * @test
> http://livehds.rasset.ie/hds-live/_definst_/newsnow/newsnow_540p.f4m
> > + * @test http://livehds.rasset.ie/hds-live/_definst_/rte1/rte1_288p.f4m
> > + * @test http://livehds.rasset.ie/hds-live/_definst_/rte2/rte2_288p.f4m
> > + * @test
> http://ooyalahd2-f.akamaihd.net/z/godtv02_delivery@17351/manifest.f4m?hdcore=2.10.3&g=ILYQWQWFPMLW
>
> These will die pretty quickly. Add a FATE test instead.
>
> [...]
>
> --
> Clément B.
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>


More information about the ffmpeg-devel mailing list