patch update. xml2 disabled by default
test passed: ../configure --cc='ccache x86_64-w64-mingw32-gcc' --arch=x86_64 --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --disable-yasm 2016-10-13 14:49 GMT+08:00 Steven Liu <l...@chinaffmpeg.org>: > init add hds demuxer > > Based-on: patch by CORY MCCARTHY <cory.mccar...@shaw.ca> > Based-on: patch by Gorilla Maguila <gorilla.magu...@gmail.com> > Signed-off-by: Steven Liu <l...@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..9f844ea 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] > + --disable-xml2 disable XML parsing using the C library > libxml2 [autodetect] > --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_select="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 > +disabled xml2 && require_pkg_config libxml-2.0 > libxml2/libxml/xmlversion.h xmlCheckVersion > 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..c99cb9c > --- /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 (strncmp(p->buf, "<?xml version", 13)) { > + 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@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel