[FFmpeg-devel] [PATCH] add hds demuxer

Steven Liu lingjiujianke at gmail.com
Sun Oct 16 06:32:37 EEST 2016


update

2016-10-15 0:08 GMT+08:00 Steven Liu <lq at chinaffmpeg.org>:

> patch update.
>
> test passed:
> Linux:
> ../configure
> MingW:
> ../configure --cc='ccache x86_64-w64-mingw32-gcc'   --arch=x86_64
> --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --disable-yasm
>
> test passed with hds demux
> Linux:
> ../configure --enable-xml2
> OSX:
> ../configure --disable-everything --enable-demuxer=hds --disable-yasm
> --enable-protocol=file --enable-decoder=h264 --enable-decoder=aac
> --enable-xml2
>
> init add hds demuxer
> TODO: xml parser from libxml to libexpat or simpleparser[implementation by
> myself]
> TODO: docs, version bump,
> TODO: option passing support (as seperate commit/patch)
> TODO: refine AMF parser
> TODO: refine Metadata
>
> Based-on: patch by CORY MCCARTHY <cory.mccarthy at shaw.ca>
> Based-on: patch by Gorilla Maguila <gorilla.maguila at gmail.com>
> Signed-off-by: Steven Liu <lq at chinaffmpeg.org>
> ---
>  configure                 |    4 +
>  libavformat/Makefile      |    1 +
>  libavformat/allformats.c  |    2 +-
>  libavformat/amfmetadata.c |  219 +++++++++++++
>  libavformat/amfmetadata.h |   39 +++
>  libavformat/f4fbox.c      |  381 +++++++++++++++++++++++
>  libavformat/f4fbox.h      |   95 ++++++
>  libavformat/f4mmanifest.c |  324 +++++++++++++++++++
>  libavformat/f4mmanifest.h |   59 ++++
>  libavformat/flvtag.c      |  370 ++++++++++++++++++++++
>  libavformat/flvtag.h      |   32 ++
>  libavformat/hdsdec.c      |  759 ++++++++++++++++++++++++++++++
> +++++++++++++++
>  12 files changed, 2284 insertions(+), 1 deletions(-)
>  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 8d9b21b..6938b28 100755
> --- a/configure
> +++ b/configure
> @@ -295,6 +295,7 @@ External library support:
>                             on OSX if openssl and gnutls are not used
> [autodetect]
>    --enable-x11grab         enable X11 grabbing (legacy) [no]
>    --disable-xlib           disable xlib [autodetect]
> +  --enable-xml2            enable XML parsing using the C library libxml2
> [no]
>    --disable-zlib           disable zlib [autodetect]
>
>    The following libraries provide various hardware acceleration features:
> @@ -1552,6 +1553,7 @@ EXTERNAL_LIBRARY_LIST="
>      videotoolbox
>      x11grab
>      xlib
> +    xml2
>      zlib
>  "
>
> @@ -2854,6 +2856,7 @@ eac3_demuxer_select="ac3_parser"
>  f4v_muxer_select="mov_muxer"
>  fifo_muxer_deps="threads"
>  flac_demuxer_select="flac_parser"
> +hds_demuxer_deps="xml2"
>  hds_muxer_select="flv_muxer"
>  hls_muxer_select="mpegts_muxer"
>  image2_alias_pix_demuxer_select="image2_demuxer"
> @@ -5675,6 +5678,7 @@ enabled jni               && { [ $target_os =
> "android" ] && check_header jni.h
>                                 check_lib2 "dlfcn.h" dlopen -ldl; }
>  enabled ladspa            && { check_header ladspa.h || die "ERROR:
> ladspa.h header not found"; }
>  enabled libiec61883       && require libiec61883 libiec61883/iec61883.h
> iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883
> +enabled xml2              && { require_pkg_config libxml-2.0
> libxml2/libxml/xmlversion.h xmlCheckVersion || disabled-xml2; }
>  enabled libass            && require_pkg_config libass ass/ass.h
> ass_library_init
>  enabled libbluray         && require_pkg_config libbluray
> libbluray/bluray.h bd_open
>  enabled libbs2b           && require_pkg_config libbs2b bs2b.h bs2b_open
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 5d827d3..e2b4dd4 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -202,6 +202,7 @@ OBJS-$(CONFIG_H264_DEMUXER)              += h264dec.o
> rawdec.o
>  OBJS-$(CONFIG_H264_MUXER)                += rawenc.o
>  OBJS-$(CONFIG_HASH_MUXER)                += hashenc.o
>  OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> +OBJS-$(CONFIG_HDS_DEMUXER)               += hdsdec.o amfmetadata.o
> f4mmanifest.o f4fbox.o flvtag.o
>  OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
>  OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
>  OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index 6a216ef..39505c3 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -146,9 +146,9 @@ void av_register_all(void)
>      REGISTER_MUXDEMUX(H263,             h263);
>      REGISTER_MUXDEMUX(H264,             h264);
>      REGISTER_MUXER   (HASH,             hash);
> -    REGISTER_MUXER   (HDS,              hds);
>      REGISTER_MUXDEMUX(HEVC,             hevc);
>      REGISTER_MUXDEMUX(HLS,              hls);
> +    REGISTER_MUXDEMUX(HDS,              hds);
>      REGISTER_DEMUXER (HNM,              hnm);
>      REGISTER_MUXDEMUX(ICO,              ico);
>      REGISTER_DEMUXER (IDCIN,            idcin);
> diff --git a/libavformat/amfmetadata.c b/libavformat/amfmetadata.c
> new file mode 100644
> index 0000000..0e7a2ea
> --- /dev/null
> +++ b/libavformat/amfmetadata.c
> @@ -0,0 +1,219 @@
> +/*
> + * 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
> + */
> +
> +
> +#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);
> +
> +static int amf_get_string(AVIOContext *in, char *buffer, int buffsize)
> +{
> +    int length;
> +
> +    length = avio_rb16(in);
> +    if (length >= buffsize) {
> +        avio_skip(in, length);
> +        return AVERROR_INVALIDDATA;
> +    }
> +    avio_read(in, 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){
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    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 = value;
> +    } else if (!av_strcasecmp("height", name)){
> +        metadata->height = value;
> +    } else if (!av_strcasecmp("framerate", name)){
> +        metadata->frame_rate = value;
> +    } else if (!av_strcasecmp("videodatarate", name)){
> +        metadata->video_data_rate = value;
> +    } else if (!av_strcasecmp("audiosamplerate", name)){
> +        metadata->audio_sample_rate = value;
> +    } else if (!av_strcasecmp("audiochannels", name)){
> +        metadata->nb_audio_channels = value;
> +    } else if (!av_strcasecmp("stereo", name)){
> +        metadata->nb_audio_channels = (value) ? 2 : 1;
> +    } else if (!av_strcasecmp("audiodatarate", name)){
> +        metadata->audio_data_rate = value;
> +    } else if (!av_strcasecmp("audiocodecid", name)){
> +        if (value == 10)
> +            metadata->audio_codec_id = AV_CODEC_ID_AAC;
> +    } else if (!av_strcasecmp("videocodecid", name)){
> +        if (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)){
> +            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[1024];
> +    int ret;
> +
> +    if ((ret = amf_get_string(in, name, sizeof(name))) < 0)
> +        return ret;
> +
> +    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[1024];
> +    double value_number;
> +    int ret = 0;
> +
> +    type = avio_r8(in);
> +    switch (type){
> +        case AMF_DATA_TYPE_NUMBER:
> +            value_number = av_int2double(avio_rb64(in));
> +            amf_metadata_assign_property_number(metadata, name,
> value_number);
> +            break;
> +        case AMF_DATA_TYPE_BOOL:
> +            value_number = avio_r8(in);
> +            amf_metadata_assign_property_number(metadata, name,
> value_number);
> +            break;
> +        case 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);
> +            break;
> +        case AMF_DATA_TYPE_OBJECT:
> +            ret = amf_metadata_parse_object(in, metadata);
> +            break;
> +        case AMF_DATA_TYPE_MIXEDARRAY:
> +            avio_skip(in, 4);
> +            ret = amf_metadata_parse_object(in, metadata);
> +            break;
> +        case AMF_DATA_TYPE_ARRAY:
> +            ret = amf_metadata_parse_strict_array(in, metadata);
> +            break;
> +        default:
> +            break;
> +    }
> +    return ret;
> +}
> +
> +static int amf_metadata_parse(AVFormatContext *s, AVIOContext *in,
> AMFMetadata *metadata)
> +{
> +    char name[1024];
> +    int ret;
> +
> +    if ((ret = amf_metadata_read_string_value(in, name, sizeof(name))) <
> 0) {
> +        av_log(s, AV_LOG_ERROR, "amfmetadata Failed to read onMetadata
> string, ret: %d \n", ret);
> +        return ret;
> +    }
> +
> +    if (av_strcasecmp(name, "onMetaData")) {
> +        av_log(s, AV_LOG_ERROR, "amfmetadata Expected onMetadata, str =
> %s \n", name);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    return amf_metadata_parse_value(in, metadata, name);
> +}
> +
> +int ff_parse_amf_metadata(AVFormatContext *s, AMFMetadata *metadata,
> uint8_t *buffer, int buffer_size)
> +{
> +    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(s, in, metadata);
> +    av_freep(&in);
> +
> +    return ret;
> +}
> diff --git a/libavformat/amfmetadata.h b/libavformat/amfmetadata.h
> new file mode 100644
> index 0000000..f0c85c3
> --- /dev/null
> +++ b/libavformat/amfmetadata.h
> @@ -0,0 +1,39 @@
> +/*
> + * 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
> + */
> +
> +
> +#include "avformat.h"
> +#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(AVFormatContext *s ,AMFMetadata *metadata,
> uint8_t *buffer, int buffer_size);
> diff --git a/libavformat/f4fbox.c b/libavformat/f4fbox.c
> new file mode 100644
> index 0000000..21d4eb0
> --- /dev/null
> +++ b/libavformat/f4fbox.c
> @@ -0,0 +1,381 @@
> +/*
> + * 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
> + */
> +
> +
> +#include "f4fbox.h"
> +#include "avformat.h"
> +
> +static int f4fbox_parse_asrt(AVFormatContext *s, 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(*asrt));
> +    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(*entry));
> +        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);
> +        entry->fragments_per_segment = avio_rb32(in);
> +    }
> +
> +    return 0;
> +}
> +
> +static int f4fbox_parse_afrt(AVFormatContext *s, 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] = {0, };
> +    int i;
> +
> +    afrt = av_mallocz(sizeof(*afrt));
> +    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(*entry));
> +        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) {
> +            entry->discontinuity_indicator = avio_r8(in);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +
> +static int f4fbox_parse_single_afrt(AVFormatContext *s, 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){
> +        return AVERROR_INVALIDDATA;
> +    }
> +    if (type == MKTAG('a', 'f', 'r', 't')) {
> +        ret = f4fbox_parse_afrt(s, in, size, opaque);
> +    } else {
> +        return AVERROR_INVALIDDATA;
> +    }
> +    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_single_asrt(AVFormatContext *s, 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){
> +        return AVERROR_INVALIDDATA;
> +    }
> +    if (type == MKTAG('a', 's', 'r', 't')) {
> +        ret = f4fbox_parse_asrt(s, in, size, opaque);
> +    } else {
> +        return AVERROR_INVALIDDATA;
> +    }
> +    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_abst(AVFormatContext *s, 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] = {0, };
> +    int i;
> +    int ret = 0;
> +
> +    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;
> +
> +    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_asrt(s, in, abst)) < 0) {
> +            av_log(s, 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_afrt(s, in, abst)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "f4fbox Failed to parse afrt box,
> ret: %d \n", ret);
> +            return ret;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +static int f4fbox_parse_mdat(AVFormatContext *s, AVIOContext *in,
> int64_t data_size, void *opaque)
> +{
> +    F4FBox *parent = (F4FBox*)opaque;
> +    F4FMediaDataBox *mdat = &(parent->mdat);
> +
> +    mdat->data = av_malloc(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(AVFormatContext *s, 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){
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    switch (type) {
> +        case MKTAG('a', 'b', 's', 't'):
> +            ret = f4fbox_parse_abst(s, in, size, opaque);
> +            break;
> +        case MKTAG('a', 's', 'r', 't'):
> +            ret = f4fbox_parse_asrt(s, in, size, opaque);
> +            break;
> +        case MKTAG('a', 'f', 'r', 't'):
> +            ret = f4fbox_parse_afrt(s, in, size, opaque);
> +            break;
> +        case MKTAG('m', 'd', 'a', 't'):
> +            ret = f4fbox_parse_mdat(s, in, size, opaque);
> +            break;
> +        default:
> +            break;
> +    }
> +
> +    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(AVFormatContext *s, 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(s, in, opaque)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "f4fbox Failed to parse box, ret: %d
> \n", ret);
> +            return ret;
> +        }
> +        bytes_read += ret;
> +    }
> +
> +    return 0;
> +}
> +
> +int ff_parse_f4f_box(AVFormatContext *s, uint8_t *buffer, int
> buffer_size, void *opaque)
> +{
> +    AVIOContext *in;
> +    int ret;
> +
> +    in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL,
> NULL);
> +    if (!in)
> +        return AVERROR(ENOMEM);
> +
> +    ret = f4fbox_parse(s, in, buffer_size, opaque);
> +    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(*box));
> +
> +    return 0;
> +}
> diff --git a/libavformat/f4fbox.h b/libavformat/f4fbox.h
> new file mode 100644
> index 0000000..c0aa4f2
> --- /dev/null
> +++ b/libavformat/f4fbox.h
> @@ -0,0 +1,95 @@
> +/*
> + * 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
> + */
> +
> +#include "avformat.h"
> +#include "avio_internal.h"
> +
> +#define MAX_NB_SEGMENT_RUN_TABLE_BOXES 256
> +#define MAX_NB_FRAGMENT_RUN_TABLE_BOXES 256
> +
> +#define MAX_NB_SEGMENT_RUN_ENTRIES 1024
> +#define MAX_NB_FRAGMENT_RUN_ENTRIES 1024
> +
> +typedef struct F4FFragmentRunEntry {
> +    uint32_t first_fragment;
> +    uint64_t first_fragment_time_stamp;
> +    uint32_t fragment_duration;
> +    uint8_t discontinuity_indicator;
> +} F4FFragmentRunEntry;
> +
> +typedef struct F4FFragmentRunTableBox {
> +    uint8_t version;
> +    uint32_t flags;
> +    uint32_t timescale;
> +
> +    uint32_t nb_fragment_run_entries;
> +    F4FFragmentRunEntry *fragment_run_entries[MAX_NB_
> FRAGMENT_RUN_ENTRIES];
> +} F4FFragmentRunTableBox;
> +
> +typedef struct F4FSegmentRunEntry {
> +    uint32_t first_segment;
> +    uint32_t fragments_per_segment;
> +} F4FSegmentRunEntry;
> +
> +typedef struct F4FSegmentRunTableBox {
> +    uint8_t version;
> +    uint32_t flags;
> +
> +    uint32_t nb_segment_run_entries;
> +    F4FSegmentRunEntry *segment_run_entries[MAX_NB_SEGMENT_RUN_ENTRIES];
> +} F4FSegmentRunTableBox;
> +
> +typedef struct F4FBootstrapInfoBox {
> +    uint8_t version;
> +    uint32_t flags;
> +    uint32_t bootstrap_info_version;
> +
> +    uint8_t profile;
> +    uint8_t is_live;
> +    uint8_t is_update;
> +
> +    uint32_t timescale;
> +    uint64_t current_media_time;
> +    uint64_t smpte_time_code_offset;
> +
> +    char movie_id[1024];
> +    char drm_data[1024];
> +    char metadata[1024];
> +
> +    uint8_t nb_segment_run_table_boxes;
> +    F4FSegmentRunTableBox *segment_run_table_boxes[MAX_
> NB_SEGMENT_RUN_TABLE_BOXES];
> +
> +    uint8_t nb_fragment_run_table_boxes;
> +    F4FFragmentRunTableBox *fragment_run_table_boxes[MAX_
> NB_FRAGMENT_RUN_TABLE_BOXES];
> +} F4FBootstrapInfoBox;
> +
> +typedef struct F4FMediaDataBox {
> +    uint32_t size;
> +    uint8_t *data;
> +} F4FMediaDataBox;
> +
> +typedef struct F4FBox {
> +    F4FBootstrapInfoBox abst;
> +    F4FMediaDataBox mdat;
> +} F4FBox;
> +
> +int ff_parse_f4f_box(AVFormatContext *s, uint8_t *buffer, int
> buffer_size, void *opaque);
> +int ff_free_f4f_box(F4FBox *box);
> diff --git a/libavformat/f4mmanifest.c b/libavformat/f4mmanifest.c
> new file mode 100644
> index 0000000..7dd51be
> --- /dev/null
> +++ b/libavformat/f4mmanifest.c
> @@ -0,0 +1,324 @@
> +/*
> + * Adobe Media Manifest (F4M) File 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
> + */
> +
> +#include <libxml/parser.h>
> +#include <libxml/tree.h>
> +
> +#include "f4mmanifest.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/base64.h"
> +
> +#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_xml_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++;
> +    result = FFMIN(result, MAX_URL_SIZE);
> +
> +    return result;
> +}
> +
> +static int f4m_parse_bootstrap_info_node(AVFormatContext *s, xmlNodePtr
> node, F4MBootstrapInfo *bootstrap_info)
> +{
> +    xmlChar *p;
> +    uint8_t *dst;
> +    int ret;
> +    int len;
> +    int offset;
> +
> +    p = xmlGetProp(node, "id");
> +    if (p) {
> +        av_strlcpy(bootstrap_info->id, p, sizeof(bootstrap_info->id));
> +        xmlFree(p);
> +    }
> +
> +    p = xmlGetProp(node, "url");
> +    if (p) {
> +        av_strlcpy(bootstrap_info->url, p, sizeof(bootstrap_info->url));
> +        xmlFree(p);
> +    }
> +
> +    p = xmlGetProp(node, "profile");
> +    if (p) {
> +        av_strlcpy(bootstrap_info->profile, p, sizeof(bootstrap_info->
> profile));
> +        xmlFree(p);
> +    }
> +
> +    p = xmlNodeGetContent(node);
> +    if (p) {
> +        len = f4m_get_xml_content_length(p);
> +        offset = f4m_get_xml_content_offset(p);
> +        dst = av_mallocz(len);
> +        if (!dst) {
> +            xmlFree(p);
> +            return AVERROR(ENOMEM);
> +        }
> +        if ((ret = av_base64_decode(dst, p + offset, len)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "f4mmanifest Failed to decode
> bootstrap node base64 metadata, ret: %d \n", ret);
> +            xmlFree(p);
> +            av_freep(&dst);
> +            return ret;
> +        }
> +
> +        bootstrap_info->metadata = av_mallocz(ret);
> +        if (!bootstrap_info->metadata) {
> +            xmlFree(p);
> +            av_freep(&dst);
> +            return AVERROR(ENOMEM);
> +        }
> +
> +        bootstrap_info->metadata_size = ret;
> +        memcpy(bootstrap_info->metadata, dst, ret);
> +        av_freep(&dst);
> +        xmlFree(p);
> +    }
> +
> +    return 0;
> +}
> +
> +static int f4m_parse_metadata_node(AVFormatContext *s, xmlNodePtr node,
> F4MMedia *media)
> +{
> +    xmlNodePtr metadata_node;
> +    xmlChar *p;
> +    uint8_t *dst;
> +    int len;
> +    int offset;
> +    int ret;
> +
> +    metadata_node = node->children;
> +    while (metadata_node) {
> +        if (!av_strcasecmp(metadata_node->name, "metadata")) {
> +            p = xmlNodeGetContent(metadata_node);
> +            break;
> +        }
> +        metadata_node = metadata_node->next;
> +    }
> +
> +    if (!p)
> +        return 0;
> +
> +    len = f4m_get_xml_content_length(p);
> +    offset = f4m_get_xml_content_offset(p);
> +
> +    dst = av_mallocz(len);
> +    if (!dst) {
> +        xmlFree(p);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    if ((ret = av_base64_decode(dst, p + offset, len)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "f4mmanifest Failed to decode base64
> metadata, ret: %d \n", ret);
> +        xmlFree(p);
> +        av_freep(&dst);
> +        return ret;
> +    }
> +
> +    media->metadata = av_mallocz(ret);
> +    if (!media->metadata) {
> +        xmlFree(p);
> +        av_freep(&dst);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    media->metadata_size = ret;
> +    memcpy(media->metadata, dst, ret);
> +
> +    xmlFree(p);
> +    av_freep(&dst);
> +
> +    return 0;
> +}
> +
> +static int f4m_parse_media_node(AVFormatContext *s, xmlNodePtr node,
> F4MMedia *media, F4MManifest *manifest)
> +{
> +    xmlChar *p;
> +    int ret;
> +
> +    p = xmlGetProp(node, "bitrate");
> +    if (p) {
> +        media->bitrate = strtoul(p, NULL, 10);
> +        xmlFree(p);
> +    }
> +    p = xmlGetProp(node, "url");
> +    if (p) {
> +        av_strlcpy(media->url, p, sizeof(media->url));
> +        xmlFree(p);
> +    }
> +    p = xmlGetProp(node, "bootstrapInfoId");
> +    if (p) {
> +        av_strlcpy(media->bootstrap_info_id, p,
> sizeof(media->bootstrap_info_id));
> +        xmlFree(p);
> +    }
> +    if ((ret = f4m_parse_metadata_node(s, node, media)) < 0) {
> +        return ret;
> +    }
> +    return 0;
> +}
> +
> +static int f4m_parse_manifest_node(AVFormatContext *s, xmlNodePtr
> root_node, F4MManifest *manifest)
> +{
> +    F4MBootstrapInfo *bootstrap_info;
> +    F4MMedia *media;
> +    xmlNodePtr node;
> +    xmlChar *node_content;
> +    int ret;
> +    int len;
> +    int offset;
> +
> +    for (node = root_node->children; node != root_node->last; node =
> node->next) {
> +        if (!av_strcasecmp(node->name, "text"))
> +            continue;
> +
> +        node_content = xmlNodeGetContent(node);
> +        if (!node_content)
> +            return AVERROR(ENOMEM);
> +
> +        offset = f4m_get_xml_content_offset(node_content);
> +        len = f4m_get_xml_content_length(node_content);
> +        if (!av_strcasecmp(node->name, "id") && node_content) {
> +            av_strlcpy(manifest->id, node_content + offset, len);
> +        }
> +        if (!av_strcasecmp(node->name, "streamType") && node_content) {
> +            av_strlcpy(manifest->stream_type, node_content + offset,
> len);
> +        } else if (!av_strcasecmp(node->name, "bootstrapInfo")) {
> +            bootstrap_info = av_mallocz(sizeof(*bootstrap_info));
> +            if (!bootstrap_info) {
> +                xmlFree(node_content);
> +                return AVERROR(ENOMEM);
> +            }
> +            manifest->bootstraps[manifest->nb_bootstraps++] =
> bootstrap_info;
> +            ret = f4m_parse_bootstrap_info_node(s, node, bootstrap_info);
> +        } else if (!av_strcasecmp(node->name, "media")) {
> +            media = av_mallocz(sizeof(*media));
> +            if (!media) {
> +                xmlFree(node_content);
> +                return AVERROR(ENOMEM);
> +            }
> +            manifest->media[manifest->nb_media++] = media;
> +            ret = f4m_parse_media_node(s, node, media, manifest);
> +        }
> +
> +        if (node_content)
> +            xmlFree(node_content);
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +static int f4m_parse_xml_file(AVFormatContext *s, uint8_t *buffer, int
> size, F4MManifest *manifest)
> +{
> +    xmlDocPtr doc;
> +    xmlNodePtr root_node;
> +    int ret;
> +
> +    doc = xmlReadMemory(buffer, size, "noname.xml", NULL, 0);
> +    if (!doc) {
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    root_node = xmlDocGetRootElement(doc);
> +    if (!root_node) {
> +        av_log(s, AV_LOG_ERROR, "f4mmanifest Root element not found \n");
> +        xmlFreeDoc(doc);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (av_strcasecmp(root_node->name, "manifest")) {
> +        av_log(s, AV_LOG_ERROR, "f4mmanifest Root element is not named
> manifest, name = %s \n",
> +               root_node->name);
> +        xmlFreeDoc(doc);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    ret = f4m_parse_manifest_node(s, root_node, manifest);
> +    xmlFreeDoc(doc);
> +
> +    return ret;
> +}
> +
> +int ff_parse_f4m_manifest(AVFormatContext *s, uint8_t *buffer, int size,
> F4MManifest *manifest)
> +{
> +    return f4m_parse_xml_file(s, buffer, size, manifest);
> +}
> +
> +int ff_free_manifest(F4MManifest *manifest)
> +{
> +    F4MBootstrapInfo *bootstrap_info;
> +    F4MMedia *media;
> +    int i;
> +
> +    for (i = 0; i < manifest->nb_bootstraps; i++) {
> +        bootstrap_info = manifest->bootstraps[i];
> +        av_freep(&bootstrap_info->metadata);
> +        av_freep(&bootstrap_info);
> +    }
> +
> +    for (i = 0; i < manifest->nb_media; i++) {
> +        media = manifest->media[i];
> +        av_freep(&media->metadata);
> +        av_freep(&media);
> +    }
> +
> +    memset(manifest, 0x00, sizeof(*manifest));
> +
> +    return 0;
> +}
> diff --git a/libavformat/f4mmanifest.h b/libavformat/f4mmanifest.h
> new file mode 100644
> index 0000000..0986c3f
> --- /dev/null
> +++ b/libavformat/f4mmanifest.h
> @@ -0,0 +1,59 @@
> +/*
> + * Adobe Media Manifest (F4M) File 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
> + */
> +
> +#include "avformat.h"
> +#include "internal.h"
> +
> +#define MAX_NB_BOOTSTRAPS 32
> +#define MAX_NB_MEDIA 32
> +
> +
> +typedef struct F4MBootstrapInfo {
> +    char id[MAX_URL_SIZE];
> +    char url[MAX_URL_SIZE];
> +    char profile[MAX_URL_SIZE];
> +
> +    int metadata_size;
> +    uint8_t *metadata;
> +} F4MBootstrapInfo;
> +
> +typedef struct F4MMedia {
> +    int bitrate;
> +    char url[MAX_URL_SIZE];
> +    char bootstrap_info_id[MAX_URL_SIZE];
> +
> +    int metadata_size;
> +    uint8_t *metadata;
> +} F4MMedia;
> +
> +
> +typedef struct F4MManifest {
> +    char id[MAX_URL_SIZE];
> +    char stream_type[MAX_URL_SIZE];
> +    int nb_bootstraps;
> +    F4MBootstrapInfo *bootstraps[MAX_NB_BOOTSTRAPS];
> +
> +    int nb_media;
> +    F4MMedia *media[MAX_NB_MEDIA];
> +} F4MManifest;
> +
> +int ff_parse_f4m_manifest(AVFormatContext *s, uint8_t *buffer, int size,
> F4MManifest *manifest);
> +int ff_free_manifest(F4MManifest *manifest);
> diff --git a/libavformat/flvtag.c b/libavformat/flvtag.c
> new file mode 100644
> index 0000000..af080b7
> --- /dev/null
> +++ b/libavformat/flvtag.c
> @@ -0,0 +1,370 @@
> +/*
> + * Adobe FLV Tag 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
> + */
> +
> +#include "flvtag.h"
> +#include "libavformat/avio.h"
> +
> +typedef struct FLVTagAudioHeader {
> +    uint8_t sound_format;
> +    uint8_t sound_rate;
> +    uint8_t sound_size;
> +    uint8_t sound_type;
> +
> +    uint8_t aac_packet_type;
> +} FLVTagAudioHeader;
> +
> +typedef struct FLVTagAudioBody {
> +    uint8_t sound_format;
> +    uint8_t sound_rate;
> +    uint8_t sound_size;
> +    uint8_t sound_type;
> +
> +    uint8_t aac_packet_type;
> +} FLVTagAudioBody;
> +
> +typedef struct FLVTagVideoHeader {
> +    uint8_t frame_type;
> +    uint8_t codec_id;
> +
> +    uint8_t avc_packet_type;
> +    int32_t composition_time;
> +} FLVTagVideoHeader;
> +
> +typedef struct FLVTagVideoBody {
> +    uint8_t configuration_version;
> +    uint8_t avc_profile_indication;
> +    uint8_t profile_compatibility;
> +    uint8_t avc_level_indication;
> +
> +    uint8_t length_size_minus_one;
> +
> +    uint8_t *sps_data;
> +    int sps_data_size;
> +
> +    uint8_t *pps_data;
> +    int pps_data_size;
> +} FLVTagVideoBody;
> +
> +static int flv_tag_parse_audio_header(AVIOContext *in,
> +    FLVTagAudioHeader *header)
> +{
> +    int ret = 0;
> +    uint8_t byte;
> +
> +    byte = avio_r8(in);
> +    ret++;
> +
> +    header->sound_format = (byte >> 4) & 0x0F;
> +    header->sound_rate   = (byte >> 2) & 0x03;
> +    header->sound_size   = (byte >> 1) & 0x01;
> +    header->sound_type   = (byte >> 0) & 0x01;
> +
> +    if (header->sound_format == 10) {
> +        header->aac_packet_type = avio_r8(in);
> +        ret++;
> +    }
> +
> +    return ret;
> +}
> +
> +static int flv_tag_parse_audio_body(AVFormatContext *s, AVIOContext *in,
> uint32_t data_size,
> +    FLVTagAudioHeader *header, FLVTagAudioBody *body, FLVMediaSample
> **sample_out)
> +{
> +    FLVMediaSample *sample;
> +
> +    if (header->sound_format != 10) {
> +        av_log(s, AV_LOG_ERROR, "flvtag Unhandled sound format, fmt: %d
> \n", header->sound_format);
> +        return 0;
> +    }
> +
> +    if (!header->aac_packet_type)
> +        return 0;//skip AudioSpecificConfig
> +
> +    if (header->aac_packet_type != 1) {
> +        av_log(s, AV_LOG_ERROR, "flvtag Unhandled aac_packet_type, type:
> %d \n", header->aac_packet_type);
> +        return 0;
> +    }
> +
> +    sample = av_mallocz(sizeof(*sample));
> +    if (!sample)
> +        return AVERROR(ENOMEM);
> +
> +    sample->type = AVMEDIA_TYPE_AUDIO;
> +    sample->data = av_malloc(data_size);
> +    if (!sample->data)
> +        return AVERROR(ENOMEM);
> +
> +    sample->data_size = data_size;
> +    avio_read(in, sample->data, sample->data_size);
> +
> +    if (sample_out)
> +        *sample_out = sample;
> +
> +    return data_size;
> +}
> +
> +static int flv_tag_parse_video_header(AVIOContext *in,
> +    FLVTagVideoHeader *header)
> +{
> +    int ret = 0;
> +    uint8_t byte;
> +
> +    byte = avio_r8(in);
> +    ret++;
> +
> +    header->frame_type = (byte >> 4) & 0x0F;
> +    header->codec_id = byte & 0x0F;
> +
> +    if (header->codec_id == 0x07) {
> +        header->avc_packet_type = avio_r8(in);
> +        header->composition_time = avio_rb24(in);
> +        ret += 4;
> +    }
> +
> +    return ret;
> +}
> +
> +static int flv_tag_parse_video_body(AVFormatContext *s, AVIOContext *in,
> uint32_t data_size,
> +    FLVTagVideoHeader *header, FLVTagVideoBody *body, FLVMediaSample
> **sample_out)
> +{
> +    FLVMediaSample *sample;
> +    uint8_t *p;
> +    uint8_t nb_sps, nb_pps;
> +    uint16_t sps_length, pps_length;
> +    uint32_t nal_size;
> +    int i, ret = 0;
> +
> +    if (header->frame_type == 0x05) {
> +        avio_r8(in);
> +        return 1;
> +    }
> +
> +    if (header->codec_id != 0x07) {
> +        av_log(s, AV_LOG_ERROR, "flvtag Unhandled video codec id, id: %d
> \n", header->codec_id);
> +        return 0;
> +    }
> +
> +    if (header->avc_packet_type == 0x00) {
> +        body->configuration_version = avio_r8(in);
> +        body->avc_profile_indication = avio_r8(in);
> +        body->profile_compatibility = avio_r8(in);
> +        body->avc_level_indication = avio_r8(in);
> +        ret += 4;
> +
> +        body->length_size_minus_one = avio_r8(in) & 0x03;
> +        ret++;
> +
> +        if (body->sps_data_size > 0)
> +            av_freep(&body->sps_data);
> +        if (body->pps_data_size > 0)
> +            av_freep(&body->pps_data);
> +
> +        nb_sps = avio_r8(in) & 0x1F;
> +        ret++;
> +
> +        for (i = 0; i < nb_sps; i++) {
> +            sps_length = avio_rb16(in);
> +            ret += 2;
> +
> +            body->sps_data = av_realloc(body->sps_data,
> +                body->sps_data_size + sps_length + 4);
> +            if (!body->sps_data)
> +                return AVERROR(ENOMEM);
> +
> +            p = body->sps_data + body->sps_data_size;
> +
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x01;
> +            body->sps_data_size += 4;
> +
> +            avio_read(in, p, sps_length);
> +            body->sps_data_size += sps_length;
> +
> +            ret += sps_length;
> +        }
> +
> +        nb_pps = avio_r8(in);
> +        ret++;
> +
> +        for (i = 0; i < nb_pps; i++) {
> +            pps_length = avio_rb16(in);
> +            ret += 2;
> +
> +            body->pps_data = av_realloc(body->pps_data,
> +                body->pps_data_size + pps_length + 4);
> +            if (!body->pps_data)
> +                return AVERROR(ENOMEM);
> +
> +            p = body->pps_data + body->pps_data_size;
> +
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x01;
> +            body->pps_data_size += 4;
> +
> +            avio_read(in, p, pps_length);
> +            body->pps_data_size += pps_length;
> +
> +            ret += pps_length;
> +        }
> +    } else if (header->avc_packet_type == 0x01) {
> +        sample = av_mallocz(sizeof(*sample));
> +        if (!sample)
> +            return AVERROR(ENOMEM);
> +
> +        sample->type = AVMEDIA_TYPE_VIDEO;
> +        sample->data_size = body->sps_data_size + body->pps_data_size;
> +        sample->data_size += (4 + data_size);
> +        sample->data = av_malloc(sample->data_size);
> +        if (!sample->data)
> +            return AVERROR(ENOMEM);
> +
> +        p = sample->data;
> +
> +        memcpy(p, body->sps_data, body->sps_data_size);
> +        p += body->sps_data_size;
> +
> +        memcpy(p, body->pps_data, body->pps_data_size);
> +        p += body->pps_data_size;
> +
> +        while (ret < data_size) {
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x00;
> +            *p++ = 0x01;
> +
> +            nal_size = avio_rb32(in);
> +            ret += 4;
> +
> +            avio_read(in, p, nal_size);
> +            p += nal_size;
> +            ret += nal_size;
> +        }
> +    }
> +
> +    if (sample_out)
> +        *sample_out = sample;
> +
> +    return ret;
> +}
> +
> +static int flv_tag_decode_body(AVFormatContext *s, uint8_t *buffer, int
> buffer_size,
> +    FLVMediaSample **samples, int *nb_samples_out)
> +{
> +    FLVMediaSample *sample;
> +    AVIOContext *in;
> +    FLVTagAudioHeader audio_header;
> +    FLVTagAudioBody audio_body;
> +    FLVTagVideoHeader video_header;
> +    FLVTagVideoBody video_body;
> +    uint8_t byte, filter, tag_type;
> +    uint32_t data_size, timestamp, timestamp_extended, dts, stream_id;
> +    int nb_samples = 0;
> +    int ret;
> +
> +    memset(&audio_header, 0x00, sizeof(audio_header));
> +    memset(&audio_body, 0x00, sizeof(audio_body));
> +    memset(&video_header, 0x00, sizeof(video_header));
> +    memset(&video_body, 0x00, sizeof(video_body));
> +
> +    in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL,
> NULL);
> +    if (!in)
> +        return AVERROR(ENOMEM);
> +
> +    while (!avio_feof(in)) {
> +        byte = avio_r8(in);
> +        filter = (byte >> 5) & 0x01;
> +        tag_type = (byte & 0x01F);
> +        data_size = avio_rb24(in);
> +        timestamp = avio_rb24(in);
> +        timestamp_extended = avio_r8(in);
> +        dts = ((timestamp_extended << 24) & 0xFF000000) | timestamp;
> +
> +        stream_id = avio_rb24(in);
> +        if (stream_id) {
> +            av_log(s, AV_LOG_ERROR, "flvtag Invalid stream_id %d \n",
> stream_id);
> +            return -1;
> +        }
> +
> +        if (tag_type == 0x08) {
> +            data_size -= flv_tag_parse_audio_header(in, &audio_header);
> +        } else if (tag_type == 0x09) {
> +            data_size -= flv_tag_parse_video_header(in, &video_header);
> +        }
> +
> +        if (filter == 0x01) {
> +            //EncryptionTagHeader
> +            //FilterParams
> +        }
> +
> +        sample = NULL;
> +
> +        if (tag_type == 8) {
> +            if ((ret = flv_tag_parse_audio_body(s, in, data_size,
> +                    &audio_header, &audio_body, &sample)) < 0) {
> +                av_freep(&in);
> +                return ret;
> +            }
> +            data_size -= ret;
> +        } else if (tag_type == 9) {
> +            if ((ret = flv_tag_parse_video_body(s, in, data_size,
> +                    &video_header, &video_body, &sample)) < 0) {
> +
> +                av_freep(&in);
> +                return ret;
> +            }
> +            data_size -= ret;
> +        } else if (tag_type == 18) {
> +            //ScriptData
> +        }
> +
> +        if (sample) {
> +            sample->timestamp = dts;
> +            samples[nb_samples++] = sample;
> +        }
> +
> +        if (data_size) {
> +            avio_skip(in, data_size);
> +        }
> +        avio_rb32(in);
> +    }
> +
> +    av_freep(&in);
> +
> +    if (video_body.sps_data_size > 0)
> +        av_freep(&video_body.sps_data);
> +    if (video_body.pps_data_size > 0)
> +        av_freep(&video_body.pps_data);
> +
> +    if (nb_samples_out)
> +        *nb_samples_out = nb_samples;
> +
> +    return 0;
> +}
> +
> +int ff_decode_flv_body(AVFormatContext *s, uint8_t *buffer, int
> buffer_size,
> +    FLVMediaSample **samples, int *nb_samples_out)
> +{
> +    return flv_tag_decode_body(s, buffer, buffer_size, samples,
> nb_samples_out);
> +}
> diff --git a/libavformat/flvtag.h b/libavformat/flvtag.h
> new file mode 100644
> index 0000000..7c468af
> --- /dev/null
> +++ b/libavformat/flvtag.h
> @@ -0,0 +1,32 @@
> +/*
> + * Adobe FLV Tag 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
> + */
> +
> +#include "avformat.h"
> +#include "libavutil/avutil.h"
> +
> +typedef struct FLVMediaSample {
> +    enum AVMediaType type;
> +    uint32_t timestamp;
> +    int data_size;
> +    uint8_t *data;
> +} FLVMediaSample;
> +
> +int ff_decode_flv_body(AVFormatContext *s, uint8_t *buffer, int
> buffer_size, FLVMediaSample **samples, int *nb_samples_out);
> diff --git a/libavformat/hdsdec.c b/libavformat/hdsdec.c
> new file mode 100644
> index 0000000..6d7654b
> --- /dev/null
> +++ b/libavformat/hdsdec.c
> @@ -0,0 +1,759 @@
> +/*
> + * Adobe HTTP Dynamic Streaming (HDS) demuxer
> + * 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 HTTP Dynamic Streaming (HDS) demuxer
> + * @author Cory McCarthy
> + * @see http://www.adobe.com/devnet/hds.html
> + * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/
> Adobe/en/devnet/hds/pdfs/adobe-hds-specification.pdf
> + * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/
> Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf
> + * @see http://download.macromedia.com/f4v/video_file_format_
> spec_v10_1.pdf
> + *
> + * @note Link for a HDS test player below:
> + * @see http://mediapm.edgesuite.net/edgeflash/public/zeri/debug/
> Main.html
> + *
> + * @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
> + */
> +
> +#include "avformat.h"
> +#include "internal.h"
> +#include "url.h"
> +#include "avio_internal.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/parseutils.h"
> +#include "libavutil/time.h"
> +
> +#include "amfmetadata.h"
> +#include "f4mmanifest.h"
> +#include "f4fbox.h"
> +#include "flvtag.h"
> +
> +#define MAX_NB_SAMPLES 1024
> +
> +typedef struct HDSBootstrapInfo {
> +    char id[MAX_URL_SIZE];
> +    char url[MAX_URL_SIZE];
> +    char profile[MAX_URL_SIZE];
> +
> +    F4FBox box;
> +} HDSBootstrapInfo;
> +
> +typedef struct HDSMedia {
> +    int bitrate;
> +    char url[MAX_URL_SIZE];
> +    char bootstrap_info_id[MAX_URL_SIZE];
> +
> +    AVStream *audio_stream;
> +    AVStream *video_stream;
> +    int nb_samples;
> +    FLVMediaSample *samples[MAX_NB_SAMPLES];
> +    int sample_index;
> +
> +    int nb_first_fragment;
> +    int nb_offset_fragment;
> +    int nb_fragments_read;
> +    int nb_fragments_total;
> +} HDSMedia;
> +
> +typedef struct HDSContext {
> +    char id[MAX_URL_SIZE];
> +    int is_live;
> +    char base_url[MAX_URL_SIZE];
> +    int nb_bootstraps;
> +    HDSBootstrapInfo *bootstrap_info[MAX_NB_BOOTSTRAPS];
> +    int nb_media;
> +    HDSMedia *media[MAX_NB_MEDIA];
> +} HDSContext;
> +
> +static void construct_bootstrap_url(const char *base_url, const char
> *bootstrap_url,
> +    const char *suffix, char *url_out, size_t url_size)
> +{
> +    char *p = url_out;
> +
> +    p += av_strlcat(p, base_url, url_size);
> +    p += av_strlcat(p, bootstrap_url, url_size);
> +    p += av_strlcat(p, suffix, url_size);
> +}
> +
> +static int download_bootstrap(AVFormatContext *s, HDSBootstrapInfo
> *bootstrap,
> +    uint8_t **buffer_out, int *buffer_size_out)
> +{
> +    HDSContext *c = s->priv_data;
> +    URLContext *puc;
> +    char url[MAX_URL_SIZE];
> +    uint8_t *buffer;
> +    int buffer_size;
> +    int ret = 0;
> +
> +    memset(url, 0x00, sizeof(url));
> +
> +    if (!av_stristr(bootstrap->url, "?") && av_stristr(s->filename, "?"))
> {
> +        construct_bootstrap_url(c->base_url, bootstrap->url,
> av_stristr(s->filename, "?"), url, MAX_URL_SIZE);
> +    } else {
> +        construct_bootstrap_url(c->base_url, bootstrap->url, "", url,
> MAX_URL_SIZE);
> +    }
> +
> +    if ((ret = ffurl_open(&puc, url, AVIO_FLAG_READ,
> &s->interrupt_callback, NULL)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to start downloading
> bootstrap, ret: %d \n", ret);
> +        return ret;
> +    }
> +
> +    buffer_size = ffurl_size(puc);
> +    buffer = av_mallocz(buffer_size+FF_INPUT_BUFFER_PADDING_SIZE);
> +    if (!buffer)
> +        return AVERROR(ENOMEM);
> +
> +    if ((ret = ffurl_read_complete(puc, buffer, buffer_size)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to downloaded bootstrap, ret:
> %d \n", ret);
> +        av_freep(&buffer);
> +        return ret;
> +    }
> +
> +    if ((ret = ffurl_close(puc)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to finish downloading
> bootstrap, ret: %d \n", ret);
> +        av_freep(&buffer);
> +        return ret;
> +    }
> +
> +    if (buffer_out)
> +        *buffer_out = buffer;
> +    if (buffer_size_out)
> +        *buffer_size_out = buffer_size;
> +
> +    return ret;
> +}
> +
> +static int create_bootstrap_info(AVFormatContext *s, F4MBootstrapInfo
> *f4m_bootstrap_info)
> +{
> +    HDSContext *c = s->priv_data;
> +    HDSBootstrapInfo *bootstrap_info;
> +    uint8_t *buffer;
> +    int buffer_size, ret;
> +
> +    bootstrap_info = av_mallocz(sizeof(HDSBootstrapInfo));
> +    if (!bootstrap_info)
> +        return AVERROR(ENOMEM);
> +
> +    c->bootstrap_info[c->nb_bootstraps++] = bootstrap_info;
> +
> +    memcpy(bootstrap_info->id, f4m_bootstrap_info->id,
> sizeof(bootstrap_info->id));
> +    memcpy(bootstrap_info->url, f4m_bootstrap_info->url,
> sizeof(bootstrap_info->url));
> +    memcpy(bootstrap_info->profile, f4m_bootstrap_info->profile,
> sizeof(bootstrap_info->profile));
> +
> +    if (f4m_bootstrap_info->metadata_size > 0) {
> +        buffer = f4m_bootstrap_info->metadata;
> +        buffer_size = f4m_bootstrap_info->metadata_size;
> +
> +        if ((ret = ff_parse_f4f_box(s, buffer, buffer_size,
> &(bootstrap_info->box))) < 0) {
> +            av_log(s, AV_LOG_ERROR, "hds Failed to parse metadata
> bootstrap box, ret: %d \n", ret);
> +            return ret;
> +        }
> +    } else {
> +        if ((ret = download_bootstrap(s, bootstrap_info, &buffer,
> &buffer_size)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "hds Failed to download bootstrap,
> ret: %d \n", ret);
> +            return ret;
> +        }
> +
> +        if ((ret = ff_parse_f4f_box(s, buffer, buffer_size,
> &(bootstrap_info->box))) < 0) {
> +            av_log(s, AV_LOG_ERROR, "hds Failed to parse downloaded
> bootstrap box, ret: %d \n", ret);
> +            av_freep(&buffer);
> +            return ret;
> +        }
> +
> +        av_freep(&buffer);
> +    }
> +
> +    return 0;
> +}
> +
> +static int create_streams(AVFormatContext *s, HDSMedia *media,
> AMFMetadata *metadata)
> +{
> +    AVStream *st;
> +
> +    st = avformat_new_stream(s, NULL);
> +    if (!st)
> +        return AVERROR(ENOMEM);
> +
> +    media->video_stream = st;
> +
> +    st->id = 0;
> +    avpriv_set_pts_info(st, 32, 1, 1000);
> +
> +    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
> +    st->codecpar->codec_id = metadata->video_codec_id;
> +    st->codecpar->width = metadata->width;
> +    st->codecpar->height = metadata->height;
> +    st->codecpar->bit_rate = metadata->video_data_rate * 1000;
> +
> +    st = avformat_new_stream(s, NULL);
> +    if (!st)
> +        return AVERROR(ENOMEM);
> +
> +    media->audio_stream = st;
> +
> +    st->id = 0;
> +    avpriv_set_pts_info(st, 32, 1, 1000);
> +
> +    st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
> +    st->codecpar->codec_id = metadata->audio_codec_id;
> +    st->codecpar->channels = metadata->nb_audio_channels;
> +    st->codecpar->sample_rate = metadata->audio_sample_rate;
> +    st->codecpar->bits_per_raw_sample = AV_SAMPLE_FMT_S16;
> +    st->codecpar->bit_rate = metadata->audio_data_rate * 1000;
> +
> +    return 0;
> +}
> +
> +static int create_media(AVFormatContext *s, F4MMedia *f4m_media)
> +{
> +    HDSContext *c = s->priv_data;
> +    HDSMedia *media;
> +    AMFMetadata metadata;
> +    int ret;
> +
> +    media = av_mallocz(sizeof(*media));
> +    if (!media)
> +        return AVERROR(ENOMEM);
> +    c->media[c->nb_media++] = media;
> +
> +    media->bitrate = f4m_media->bitrate;
> +    memcpy(media->url, f4m_media->url, sizeof(media->url));
> +    memcpy(media->bootstrap_info_id, f4m_media->bootstrap_info_id,
> sizeof(media->bootstrap_info_id));
> +
> +    memset(&metadata, 0x00, sizeof(metadata));
> +    if ((ret = ff_parse_amf_metadata(s, &metadata, f4m_media->metadata,
> f4m_media->metadata_size)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to parse metadata, ret: %d
> \n", ret);
> +        return ret;
> +    }
> +
> +    if ((ret = create_streams(s, media, &metadata)) < 0)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static int create_pmt(AVFormatContext *s)
> +{
> +    HDSContext *c = s->priv_data;
> +    HDSMedia *media;
> +    AVProgram *p;
> +    uint8_t value[1024];
> +    int i, j = 0;
> +
> +    for (i = 0; i < c->nb_media; i++) {
> +        media = c->media[i];
> +        p = av_new_program(s, j++);
> +        if (!p)
> +            return AVERROR(ENOMEM);
> +        snprintf(value, sizeof(value),"Bandwidth: %dKbps",
> media->bitrate);
> +        av_dict_set(&p->metadata,"name", value, 0);
> +        av_program_add_stream_index(s, p->id, media->video_stream->index);
> +        av_program_add_stream_index(s, p->id, media->audio_stream->index);
> +    }
> +
> +    return 0;
> +}
> +
> +static int hds_create_context(AVFormatContext *s, F4MManifest *manifest)
> +{
> +    HDSContext *c = s->priv_data;
> +    F4MBootstrapInfo *f4m_bootstrap_info;
> +    F4MMedia *f4m_media;
> +    int i, ret;
> +
> +    for (i = 0; i < manifest->nb_bootstraps; i++) {
> +        f4m_bootstrap_info = manifest->bootstraps[i];
> +        if ((ret = create_bootstrap_info(s, f4m_bootstrap_info)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "hds Failed to create bootstrap_info,
> ret: %d \n", ret);
> +            return ret;
> +        }
> +    }
> +
> +    for (i = 0; i < manifest->nb_media; i++) {
> +        f4m_media = manifest->media[i];
> +        if ((ret = create_media(s, f4m_media)) < 0) {
> +            av_log(s, AV_LOG_ERROR, "hds Failed to create media, ret: %d
> \n", ret);
> +            return ret;
> +        }
> +    }
> +
> +    if ((ret = create_pmt(s)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to create PMT, ret: %d \n",
> ret);
> +        return ret;
> +    }
> +
> +    if (!av_strcasecmp(manifest->stream_type, "live"))
> +        c->is_live = 1;
> +    av_log(s, AV_LOG_DEBUG, "hds stream_type live %s \n", c->is_live ?
> "TRUE" : "FALSE");
> +
> +    return 0;
> +}
> +
> +static int hds_read_header(AVFormatContext *s)
> +{
> +    HDSContext *c = s->priv_data;
> +    AVIOContext *in = s->pb;
> +    F4MManifest manifest;
> +    int64_t filesize;
> +    uint8_t *buf;
> +    char *p, *pch;
> +    int ret;
> +
> +    p = av_stristr(s->filename, ".f4m");
> +    pch = strrchr(s->filename, '/');
> +    if (!p || !pch) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to build base url, url: %s
> \n", s->filename);
> +        return -1;
> +    }
> +    av_strlcpy(c->base_url, s->filename, pch - s->filename + 2);
> +    av_log(s, AV_LOG_DEBUG, "hds build base url: %s \n", c->base_url);
> +    filesize = avio_size(in);
> +    if (filesize <= 0){
> +        filesize = 8 * 1024;
> +    }
> +    buf = av_mallocz(filesize);
> +    if (!buf)
> +        return AVERROR(ENOMEM);
> +
> +    avio_read(in, buf, filesize);
> +    memset(&manifest, 0x00, sizeof(manifest));
> +    if ((ret = ff_parse_f4m_manifest(s, buf, filesize, &manifest)) < 0) {
> +        av_freep(&buf);
> +        ff_free_manifest(&manifest);
> +        return ret;
> +    }
> +
> +    av_freep(&buf);
> +
> +    ret = hds_create_context(s, &manifest);
> +    ff_free_manifest(&manifest);
> +
> +    return ret;
> +}
> +
> +static void construct_fragment_url(const char *base_url, const char
> *media_url,
> +    int segment, int fragment, const char *suffix, char *url_out, size_t
> url_size)
> +{
> +    char *p;
> +    char fragment_str[1024];
> +
> +    p = url_out;
> +    p += av_strlcat(p, base_url, url_size);
> +    p += av_strlcat(p, media_url, url_size);
> +
> +    snprintf(fragment_str, sizeof (fragment_str), "Seg%d-Frag%d",
> segment, fragment);
> +    p += av_strlcat(p, fragment_str, url_size);
> +    p += av_strlcat(p, suffix, url_size);
> +}
> +
> +static int get_offset_fragment(HDSBootstrapInfo *bootstrap_info)
> +{
> +    F4FBootstrapInfoBox *abst = &(bootstrap_info->box.abst);
> +    F4FSegmentRunTableBox *asrt;
> +    F4FSegmentRunEntry *segment_entry;
> +    int offset = 0;
> +    int i,j;
> +
> +    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++) {
> +            segment_entry = asrt->segment_run_entries[j];
> +            offset += segment_entry->fragments_per_segment;
> +        }
> +    }
> +
> +    return offset;
> +}
> +
> +static int get_total_fragment(HDSBootstrapInfo *bootstrap_info)
> +{
> +    F4FBootstrapInfoBox *abst = &(bootstrap_info->box.abst);
> +    F4FSegmentRunTableBox *asrt;
> +    F4FSegmentRunEntry *segment_entry;
> +    int total = 0;
> +    int i;
> +
> +    for (i = 0; i < abst->nb_segment_run_table_boxes; i++) {
> +        asrt = abst->segment_run_table_boxes[i];
> +        segment_entry = asrt->segment_run_entries[0];
> +        total = asrt->nb_segment_run_entries *
> segment_entry->fragments_per_segment;
> +    }
> +
> +    return total;
> +}
> +
> +
> +static int get_current_segment(HDSBootstrapInfo *bootstrap_info)
> +{
> +    F4FBootstrapInfoBox *abst = &(bootstrap_info->box.abst);
> +    F4FSegmentRunTableBox *asrt;
> +    F4FSegmentRunEntry *segment_entry;
> +    int segment = 0;
> +    int i, j;
> +
> +    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++) {
> +            segment_entry = asrt->segment_run_entries[j];
> +            segment = segment_entry->first_segment;
> +        }
> +    }
> +    return segment;
> +}
> +
> +
> +static int get_first_fragment(HDSBootstrapInfo *bootstrap_info)
> +{
> +    F4FBootstrapInfoBox *abst = &(bootstrap_info->box.abst);
> +    F4FFragmentRunTableBox *afrt;
> +    F4FFragmentRunEntry *fragment_entry;
> +    int fragment = 0;
> +    int i, j;
> +
> +    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++) {
> +            fragment_entry = afrt->fragment_run_entries[j];
> +            if (fragment_entry->first_fragment > 0) {
> +                fragment = fragment_entry->first_fragment;
> +                return fragment;
> +            }
> +        }
> +    }
> +
> +    return fragment;
> +}
> +
> +static int get_segment_fragment(AVFormatContext *s, HDSBootstrapInfo
> *bootstrap_info,
> +    HDSMedia *media, int is_live, int *segment_out, int *fragment_out)
> +{
> +    uint8_t *buffer;
> +    int segment, fragment;
> +    int buffer_size;
> +    int ret = 0;
> +
> +    segment = get_current_segment(bootstrap_info);
> +    if (!media->nb_first_fragment)
> +        media->nb_first_fragment = get_first_fragment(bootstrap_info);
> +    if (!media->nb_fragments_total)
> +        media->nb_fragments_total = get_total_fragment(bootstrap_info);
> +
> +    fragment = media->nb_first_fragment + media->nb_fragments_read;
> +    av_log(s, AV_LOG_DEBUG, "hds current_segment: %d current_fragment:
> %d\n", segment, fragment);
> +    if (is_live) {
> +        if (!media->nb_offset_fragment)
> +            media->nb_offset_fragment = get_offset_fragment(bootstrap_
> info);
> +        fragment += media->nb_offset_fragment - 1;
> +
> +        av_log(s, AV_LOG_DEBUG, "hds live current_fragment: %d
> total_fragments: %d\n",
> +               fragment, media->nb_first_fragment +
> media->nb_fragments_total);
> +
> +        if (fragment >= (media->nb_first_fragment +
> media->nb_fragments_total)) {
> +            /* Update bootstrap info*/
> +            av_log(s, AV_LOG_DEBUG, "update bootstrap_info\n");
> +            if ((ret = ff_free_f4f_box(&(bootstrap_info->box))) < 0) {
> +                av_log(s, AV_LOG_ERROR, "hds Failed to free bootstrap
> box, ret: %d \n", ret);
> +                return ret;
> +            }
> +            if ((ret = download_bootstrap(s, bootstrap_info, &buffer,
> &buffer_size)) < 0) {
> +                av_log(s, AV_LOG_ERROR, "hds Failed to download
> bootstrap, ret: %d \n", ret);
> +                return ret;
> +            }
> +            if ((ret = ff_parse_f4f_box(s, buffer, buffer_size,
> &(bootstrap_info->box))) < 0) {
> +                av_log(s, AV_LOG_ERROR, "hds Failed to parse downloaded
> bootstrap box, ret: %d \n", ret);
> +                av_freep(&buffer);
> +                return ret;
> +            }
> +            segment = get_current_segment(bootstrap_info);
> +            media->nb_first_fragment = 0;
> +            media->nb_fragments_read = 0;
> +            media->nb_fragments_total = 0;
> +            media->nb_offset_fragment = 0;
> +            av_freep(&buffer);
> +        }
> +    }
> +
> +    if (!is_live && fragment >= (media->nb_first_fragment +
> media->nb_fragments_total)) {
> +        return AVERROR_EOF;
> +    }
> +
> +    if (segment_out)
> +        *segment_out = segment;
> +    if (fragment_out)
> +        *fragment_out = fragment;
> +    return ret;
> +}
> +
> +static int download_fragment(AVFormatContext *s, HDSBootstrapInfo
> *bootstrap_info, HDSMedia *media,
> +                             uint8_t **buffer_out, int *buffer_size_out)
> +{
> +    HDSContext *c = s->priv_data;
> +    URLContext *puc;
> +    char url[MAX_URL_SIZE];
> +    uint8_t *buffer;
> +    int buffer_size;
> +    int segment, fragment;
> +    int ret;
> +
> +    if ((ret = get_segment_fragment(s, bootstrap_info, media, c->is_live,
> &segment, &fragment)) < 0) {
> +        return ret;
> +    }
> +
> +    memset(url, 0x00, sizeof(url));
> +
> +    if (!av_stristr(media->url, "?") && av_stristr(s->filename, "?")) {
> +        construct_fragment_url(c->base_url, media->url,
> +            segment, fragment, av_stristr(s->filename, "?"), url,
> MAX_URL_SIZE);
> +    } else {
> +        construct_fragment_url(c->base_url, media->url, segment,
> fragment, "", url, MAX_URL_SIZE);
> +    }
> +
> +    if ((ret = ffurl_open(&puc, url, AVIO_FLAG_READ,
> &s->interrupt_callback, NULL)) < 0) {
> +        if (ret != AVERROR(EIO))
> +            av_log(s, AV_LOG_ERROR, "hds Failed to start downloading
> fragment, url:%s, ret:%d \n", url, ret);
> +        return ret;
> +    }
> +
> +    buffer_size = ffurl_size(puc);
> +    buffer = av_mallocz(buffer_size+FF_INPUT_BUFFER_PADDING_SIZE);
> +    if (!buffer)
> +        return AVERROR(ENOMEM);
> +
> +    if ((ret = ffurl_read_complete(puc, buffer, buffer_size)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to downloaded fragment, ret:
> %d \n", ret);
> +        av_freep(&buffer);
> +        return ret;
> +    }
> +
> +    if ((ret = ffurl_close(puc)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to finish downloading
> fragment, ret: %d \n", ret);
> +        av_freep(&buffer);
> +        return ret;
> +    }
> +
> +    media->nb_fragments_read++;
> +
> +    if (buffer_out)
> +        *buffer_out = buffer;
> +    if (buffer_size_out)
> +        *buffer_size_out = buffer_size;
> +
> +    return 0;
> +}
> +
> +static int get_next_fragment(AVFormatContext *s, HDSBootstrapInfo
> *bootstrap_info, HDSMedia *media)
> +{
> +    F4FBox box;
> +    uint8_t *buffer;
> +    int buffer_size, ret;
> +
> +    if ((ret = download_fragment(s, bootstrap_info, media, &buffer,
> &buffer_size)) < 0) {
> +        return ret;
> +    }
> +
> +    memset(&box, 0x00, sizeof(box));
> +    if ((ret = ff_parse_f4f_box(s, buffer, buffer_size, &box)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to parse bootstrap box, ret:
> %d \n", ret);
> +        av_freep(&buffer);
> +        ff_free_f4f_box(&box);
> +        return ret;
> +    }
> +    av_freep(&buffer);
> +
> +    if ((ret = ff_decode_flv_body(s ,box.mdat.data, box.mdat.size,
> media->samples, &media->nb_samples)) < 0) {
> +        av_log(s, AV_LOG_ERROR, "hds Failed to decode FLV body, ret: %d
> \n", ret);
> +        ff_free_f4f_box(&box);
> +        return ret;
> +    }
> +
> +    ff_free_f4f_box(&box);
> +
> +    return 0;
> +}
> +
> +static void read_next_sample(HDSMedia *media, AVPacket *pkt)
> +{
> +    FLVMediaSample *sample;
> +
> +    sample = media->samples[media->sample_index];
> +    media->sample_index++;
> +
> +    av_new_packet(pkt, sample->data_size);
> +    memcpy(pkt->data, sample->data, sample->data_size);
> +
> +    pkt->dts = sample->timestamp;
> +    if (sample->type == AVMEDIA_TYPE_VIDEO && media->video_stream) {
> +        pkt->stream_index = media->video_stream->index;
> +    } else if (sample->type == AVMEDIA_TYPE_AUDIO && media->audio_stream)
> {
> +        pkt->stream_index = media->audio_stream->index;
> +    }
> +}
> +
> +static void clear_samples(HDSMedia *media)
> +{
> +    FLVMediaSample *sample;
> +    int i;
> +
> +    for (i = 0; i < media->nb_samples; i++) {
> +        sample = media->samples[i];
> +        av_freep(&sample->data);
> +        av_freep(&sample);
> +        media->samples[i] = NULL;
> +    }
> +
> +    media->nb_samples = 0;
> +    media->sample_index = 0;
> +}
> +
> +static int get_next_packet(AVFormatContext *s,
> +    HDSBootstrapInfo *bootstrap_info, HDSMedia *media, AVPacket *pkt)
> +{
> +    int ret = 0;
> +
> +    if (!media->nb_samples) {
> +        if ((ret = get_next_fragment(s, bootstrap_info, media)) < 0) {
> +            return ret;
> +        }
> +    }
> +
> +    if (media->nb_samples > 0) {
> +        read_next_sample(media, pkt);
> +    }
> +
> +    if (media->sample_index >= media->nb_samples) {
> +        clear_samples(media);
> +    }
> +
> +    return ret;
> +}
> +
> +static int hds_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> +    HDSContext *c = s->priv_data;
> +    HDSBootstrapInfo *bootstrap_info;
> +    HDSMedia *media;
> +    int i, j;
> +
> +    for (i = 0; i < c->nb_media; i++) {
> +        media = c->media[i];
> +        bootstrap_info = NULL;
> +
> +        if (media->video_stream->discard == AVDISCARD_ALL &&
> +            media->audio_stream->discard == AVDISCARD_ALL) {
> +            continue;
> +        }
> +
> +        for (j = 0; j < c->nb_bootstraps; j++) {
> +            if (av_strcasecmp(media->bootstrap_info_id,
> c->bootstrap_info[j]->id)) {
> +                continue;
> +            }
> +            bootstrap_info = c->bootstrap_info[j];
> +            break;
> +        }
> +        if (!bootstrap_info)
> +            continue;
> +
> +        break;
> +    }
> +
> +    if (!bootstrap_info) {
> +        av_log(s, AV_LOG_ERROR, "cannot find bootstrap info");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (!media) {
> +        av_log(s, AV_LOG_ERROR, "cannot find media");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    return  get_next_packet(s, bootstrap_info, media, pkt);
> +}
> +
> +static int hds_close(AVFormatContext *s)
> +{
> +    HDSContext *c = s->priv_data;
> +    HDSBootstrapInfo *bootstrap_info;
> +    HDSMedia *media;
> +    int i;
> +
> +    for (i = 0; i < c->nb_bootstraps; i++) {
> +        bootstrap_info = c->bootstrap_info[i];
> +        ff_free_f4f_box(&bootstrap_info->box);
> +        av_freep(&bootstrap_info);
> +    }
> +
> +    for (i = 0; i < c->nb_media; i++) {
> +        media = c->media[i];
> +        clear_samples(media);
> +        av_freep(&media);
> +    }
> +
> +    memset(c, 0x00, sizeof(*c));
> +
> +    return 0;
> +}
> +
> +static int hds_probe(AVProbeData *p)
> +{
> +    if (av_strcasecmp(p->buf, "<?xml version")) {
> +        return 0;
> +    }
> +
> +    if (strstr(p->buf, "<manifest xmlns=\"http://ns.adobe.com/f4m") ||
> +        strstr(p->buf, "<bootstrapInfo") ||
> +        strstr(p->buf, "<media")) {
> +        return AVPROBE_SCORE_MAX;
> +    }
> +    return 0;
> +}
> +
> +AVInputFormat ff_hds_demuxer = {
> +    .name           = "hds",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Adobe HTTP Dynamic Streaming
> Demuxer"),
> +    .priv_data_size = sizeof(HDSContext),
> +    .read_probe     = hds_probe,
> +    .read_header    = hds_read_header,
> +    .read_packet    = hds_read_packet,
> +    .read_close     = hds_close,
> +};
> --
> 1.7.1
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


More information about the ffmpeg-devel mailing list