From 894f54be142812aec8390ab0482399e912947a13 Mon Sep 17 00:00:00 2001 From: Markham Date: Wed, 3 May 2017 20:27:02 +0200 Subject: [PATCH] Lets use FFMPEG Git (> 3.3) --- .../ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch | 2774 +++++++++++++++++ .../ffmpeg/0002-ffmpeg-aac-3.x.patch | 53 + ...-Revert-lavc-Switch-bitrate-to-64bit.patch | 381 +++ make/archives.mk | 11 +- make/libraries.mk | 25 +- make/versions.mk | 2 +- 6 files changed, 3231 insertions(+), 15 deletions(-) create mode 100644 archive-patches/ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch create mode 100644 archive-patches/ffmpeg/0002-ffmpeg-aac-3.x.patch create mode 100644 archive-patches/ffmpeg/0003-ffmpeg-Revert-lavc-Switch-bitrate-to-64bit.patch diff --git a/archive-patches/ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch b/archive-patches/ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch new file mode 100644 index 0000000..40ea9b6 --- /dev/null +++ b/archive-patches/ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch @@ -0,0 +1,2774 @@ +From a6ceed822b71d46c30b0f70270b59b7afb0e5137 Mon Sep 17 00:00:00 2001 +From: Markham +Date: Wed, 3 May 2017 17:51:31 +0200 +Subject: [PATCH] ffmpeg-hds-libroxml-3.x + +--- + configure | 5 + + libavformat/Makefile | 1 + + libavformat/allformats.c | 2 +- + libavformat/amfmetadata.c | 290 +++++++++++++ + libavformat/amfmetadata.h | 47 ++ + libavformat/f4fbox.c | 345 +++++++++++++++ + libavformat/f4fbox.h | 110 +++++ + libavformat/f4mmanifest.c | 256 +++++++++++ + libavformat/f4mmanifest.h | 65 +++ + libavformat/flv.h | 2 + + libavformat/flvdec.c | 5 + + libavformat/flvtag.c | 408 ++++++++++++++++++ + libavformat/flvtag.h | 40 ++ + libavformat/hdsdec.c | 1036 +++++++++++++++++++++++++++++++++++++++++++++ + 14 files changed, 2611 insertions(+), 1 deletion(-) + create mode 100644 libavformat/amfmetadata.c + create mode 100644 libavformat/amfmetadata.h + create mode 100644 libavformat/f4fbox.c + create mode 100644 libavformat/f4fbox.h + create mode 100644 libavformat/f4mmanifest.c + create mode 100644 libavformat/f4mmanifest.h + create mode 100644 libavformat/flvtag.c + create mode 100644 libavformat/flvtag.h + create mode 100644 libavformat/hdsdec.c + +diff --git a/configure b/configure +index c3fa9d8..1934364 100755 +--- a/configure ++++ b/configure +@@ -293,6 +293,7 @@ External library support: + on OSX if openssl and gnutls are not used [autodetect] + --disable-xlib disable xlib [autodetect] + --disable-zlib disable zlib [autodetect] ++ --disable-libroxml disable XML parsing using the C library libroxml [autodetect] + + The following libraries provide various hardware acceleration features: + --disable-audiotoolbox disable Apple AudioToolbox code [autodetect] +@@ -1555,6 +1556,7 @@ EXTERNAL_LIBRARY_LIST=" + libopenmpt + libopus + libpulse ++ libroxml + librtmp + libschroedinger + libshine +@@ -2921,6 +2923,7 @@ eac3_demuxer_select="ac3_parser" + f4v_muxer_select="mov_muxer" + fifo_muxer_deps="threads" + flac_demuxer_select="flac_parser" ++hds_demuxer_select="libroxml" + hds_muxer_select="flv_muxer" + hls_muxer_select="mpegts_muxer" + image2_alias_pix_demuxer_select="image2_demuxer" +@@ -5728,6 +5731,8 @@ disabled zlib || check_lib zlib zlib.h zlibVersion -lz + disabled bzlib || check_lib bzlib bzlib.h BZ2_bzlibVersion -lbz2 + disabled lzma || check_lib lzma lzma.h lzma_version_number -llzma + ++disabled libroxml || check_lib roxml roxml.h roxml_load_buf -lroxml || disable libroxml ++ + check_lib libm math.h sin -lm && LIBM="-lm" + disabled crystalhd || check_lib crystalhd "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index d82639d..baf027e 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -200,6 +200,7 @@ OBJS-$(CONFIG_H263_DEMUXER) += h263dec.o rawdec.o + OBJS-$(CONFIG_H263_MUXER) += rawenc.o + OBJS-$(CONFIG_H264_DEMUXER) += h264dec.o rawdec.o + OBJS-$(CONFIG_H264_MUXER) += rawenc.o ++OBJS-$(CONFIG_HDS_DEMUXER) += hdsdec.o amfmetadata.o f4mmanifest.o f4fbox.o flvtag.o + OBJS-$(CONFIG_HASH_MUXER) += hashenc.o + OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o + OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 09e62c3..8e99a3b 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -142,7 +142,7 @@ static void register_all(void) + REGISTER_MUXDEMUX(H263, h263); + REGISTER_MUXDEMUX(H264, h264); + REGISTER_MUXER (HASH, hash); +- REGISTER_MUXER (HDS, hds); ++ REGISTER_MUXDEMUX(HDS, hds); + REGISTER_MUXDEMUX(HEVC, hevc); + REGISTER_MUXDEMUX(HLS, hls); + REGISTER_DEMUXER (HNM, hnm); +diff --git a/libavformat/amfmetadata.c b/libavformat/amfmetadata.c +new file mode 100644 +index 0000000..5bad763 +--- /dev/null ++++ b/libavformat/amfmetadata.c +@@ -0,0 +1,290 @@ ++/* ++ * Adobe Action Message Format Parser ++ * Copyright (c) 2013 Cory McCarthy ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Action Message Format Parser ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ * @see http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf ++ */ ++ ++#include "amfmetadata.h" ++#include "avio_internal.h" ++#include "flv.h" ++#include "libavutil/avstring.h" ++#include "libavutil/intfloat.h" ++#include "libavformat/avformat.h" ++ ++static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata *metadata, const char *name); ++ ++static int amf_metadata_read_string_value(AVIOContext *in, char *str, int str_size) ++{ ++ uint8_t type; ++ ++ type = avio_r8(in); ++ if(type != 0x02) { ++ av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected type 2, type = %d \n", type); ++ return -1; ++ } ++ ++ return flv_amf_get_string(in, str, str_size); ++} ++ ++static void amf_metadata_assign_property_number(AMFMetadata *metadata, ++ const char *name, double value) ++{ ++ if(!av_strcasecmp("width", name)) { ++ metadata->width = (int)value; ++ } ++ else ++ if(!av_strcasecmp("height", name)) { ++ metadata->height = (int)value; ++ } ++ else ++ if(!av_strcasecmp("framerate", name)) { ++ metadata->frame_rate = (int)value; ++ } ++ else ++ if(!av_strcasecmp("videodatarate", name)) { ++ metadata->video_data_rate = (int)value; ++ } ++ else ++ if(!av_strcasecmp("audiosamplerate", name)) { ++ metadata->audio_sample_rate = (int)value; ++ } ++ else ++ if(!av_strcasecmp("audiosamplesize", name)) { ++ metadata->audio_sample_size = (int)value; ++ } ++ else ++ if(!av_strcasecmp("audiochannels", name)) { ++ metadata->nb_audio_channels = (int)value; ++ } ++ else ++ if(!av_strcasecmp("stereo", name)) { ++ metadata->nb_audio_channels = ((int)value) ? 2 : 1; ++ } ++ else ++ if(!av_strcasecmp("audiodatarate", name)) { ++ metadata->audio_data_rate = (int)value; ++ } ++ else ++ if(!av_strcasecmp("audiocodecid", name)) { ++ switch(FLV_AUDIO_CODECID_MASK & ((int)value << FLV_AUDIO_CODECID_OFFSET)) { ++ case FLV_CODECID_PCM: ++ metadata->audio_codec_id = metadata->audio_sample_size == 8 ++ ? AV_CODEC_ID_PCM_U8 ++#if HAVE_BIGENDIAN ++ : AV_CODEC_ID_PCM_S16BE; ++#else ++ : AV_CODEC_ID_PCM_S16LE; ++#endif ++ break; ++ case FLV_CODECID_ADPCM: ++ metadata->audio_codec_id = AV_CODEC_ID_ADPCM_SWF; ++ break; ++ case FLV_CODECID_MP3: ++ metadata->audio_stream_need_parsing = AVSTREAM_PARSE_FULL; ++ metadata->audio_codec_id = AV_CODEC_ID_MP3; ++ break; ++ case FLV_CODECID_PCM_LE: ++ metadata->audio_codec_id = metadata->audio_sample_size == 8 ++ ? AV_CODEC_ID_PCM_U8 ++ : AV_CODEC_ID_PCM_S16LE; ++ break; ++ case FLV_CODECID_NELLYMOSER_16KHZ_MONO: ++ if (!metadata->audio_sample_rate) ++ metadata->audio_sample_rate = 16000; ++ metadata->nb_audio_channels = 1; ++ metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER; ++ break; ++ case FLV_CODECID_NELLYMOSER_8KHZ_MONO: ++ if (!metadata->audio_sample_rate) ++ metadata->audio_sample_rate = 8000; ++ metadata->nb_audio_channels = 1; ++ metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER; ++ break; ++ case FLV_CODECID_NELLYMOSER: ++ metadata->audio_codec_id = AV_CODEC_ID_NELLYMOSER; ++ break; ++ case FLV_CODECID_PCM_ALAW: ++ metadata->audio_sample_rate = 8000; ++ metadata->audio_codec_id = AV_CODEC_ID_PCM_ALAW; ++ break; ++ case FLV_CODECID_PCM_MULAW: ++ metadata->audio_sample_rate = 8000; ++ metadata->audio_codec_id = AV_CODEC_ID_PCM_MULAW; ++ break; ++ case FLV_CODECID_AAC: ++ metadata->audio_codec_id = AV_CODEC_ID_AAC; ++ break; ++ case FLV_CODECID_SPEEX: ++ metadata->audio_sample_rate = 16000; ++ metadata->audio_codec_id = AV_CODEC_ID_SPEEX; ++ break; ++ default: ++ break; ++ } ++ } ++ else ++ if(!av_strcasecmp("videocodecid", name)) { ++ if((int)value == 7) ++ metadata->video_codec_id = AV_CODEC_ID_H264; ++ } ++} ++ ++static void amf_metadata_assign_property_string(AMFMetadata *metadata, ++ const char *name, const char *value) ++{ ++ if(!av_strcasecmp("audiocodecid", name)) { ++ if(!av_strcasecmp("mp4a", value)) ++ metadata->audio_codec_id = AV_CODEC_ID_AAC; ++ else if(!av_strcasecmp(".mp3", value)) { ++ metadata->audio_stream_need_parsing = AVSTREAM_PARSE_FULL; ++ metadata->audio_codec_id = AV_CODEC_ID_MP3; ++ } ++ else ++ av_log(NULL, AV_LOG_ERROR, "amfmetadata audiocodecid=%s unhandled\n", value); ++ } ++ else ++ if(!av_strcasecmp("videocodecid", name)) { ++ if(!av_strcasecmp("avc1", value)) ++ metadata->video_codec_id = AV_CODEC_ID_H264; ++ else ++ av_log(NULL, AV_LOG_ERROR, "amfmetadata videocodecid=%s unhandled\n", value); ++ } ++} ++ ++static int amf_metadata_parse_object_property(AVIOContext *in, AMFMetadata *metadata) ++{ ++ char name[INT16_MAX]; ++ int ret; ++ ++ if((ret = flv_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) ++ break; ++ ++ return 0; ++} ++ ++static int amf_metadata_parse_strict_array(AVIOContext *in, AMFMetadata *metadata) ++{ ++ int length; ++ int ret; ++ ++ length = avio_rb32(in); ++ while(!avio_feof(in) && length > 0) { ++ if((ret = amf_metadata_parse_value(in, metadata, NULL)) < 0) ++ return ret; ++ length--; ++ } ++ ++ return 0; ++} ++ ++static int amf_metadata_parse_value(AVIOContext *in, AMFMetadata *metadata, const char *name) ++{ ++ uint8_t type; ++ char value_str[INT16_MAX]; ++ double value_number; ++ int ret = 0; ++ ++ type = avio_r8(in); ++ ++ if(type == AMF_DATA_TYPE_NUMBER) { ++ value_number = av_int2double(avio_rb64(in)); ++ amf_metadata_assign_property_number(metadata, name, value_number); ++ } ++ else if(type == AMF_DATA_TYPE_BOOL) { ++ value_number = avio_r8(in); ++ amf_metadata_assign_property_number(metadata, name, value_number); ++ } ++ else if(type == AMF_DATA_TYPE_STRING) { ++ if((ret = flv_amf_get_string(in, value_str, sizeof(value_str))) < 0) ++ return ret; ++ amf_metadata_assign_property_string(metadata, name, value_str); ++ } ++ else if(type == AMF_DATA_TYPE_OBJECT) { ++ ret = amf_metadata_parse_object(in, metadata); ++ } ++ else if(type == AMF_DATA_TYPE_MIXEDARRAY) { ++ avio_skip(in, 4); ++ ret = amf_metadata_parse_object(in, metadata); ++ } ++ else if(type == AMF_DATA_TYPE_ARRAY) { ++ ret = amf_metadata_parse_strict_array(in, metadata); ++ } ++ else if(type == AMF_END_OF_OBJECT) { ++ ret = -1; // EOF ++ } ++ ++ return ret; ++} ++ ++static int amf_metadata_parse(AVIOContext *in, AMFMetadata *metadata) ++{ ++ char name[INT16_MAX]; ++ int ret; ++ ++ if((ret = amf_metadata_read_string_value(in, name, sizeof(name))) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "amfmetadata Failed to read onMetadata string, ret: %d \n", ret); ++ return ret; ++ } ++ ++ if(av_strcasecmp(name, "onMetaData")) { ++ av_log(NULL, AV_LOG_ERROR, "amfmetadata Expected onMetadata, str = %s \n", name); ++ return -1; ++ } ++ ++ return amf_metadata_parse_value(in, metadata, name); ++} ++ ++int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata *metadata) ++{ ++ AVIOContext *in; ++ int ret; ++ ++ if(!buffer) ++ return 0; ++ if(buffer_size <= 0) ++ return 0; ++ ++ in = avio_alloc_context(buffer, buffer_size, ++ 0, NULL, NULL, NULL, NULL); ++ if(!in) ++ return AVERROR(ENOMEM); ++ ++ ret = amf_metadata_parse(in, metadata); ++ av_free(in); ++ ++ return ret; ++} +diff --git a/libavformat/amfmetadata.h b/libavformat/amfmetadata.h +new file mode 100644 +index 0000000..c47cd6e +--- /dev/null ++++ b/libavformat/amfmetadata.h +@@ -0,0 +1,47 @@ ++/* ++ * Adobe Action Message Format Parser ++ * Copyright (c) 2013 Cory McCarthy ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Action Message Format Parser ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ * @see http://www.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf-file-format-spec.pdf ++ */ ++ ++#include "libavcodec/avcodec.h" ++ ++typedef struct AMFMetadata { ++ int width; ++ int height; ++ int frame_rate; ++ int audio_sample_rate; ++ int audio_sample_size; ++ int nb_audio_channels; ++ int audio_data_rate; ++ int video_data_rate; ++ int audio_stream_need_parsing; ++ ++ enum AVCodecID audio_codec_id; ++ enum AVCodecID video_codec_id; ++} AMFMetadata; ++ ++int ff_parse_amf_metadata(uint8_t *buffer, int buffer_size, AMFMetadata *metadata); +diff --git a/libavformat/f4fbox.c b/libavformat/f4fbox.c +new file mode 100644 +index 0000000..4c05cd0 +--- /dev/null ++++ b/libavformat/f4fbox.c +@@ -0,0 +1,345 @@ ++/* ++ * Adobe Fragmented F4V File (F4F) Parser ++ * Copyright (c) 2013 Cory McCarthy ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Fragmented F4V File (F4F) Parser for Adobe HDS ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ */ ++ ++#include "f4fbox.h" ++#include "avformat.h" ++ ++static int f4fbox_parse_single_box(AVIOContext *in, void *opague); ++ ++static int f4fbox_parse_asrt(AVIOContext *in, int64_t data_size, void *opague) ++{ ++ F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opague; ++ F4FSegmentRunTableBox *asrt; ++ F4FSegmentRunEntry *entry; ++ uint32_t segment_run_entry_count; ++ char url[1024]; ++ int i; ++ ++ asrt = av_mallocz(sizeof(F4FSegmentRunTableBox)); ++ 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); ++ ++ asrt->nb_quality_entries = avio_r8(in); ++ if (asrt->nb_quality_entries) ++ asrt->quality_entries = (char **) av_malloc(asrt->nb_quality_entries * sizeof(char *)); ++ for(i = 0; i < asrt->nb_quality_entries; i++) { ++ avio_get_str(in, sizeof(url), url, sizeof(url)); ++ av_log(NULL, AV_LOG_DEBUG, "f4fbox asrt quality[%d]=%s\n", i, url); ++ asrt->quality_entries[i] = av_strdup(url); ++ } ++ ++ segment_run_entry_count = avio_rb32(in); ++ if(segment_run_entry_count > MAX_NB_SEGMENT_RUN_ENTRIES) { ++ av_log(NULL, AV_LOG_ERROR, "f4fbox segment run entry count exceeds maximum, %d > %d \n", ++ segment_run_entry_count, MAX_NB_SEGMENT_RUN_ENTRIES); ++ segment_run_entry_count = MAX_NB_SEGMENT_RUN_ENTRIES; ++ } ++ ++ for(i = 0; i < segment_run_entry_count; i++) { ++ entry = av_mallocz(sizeof(F4FSegmentRunEntry)); ++ if(!entry) ++ return AVERROR(ENOMEM); ++ ++ asrt->segment_run_entries[asrt->nb_segment_run_entries] = entry; ++ asrt->nb_segment_run_entries++; ++ ++ entry->first_segment = avio_rb32(in); ++ entry->fragments_per_segment = avio_rb32(in); ++ } ++ ++ return 0; ++} ++ ++static int f4fbox_parse_afrt(AVIOContext *in, int64_t data_size, void *opague) ++{ ++ F4FBootstrapInfoBox *parent = (F4FBootstrapInfoBox*)opague; ++ F4FFragmentRunTableBox *afrt; ++ F4FFragmentRunEntry *entry; ++ uint32_t fragment_run_entry_count; ++ char url[1024]; ++ int i; ++ ++ afrt = av_mallocz(sizeof(F4FFragmentRunTableBox)); ++ if(!afrt) ++ return AVERROR(ENOMEM); ++ ++ parent->fragment_run_table_boxes[parent->nb_fragment_run_table_boxes] = afrt; ++ parent->nb_fragment_run_table_boxes++; ++ ++ afrt->version = avio_r8(in); ++ afrt->flags = avio_rb24(in); ++ ++ afrt->timescale = avio_rb32(in); ++ ++ afrt->nb_quality_entries = avio_r8(in); ++ if (afrt->nb_quality_entries) ++ afrt->quality_entries = (char **) av_malloc(afrt->nb_quality_entries * sizeof(char *)); ++ for(i = 0; i < afrt->nb_quality_entries; i++) { ++ avio_get_str(in, sizeof(url), url, sizeof(url)); ++ av_log(NULL, AV_LOG_DEBUG, "f4fbox afrt quality[%d]=%s\n", i, url); ++ afrt->quality_entries[i] = av_strdup(url); ++ } ++ ++ fragment_run_entry_count = avio_rb32(in); ++ if(fragment_run_entry_count > MAX_NB_FRAGMENT_RUN_ENTRIES) { ++ av_log(NULL, AV_LOG_ERROR, "f4fbox fragment run entry count exceeds maximum, %d > %d \n", ++ fragment_run_entry_count, MAX_NB_FRAGMENT_RUN_ENTRIES); ++ fragment_run_entry_count = MAX_NB_FRAGMENT_RUN_ENTRIES; ++ } ++ ++ for(i = 0; i < fragment_run_entry_count; i++) { ++ entry = av_mallocz(sizeof(F4FFragmentRunEntry)); ++ if(!entry) ++ return AVERROR(ENOMEM); ++ ++ afrt->fragment_run_entries[afrt->nb_fragment_run_entries] = entry; ++ afrt->nb_fragment_run_entries++; ++ ++ entry->first_fragment = avio_rb32(in); ++ entry->first_fragment_time_stamp = avio_rb64(in); ++ entry->fragment_duration = avio_rb32(in); ++ if(entry->fragment_duration == 0) { ++ entry->discontinuity_indicator = avio_r8(in); ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static int f4fbox_parse_abst(AVIOContext *in, int64_t data_size, void *opague) ++{ ++ F4FBox *parent = (F4FBox*)opague; ++ F4FBootstrapInfoBox *abst = &(parent->abst); ++ uint8_t server_entry_count; ++ uint8_t segment_run_table_count, fragment_run_table_count; ++ uint8_t byte; ++ char url[1024]; ++ int i, ret; ++ ++ abst->version = avio_r8(in); ++ abst->flags = avio_rb24(in); ++ abst->bootstrap_info_version = avio_rb32(in); ++ ++ byte = avio_r8(in); ++ abst->profile = (byte >> 6) & 0x03; ++ abst->is_live = (byte >> 5) & 0x01; ++ abst->is_update = (byte >> 4) & 0x01; ++ ++ 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)); ++ } ++ ++ abst->nb_quality_entries = avio_r8(in); ++ if (abst->nb_quality_entries) ++ abst->quality_entries = (char **) av_malloc(abst->nb_quality_entries * sizeof(char *)); ++ for(i = 0; i < abst->nb_quality_entries; i++) { ++ avio_get_str(in, sizeof(url), url, sizeof(url)); ++ av_log(NULL, AV_LOG_DEBUG, "f4fbox abst quality[%d]=%s\n", i, url); ++ abst->quality_entries[i] = av_strdup(url); ++ } ++ ++ avio_get_str(in, sizeof(abst->drm_data), abst->drm_data, sizeof(abst->drm_data)); ++ avio_get_str(in, sizeof(abst->metadata), abst->metadata, sizeof(abst->metadata)); ++ ++ segment_run_table_count = avio_r8(in); ++ for(i = 0; i < segment_run_table_count; i++) { ++ if((ret = f4fbox_parse_single_box(in, abst)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse asrt box, ret: %d \n", ret); ++ return ret; ++ } ++ } ++ ++ fragment_run_table_count = avio_r8(in); ++ for(i = 0; i < fragment_run_table_count; i++) { ++ if((ret = f4fbox_parse_single_box(in, abst)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse afrt box, ret: %d \n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int f4fbox_parse_mdat(AVIOContext *in, int64_t data_size, void *opague) ++{ ++ F4FBox *parent = (F4FBox*)opague; ++ F4FMediaDataBox *mdat = &(parent->mdat); ++ ++ mdat->data = av_mallocz(sizeof(uint8_t)*data_size); ++ if(!mdat->data) ++ return AVERROR(ENOMEM); ++ ++ mdat->size = data_size; ++ avio_read(in, mdat->data, mdat->size); ++ ++ return 0; ++} ++ ++static int f4fbox_parse_single_box(AVIOContext *in, void *opague) ++{ ++ int64_t bytes_read, bytes_left, start_pos, end_pos; ++ uint64_t size; ++ uint32_t type; ++ int ret = 0; ++ ++ bytes_read = 0; ++ start_pos = avio_tell(in); ++ ++ size = avio_rb32(in); ++ type = avio_rl32(in); ++ bytes_read += 8; ++ ++ if(size == 1) {/* 64 bit extended size */ ++ size = avio_rb64(in) - 8; ++ bytes_read += 8; ++ } ++ ++ if(size == 0) ++ return -1; ++ ++ if(type == MKTAG('a', 'b', 's', 't')) { ++ ret = f4fbox_parse_abst(in, size, opague); ++ } ++ if(type == MKTAG('a', 's', 'r', 't')) { ++ ret = f4fbox_parse_asrt(in, size, opague); ++ } ++ if(type == MKTAG('a', 'f', 'r', 't')) { ++ ret = f4fbox_parse_afrt(in, size, opague); ++ } ++ if(type == MKTAG('m', 'd', 'a', 't')) { ++ ret = f4fbox_parse_mdat(in, size, opague); ++ } ++ ++ if(ret < 0) ++ return ret; ++ ++ end_pos = avio_tell(in); ++ bytes_left = size - (end_pos - start_pos); ++ if(bytes_left > 0) ++ avio_skip(in, bytes_left); ++ ++ bytes_read += size; ++ ++ return bytes_read; ++} ++ ++static int f4fbox_parse(AVIOContext *in, int64_t data_size, void *opague) ++{ ++ int64_t bytes_read = 0; ++ int ret; ++ ++ while(!avio_feof(in) && bytes_read + 8 < data_size) { ++ if((ret = f4fbox_parse_single_box(in, opague)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "f4fbox Failed to parse box, ret: %d \n", ret); ++ return ret; ++ } ++ bytes_read += ret; ++ } ++ ++ return 0; ++} ++ ++int ff_parse_f4f_box(uint8_t *buffer, int buffer_size, F4FBox *box) ++{ ++ AVIOContext *in; ++ int ret; ++ ++ in = avio_alloc_context(buffer, buffer_size, 0, NULL, NULL, NULL, NULL); ++ if(!in) ++ return AVERROR(ENOMEM); ++ ++ ret = f4fbox_parse(in, buffer_size, box); ++ av_free(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(j = 0; j < abst->nb_quality_entries; j++) ++ av_freep(&abst->quality_entries[j]); ++ if (abst->quality_entries) ++ av_freep(&abst->quality_entries); ++ ++ for(i = 0; i < abst->nb_segment_run_table_boxes; i++) { ++ asrt = abst->segment_run_table_boxes[i]; ++ for(j = 0; j < asrt->nb_quality_entries; j++) ++ av_freep(&asrt->quality_entries[j]); ++ if (asrt->quality_entries) ++ av_freep(&asrt->quality_entries); ++ 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_quality_entries; j++) ++ av_freep(&afrt->quality_entries[j]); ++ if (afrt->quality_entries) ++ av_freep(&afrt->quality_entries); ++ for(j = 0; j < afrt->nb_fragment_run_entries; j++) { ++ fre = afrt->fragment_run_entries[j]; ++ av_freep(&fre); ++ } ++ av_freep(&afrt); ++ } ++ ++ mdat = &(box->mdat); ++ if(mdat->size > 0) ++ av_freep(&mdat->data); ++ ++ memset(box, 0x00, sizeof(F4FBox)); ++ ++ return 0; ++} +diff --git a/libavformat/f4fbox.h b/libavformat/f4fbox.h +new file mode 100644 +index 0000000..da3ac24 +--- /dev/null ++++ b/libavformat/f4fbox.h +@@ -0,0 +1,110 @@ ++/* ++ * Adobe Fragmented F4V File (F4F) Parser ++ * Copyright (c) 2013 Cory McCarthy ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Fragmented F4V File (F4F) Parser for Adobe HDS ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ */ ++ ++#include "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; ++ ++ uint8_t nb_quality_entries; ++ char **quality_entries; ++ ++ 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; ++ ++ uint8_t nb_quality_entries; ++ char **quality_entries; ++ ++ 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_quality_entries; ++ char **quality_entries; ++ ++ 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(uint8_t *buffer, int buffer_size, F4FBox *box); ++int ff_free_f4f_box(F4FBox *box); +diff --git a/libavformat/f4mmanifest.c b/libavformat/f4mmanifest.c +new file mode 100644 +index 0000000..2f3ea7f +--- /dev/null ++++ b/libavformat/f4mmanifest.c +@@ -0,0 +1,256 @@ ++/* ++ * 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 ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Media Manifest (F4M) File Parser ++ * @author Cory McCarthy ++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf ++ */ ++ ++#include "f4mmanifest.h" ++#include "libavutil/avstring.h" ++#include "libavutil/base64.h" ++#include ++ ++static int f4m_parse_bootstrap_info_node(node_t * node, F4MBootstrapInfo *bootstrap_info) ++{ ++ const char *p; ++ uint8_t *dst; ++ int ret; ++ node_t *attr; ++ ++ attr = roxml_get_attr(node, "id", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ av_strlcpy(bootstrap_info->id, p, sizeof(bootstrap_info->id)); ++ } ++ ++ attr = roxml_get_attr(node, "url", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ av_strlcpy(bootstrap_info->url, p, sizeof(bootstrap_info->url)); ++ } ++ ++ attr = roxml_get_attr(node, "profile", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ av_strlcpy(bootstrap_info->profile, p, sizeof(bootstrap_info->profile)); ++ } ++ ++ p = roxml_get_content(node, NULL, 0, NULL); ++ if(p) { ++ dst = av_mallocz(sizeof(uint8_t)*strlen(p)); ++ if(!dst) ++ return AVERROR(ENOMEM); ++ ++ if((ret = av_base64_decode(dst, p, strlen(p))) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "f4mmanifest Failed to decode bootstrap node base64 metadata, ret: %d \n", ret); ++ av_free(dst); ++ return ret; ++ } ++ ++ bootstrap_info->metadata = av_mallocz(sizeof(uint8_t)*ret); ++ if(!bootstrap_info->metadata) ++ return AVERROR(ENOMEM); ++ ++ bootstrap_info->metadata_size = ret; ++ memcpy(bootstrap_info->metadata, dst, ret); ++ ++ av_free(dst); ++ } ++ ++ return 0; ++} ++ ++static int f4m_parse_metadata_node(node_t * node, F4MMedia *media) ++{ ++ const char *p = NULL; ++ uint8_t *dst; ++ int ret; ++ ++ node_t * metadata_node = roxml_get_chld(node, NULL, 0); ++ while(metadata_node) { ++ if(!strcmp(roxml_get_name(metadata_node, NULL, 0), "metadata")) { ++ p = roxml_get_content(metadata_node, NULL, 0, NULL); ++ break; ++ } ++ metadata_node = roxml_get_next_sibling(metadata_node); ++ } ++ ++ if(!p) ++ return 0; ++ ++ dst = av_mallocz(sizeof(uint8_t)*strlen(p)); ++ if(!dst) ++ return AVERROR(ENOMEM); ++ ++ if((ret = av_base64_decode(dst, p, strlen(p))) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "f4mmanifest Failed to decode base64 metadata, ret: %d \n", ret); ++ av_free(dst); ++ return ret; ++ } ++ ++ media->metadata = av_mallocz(sizeof(uint8_t)*ret); ++ if(!media->metadata) ++ return AVERROR(ENOMEM); ++ ++ media->metadata_size = ret; ++ memcpy(media->metadata, dst, ret); ++ ++ av_free(dst); ++ ++ return 0; ++} ++ ++static int f4m_parse_media_node(node_t * node, F4MMedia *media) ++{ ++ const char *p; ++ int ret; ++ node_t * attr; ++ ++ attr = roxml_get_attr(node, "bitrate", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ media->bitrate = strtoul(p, NULL, 10); ++ } ++ ++ attr = roxml_get_attr(node, "url", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ av_strlcpy(media->url, p, sizeof(media->url)); ++ } ++ ++ attr = roxml_get_attr(node, "bootstrapInfoId", 0); ++ p =roxml_get_content(attr,NULL,0,NULL); ++ if(p) { ++ av_strlcpy(media->bootstrap_info_id, p, sizeof(media->bootstrap_info_id)); ++ } ++ ++ if((ret = f4m_parse_metadata_node(node, media)) < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int f4m_parse_manifest_node(node_t * root_node, F4MManifest *manifest) ++{ ++ F4MBootstrapInfo *bootstrap_info; ++ F4MMedia *media; ++ node_t * node; ++ const char *node_content; ++ int ret = 0,chld_idx=0; ++ ++ for (chld_idx=0; chld_idxid, node_content, sizeof(manifest->id)); ++ } else if(!strcmp(node_name, "streamType") && node_content) { ++ av_strlcpy(manifest->stream_type, node_content, sizeof(manifest->stream_type)); ++ } else if(!strcmp(node_name, "bootstrapInfo")) { ++ bootstrap_info = av_mallocz(sizeof(F4MBootstrapInfo)); ++ if(!bootstrap_info) ++ return AVERROR(ENOMEM); ++ manifest->bootstraps[manifest->nb_bootstraps++] = bootstrap_info; ++ ret = f4m_parse_bootstrap_info_node(node, bootstrap_info); ++ } else if(!strcmp(node_name, "media")) { ++ media = av_mallocz(sizeof(F4MMedia)); ++ if(!media) ++ return AVERROR(ENOMEM); ++ manifest->media[manifest->nb_media++] = media; ++ ret = f4m_parse_media_node(node, media); ++ } else if (!strcmp(node_name, "duration")) { ++ double duration = strtod(node_content, NULL); ++ manifest->duration = duration * AV_TIME_BASE; ++ } ++ ++ if(ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int f4m_parse_xml_file(uint8_t *buffer, int size, F4MManifest *manifest) ++{ ++ node_t * doc; ++ node_t * root_node; ++ int ret; ++ ++ doc = roxml_load_buf(buffer); ++ if(!doc) { ++ return -1; ++ } ++ ++ doc = roxml_get_root(doc); ++ root_node = roxml_get_chld(doc, NULL, 0); ++ if(!root_node) { ++ av_log(NULL, AV_LOG_ERROR, "f4mmanifest Root element not found \n"); ++ roxml_close(doc); ++ return -1; ++ } ++ const char * root_node_name = roxml_get_name(root_node, NULL, 0); ++ if(strcmp(root_node_name, "manifest")) { ++ av_log(NULL, AV_LOG_ERROR, "f4mmanifest Root element is not named manifest, name = %s \n", root_node_name); ++ roxml_close(doc); ++ return -1; ++ } ++ ++ ret = f4m_parse_manifest_node(root_node, manifest); ++ roxml_close(doc); ++ ++ return ret; ++} ++ ++int ff_parse_f4m_manifest(uint8_t *buffer, int size, F4MManifest *manifest) ++{ ++ return f4m_parse_xml_file(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(F4MManifest)); ++ ++ return 0; ++} +diff --git a/libavformat/f4mmanifest.h b/libavformat/f4mmanifest.h +new file mode 100644 +index 0000000..97a5885 +--- /dev/null ++++ b/libavformat/f4mmanifest.h +@@ -0,0 +1,65 @@ ++/* ++ * 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 ++ */ ++ ++/** ++ * @file ++ * @brief Adobe Media Manifest (F4M) File Parser ++ * @author Cory McCarthy ++ * @see http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/hds/pdfs/adobe-media-manifest-specification.pdf ++ */ ++ ++#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]; ++ int64_t duration; ++ ++ int nb_bootstraps; ++ F4MBootstrapInfo *bootstraps[MAX_NB_BOOTSTRAPS]; ++ ++ int nb_media; ++ F4MMedia *media[MAX_NB_MEDIA]; ++} F4MManifest; ++ ++int ff_parse_f4m_manifest(uint8_t *buffer, int size, F4MManifest *manifest); ++int ff_free_manifest(F4MManifest *manifest); +diff --git a/libavformat/flv.h b/libavformat/flv.h +index df5ce3d..b204817 100644 +--- a/libavformat/flv.h ++++ b/libavformat/flv.h +@@ -135,4 +135,6 @@ typedef enum { + AMF_DATA_TYPE_UNSUPPORTED = 0x0d, + } AMFDataType; + ++int flv_amf_get_string(AVIOContext *ioc, char *buffer, int buffsize); ++ + #endif /* AVFORMAT_FLV_H */ +diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c +index 94c9e28..84d081a 100644 +--- a/libavformat/flvdec.c ++++ b/libavformat/flvdec.c +@@ -368,6 +368,11 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) + return length; + } + ++int flv_amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) ++{ ++ return amf_get_string(ioc, buffer, buffsize); ++} ++ + static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t max_pos) + { + FLVContext *flv = s->priv_data; +diff --git a/libavformat/flvtag.c b/libavformat/flvtag.c +new file mode 100644 +index 0000000..5087fdb +--- /dev/null ++++ b/libavformat/flvtag.c +@@ -0,0 +1,408 @@ ++/* ++ * Adobe FLV Tag Parser ++ * Copyright (c) 2013 Cory McCarthy ++ * Copyright (c) 2014 martii ++ * ++ * 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 FLV Tag Parser for Adobe HDS F4F Files ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ */ ++ ++#include "flvtag.h" ++#include "libavformat/avio.h" ++#include "libavcodec/get_bits.h" ++#include "libavutil/channel_layout.h" ++#include "flv.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 cts; ++} FLVTagVideoHeader; ++ ++typedef struct FLVTagVideoBody { ++ //AVCDecoderConfigurationRecord ++ 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 & FLV_AUDIO_CODECID_MASK; ++ header->sound_rate = (byte >> FLV_AUDIO_SAMPLERATE_OFFSET) & 0x03; ++ header->sound_size = (byte >> FLV_AUDIO_SAMPLESSIZE_OFFSET) & 0x01; ++ header->sound_type = byte & 0x01; ++ ++ if(header->sound_format == FLV_CODECID_AAC) { ++ header->aac_packet_type = avio_r8(in); ++ ret++; ++ } ++ ++ return ret; ++} ++ ++static int flv_tag_parse_audio_body(AVIOContext *in, uint32_t data_size, ++ FLVTagAudioHeader *header, FLVTagAudioBody *body, FLVMediaSample **sample_out) ++{ ++ FLVMediaSample *sample; ++ ++ if(header->sound_format != FLV_CODECID_AAC) { ++ av_log(NULL, AV_LOG_ERROR, "flvtag Unhandled sound format %d\n", header->sound_format >> FLV_AUDIO_CODECID_OFFSET); ++ return 0; ++ } ++ ++ if(header->aac_packet_type == 0) { ++ av_log(NULL, AV_LOG_DEBUG, "flvtag skipped aac_packet_type %d\n", header->aac_packet_type); ++ return 0; ++ } ++ ++ if(header->aac_packet_type != 1) { ++ av_log(NULL, AV_LOG_ERROR, "flvtag Unhandled aac_packet_type %d\n", header->aac_packet_type); ++ return 0; ++ } ++ ++ sample = av_mallocz(sizeof(FLVMediaSample)); ++ if(!sample) ++ return AVERROR(ENOMEM); ++ ++ sample->type = AVMEDIA_TYPE_AUDIO; ++ ++ sample->data = av_mallocz(sizeof(uint8_t) * data_size); ++ if(!sample->data) { ++ av_free(sample); ++ 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; ++ header->codec_id = byte & 0x0F; ++ ++ if(header->codec_id == 0x07) { ++ header->avc_packet_type = avio_r8(in); ++ header->cts = (avio_rb24(in) + 0xff800000) ^ 0xff800000; ++ ret += 4; ++ } ++ ++ return ret; ++} ++ ++static int flv_tag_parse_video_body(AVIOContext *in, uint32_t data_size, ++ FLVTagVideoHeader *header, FLVTagVideoBody *body, FLVMediaSample **sample_out) ++{ ++ FLVMediaSample *sample = NULL; ++ uint8_t *p; ++ uint8_t nb_sps, nb_pps; ++ uint16_t sps_length, pps_length; ++ int i, ret = 0; ++ ++ if(header->frame_type == 0x05) { ++ avio_r8(in); ++ return 1; ++ } ++ ++ if(header->codec_id != FLV_CODECID_H264) { ++ av_log(NULL, 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) { // AVC NALU ++ sample = av_mallocz(sizeof(FLVMediaSample)); ++ 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_mallocz(sizeof(uint8_t) * sample->data_size); ++ if(!sample->data) { ++ av_free(sample); ++ 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) { ++ int length_size = body->length_size_minus_one + 1; ++ uint32_t nal_size = 0; ++ *p++ = 0x00; ++ *p++ = 0x00; ++ *p++ = 0x00; ++ *p++ = 0x01; ++ ++ while (length_size) { ++ nal_size <<= 8; ++ nal_size |= avio_r8(in); ++ length_size--; ++ ret++; ++ } ++ ++ 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(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, dts, stream_id; ++ int nb_samples = 0; ++ int ret; ++ ++ memset(&audio_header, 0x00, sizeof(FLVTagAudioHeader)); ++ memset(&audio_body, 0x00, sizeof(FLVTagAudioBody)); ++ memset(&video_header, 0x00, sizeof(FLVTagVideoHeader)); ++ memset(&video_body, 0x00, sizeof(FLVTagVideoBody)); ++ ++ 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 & 0x20; ++ tag_type = (byte & 0x01F); ++ ++ data_size = avio_rb24(in); ++ ++ dts = avio_rb24(in); ++ dts |= avio_r8(in) << 24; ++ ++ stream_id = avio_rb24(in); ++ if(stream_id != 0) { ++ av_log(NULL, AV_LOG_ERROR, "flvtag Invalid stream_id %d \n", stream_id); ++ return -1; ++ } ++ ++ if(tag_type == FLV_TAG_TYPE_AUDIO) { ++ data_size -= flv_tag_parse_audio_header(in, &audio_header); ++ } else if(tag_type == FLV_TAG_TYPE_VIDEO) { ++ data_size -= flv_tag_parse_video_header(in, &video_header); ++ } ++ ++#if 0 ++ if(filter) { ++ //EncryptionTagHeader ++ //FilterParams ++ } ++#endif ++ ++ sample = NULL; ++ ++ if(tag_type == FLV_TAG_TYPE_AUDIO) { ++ if((ret = flv_tag_parse_audio_body(in, data_size, ++ &audio_header, &audio_body, &sample)) < 0) { ++ ++ av_free(in); ++ return ret; ++ } ++ data_size -= ret; ++ } else if(tag_type == FLV_TAG_TYPE_VIDEO) { ++ if((ret = flv_tag_parse_video_body(in, data_size, ++ &video_header, &video_body, &sample)) < 0) { ++ ++ av_free(in); ++ return ret; ++ } ++ data_size -= ret; ++ } ++#if 0 ++ else if(tag_type == 18) { ++ //ScriptData ++ } ++#endif ++ ++ if(sample) { ++ sample->dts = dts; ++ if (tag_type == FLV_TAG_TYPE_VIDEO) ++ sample->pts = dts + video_header.cts; ++ else ++ sample->pts = AV_NOPTS_VALUE; ++ samples[nb_samples++] = sample; ++ } ++ ++ if(data_size) { ++ avio_skip(in, data_size); ++ } ++ avio_rb32(in); ++ } ++ ++ av_free(in); ++ ++ if(video_body.sps_data_size > 0) ++ av_free(video_body.sps_data); ++ if(video_body.pps_data_size > 0) ++ av_free(video_body.pps_data); ++ ++ if(nb_samples_out) ++ *nb_samples_out = nb_samples; ++ ++ return 0; ++} ++ ++int ff_decode_flv_body(uint8_t *buffer, int buffer_size, ++ FLVMediaSample **samples, int *nb_samples_out) ++{ ++ return flv_tag_decode_body(buffer, buffer_size, samples, nb_samples_out); ++} +diff --git a/libavformat/flvtag.h b/libavformat/flvtag.h +new file mode 100644 +index 0000000..51b94b4 +--- /dev/null ++++ b/libavformat/flvtag.h +@@ -0,0 +1,40 @@ ++/* ++ * 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 ++ */ ++ ++/** ++ * @file ++ * @brief Adobe FLV Tag Parser for Adobe HDS F4F Files ++ * @author Cory McCarthy ++ * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf ++ */ ++ ++#include "libavutil/avutil.h" ++ ++typedef struct FLVMediaSample { ++ enum AVMediaType type; ++ uint64_t dts; ++ uint64_t pts; ++ int data_size; ++ uint8_t *data; ++} FLVMediaSample; ++ ++int ff_decode_flv_body(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..1fadfc8 +--- /dev/null ++++ b/libavformat/hdsdec.c +@@ -0,0 +1,1036 @@ ++/* ++ * Adobe HTTP Dynamic Streaming (HDS) demuxer ++ * Copyright (c) 2013 Cory McCarthy ++ * Copyright (c) 2014 martii ++ * ++ * 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 ++ */ ++ ++#include "avformat.h" ++#include "internal.h" ++#include "url.h" ++#include "avio_internal.h" ++#include "libavutil/avstring.h" ++#include "libavutil/parseutils.h" ++#include "libavutil/opt.h" ++#include "libavutil/dict.h" ++#include "libavutil/time.h" ++ ++#include "amfmetadata.h" ++#include "f4mmanifest.h" ++#include "f4fbox.h" ++#include "flvtag.h" ++ ++#include ++#include ++#include ++#include ++ ++#define MAX_NB_SAMPLES 1024 ++ ++struct HDSDownloadThreadData ++{ ++ pthread_mutex_t mutex; // protects access to this struct ++ pthread_t thread; // download thread id ++ sem_t *to_thread; // send signal to thread ++ sem_t *to_caller; // send signal to caller ++ sem_t _to_thread; // send signal to thread ++ sem_t _to_caller; // send signal to caller ++ char *cookies; // cookies, if any ++ char *url; // current url ++ int run; // thread will run until == 0 ++ int abort; // setting this to !=0 aborts download ++ uint8_t *buffer; // set by thread, unset by thread or caller ++ uint32_t buflen; // set by thread, unset by thread or caller ++ AVIOInterruptCB *interrupt_callback; ++ AVIOInterruptCB *abort_callback; ++ AVIOInterruptCB download_abort_callback; ++}; ++ ++typedef struct HDSBootstrapInfo { ++ char id[MAX_URL_SIZE]; ++ char url[MAX_URL_SIZE]; ++ char profile[MAX_URL_SIZE]; ++ char *quality; ++ ++ F4FBox box; ++} HDSBootstrapInfo; ++ ++typedef struct HDSMedia { ++ int media_index; ++ int bitrate; ++ char url[MAX_URL_SIZE]; ++ HDSBootstrapInfo *bootstrap_info; ++ ++ AVStream *audio_stream; ++ AVStream *video_stream; ++ ++ int nb_samples; ++ FLVMediaSample *samples[MAX_NB_SAMPLES]; ++ int sample_index; ++ ++ unsigned int nb_fragments_read; ++ ++ struct HDSDownloadThreadData download_data; ++} HDSMedia; ++ ++typedef struct HDSContext { ++ char id[MAX_URL_SIZE]; ++ char base_url[MAX_URL_SIZE]; ++ int is_live; ++ int last_media_index; ++ ++ int nb_bootstraps; ++ HDSBootstrapInfo *bootstrap_info[MAX_NB_BOOTSTRAPS]; ++ ++ int nb_media; ++ HDSMedia *media[MAX_NB_MEDIA]; ++ ++ int64_t seek_timestamp; ++ char *cookies; ++} 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; ++ ++ 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 = NULL; ++ AVDictionary *opts = NULL; ++ char url[MAX_URL_SIZE]; ++ uint8_t *buffer; ++ int buffer_size; ++ int ret; ++ ++ 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); ++ } ++ ++ av_dict_set(&opts, "cookies", c->cookies, 0); ++ ret = ffurl_open(&puc, url, AVIO_FLAG_READ, &s->interrupt_callback, &opts); ++ av_dict_free(&opts); ++ ++ if(ret < 0) { ++ av_log(NULL, 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(NULL, AV_LOG_ERROR, "hds Failed to download bootstrap, ret: %d\n", ret); ++ av_free(buffer); ++ return ret; ++ } ++ ++ if (c->cookies) ++ av_freep(&c->cookies); ++ av_opt_get(puc->priv_data, "cookies", 0, (uint8_t **) &c->cookies); ++ if (c->cookies && !strlen(c->cookies)) ++ av_freep(&c->cookies); ++ ++ if((ret = ffurl_close(puc)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to finish downloading bootstrap, ret: %d\n", ret); ++ av_free(buffer); ++ return ret; ++ } ++ ++ if(buffer_out) ++ *buffer_out = buffer; ++ else ++ av_free(buffer); ++ ++ if(buffer_size_out) ++ *buffer_size_out = buffer_size; ++ ++ return 0; ++} ++ ++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)); ++ ++ buffer = f4m_bootstrap_info->metadata; ++ buffer_size = f4m_bootstrap_info->metadata_size; ++ ++ if(buffer_size > 0) { ++ if((ret = ff_parse_f4f_box(buffer, buffer_size, &(bootstrap_info->box))) < 0) { ++ av_log(NULL, 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(NULL, AV_LOG_ERROR, "hds Failed to download bootstrap, ret: %d\n", ret); ++ return ret; ++ } ++ ++ if((ret = ff_parse_f4f_box(buffer, buffer_size, &(bootstrap_info->box))) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to parse downloaded bootstrap box, ret: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int create_streams(AVFormatContext *s, HDSMedia *media, AMFMetadata *metadata, int i) ++{ ++ if (metadata->video_codec_id) { ++ AVStream *st = avformat_new_stream(s, NULL); ++ if(!st) ++ return AVERROR(ENOMEM); ++ ++ media->video_stream = st; ++ avpriv_set_pts_info(st, 32, 1, 1000); ++ st->discard = AVDISCARD_ALL; ++ st->id = 0 + 2 * i; ++ st->codec->codec_type = AVMEDIA_TYPE_VIDEO; ++ st->codec->codec_id = metadata->video_codec_id; ++ st->codec->width = metadata->width; ++ st->codec->height = metadata->height; ++ st->codec->bit_rate = metadata->video_data_rate * 1000; ++ } ++ ++ if (metadata->audio_codec_id) { ++ AVStream *st = avformat_new_stream(s, NULL); ++ if(!st) ++ return AVERROR(ENOMEM); ++ ++ media->audio_stream = st; ++ avpriv_set_pts_info(st, 32, 1, 1000); ++ st->discard = AVDISCARD_ALL; ++ st->id = 1 + 2 * i; ++ st->codec->codec_type = AVMEDIA_TYPE_AUDIO; ++ st->codec->codec_id = metadata->audio_codec_id; ++ st->codec->channels = metadata->nb_audio_channels; ++ st->codec->channel_layout = (st->codec->channels == 2) ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; ++ st->codec->sample_rate = metadata->audio_sample_rate; ++ st->codec->sample_fmt = AV_SAMPLE_FMT_S16; ++ st->codec->bit_rate = metadata->audio_data_rate * 1000; ++ st->need_parsing = metadata->audio_stream_need_parsing; ++ } ++ ++ return 0; ++} ++ ++static int create_media(AVFormatContext *s, F4MMedia *f4m_media, int i) ++{ ++ HDSContext *c = s->priv_data; ++ HDSMedia *media; ++ AMFMetadata metadata; ++ int ret, j; ++ ++ media = av_mallocz(sizeof(HDSMedia)); ++ if(!media) ++ return AVERROR(ENOMEM); ++ ++ c->media[c->nb_media++] = media; ++ ++ media->media_index = i; ++ media->bitrate = f4m_media->bitrate; ++ memcpy(media->url, f4m_media->url, sizeof(media->url)); ++ ++ for(j = 0; j < c->nb_bootstraps; j++) { ++ if(!av_strcasecmp(f4m_media->bootstrap_info_id, c->bootstrap_info[j]->id)) ++ continue; ++ media->bootstrap_info = c->bootstrap_info[j]; ++ break; ++ } ++ ++ memset(&metadata, 0x00, sizeof(AMFMetadata)); ++ metadata.nb_audio_channels = 1; ++ if((ret = ff_parse_amf_metadata(f4m_media->metadata, f4m_media->metadata_size, &metadata)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to parse metadata, ret: %d\n", ret); ++ return ret; ++ } ++ ++ return create_streams(s, media, &metadata, i); ++} ++ ++static int create_pmt(AVFormatContext *s) ++{ ++ HDSContext *c = s->priv_data; ++ HDSMedia *media; ++ AVProgram *p; ++ int i, j; ++ ++ 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); ++ ++ av_dict_set(&p->metadata,"name", ++ av_asprintf("%d kbit/s", media->bitrate), 0); ++ ++ if (media->video_stream) ++ av_program_add_stream_index(s, p->id, media->video_stream->index); ++ if (media->audio_stream) ++ av_program_add_stream_index(s, p->id, media->audio_stream->index); ++ } ++ ++ return 0; ++} ++ ++static void download_thread_start(AVFormatContext *s, HDSMedia *media); ++ ++static int initialize_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(NULL, 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, i)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to create media, ret: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ if((ret = create_pmt(s)) < 0) { ++ av_log(NULL, 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; ++ ++ s->duration = manifest->duration; ++ c->seek_timestamp = AV_NOPTS_VALUE; ++ ++ for(i = 0; i < c->nb_media; i++) ++ download_thread_start(s, c->media[i]); ++ ++ return 0; ++} ++ ++//#define HDS_ENABLE_LOG_CALLBACK ++#ifdef HDS_ENABLE_LOG_CALLBACK ++static void log_callback(void *ptr __attribute__ ((unused)), int lvl __attribute__ ((unused)), const char *format, va_list ap) ++{ ++ vfprintf(stderr, format, ap); ++} ++#endif ++ ++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; ++ int ret; ++#ifdef HDS_ENABLE_LOG_CALLBACK ++ av_log_set_callback(log_callback); ++#endif ++ ++ p = av_stristr(s->filename, "manifest.f4m"); ++ if(!p) { ++ av_log(NULL, AV_LOG_ERROR, "hds \"manifest.f4m\" is not a substring of \"%s\"\n", s->filename); ++ return -1; ++ } ++ av_strlcpy(c->base_url, s->filename, p - s->filename + 1); ++ ++ filesize = avio_size(in); ++ if(filesize <= 0) ++ return -1; ++ ++ buf = av_mallocz(filesize*sizeof(uint8_t)); ++ if(!buf) ++ return AVERROR(ENOMEM); ++ ++ avio_read(in, buf, filesize); ++ ++ memset(&manifest, 0x00, sizeof(F4MManifest)); ++ ret = ff_parse_f4m_manifest(buf, filesize, &manifest); ++ ++ av_free(buf); ++ ++ if (ret > -1) ++ ret = initialize_context(s, &manifest); ++ ++ ff_free_manifest(&manifest); ++ ++ return ret; ++} ++ ++static void construct_fragment_url(const char *base_url, const char *media_url, ++ unsigned int segment, unsigned int fragment, const char *suffix, char *url_out, size_t url_size) ++{ ++ char *p; ++ char *fragment_str; ++ ++ p = url_out; ++ p += av_strlcat(p, base_url, url_size); ++ p += av_strlcat(p, media_url, url_size); ++ ++ fragment_str = av_asprintf("Seg%u-Frag%u", segment, fragment); ++ p += av_strlcat(p, fragment_str, url_size); ++ av_free(fragment_str); ++ ++ p += av_strlcat(p, suffix, url_size); ++} ++ ++static int get_fragment_offset(HDSMedia *media, int64_t timestamp) ++{ ++ F4FBootstrapInfoBox *abst = &(media->bootstrap_info->box.abst); ++ int fragments_max = 0; ++ int i, j; ++ ++ for(i = 0; i < abst->nb_segment_run_table_boxes; i++) { ++ F4FSegmentRunTableBox *asrt = abst->segment_run_table_boxes[i]; ++ int found = 0; ++ if (asrt->nb_quality_entries && media->bootstrap_info->quality) ++ for(j = 0; !found && j < asrt->nb_quality_entries; j++) ++ found = !strcmp(asrt->quality_entries[j], media->bootstrap_info->quality); ++ else ++ found = 1; ++ ++ if (found) { ++ F4FSegmentRunEntry *segment_entry = asrt->segment_run_entries[asrt->nb_segment_run_entries - 1]; ++ fragments_max = segment_entry->fragments_per_segment; ++ break; ++ } ++ } ++ ++ for(i = 0; i < abst->nb_fragment_run_table_boxes; i++) { ++ int found = 0; ++ F4FFragmentRunTableBox *afrt = abst->fragment_run_table_boxes[i]; ++ if (afrt->nb_quality_entries && media->bootstrap_info->quality) ++ for(j = 0; !found && j < afrt->nb_quality_entries; j++) ++ found = !strcmp(afrt->quality_entries[j], media->bootstrap_info->quality); ++ else ++ found = 1; ++ ++ if (found) { ++ for(j = 0; j < afrt->nb_fragment_run_entries; j++) { ++ F4FFragmentRunEntry *fre = afrt->fragment_run_entries[j]; ++ int fragcount; ++ if (j + 1 < afrt->nb_fragment_run_entries) { ++ fragcount = afrt->fragment_run_entries[j + 1]->first_fragment - fre->first_fragment; ++ fragments_max -= fragcount; ++ } else ++ fragcount = fragments_max; ++ ++ if (timestamp >= fre->first_fragment_time_stamp && timestamp <= fre->first_fragment_time_stamp + fragcount * fre->fragment_duration) ++ return fre->first_fragment + (timestamp - fre->first_fragment_time_stamp)/fre->fragment_duration; ++ } ++ break; ++ } ++ } ++ return 0; ++} ++ ++static int get_segment_fragment(int is_live, HDSMedia *media, unsigned int *segment_out, unsigned int *fragment_out) ++{ ++ F4FBootstrapInfoBox *abst = &(media->bootstrap_info->box.abst); ++ unsigned int segment = ~0, fragment = ~0, fragments_max = 0; ++ int i, j, skip; ++ ++ if (is_live) { ++ // FIXME. This is a crude hack. ++ *segment_out = 1; ++ *fragment_out = media->nb_fragments_read; ++ return 0; ++ } ++ ++ skip = media->nb_fragments_read; ++ for(i = 0; segment == ~0 && i < abst->nb_segment_run_table_boxes; i++) { ++ F4FSegmentRunTableBox *asrt = abst->segment_run_table_boxes[i]; ++ int found = 0; ++ if (asrt->nb_quality_entries && media->bootstrap_info->quality) ++ for(j = 0; !found && j < asrt->nb_quality_entries; j++) ++ found = !strcmp(asrt->quality_entries[j], media->bootstrap_info->quality); ++ else ++ found = 1; ++ ++ if (found) { ++ for (j = 0; segment == ~0 && j < asrt->nb_segment_run_entries; j++) { ++ F4FSegmentRunEntry *segment_entry = asrt->segment_run_entries[j]; ++ ++ if (segment_entry->fragments_per_segment < skip) { ++ skip -= segment_entry->fragments_per_segment; ++ fragments_max = segment_entry->fragments_per_segment; ++ } else ++ segment = segment_entry->first_segment; ++ } ++ break; ++ } ++ } ++ ++ if(segment == ~0) { ++ av_log(NULL, AV_LOG_ERROR, "hds segment entry for next fragment (%u) not found, skip: %d\n", media->nb_fragments_read, skip); ++ return 0; ++ } ++ ++ skip = media->nb_fragments_read; ++ for(i = 0; fragment == ~0 && i < abst->nb_fragment_run_table_boxes; i++) { ++ int found = 0; ++ F4FFragmentRunTableBox *afrt = abst->fragment_run_table_boxes[i]; ++ if (afrt->nb_quality_entries && media->bootstrap_info->quality) ++ for(j = 0; !found && j < afrt->nb_quality_entries; j++) ++ found = !strcmp(afrt->quality_entries[j], media->bootstrap_info->quality); ++ else ++ found = 1; ++ ++ if (found) { ++ for(j = 0; fragment == ~0 && j < afrt->nb_fragment_run_entries; j++) { ++ int fragcount; ++ F4FFragmentRunEntry *fre = afrt->fragment_run_entries[j]; ++ if (j + 1 < afrt->nb_fragment_run_entries) { ++ fragcount = afrt->fragment_run_entries[j + 1]->first_fragment - fre->first_fragment; ++ fragments_max -= fragcount; ++ } else ++ fragcount = fragments_max; ++ ++ if (fragcount < skip) ++ fragcount -= skip; ++ else { ++ fragment = fre->first_fragment + skip; ++ skip = 0; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (!is_live && skip > 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds fragment %d fragments beyond EOF\n", skip); ++ return AVERROR_EOF; ++ } ++ ++ if(fragment == ~0) { ++ av_log(NULL, AV_LOG_ERROR, "hds fragment entry not found\n"); ++ return -1; ++ } ++ ++ if(segment_out) ++ *segment_out = segment; ++ if(fragment_out) ++ *fragment_out = fragment; ++ ++ return 0; ++} ++ ++static int download_abort_callback_function(void *opaque) ++{ ++ HDSMedia *media = (HDSMedia *) opaque; ++ return media->download_data.abort || ff_check_interrupt(media->download_data.interrupt_callback); ++} ++ ++static void *download_thread(void *opaque) ++{ ++ HDSMedia *media = (HDSMedia *) opaque; ++ ++ while (media->download_data.run && !download_abort_callback_function(media)) { ++ AVDictionary *opts = NULL; ++ URLContext *puc = NULL; ++ uint8_t *buffer = NULL; ++ int buffer_size = 0, ret; ++ char *url = NULL; ++ int url_len; ++ int tries_left = 15; ++ ++ if (sem_wait(media->download_data.to_thread)) ++ break; ++ if (!media->download_data.run) ++ continue; ++ ++ pthread_mutex_lock(&media->download_data.mutex); ++ media->download_data.abort = 0; ++ if (media->download_data.buffer) ++ av_freep(&media->download_data.buffer); ++ media->download_data.buflen = 0; ++ // generate local copies from HDSDownloadThreadData ++ if (media->download_data.cookies) ++ av_dict_set(&opts, "cookies", media->download_data.cookies, 0); ++ url_len = strlen(media->download_data.url) + 1; ++ url = alloca(url_len); ++ strncpy(url, media->download_data.url, url_len); ++ pthread_mutex_unlock(&media->download_data.mutex); ++ //av_log(NULL, AV_LOG_DEBUG, "%s %d: downloading %s\n", __FILE__,__LINE__, url); ++ ++ // initiate download ++ do { ++ ret = ffurl_open(&puc, url, AVIO_FLAG_READ, &media->download_data.download_abort_callback, &opts); ++ if (ret < 0) { ++ sleep(1); ++ if (download_abort_callback_function(media)) ++ break; ++ sleep(1); ++ tries_left--; ++ } ++ } while (ret < 0 && media->download_data.run && !download_abort_callback_function(media) && tries_left > 0); ++ ++ if (opts) ++ av_dict_free(&opts); ++ ++ if(ret < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to start downloading url:%s, ret:%d\n", url, ret); ++ } else { ++ buffer_size = ffurl_size(puc); ++ if (buffer_size > -1) ++ buffer = av_mallocz(buffer_size+FF_INPUT_BUFFER_PADDING_SIZE); ++ if(!buffer) ++ av_log(NULL, AV_LOG_DEBUG, "hds Failed to allocate %d bytes buffer\n", buffer_size); ++ } ++ ++ if(buffer && (ret = ffurl_read_complete(puc, buffer, buffer_size)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to download fragment, ret: %d\n", ret); ++ av_freep(&buffer); ++ } ++ ++ pthread_mutex_lock(&media->download_data.mutex); ++ if (media->download_data.abort || !buffer) { ++ media->download_data.abort = 0; ++ } else { ++ //av_log(NULL, AV_LOG_DEBUG, "%s %d: downloaded %s\n", __FILE__,__LINE__, media->download_data.url); ++ if (media->download_data.cookies) ++ av_freep(&media->download_data.cookies); ++ av_opt_get(puc->priv_data, "cookies", 0, (uint8_t **) &media->download_data.cookies); ++ if (media->download_data.cookies && !strlen(media->download_data.cookies)) ++ av_freep(&media->download_data.cookies); ++ media->download_data.buffer = buffer; ++ media->download_data.buflen = buffer_size; ++ } ++ sem_post(media->download_data.to_caller); // confirm download ++ pthread_mutex_unlock(&media->download_data.mutex); ++ if (puc) ++ ffurl_close(puc); ++ } ++ pthread_exit(NULL); ++} ++ ++static void download_thread_start(AVFormatContext *s, HDSMedia *media) ++{ ++#if defined(__APPLE__) ++ char buf[40]; ++#endif ++ pthread_mutex_init(&media->download_data.mutex, NULL); ++#if defined(__APPLE__) // no unnamed semaphores on darwin ++ snprintf(buf, sizeof(buf), "sem_to_thread%d", media->media_index); ++ media->download_data.to_thread = sem_open(buf, O_CREAT, 0644, 0); ++ snprintf(buf, sizeof(buf), "sem_to_caller%d", media->media_index); ++ media->download_data.to_caller = sem_open(buf, O_CREAT, 0644, 0); ++#else ++ sem_init(&media->download_data._to_thread, 0, 0); ++ sem_init(&media->download_data._to_caller, 0, 0); ++ media->download_data.to_thread = &media->download_data._to_thread; ++ media->download_data.to_caller = &media->download_data._to_caller; ++#endif ++ media->download_data.thread = 0; ++ media->download_data.run = 1; ++ media->download_data.abort = 0; ++ media->download_data.url = NULL; ++ media->download_data.buffer = NULL; ++ media->download_data.buflen = 0; ++ media->download_data.cookies = NULL; ++ media->download_data.interrupt_callback = &s->interrupt_callback; ++ media->download_data.download_abort_callback.callback = download_abort_callback_function; ++ media->download_data.download_abort_callback.opaque = media; ++ media->download_data.abort_callback = &media->download_data.download_abort_callback; ++ if (pthread_create(&media->download_data.thread, NULL, download_thread, media)) ++ av_log(NULL, AV_LOG_ERROR, "hds: creating download thread failed\n"); ++} ++ ++static void download_thread_stop(HDSMedia *media) ++{ ++ if (media->download_data.thread) { ++#if defined(__APPLE__) ++ char buf[40]; ++#endif ++ media->download_data.run = 0; ++ media->download_data.abort = 1; ++ sem_post(media->download_data.to_thread); ++ pthread_join(media->download_data.thread, NULL); ++ media->download_data.thread = 0; ++ if (media->download_data.url) ++ av_freep(&media->download_data.url); ++ if (media->download_data.cookies) ++ av_freep(&media->download_data.cookies); ++ if (media->download_data.buffer) ++ av_freep(&media->download_data.buffer); ++ media->download_data.buflen = 0; ++#if defined(__APPLE__) ++ sem_close(media->download_data.to_thread); ++ sem_close(media->download_data.to_caller); ++ snprintf(buf, sizeof(buf), "sem_to_thread%d", media->media_index); ++ sem_unlink(buf); ++ snprintf(buf, sizeof(buf), "sem_to_caller%d", media->media_index); ++ sem_unlink(buf); ++#else ++ sem_destroy(&media->download_data._to_thread); ++ sem_destroy(&media->download_data._to_caller); ++#endif ++ pthread_mutex_destroy(&media->download_data.mutex); ++ } ++} ++ ++static int download_fragment(AVFormatContext *s, HDSMedia *media, uint8_t **buffer_out, int *buffer_size_out) ++{ ++ HDSContext *c = s->priv_data; ++ char url[MAX_URL_SIZE]; ++ unsigned int segment, fragment; ++ int ret; ++ ++ if((ret = get_segment_fragment(c->is_live, media, &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); ++ ++ pthread_mutex_lock(&media->download_data.mutex); ++ ++ if (!media->download_data.cookies) ++ media->download_data.cookies = av_strdup(c->cookies); ++ if (media->download_data.url && strcmp(media->download_data.url, url)) { ++ // download in progress or finished, but not the wanted one. abort it. ++ media->download_data.abort = 1; // initiate abort ++ pthread_mutex_unlock(&media->download_data.mutex); ++ sem_wait(media->download_data.to_caller); // wait until current transfer has finished ++ pthread_mutex_lock(&media->download_data.mutex); ++ av_freep(&media->download_data.url); ++ } ++ if (!media->download_data.url) { ++ // queue retrieval of wanted url ++ media->download_data.url = av_strdup(url); ++ sem_post(media->download_data.to_thread); // initiate download ++ } ++ if (media->download_data.url && !strcmp(media->download_data.url, url)) { ++ // download matches what we want ++ pthread_mutex_unlock(&media->download_data.mutex); ++ sem_wait(media->download_data.to_caller); // wait until finished ++ pthread_mutex_lock(&media->download_data.mutex); ++ av_freep(&media->download_data.url); ++ if (media->download_data.buffer) { ++ *buffer_out = media->download_data.buffer; ++ *buffer_size_out = media->download_data.buflen; ++ media->download_data.buffer = NULL; ++ media->download_data.buflen = 0; ++ ret = 0; ++ media->nb_fragments_read++; ++ //initiate retrieval of next url ++ if(!get_segment_fragment(c->is_live, media, &segment, &fragment)) ++ 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); ++ media->download_data.url = av_strdup(url); ++ sem_post(media->download_data.to_thread); // initiate download ++ } else { ++ // finished but failed ++ ret = AVERROR(EIO); ++ } ++ } ++ pthread_mutex_unlock(&media->download_data.mutex); ++ return ret; ++} ++ ++static int get_next_fragment(AVFormatContext *s, HDSMedia *media) ++{ ++ F4FBox box; ++ uint8_t *buffer = NULL; ++ int buffer_size = 0, ret; ++ ++ if (ff_check_interrupt(media->download_data.interrupt_callback)) ++ return AVERROR(EIO); ++ ++ if((ret = download_fragment(s, media, &buffer, &buffer_size)) < 0) ++ return ret; ++ ++ memset(&box, 0x00, sizeof(F4FBox)); ++ if((ret = ff_parse_f4f_box(buffer, buffer_size, &box)) < 0) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to parse bootstrap box, ret: %d\n", ret); ++ av_free(buffer); ++ ff_free_f4f_box(&box); ++ return ret; ++ } ++ av_free(buffer); ++ ++ if((ret = ff_decode_flv_body(box.mdat.data, box.mdat.size, media->samples, &media->nb_samples)) < 0) { ++ av_log(NULL, 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->dts; ++ pkt->pts = sample->pts; ++ ++ 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, HDSMedia *media, AVPacket *pkt) ++{ ++ HDSContext *c = s->priv_data; ++ int ret; ++ ++ if (c->is_live && !media->nb_fragments_read) { ++ int64_t ts = media->bootstrap_info->box.abst.current_media_time; ++ media->nb_fragments_read = get_fragment_offset(media, ts); ++ if (media->nb_fragments_read > 1) ++ media->nb_fragments_read--; ++ } ++ ++ if (c->seek_timestamp != AV_NOPTS_VALUE) { ++ int64_t ts = c->seek_timestamp; ++ c->seek_timestamp = AV_NOPTS_VALUE; ++ media->nb_fragments_read = get_fragment_offset(media, ts); ++ clear_samples(media); ++ } ++ ++ if(media->nb_samples == 0) { ++ if((ret = get_next_fragment(s, 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 0; ++} ++ ++static int hds_read_packet(AVFormatContext *s, AVPacket *pkt) ++{ ++ HDSContext *c = s->priv_data; ++ HDSMedia *media = NULL; ++ int i, ret; ++ ++ for (i = c->last_media_index + 1; !media && i < c->nb_media; i++) { ++ media = c->media[i]; ++ if ((!media->video_stream || (media->video_stream->discard == AVDISCARD_ALL)) ++ && (!media->audio_stream || (media->audio_stream->discard == AVDISCARD_ALL))) ++ media = NULL; ++ } ++ for (i = 0; !media && i < c->nb_media; i++) { ++ media = c->media[i]; ++ if ((!media->video_stream || (media->video_stream->discard == AVDISCARD_ALL)) ++ && (!media->audio_stream || (media->audio_stream->discard == AVDISCARD_ALL))) ++ media = NULL; ++ } ++ c->last_media_index = i; ++ ++ if (media && !media->bootstrap_info && c->nb_bootstraps) ++ media->bootstrap_info = c->bootstrap_info[0]; ++ ++ if (!media || !media->bootstrap_info) { ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to find valid stream\n"); ++ return AVERROR(EIO); ++ } ++ ++ if((ret = get_next_packet(s, media, pkt)) < 0) { ++ if(ret != AVERROR_EOF) ++ av_log(NULL, AV_LOG_ERROR, "hds Failed to get next packet, ret: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int hds_close(AVFormatContext *s) ++{ ++ HDSContext *c = s->priv_data; ++ HDSBootstrapInfo *bootstrap_info; ++ HDSMedia *media; ++ int i; ++ ++ for(i = 0; i < c->nb_media; i++) ++ download_thread_stop(c->media[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]; ++ ++ if (media->download_data.buffer) ++ av_freep(&media->download_data.buffer); ++ ++ clear_samples(media); ++ av_freep(&media); ++ } ++ ++ if(c->cookies) ++ av_freep(&c->cookies); ++ ++ memset(c, 0x00, sizeof(HDSContext)); ++ ++ return 0; ++} ++ ++static int hds_probe(AVProbeData *p) ++{ ++ if(p->filename && av_stristr(p->filename, ".f4m")) ++ return AVPROBE_SCORE_MAX; ++ return 0; ++} ++ ++static int hds_read_seek(AVFormatContext *s, int stream_index, ++ int64_t timestamp, int flags) ++{ ++ HDSContext *c = s->priv_data; ++ ++ if (flags & AVSEEK_FLAG_BYTE) ++ return AVERROR(ENOSYS); ++ if (s->duration < c->seek_timestamp) { ++ c->seek_timestamp = AV_NOPTS_VALUE; ++ return AVERROR(EIO); ++ } ++ c->seek_timestamp = timestamp; ++ ++ if (c->is_live) { ++ int i; ++ for(i = 0; i < c->nb_media; i++) ++ c->media[i]->nb_fragments_read = 0; ++ c->seek_timestamp = AV_NOPTS_VALUE; ++ return 0; ++ } ++ ++ 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, ++ .read_seek = hds_read_seek, ++}; +-- +1.9.1 + diff --git a/archive-patches/ffmpeg/0002-ffmpeg-aac-3.x.patch b/archive-patches/ffmpeg/0002-ffmpeg-aac-3.x.patch new file mode 100644 index 0000000..e6ae1b2 --- /dev/null +++ b/archive-patches/ffmpeg/0002-ffmpeg-aac-3.x.patch @@ -0,0 +1,53 @@ +--- a/libavcodec/aacdec_template.c ++++ b/libavcodec/aacdec_template.c +@@ -2359,7 +2359,7 @@ + * @param decode 1 if tool is used normally, 0 if tool is used in LTP. + * @param coef spectral coefficients + */ +-static void apply_tns(INTFLOAT coef[1024], TemporalNoiseShaping *tns, ++static __attribute__((optimize(0))) void apply_tns(INTFLOAT coef[1024], TemporalNoiseShaping *tns, + IndividualChannelStream *ics, int decode) + { + const int mmm = FFMIN(ics->tns_max_bands, ics->max_sfb); +--- a/libavcodec/mdct_template.c ++++ b/libavcodec/mdct_template.c +@@ -102,7 +102,7 @@ av_cold int ff_mdct_init(FFTContext *s, int nbits, int inverse, double scale) + * @param output N/2 samples + * @param input N/2 samples + */ +-void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) ++void __attribute__((optimize(0))) ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) + { + int k, n8, n4, n2, n, j; + const uint16_t *revtab = s->revtab; +--- a/libavcodec/aacps.c ++++ b/libavcodec/aacps.c +@@ -654,7 +654,7 @@ + par[ 1] = AAC_HALF_SUM(par[ 0], par[ 1]); + } + +-static void decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const INTFLOAT (*s)[32][2], int is34) ++static void __attribute__((optimize(0))) decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const INTFLOAT (*s)[32][2], int is34) + { + LOCAL_ALIGNED_16(INTFLOAT, power, [34], [PS_QMF_TIME_SLOTS]); + LOCAL_ALIGNED_16(INTFLOAT, transient_gain, [34], [PS_QMF_TIME_SLOTS]); +--- a/libavcodec/fft_template.c ++++ b/libavcodec/fft_template.c +@@ -475,7 +475,7 @@ + pass(z,FFT_NAME(ff_cos_##n),n4/2);\ + } + +-static void fft4(FFTComplex *z) ++static void __attribute__((optimize(0))) fft4(FFTComplex *z) + { + FFTDouble t1, t2, t3, t4, t5, t6, t7, t8; + +@@ -489,7 +489,7 @@ + BF(z[2].im, z[0].im, t2, t5); + } + +-static void fft8(FFTComplex *z) ++static void __attribute__((optimize(0))) fft8(FFTComplex *z) + { + FFTDouble t1, t2, t3, t4, t5, t6; + diff --git a/archive-patches/ffmpeg/0003-ffmpeg-Revert-lavc-Switch-bitrate-to-64bit.patch b/archive-patches/ffmpeg/0003-ffmpeg-Revert-lavc-Switch-bitrate-to-64bit.patch new file mode 100644 index 0000000..f6d6b7e --- /dev/null +++ b/archive-patches/ffmpeg/0003-ffmpeg-Revert-lavc-Switch-bitrate-to-64bit.patch @@ -0,0 +1,381 @@ +diff --git a/ffserver.c b/ffserver.c +index 8b819b6..2732d7f 100644 +--- a/ffserver.c ++++ b/ffserver.c +@@ -1914,9 +1914,9 @@ static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream) + abort(); + } + +- avio_printf(pb, "%d%s%"PRId64 ++ avio_printf(pb, "%d%s%d" + "%s%s\n", +- i, type, (int64_t)st->codecpar->bit_rate/1000, ++ i, type, st->codecpar->bit_rate/1000, + codec ? codec->name : "", parameters); + } + +diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h +index 35df4f6..34363a7 100644 +--- a/libavcodec/avcodec.h ++++ b/libavcodec/avcodec.h +@@ -1794,7 +1794,7 @@ typedef struct AVCodecContext { + * - decoding: Set by user, may be overwritten by libavcodec + * if this info is available in the stream + */ +- int64_t bit_rate; ++ int bit_rate; + + /** + * number of bits the bitstream is allowed to diverge from the reference. +@@ -2729,14 +2729,14 @@ typedef struct AVCodecContext { + * - encoding: Set by user. + * - decoding: Set by user, may be overwritten by libavcodec. + */ +- int64_t rc_max_rate; ++ int rc_max_rate; + + /** + * minimum bitrate + * - encoding: Set by user. + * - decoding: unused + */ +- int64_t rc_min_rate; ++ int rc_min_rate; + + #if FF_API_MPV_OPT + /** +diff --git a/libavcodec/cook.c b/libavcodec/cook.c +index 53cb8385..2cd96cf 100644 +--- a/libavcodec/cook.c ++++ b/libavcodec/cook.c +@@ -1035,7 +1035,7 @@ static void dump_cook_context(COOKContext *q) + } + ff_dlog(q->avctx, "COOKContext\n"); + PRINT("nb_channels", q->avctx->channels); +- PRINT("bit_rate", (int)q->avctx->bit_rate); ++ PRINT("bit_rate", q->avctx->bit_rate); + PRINT("sample_rate", q->avctx->sample_rate); + PRINT("samples_per_channel", q->subpacket[0].samples_per_channel); + PRINT("subbands", q->subpacket[0].subbands); +diff --git a/libavcodec/dcaenc.c b/libavcodec/dcaenc.c +index 3c5c33c..28ab9b2 100644 +--- a/libavcodec/dcaenc.c ++++ b/libavcodec/dcaenc.c +@@ -161,7 +161,7 @@ static int encode_init(AVCodecContext *avctx) + c->samplerate_index = i; + + if (avctx->bit_rate < 32000 || avctx->bit_rate > 3840000) { +- av_log(avctx, AV_LOG_ERROR, "Bit rate %"PRId64" not supported.", (int64_t)avctx->bit_rate); ++ av_log(avctx, AV_LOG_ERROR, "Bit rate %i not supported.", avctx->bit_rate); + return AVERROR(EINVAL); + } + for (i = 0; ff_dca_bit_rates[i] < avctx->bit_rate; i++) +diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c +index 98a817b..5df0c90 100644 +--- a/libavcodec/libfdk-aacenc.c ++++ b/libavcodec/libfdk-aacenc.c +@@ -215,8 +215,8 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) + } + if ((err = aacEncoder_SetParam(s->handle, AACENC_BITRATE, + avctx->bit_rate)) != AACENC_OK) { +- av_log(avctx, AV_LOG_ERROR, "Unable to set the bitrate %"PRId64": %s\n", +- (int64_t)avctx->bit_rate, aac_get_error(err)); ++ av_log(avctx, AV_LOG_ERROR, "Unable to set the bitrate %d: %s\n", ++ avctx->bit_rate, aac_get_error(err)); + goto error; + } + } +diff --git a/libavcodec/libgsmenc.c b/libavcodec/libgsmenc.c +index 69ce439..45fdb8e 100644 +--- a/libavcodec/libgsmenc.c ++++ b/libavcodec/libgsmenc.c +@@ -62,8 +62,8 @@ static av_cold int libgsm_encode_init(AVCodecContext *avctx) { + if (avctx->bit_rate != 13000 /* Official */ && + avctx->bit_rate != 13200 /* Very common */ && + avctx->bit_rate != 0 /* Unknown; a.o. mov does not set bitrate when decoding */ ) { +- av_log(avctx, AV_LOG_ERROR, "Bitrate 13000bps required for GSM, got %"PRId64"bps\n", +- (int64_t)avctx->bit_rate); ++ av_log(avctx, AV_LOG_ERROR, "Bitrate 13000bps required for GSM, got %dbps\n", ++ avctx->bit_rate); + if (avctx->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) + return -1; + } +diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c +index c40fcde..cab7e3a 100644 +--- a/libavcodec/libopusenc.c ++++ b/libavcodec/libopusenc.c +@@ -351,12 +351,12 @@ static av_cold int libopus_encode_init(AVCodecContext *avctx) + avctx->bit_rate = 64000 * opus->stream_count + + 32000 * coupled_stream_count; + av_log(avctx, AV_LOG_WARNING, +- "No bit rate set. Defaulting to %"PRId64" bps.\n", (int64_t)avctx->bit_rate); ++ "No bit rate set. Defaulting to %d bps.\n", avctx->bit_rate); + } + + if (avctx->bit_rate < 500 || avctx->bit_rate > 256000 * avctx->channels) { +- av_log(avctx, AV_LOG_ERROR, "The bit rate %"PRId64" bps is unsupported. " +- "Please choose a value between 500 and %d.\n", (int64_t)avctx->bit_rate, ++ av_log(avctx, AV_LOG_ERROR, "The bit rate %d bps is unsupported. " ++ "Please choose a value between 500 and %d.\n", avctx->bit_rate, + 256000 * avctx->channels); + ret = AVERROR(EINVAL); + goto fail; +diff --git a/libavcodec/libspeexenc.c b/libavcodec/libspeexenc.c +index 4bdb961..b96b217 100644 +--- a/libavcodec/libspeexenc.c ++++ b/libavcodec/libspeexenc.c +@@ -125,10 +125,10 @@ static av_cold void print_enc_params(AVCodecContext *avctx, + av_log(avctx, AV_LOG_DEBUG, " quality: %f\n", s->vbr_quality); + } else if (s->abr) { + av_log(avctx, AV_LOG_DEBUG, "rate control: ABR\n"); +- av_log(avctx, AV_LOG_DEBUG, " bitrate: %"PRId64" bps\n", (int64_t)avctx->bit_rate); ++ av_log(avctx, AV_LOG_DEBUG, " bitrate: %d bps\n", avctx->bit_rate); + } else { + av_log(avctx, AV_LOG_DEBUG, "rate control: CBR\n"); +- av_log(avctx, AV_LOG_DEBUG, " bitrate: %"PRId64" bps\n", (int64_t)avctx->bit_rate); ++ av_log(avctx, AV_LOG_DEBUG, " bitrate: %d bps\n", avctx->bit_rate); + } + av_log(avctx, AV_LOG_DEBUG, "complexity: %d\n", + avctx->compression_level); +diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c +index db241c8..46ee6a4 100644 +--- a/libavcodec/mpegvideo_enc.c ++++ b/libavcodec/mpegvideo_enc.c +@@ -506,7 +506,7 @@ FF_ENABLE_DEPRECATION_WARNINGS + avctx->bit_rate * av_q2d(avctx->time_base) > + avctx->bit_rate_tolerance) { + av_log(avctx, AV_LOG_WARNING, +- "bitrate tolerance %d too small for bitrate %"PRId64", overriding\n", avctx->bit_rate_tolerance, (int64_t)avctx->bit_rate); ++ "bitrate tolerance %d too small for bitrate %d, overriding\n", avctx->bit_rate_tolerance, avctx->bit_rate); + avctx->bit_rate_tolerance = 5 * avctx->bit_rate * av_q2d(avctx->time_base); + } + +diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h +index 022cb1d..14dc914 100644 +--- a/libavcodec/options_table.h ++++ b/libavcodec/options_table.h +@@ -42,8 +42,8 @@ + #define AV_CODEC_DEFAULT_BITRATE 200*1000 + + static const AVOption avcodec_options[] = { +-{"b", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT64, {.i64 = AV_CODEC_DEFAULT_BITRATE }, 0, INT64_MAX, A|V|E}, +-{"ab", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT64, {.i64 = 128*1000 }, 0, INT_MAX, A|E}, ++{"b", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE }, 0, INT64_MAX, A|V|E}, ++{"ab", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT, {.i64 = 128*1000 }, 0, INT_MAX, A|E}, + {"bt", "Set video bitrate tolerance (in bits/s). In 1-pass mode, bitrate tolerance specifies how far " + "ratecontrol is willing to deviate from the target average bitrate value. This is not related " + "to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.", +@@ -194,9 +194,9 @@ static const AVOption avcodec_options[] = { + #if FF_API_MPV_OPT + {"rc_eq", "deprecated, use encoder private options instead", OFFSET(rc_eq), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, V|E}, + #endif +-{"maxrate", "maximum bitrate (in bits/s). Used for VBV together with bufsize.", OFFSET(rc_max_rate), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, 0, INT_MAX, V|A|E}, ++{"maxrate", "maximum bitrate (in bits/s). Used for VBV together with bufsize.", OFFSET(rc_max_rate), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|A|E}, + {"minrate", "minimum bitrate (in bits/s). Most useful in setting up a CBR encode. It is of little use otherwise.", +- OFFSET(rc_min_rate), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|A|E}, ++ OFFSET(rc_min_rate), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|A|E}, + {"bufsize", "set ratecontrol buffer size (in bits)", OFFSET(rc_buffer_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|V|E}, + #if FF_API_MPV_OPT + {"rc_buf_aggressivity", "deprecated, use encoder private options instead", OFFSET(rc_buffer_aggressivity), AV_OPT_TYPE_FLOAT, {.dbl = 1.0 }, -FLT_MAX, FLT_MAX, V|E}, +diff --git a/libavcodec/pcm-bluray.c b/libavcodec/pcm-bluray.c +index 22c1c08..e7f9ee4 100644 +--- a/libavcodec/pcm-bluray.c ++++ b/libavcodec/pcm-bluray.c +@@ -117,9 +117,9 @@ static int pcm_bluray_parse_header(AVCodecContext *avctx, + + if (avctx->debug & FF_DEBUG_PICT_INFO) + ff_dlog(avctx, +- "pcm_bluray_parse_header: %d channels, %d bits per sample, %d Hz, %"PRId64" bit/s\n", ++ "pcm_bluray_parse_header: %d channels, %d bits per sample, %d Hz, %d bit/s\n", + avctx->channels, avctx->bits_per_coded_sample, +- avctx->sample_rate, (int64_t)avctx->bit_rate); ++ avctx->sample_rate, avctx->bit_rate); + return 0; + } + +diff --git a/libavcodec/pcm-dvd.c b/libavcodec/pcm-dvd.c +index 04c321e..46f730d 100644 +--- a/libavcodec/pcm-dvd.c ++++ b/libavcodec/pcm-dvd.c +@@ -140,9 +140,9 @@ static int pcm_dvd_parse_header(AVCodecContext *avctx, const uint8_t *header) + + if (avctx->debug & FF_DEBUG_PICT_INFO) + ff_dlog(avctx, +- "pcm_dvd_parse_header: %d channels, %d bits per sample, %d Hz, %"PRId64" bit/s\n", ++ "pcm_dvd_parse_header: %d channels, %d bits per sample, %d Hz, %d bit/s\n", + avctx->channels, avctx->bits_per_coded_sample, +- avctx->sample_rate, (int64_t)avctx->bit_rate); ++ avctx->sample_rate, avctx->bit_rate); + + s->last_header = header_int; + +diff --git a/libavcodec/sipr.c b/libavcodec/sipr.c +index 70d460a..5e428ef 100644 +--- a/libavcodec/sipr.c ++++ b/libavcodec/sipr.c +@@ -493,8 +493,8 @@ static av_cold int sipr_decoder_init(AVCodecContext * avctx) + else if (avctx->bit_rate > 5750 ) ctx->mode = MODE_6k5; + else ctx->mode = MODE_5k0; + av_log(avctx, AV_LOG_WARNING, +- "Invalid block_align: %d. Mode %s guessed based on bitrate: %"PRId64"\n", +- avctx->block_align, modes[ctx->mode].mode_name, (int64_t)avctx->bit_rate); ++ "Invalid block_align: %d. Mode %s guessed based on bitrate: %d\n", ++ avctx->block_align, modes[ctx->mode].mode_name, avctx->bit_rate); + } + + av_log(avctx, AV_LOG_DEBUG, "Mode: %s\n", modes[ctx->mode].mode_name); +diff --git a/libavcodec/utils.c b/libavcodec/utils.c +index 1336e92..7877488 100644 +--- a/libavcodec/utils.c ++++ b/libavcodec/utils.c +@@ -967,7 +967,7 @@ FF_ENABLE_DEPRECATION_WARNINGS + } + if ( (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) + && avctx->bit_rate>0 && avctx->bit_rate<1000) { +- av_log(avctx, AV_LOG_WARNING, "Bitrate %"PRId64" is extremely low, maybe you mean %"PRId64"k\n", (int64_t)avctx->bit_rate, (int64_t)avctx->bit_rate); ++ av_log(avctx, AV_LOG_WARNING, "Bitrate %d is extremely low, maybe you mean %dk\n", avctx->bit_rate, avctx->bit_rate); + } + + if (!avctx->rc_initial_buffer_occupancy) +@@ -1522,7 +1522,7 @@ void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) + ", %"PRId64" kb/s", bitrate / 1000); + } else if (enc->rc_max_rate > 0) { + snprintf(buf + strlen(buf), buf_size - strlen(buf), +- ", max. %"PRId64" kb/s", (int64_t)enc->rc_max_rate / 1000); ++ ", max. %d kb/s", enc->rc_max_rate / 1000); + } + } + +diff --git a/libavcodec/wma.c b/libavcodec/wma.c +index f70937f..717f950 100644 +--- a/libavcodec/wma.c ++++ b/libavcodec/wma.c +@@ -185,8 +185,8 @@ av_cold int ff_wma_init(AVCodecContext *avctx, int flags2) + high_freq = high_freq * 0.5; + } + ff_dlog(s->avctx, "flags2=0x%x\n", flags2); +- ff_dlog(s->avctx, "version=%d channels=%d sample_rate=%d bitrate=%"PRId64" block_align=%d\n", +- s->version, avctx->channels, avctx->sample_rate, (int64_t)avctx->bit_rate, ++ ff_dlog(s->avctx, "version=%d channels=%d sample_rate=%d bitrate=%d block_align=%d\n", ++ s->version, avctx->channels, avctx->sample_rate, avctx->bit_rate, + avctx->block_align); + ff_dlog(s->avctx, "bps=%f bps1=%f high_freq=%f bitoffset=%d\n", + bps, bps1, high_freq, s->byte_offset_bits); +diff --git a/libavcodec/wmaenc.c b/libavcodec/wmaenc.c +index c68babd..765f570 100644 +--- a/libavcodec/wmaenc.c ++++ b/libavcodec/wmaenc.c +@@ -52,8 +52,8 @@ static av_cold int encode_init(AVCodecContext *avctx) + + if (avctx->bit_rate < 24 * 1000) { + av_log(avctx, AV_LOG_ERROR, +- "bitrate too low: got %"PRId64", need 24000 or higher\n", +- (int64_t)avctx->bit_rate); ++ "bitrate too low: got %i, need 24000 or higher\n", ++ avctx->bit_rate); + return AVERROR(EINVAL); + } + +diff --git a/libavdevice/fbdev_dec.c b/libavdevice/fbdev_dec.c +index 3b31373..33a2054 100644 +--- a/libavdevice/fbdev_dec.c ++++ b/libavdevice/fbdev_dec.c +@@ -136,11 +136,11 @@ static av_cold int fbdev_read_header(AVFormatContext *avctx) + fbdev->width * fbdev->height * fbdev->bytes_per_pixel * av_q2d(fbdev->framerate_q) * 8; + + av_log(avctx, AV_LOG_INFO, +- "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%"PRId64"\n", ++ "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%d\n", + fbdev->width, fbdev->height, fbdev->varinfo.bits_per_pixel, + av_get_pix_fmt_name(pix_fmt), + fbdev->framerate_q.num, fbdev->framerate_q.den, +- (int64_t)st->codecpar->bit_rate); ++ st->codecpar->bit_rate); + return 0; + + fail: +diff --git a/libavformat/movenc.c b/libavformat/movenc.c +index d20d272..9adf08f 100644 +--- a/libavformat/movenc.c ++++ b/libavformat/movenc.c +@@ -3897,7 +3897,7 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat + manifest_bit_rate = props->max_bitrate; + } + +- avio_printf(pb, "<%s systemBitrate=\"%"PRId64"\">\n", type, ++ avio_printf(pb, "<%s systemBitrate=\"%d\">\n", type, + manifest_bit_rate); + param_write_int(pb, "systemBitrate", manifest_bit_rate); + param_write_int(pb, "trackID", track_id); +diff --git a/libavformat/rdt.c b/libavformat/rdt.c +index 8670ead..57ee07e 100644 +--- a/libavformat/rdt.c ++++ b/libavformat/rdt.c +@@ -448,7 +448,7 @@ real_parse_asm_rule(AVStream *st, const char *p, const char *end) + { + do { + /* can be either averagebandwidth= or AverageBandwidth= */ +- if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%"SCNd64, &st->codecpar->bit_rate) == 1) ++ if (sscanf(p, " %*1[Aa]verage%*1[Bb]andwidth=%d", &st->codecpar->bit_rate) == 1) + break; + if (!(p = strchr(p, ',')) || p > end) + p = end; +diff --git a/libavformat/sdp.c b/libavformat/sdp.c +index 4e37f65..df2cb43 100644 +--- a/libavformat/sdp.c ++++ b/libavformat/sdp.c +@@ -748,7 +748,7 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx, + av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type); + sdp_write_address(buff, size, dest_addr, dest_type, ttl); + if (p->bit_rate) { +- av_strlcatf(buff, size, "b=AS:%"PRId64"\r\n", (int64_t)p->bit_rate / 1000); ++ av_strlcatf(buff, size, "b=AS:%d\r\n", p->bit_rate / 1000); + } + + sdp_write_media_attributes(buff, size, st, payload_type, fmt); +diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c +index dabd1ea..6d126e9 100644 +--- a/libavformat/smoothstreamingenc.c ++++ b/libavformat/smoothstreamingenc.c +@@ -263,7 +263,7 @@ static int write_manifest(AVFormatContext *s, int final) + if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + continue; + last = i; +- avio_printf(out, "\n", index, (int64_t)s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str); ++ avio_printf(out, "\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str); + index++; + } + output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size); +@@ -277,7 +277,7 @@ static int write_manifest(AVFormatContext *s, int final) + if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) + continue; + last = i; +- avio_printf(out, "\n", index, (int64_t)s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, s->streams[i]->codecpar->channels, os->packet_size, os->audio_tag, os->private_str); ++ avio_printf(out, "\n", index, s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, s->streams[i]->codecpar->channels, os->packet_size, os->audio_tag, os->private_str); + index++; + } + output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size); +@@ -324,7 +324,7 @@ static int ism_write_header(AVFormatContext *s) + ret = AVERROR(EINVAL); + goto fail; + } +- snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%"PRId64")", s->filename, (int64_t)s->streams[i]->codecpar->bit_rate); ++ snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%d)", s->filename, s->streams[i]->codecpar->bit_rate); + if (mkdir(os->dirname, 0777) == -1 && errno != EEXIST) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, "mkdir failed\n"); +diff --git a/libavformat/vqf.c b/libavformat/vqf.c +index 841840e..b5cdb78 100644 +--- a/libavformat/vqf.c ++++ b/libavformat/vqf.c +@@ -211,8 +211,8 @@ static int vqf_read_header(AVFormatContext *s) + size = 2048; + break; + default: +- av_log(s, AV_LOG_ERROR, "Mode not supported: %d Hz, %"PRId64" kb/s.\n", +- st->codecpar->sample_rate, (int64_t)st->codecpar->bit_rate); ++ av_log(s, AV_LOG_ERROR, "Mode not supported: %d Hz, %d kb/s.\n", ++ st->codecpar->sample_rate, st->codecpar->bit_rate); + return -1; + } + c->frame_bit_len = st->codecpar->bit_rate*size/st->codecpar->sample_rate; diff --git a/make/archives.mk b/make/archives.mk index ad77871..b161f0d 100755 --- a/make/archives.mk +++ b/make/archives.mk @@ -33,8 +33,15 @@ $(ARCHIVE)/hotplug.tar.gz: $(ARCHIVE)/fbshot-0.3.tar.gz: $(WGET) http://www.fhloston-paradise.de/fbshot-0.3.tar.gz -$(ARCHIVE)/ffmpeg-$(FFMPEG_VER).tar.xz: - $(WGET) http://www.ffmpeg.org/releases/ffmpeg-$(FFMPEG_VER).tar.xz +$(ARCHIVE)/ffmpeg-git-$(FFMPEG_GIT).tar.gz: + set -e; cd $(BUILD_TMP); \ + rm -rf ffmpeg-git-$(FFMPEG_GIT); \ + git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg-git-$(FFMPEG_GIT) + set -e; cd $(BUILD_TMP)/ffmpeg-git-$(FFMPEG_GIT); \ + git reset --hard $(FFMPEG_GIT) + set -e; cd $(BUILD_TMP); \ + tar cvpzf $@ ffmpeg-git-$(FFMPEG_GIT) + $(REMOVE)/ffmpeg-git-$(FFMPEG_GIT) $(ARCHIVE)/freetype-$(FREETYPE_VER).tar.bz2: $(WGET) http://downloads.sourceforge.net/project/freetype/freetype2/$(FREETYPE_VER)/freetype-$(FREETYPE_VER).tar.bz2 diff --git a/make/libraries.mk b/make/libraries.mk index e9d0607..6ac4f99 100755 --- a/make/libraries.mk +++ b/make/libraries.mk @@ -511,7 +511,7 @@ FFMPEG_CONFIGURE = \ --enable-cross-compile \ --enable-shared \ --enable-small \ - --enable-bzlib \ + --disable-bzlib \ --enable-zlib \ --disable-debug \ --enable-stripping \ @@ -519,27 +519,28 @@ FFMPEG_CONFIGURE = \ --enable-decoder=vc1 \ --target-os=linux \ --disable-neon \ + --disable-runtime-cpudetect \ --arch=arm endif # ifeq ($(BOXARCH), arm) ifeq ($(PLATFORM), $(filter $(PLATFORM), apollo kronos)) -FFMPEG_CONFIGURE += --cpu=cortex-a9 --extra-cflags="-mfpu=vfpv3-d16 -mfloat-abi=hard -I$(TARGETPREFIX)/include" +FFMPEG_CONFIGURE += --cpu=cortex-a9 --enable-vfp --extra-cflags="-mfpu=vfpv3-d16 -mfloat-abi=hard -I$(TARGETPREFIX)/include" else FFMPEG_CONFIGURE += --disable-iconv -FFMPEG_CONFIGURE += --cpu=armv6 --extra-cflags="-I$(TARGETPREFIX)/include" +FFMPEG_CONFIGURE += --cpu=armv6 --disable-vfp --extra-cflags="-I$(TARGETPREFIX)/include" endif -$(D)/ffmpeg: $(D)/librtmp $(D)/libroxml $(ARCHIVE)/ffmpeg-$(FFMPEG_VER).tar.xz | $(TARGETPREFIX) - $(REMOVE)/ffmpeg-$(FFMPEG_VER) - $(UNTAR)/ffmpeg-$(FFMPEG_VER).tar.xz - set -e; pushd $(BUILD_TMP)/ffmpeg-$(FFMPEG_VER) && \ - $(PATCH)/ffmpeg-hds-libroxml-2.8.x.patch; \ - $(PATCH)/ffmpeg-aac-2.8.x.patch; \ - $(PATCH)/ffmpeg-fixed-memleak-in-mpegts-demuxer-on-some-malformed-mpegts-files.patch; \ +$(D)/ffmpeg: $(D)/librtmp $(D)/libroxml $(ARCHIVE)/ffmpeg-git-$(FFMPEG_GIT).tar.gz | $(TARGETPREFIX) + $(REMOVE)/ffmpeg-git-$(FFMPEG_GIT) + $(UNTAR)/ffmpeg-git-$(FFMPEG_GIT).tar.gz + set -e; pushd $(BUILD_TMP)/ffmpeg-git-$(FFMPEG_GIT) && \ + $(PATCH)/ffmpeg/0001-ffmpeg-hds-libroxml-3.x.patch; \ + $(PATCH)/ffmpeg/0002-ffmpeg-aac-3.x.patch; \ + $(PATCH)/ffmpeg/0003-ffmpeg-Revert-lavc-Switch-bitrate-to-64bit.patch; \ $(BUILDENV) \ ./configure \ $(FFMPEG_CONFIGURE) \ --extra-ldflags="-L$(TARGETPREFIX)/lib -lz" \ - --logfile=$(BUILD_TMP)/Config-$(FFMPEG_VER).log \ + --logfile=$(BUILD_TMP)/Config-$(FFMPEG_GIT).log \ --cross-prefix=$(TARGET)- \ --prefix=$(TARGETPREFIX) \ --mandir=/.remove; \ @@ -551,7 +552,7 @@ $(D)/ffmpeg: $(D)/librtmp $(D)/libroxml $(ARCHIVE)/ffmpeg-$(FFMPEG_VER).tar.xz | $(REWRITE_PKGCONF) $(PKG_CONFIG_PATH)/libavcodec.pc $(REWRITE_PKGCONF) $(PKG_CONFIG_PATH)/libavutil.pc $(REWRITE_PKGCONF) $(PKG_CONFIG_PATH)/libswresample.pc - $(REMOVE)/ffmpeg $(TARGETPREFIX)/.remove + $(REMOVE)/ffmpeg-git-$(FFMPEG_GIT) $(TARGETPREFIX)/.remove touch $@ $(D)/libroxml: $(ARCHIVE)/libroxml-$(LIBROXML_VER).tar.gz | $(TARGETPREFIX) diff --git a/make/versions.mk b/make/versions.mk index d462376..50f227b 100644 --- a/make/versions.mk +++ b/make/versions.mk @@ -42,7 +42,7 @@ E2FSPROGS_VER=1.42.13 EXPAT_VER = 2.1.0 # A complete, cross-platform solution to record, convert and stream audio and video -FFMPEG_VER=2.8.11 +FFMPEG_GIT = dec2fa8 # FLEX (the fast lexical analyser) FLEX_VER=2.5.35 -- 2.39.5