[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