diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-24 12:15:48 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:30:04 +0000 |
commit | b014812705fc80bff0a5c120dfcef88f349816dc (patch) | |
tree | 25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/third_party/ffmpeg/libavformat | |
parent | 9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff) | |
download | qtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz |
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/ffmpeg/libavformat')
46 files changed, 3005 insertions, 869 deletions
diff --git a/chromium/third_party/ffmpeg/libavformat/Makefile b/chromium/third_party/ffmpeg/libavformat/Makefile index e64c5b30a1e..3eeca5091df 100644 --- a/chromium/third_party/ffmpeg/libavformat/Makefile +++ b/chromium/third_party/ffmpeg/libavformat/Makefile @@ -133,9 +133,9 @@ OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o OBJS-$(CONFIG_CINE_DEMUXER) += cinedec.o -OBJS-$(CONFIG_CODEC2_DEMUXER) += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o -OBJS-$(CONFIG_CODEC2_MUXER) += ../libavcodec/codec2utils.o codec2.o rawenc.o -OBJS-$(CONFIG_CODEC2RAW_DEMUXER) += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o +OBJS-$(CONFIG_CODEC2_DEMUXER) += codec2.o rawdec.o pcm.o +OBJS-$(CONFIG_CODEC2_MUXER) += codec2.o rawenc.o +OBJS-$(CONFIG_CODEC2RAW_DEMUXER) += codec2.o rawdec.o pcm.o OBJS-$(CONFIG_CODEC2RAW_MUXER) += rawenc.o OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o @@ -462,6 +462,7 @@ OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o OBJS-$(CONFIG_SDX_DEMUXER) += sdxdec.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o +OBJS-$(CONFIG_SEGAFILM_MUXER) += segafilmenc.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += shortendec.o rawdec.o OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o @@ -566,6 +567,7 @@ OBJS-$(CONFIG_LIBRTMPE_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o +OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o diff --git a/chromium/third_party/ffmpeg/libavformat/aacdec.c b/chromium/third_party/ffmpeg/libavformat/aacdec.c index 5ec706bdc7a..685458b9114 100644 --- a/chromium/third_party/ffmpeg/libavformat/aacdec.c +++ b/chromium/third_party/ffmpeg/libavformat/aacdec.c @@ -154,11 +154,15 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret, fsize; - ret = av_get_packet(s->pb, pkt, FFMAX(ID3v2_HEADER_SIZE, ADTS_HEADER_SIZE)); - - if (ret >= ID3v2_HEADER_SIZE && ff_id3v2_match(pkt->data, ID3v2_DEFAULT_MAGIC)) { - if ((ret = handle_id3(s, pkt)) >= 0) - ret = av_get_packet(s->pb, pkt, ADTS_HEADER_SIZE); + // Parse all the ID3 headers between frames + while (1) { + ret = av_get_packet(s->pb, pkt, FFMAX(ID3v2_HEADER_SIZE, ADTS_HEADER_SIZE)); + if (ret >= ID3v2_HEADER_SIZE && ff_id3v2_match(pkt->data, ID3v2_DEFAULT_MAGIC)) { + if ((ret = handle_id3(s, pkt)) >= 0) { + continue; + } + } + break; } if (ret < 0) diff --git a/chromium/third_party/ffmpeg/libavformat/allformats.c b/chromium/third_party/ffmpeg/libavformat/allformats.c index 9dc5ce8a767..d582778b3b3 100644 --- a/chromium/third_party/ffmpeg/libavformat/allformats.c +++ b/chromium/third_party/ffmpeg/libavformat/allformats.c @@ -20,14 +20,12 @@ */ #include "libavutil/thread.h" +#include "libavformat/internal.h" #include "avformat.h" #include "rtp.h" #include "rdt.h" #include "url.h" #include "version.h" -#if FF_API_NEXT -#include "internal.h" -#endif /* (de)muxers */ extern AVOutputFormat ff_a64_muxer; @@ -364,6 +362,7 @@ extern AVInputFormat ff_sdr2_demuxer; extern AVInputFormat ff_sds_demuxer; extern AVInputFormat ff_sdx_demuxer; extern AVInputFormat ff_segafilm_demuxer; +extern AVOutputFormat ff_segafilm_muxer; extern AVOutputFormat ff_segment_muxer; extern AVOutputFormat ff_stream_segment_muxer; extern AVInputFormat ff_shorten_demuxer; @@ -487,76 +486,84 @@ extern AVInputFormat ff_libopenmpt_demuxer; #include "libavformat/muxer_list.c" #include "libavformat/demuxer_list.c" +static const AVInputFormat * const *indev_list = NULL; +static const AVOutputFormat * const *outdev_list = NULL; + const AVOutputFormat *av_muxer_iterate(void **opaque) { + static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1; uintptr_t i = (uintptr_t)*opaque; - const AVOutputFormat *f = muxer_list[i]; + const AVOutputFormat *f = NULL; + + if (i < size) { + f = muxer_list[i]; + } else if (indev_list) { + f = outdev_list[i - size]; + } if (f) *opaque = (void*)(i + 1); return f; } -const AVInputFormat *av_demuxer_iterate(void **opaque){ +const AVInputFormat *av_demuxer_iterate(void **opaque) +{ + static const uintptr_t size = sizeof(demuxer_list)/sizeof(demuxer_list[0]) - 1; uintptr_t i = (uintptr_t)*opaque; - const AVInputFormat *f = demuxer_list[i]; + const AVInputFormat *f = NULL; + + if (i < size) { + f = demuxer_list[i]; + } else if (outdev_list) { + f = indev_list[i - size]; + } if (f) *opaque = (void*)(i + 1); return f; } +static AVMutex avpriv_register_devices_mutex = AV_MUTEX_INITIALIZER; + #if FF_API_NEXT FF_DISABLE_DEPRECATION_WARNINGS static AVOnce av_format_next_init = AV_ONCE_INIT; -static const AVInputFormat * const *indev_list = NULL; -static const AVOutputFormat * const *outdev_list = NULL; - static void av_format_init_next(void) { AVOutputFormat *prevout = NULL, *out; AVInputFormat *previn = NULL, *in; - void *i = 0; - while ((out = (AVOutputFormat*)av_muxer_iterate(&i))) { + ff_mutex_lock(&avpriv_register_devices_mutex); + + for (int i = 0; (out = (AVOutputFormat*)muxer_list[i]); i++) { if (prevout) prevout->next = out; prevout = out; } if (outdev_list) { - for (int j = 0; (out = (AVOutputFormat*)outdev_list[j]); j++) { + for (int i = 0; (out = (AVOutputFormat*)outdev_list[i]); i++) { if (prevout) prevout->next = out; prevout = out; } } - i = 0; - while ((in = (AVInputFormat*)av_demuxer_iterate(&i))) { + for (int i = 0; (in = (AVInputFormat*)demuxer_list[i]); i++) { if (previn) previn->next = in; previn = in; } if (indev_list) { - for (int j = 0; (in = (AVInputFormat*)indev_list[j]); j++) { + for (int i = 0; (in = (AVInputFormat*)indev_list[i]); i++) { if (previn) previn->next = in; previn = in; } } -} - -void avpriv_register_devices(const AVOutputFormat * const o[], const AVInputFormat * const i[]) -{ - static AVMutex avpriv_register_devices_mutex = AV_MUTEX_INITIALIZER; - ff_mutex_lock(&avpriv_register_devices_mutex); - outdev_list = o; - indev_list = i; - av_format_init_next(); ff_mutex_unlock(&avpriv_register_devices_mutex); } @@ -566,10 +573,10 @@ AVInputFormat *av_iformat_next(const AVInputFormat *f) if (f) return f->next; - else - /* If there are no demuxers but input devices, then return the first input device. - * This will still return null if both there are both no demuxers or input devices. */ - return demuxer_list[0] ? (AVInputFormat*)demuxer_list[0] : (indev_list ? (AVInputFormat*)indev_list[0] : NULL); + else { + void *opaque = NULL; + return (AVInputFormat *)av_demuxer_iterate(&opaque); + } } AVOutputFormat *av_oformat_next(const AVOutputFormat *f) @@ -578,8 +585,10 @@ AVOutputFormat *av_oformat_next(const AVOutputFormat *f) if (f) return f->next; - else - return muxer_list[0] ? (AVOutputFormat*)muxer_list[0] : (outdev_list ? (AVOutputFormat*)outdev_list[0] : NULL); + else { + void *opaque = NULL; + return (AVOutputFormat *)av_muxer_iterate(&opaque); + } } void av_register_all(void) @@ -598,3 +607,14 @@ void av_register_output_format(AVOutputFormat *format) } FF_ENABLE_DEPRECATION_WARNINGS #endif + +void avpriv_register_devices(const AVOutputFormat * const o[], const AVInputFormat * const i[]) +{ + ff_mutex_lock(&avpriv_register_devices_mutex); + outdev_list = o; + indev_list = i; + ff_mutex_unlock(&avpriv_register_devices_mutex); +#if FF_API_NEXT + av_format_init_next(); +#endif +} diff --git a/chromium/third_party/ffmpeg/libavformat/amr.c b/chromium/third_party/ffmpeg/libavformat/amr.c index 8b4d736d2f9..f954803d461 100644 --- a/chromium/third_party/ffmpeg/libavformat/amr.c +++ b/chromium/third_party/ffmpeg/libavformat/amr.c @@ -178,7 +178,7 @@ AVInputFormat ff_amr_demuxer = { #if CONFIG_AMRNB_DEMUXER static int amrnb_probe(AVProbeData *p) { - int mode, i = 0, valid = 0; + int mode, i = 0, valid = 0, invalid = 0; const uint8_t *b = p->buf; while (i < p->buf_size) { @@ -197,10 +197,11 @@ static int amrnb_probe(AVProbeData *p) } } else { valid = 0; + invalid++; i++; } } - if (valid > 100) + if (valid > 100 && valid > invalid) return AVPROBE_SCORE_EXTENSION / 2 + 1; return 0; } @@ -234,7 +235,7 @@ AVInputFormat ff_amrnb_demuxer = { #if CONFIG_AMRWB_DEMUXER static int amrwb_probe(AVProbeData *p) { - int mode, i = 0, valid = 0; + int mode, i = 0, valid = 0, invalid = 0; const uint8_t *b = p->buf; while (i < p->buf_size) { @@ -253,10 +254,11 @@ static int amrwb_probe(AVProbeData *p) } } else { valid = 0; + invalid++; i++; } } - if (valid > 100) + if (valid > 100 && valid > invalid) return AVPROBE_SCORE_EXTENSION / 2 - 1; return 0; } diff --git a/chromium/third_party/ffmpeg/libavformat/aviobuf.c b/chromium/third_party/ffmpeg/libavformat/aviobuf.c index 95b33644784..e752d0e1a65 100644 --- a/chromium/third_party/ffmpeg/libavformat/aviobuf.c +++ b/chromium/third_party/ffmpeg/libavformat/aviobuf.c @@ -823,6 +823,14 @@ int ff_get_line(AVIOContext *s, char *buf, int maxlen) return i; } +int ff_get_chomp_line(AVIOContext *s, char *buf, int maxlen) +{ + int len = ff_get_line(s, buf, maxlen); + while (len > 0 && av_isspace(buf[len - 1])) + buf[--len] = '\0'; + return len; +} + int64_t ff_read_line_to_bprint(AVIOContext *s, AVBPrint *bp) { int len, end; diff --git a/chromium/third_party/ffmpeg/libavformat/concatdec.c b/chromium/third_party/ffmpeg/libavformat/concatdec.c index 8fff9cc2cbc..bbe13136fa2 100644 --- a/chromium/third_party/ffmpeg/libavformat/concatdec.c +++ b/chromium/third_party/ffmpeg/libavformat/concatdec.c @@ -603,7 +603,6 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) av_packet_unref(pkt); continue; } - pkt->stream_index = cs->out_stream_index; break; } if ((ret = filter_packet(avf, cs, pkt))) @@ -646,6 +645,7 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) } } + pkt->stream_index = cs->out_stream_index; return ret; } diff --git a/chromium/third_party/ffmpeg/libavformat/dashdec.c b/chromium/third_party/ffmpeg/libavformat/dashdec.c index 2b396a01b71..a2e2e13382b 100644 --- a/chromium/third_party/ffmpeg/libavformat/dashdec.c +++ b/chromium/third_party/ffmpeg/libavformat/dashdec.c @@ -149,6 +149,11 @@ typedef struct DASHContext { char *allowed_extensions; AVDictionary *avio_opts; int max_url_size; + + /* Flags for init section*/ + int is_init_section_common_video; + int is_init_section_common_audio; + } DASHContext; static int ishttp(char *url) @@ -416,9 +421,9 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, if (av_strstart(proto_name, "file", NULL)) { if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) { av_log(s, AV_LOG_ERROR, - "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n" - "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n", - url); + "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n" + "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n", + url); return AVERROR_INVALIDDATA; } } else if (av_strstart(proto_name, "http", NULL)) { @@ -805,7 +810,8 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, xmlNodePtr fragment_template_node, xmlNodePtr content_component_node, xmlNodePtr adaptionset_baseurl_node, - xmlNodePtr adaptionset_segmentlist_node) + xmlNodePtr adaptionset_segmentlist_node, + xmlNodePtr adaptionset_supplementalproperty_node) { int32_t ret = 0; int32_t audio_rep_idx = 0; @@ -825,6 +831,7 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, char *timescale_val = NULL; char *initialization_val = NULL; char *media_val = NULL; + char *val = NULL; xmlNodePtr baseurl_nodes[4]; xmlNodePtr representation_node = node; char *rep_id_val = xmlGetProp(representation_node, "id"); @@ -920,6 +927,17 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10); xmlFree(startnumber_val); } + if (adaptionset_supplementalproperty_node) { + if (!av_strcasecmp(xmlGetProp(adaptionset_supplementalproperty_node,"schemeIdUri"), "http://dashif.org/guidelines/last-segment-number")) { + val = xmlGetProp(adaptionset_supplementalproperty_node,"value"); + if (!val) { + av_log(s, AV_LOG_ERROR, "Missing value attribute in adaptionset_supplementalproperty_node\n"); + } else { + rep->last_seq_no =(int64_t) strtoll(val, NULL, 10) - 1; + xmlFree(val); + } + } + } fragment_timeline_node = find_child_node_by_name(representation_segmenttemplate_node, "SegmentTimeline"); @@ -1054,6 +1072,7 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, xmlNodePtr content_component_node = NULL; xmlNodePtr adaptionset_baseurl_node = NULL; xmlNodePtr adaptionset_segmentlist_node = NULL; + xmlNodePtr adaptionset_supplementalproperty_node = NULL; xmlNodePtr node = NULL; node = xmlFirstElementChild(adaptionset_node); @@ -1066,6 +1085,8 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, adaptionset_baseurl_node = node; } else if (!av_strcasecmp(node->name, (const char *)"SegmentList")) { adaptionset_segmentlist_node = node; + } else if (!av_strcasecmp(node->name, (const char *)"SupplementalProperty")) { + adaptionset_supplementalproperty_node = node; } else if (!av_strcasecmp(node->name, (const char *)"Representation")) { ret = parse_manifest_representation(s, url, node, adaptionset_node, @@ -1076,7 +1097,8 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, fragment_template_node, content_component_node, adaptionset_baseurl_node, - adaptionset_segmentlist_node); + adaptionset_segmentlist_node, + adaptionset_supplementalproperty_node); if (ret < 0) { return ret; } @@ -1106,8 +1128,8 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) xmlNodePtr adaptionset_node = NULL; xmlAttrPtr attr = NULL; char *val = NULL; - uint32_t perdiod_duration_sec = 0; - uint32_t perdiod_start_sec = 0; + uint32_t period_duration_sec = 0; + uint32_t period_start_sec = 0; if (!in) { close_in = 1; @@ -1143,7 +1165,7 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) } else { LIBXML_TEST_VERSION - doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0); + doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0); root_element = xmlDocGetRootElement(doc); node = root_element; @@ -1202,23 +1224,23 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) node = xmlFirstElementChild(node); while (node) { if (!av_strcasecmp(node->name, (const char *)"Period")) { - perdiod_duration_sec = 0; - perdiod_start_sec = 0; + period_duration_sec = 0; + period_start_sec = 0; attr = node->properties; while (attr) { val = xmlGetProp(node, attr->name); if (!av_strcasecmp(attr->name, (const char *)"duration")) { - perdiod_duration_sec = get_duration_insec(s, (const char *)val); + period_duration_sec = get_duration_insec(s, (const char *)val); } else if (!av_strcasecmp(attr->name, (const char *)"start")) { - perdiod_start_sec = get_duration_insec(s, (const char *)val); + period_start_sec = get_duration_insec(s, (const char *)val); } attr = attr->next; xmlFree(val); } - if ((perdiod_duration_sec) >= (c->period_duration)) { + if ((period_duration_sec) >= (c->period_duration)) { period_node = node; - c->period_duration = perdiod_duration_sec; - c->period_start = perdiod_start_sec; + c->period_duration = period_duration_sec; + c->period_start = period_start_sec; if (c->period_start > 0) c->media_presentation_duration = c->period_duration; } @@ -1379,14 +1401,14 @@ static int refresh_manifest(AVFormatContext *s) if (c->n_videos != n_videos) { av_log(c, AV_LOG_ERROR, - "new manifest has mismatched no. of video representations, %d -> %d\n", - n_videos, c->n_videos); + "new manifest has mismatched no. of video representations, %d -> %d\n", + n_videos, c->n_videos); return AVERROR_INVALIDDATA; } if (c->n_audios != n_audios) { av_log(c, AV_LOG_ERROR, - "new manifest has mismatched no. of audio representations, %d -> %d\n", - n_audios, c->n_audios); + "new manifest has mismatched no. of audio representations, %d -> %d\n", + n_audios, c->n_audios); return AVERROR_INVALIDDATA; } @@ -1819,7 +1841,10 @@ static int open_demux_for_component(AVFormatContext *s, struct representation *p pls->parent = s; pls->cur_seq_no = calc_cur_seg_no(s, pls); - pls->last_seq_no = calc_max_seg_no(pls, s->priv_data); + + if (!pls->last_seq_no) { + pls->last_seq_no = calc_max_seg_no(pls, s->priv_data); + } ret = reopen_demux_for_component(s, pls); if (ret < 0) { @@ -1842,6 +1867,45 @@ fail: return ret; } +static int init_section_compare_video(DASHContext *c) +{ + int i = 0; + char *url = c->videos[0]->init_section->url; + int64_t url_offset = c->videos[0]->init_section->url_offset; + int64_t size = c->videos[0]->init_section->size; + for (i=0;i<c->n_videos;i++) { + if (av_strcasecmp(c->videos[i]->init_section->url,url) || c->videos[i]->init_section->url_offset != url_offset || c->videos[i]->init_section->size != size) { + return 0; + } + } + return 1; +} + +static int init_section_compare_audio(DASHContext *c) +{ + int i = 0; + char *url = c->audios[0]->init_section->url; + int64_t url_offset = c->audios[0]->init_section->url_offset; + int64_t size = c->audios[0]->init_section->size; + for (i=0;i<c->n_audios;i++) { + if (av_strcasecmp(c->audios[i]->init_section->url,url) || c->audios[i]->init_section->url_offset != url_offset || c->audios[i]->init_section->size != size) { + return 0; + } + } + return 1; +} + +static void copy_init_section(struct representation *rep_dest, struct representation *rep_src) +{ + *rep_dest->init_section = *rep_src->init_section; + rep_dest->init_sec_buf = av_mallocz(rep_src->init_sec_buf_size); + memcpy(rep_dest->init_sec_buf, rep_src->init_sec_buf, rep_src->init_sec_data_len); + rep_dest->init_sec_buf_size = rep_src->init_sec_buf_size; + rep_dest->init_sec_data_len = rep_src->init_sec_data_len; + rep_dest->cur_timestamp = rep_src->cur_timestamp; +} + + static int dash_read_header(AVFormatContext *s) { void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb; @@ -1870,19 +1934,35 @@ static int dash_read_header(AVFormatContext *s) s->duration = (int64_t) c->media_presentation_duration * AV_TIME_BASE; } + if (c->n_videos) { + c->is_init_section_common_video = init_section_compare_video(c); + } + /* Open the demuxer for video and audio components if available */ for (i = 0; i < c->n_videos; i++) { struct representation *cur_video = c->videos[i]; + if (i > 0 && c->is_init_section_common_video) { + copy_init_section(cur_video,c->videos[0]); + } ret = open_demux_for_component(s, cur_video); + if (ret) goto fail; cur_video->stream_index = stream_index; ++stream_index; } + if (c->n_audios) { + c->is_init_section_common_audio = init_section_compare_audio(c); + } + for (i = 0; i < c->n_audios; i++) { struct representation *cur_audio = c->audios[i]; + if (i > 0 && c->is_init_section_common_audio) { + copy_init_section(cur_audio,c->audios[0]); + } ret = open_demux_for_component(s, cur_audio); + if (ret) goto fail; cur_audio->stream_index = stream_index; @@ -1911,7 +1991,7 @@ static int dash_read_header(AVFormatContext *s) av_dict_set_int(&pls->assoc_stream->metadata, "variant_bitrate", pls->bandwidth, 0); if (pls->id[0]) av_dict_set(&pls->assoc_stream->metadata, "id", pls->id, 0); - } + } for (i = 0; i < c->n_audios; i++) { struct representation *pls = c->audios[i]; @@ -2028,7 +2108,7 @@ static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t see int64_t duration = 0; av_log(pls->parent, AV_LOG_VERBOSE, "DASH seek pos[%"PRId64"ms], playlist %d%s\n", - seek_pos_msec, pls->rep_idx, dry_run ? " (dry)" : ""); + seek_pos_msec, pls->rep_idx, dry_run ? " (dry)" : ""); // single fragment mode if (pls->n_fragments == 1) { diff --git a/chromium/third_party/ffmpeg/libavformat/dashenc.c b/chromium/third_party/ffmpeg/libavformat/dashenc.c index 79d63e52d43..fefe3ce0ca4 100644 --- a/chromium/third_party/ffmpeg/libavformat/dashenc.c +++ b/chromium/third_party/ffmpeg/libavformat/dashenc.c @@ -76,15 +76,17 @@ typedef struct OutputStream { int nb_segments, segments_size, segment_index; Segment **segments; int64_t first_pts, start_pts, max_pts; - int64_t last_dts; + int64_t last_dts, last_pts; int bit_rate; - char bandwidth_str[64]; char codec_str[100]; int written_len; char filename[1024]; char full_path[1024]; char temp_path[1024]; + double availability_time_offset; + int total_pkt_size; + int muxer_overhead; } OutputStream; typedef struct DASHContext { @@ -94,7 +96,10 @@ typedef struct DASHContext { int nb_as; int window_size; int extra_window_size; +#if FF_API_DASH_MIN_SEG_DURATION int min_seg_duration; +#endif + int64_t seg_duration; int remove_at_exit; int use_template; int use_timeline; @@ -117,6 +122,8 @@ typedef struct DASHContext { AVIOContext *mpd_out; AVIOContext *m3u8_out; int streaming; + int64_t timeout; + int index_correction; } DASHContext; static struct codec_string { @@ -269,6 +276,8 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); if (c->http_persistent) av_dict_set_int(options, "multiple_requests", 1, 0); + if (c->timeout >= 0) + av_dict_set_int(options, "timeout", c->timeout, 0); } static void get_hls_playlist_name(char *playlist_name, int string_size, @@ -340,8 +349,12 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont if (c->use_template) { int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; avio_printf(out, "\t\t\t\t<SegmentTemplate timescale=\"%d\" ", timescale); - if (!c->use_timeline) - avio_printf(out, "duration=\"%"PRId64"\" ", c->last_duration); + if (!c->use_timeline) { + avio_printf(out, "duration=\"%"PRId64"\" ", c->seg_duration); + if (c->streaming && os->availability_time_offset) + avio_printf(out, "availabilityTimeOffset=\"%.3f\" ", + os->availability_time_offset); + } avio_printf(out, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\">\n", c->init_seg_name, c->media_seg_name, c->use_timeline ? start_number : 1); if (c->use_timeline) { int64_t cur_time = 0; @@ -536,20 +549,25 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind for (i = 0; i < s->nb_streams; i++) { OutputStream *os = &c->streams[i]; + char bandwidth_str[64] = {'\0'}; if (os->as_idx - 1 != as_index) continue; + if (os->bit_rate > 0) + snprintf(bandwidth_str, sizeof(bandwidth_str), " bandwidth=\"%d\"", + os->bit_rate + os->muxer_overhead); + if (as->media_type == AVMEDIA_TYPE_VIDEO) { AVStream *st = s->streams[i]; avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"", - i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); + i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); if (st->avg_frame_rate.num) avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den); avio_printf(out, ">\n"); } else { avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n", - i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate); + i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", s->streams[i]->codecpar->channels); } @@ -737,9 +755,6 @@ static int write_manifest(AVFormatContext *s, int final) update_period = 500; avio_printf(out, "\tminimumUpdatePeriod=\"PT%"PRId64"S\"\n", update_period); avio_printf(out, "\tsuggestedPresentationDelay=\"PT%"PRId64"S\"\n", c->last_duration / AV_TIME_BASE); - if (!c->availability_start_time[0] && s->nb_streams > 0 && c->streams[0].nb_segments > 0) { - format_date_now(c->availability_start_time, sizeof(c->availability_start_time)); - } if (c->availability_start_time[0]) avio_printf(out, "\tavailabilityStartTime=\"%s\"\n", c->availability_start_time); format_date_now(now_str, sizeof(now_str)); @@ -812,25 +827,28 @@ static int write_manifest(AVFormatContext *s, int final) } av_dict_free(&opts); - ff_hls_write_playlist_version(out, 6); + ff_hls_write_playlist_version(out, 7); for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; AVStream *st = s->streams[i]; + OutputStream *os = &c->streams[i]; if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); ff_hls_write_audio_rendition(out, (char *)audio_group, playlist_file, i, is_default); - max_audio_bitrate = FFMAX(st->codecpar->bit_rate, max_audio_bitrate); + max_audio_bitrate = FFMAX(st->codecpar->bit_rate + + os->muxer_overhead, max_audio_bitrate); is_default = 0; } for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; AVStream *st = s->streams[i]; + OutputStream *os = &c->streams[i]; char *agroup = NULL; - int stream_bitrate = st->codecpar->bit_rate; + int stream_bitrate = st->codecpar->bit_rate + os->muxer_overhead; if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && max_audio_bitrate) { agroup = (char *)audio_group; stream_bitrate += max_audio_bitrate; @@ -868,6 +886,13 @@ static int dash_init(AVFormatContext *s) if (c->single_file) c->use_template = 0; +#if FF_API_DASH_MIN_SEG_DURATION + if (c->min_seg_duration != 5000000) { + av_log(s, AV_LOG_WARNING, "The min_seg_duration option is deprecated and will be removed. Please use the -seg_duration\n"); + c->seg_duration = c->min_seg_duration; + } +#endif + av_strlcpy(c->dirname, s->url, sizeof(c->dirname)); ptr = strrchr(c->dirname, '/'); if (ptr) { @@ -898,10 +923,7 @@ static int dash_init(AVFormatContext *s) char filename[1024]; os->bit_rate = s->streams[i]->codecpar->bit_rate; - if (os->bit_rate) { - snprintf(os->bandwidth_str, sizeof(os->bandwidth_str), - " bandwidth=\"%d\"", os->bit_rate); - } else { + if (!os->bit_rate) { int level = s->strict_std_compliance >= FF_COMPLIANCE_STRICT ? AV_LOG_ERROR : AV_LOG_WARNING; av_log(s, level, "No bit rate set for stream %d\n", i); @@ -971,7 +993,7 @@ static int dash_init(AVFormatContext *s) else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); } else { - av_dict_set_int(&opts, "cluster_time_limit", c->min_seg_duration / 1000, 0); + av_dict_set_int(&opts, "cluster_time_limit", c->seg_duration / 1000, 0); av_dict_set_int(&opts, "cluster_size_limit", 5 * 1024 * 1024, 0); // set a large cluster size limit av_dict_set_int(&opts, "dash", 1, 0); av_dict_set_int(&opts, "dash_track_number", i + 1, 0); @@ -1017,8 +1039,8 @@ static int dash_init(AVFormatContext *s) os->segment_index = 1; } - if (!c->has_video && c->min_seg_duration <= 0) { - av_log(s, AV_LOG_WARNING, "no video stream and no min seg duration set\n"); + if (!c->has_video && c->seg_duration <= 0) { + av_log(s, AV_LOG_WARNING, "no video stream and no seg duration set\n"); return AVERROR(EINVAL); } return 0; @@ -1030,21 +1052,16 @@ static int dash_write_header(AVFormatContext *s) int i, ret; for (i = 0; i < s->nb_streams; i++) { OutputStream *os = &c->streams[i]; - if ((ret = avformat_write_header(os->ctx, NULL)) < 0) { - dash_free(s); + if ((ret = avformat_write_header(os->ctx, NULL)) < 0) return ret; - } } - ret = write_manifest(s, 0); - if (!ret) - av_log(s, AV_LOG_VERBOSE, "Manifest written to: %s\n", s->url); return ret; } static int add_segment(OutputStream *os, const char *file, int64_t time, int duration, int64_t start_pos, int64_t range_length, - int64_t index_length) + int64_t index_length, int next_exp_index) { int err; Segment *seg; @@ -1072,6 +1089,12 @@ static int add_segment(OutputStream *os, const char *file, seg->index_length = index_length; os->segments[os->nb_segments++] = seg; os->segment_index++; + //correcting the segment index if it has fallen behind the expected value + if (os->segment_index < next_exp_index) { + av_log(NULL, AV_LOG_WARNING, "Correcting the segment index after file %s: current=%d corrected=%d\n", + file, os->segment_index, next_exp_index); + os->segment_index = next_exp_index; + } return 0; } @@ -1161,10 +1184,22 @@ static int dash_flush(AVFormatContext *s, int final, int stream) const char *proto = avio_find_protocol_name(s->url); int use_rename = proto && !strcmp(proto, "file"); - int cur_flush_segment_index = 0; - if (stream >= 0) + int cur_flush_segment_index = 0, next_exp_index = -1; + if (stream >= 0) { cur_flush_segment_index = c->streams[stream].segment_index; + //finding the next segment's expected index, based on the current pts value + if (c->use_template && !c->use_timeline && c->index_correction && + c->streams[stream].last_pts != AV_NOPTS_VALUE && + c->streams[stream].first_pts != AV_NOPTS_VALUE) { + int64_t pts_diff = av_rescale_q(c->streams[stream].last_pts - + c->streams[stream].first_pts, + s->streams[stream]->time_base, + AV_TIME_BASE_Q); + next_exp_index = (pts_diff / c->seg_duration) + 1; + } + } + for (i = 0; i < s->nb_streams; i++) { OutputStream *os = &c->streams[i]; AVStream *st = s->streams[i]; @@ -1209,18 +1244,22 @@ static int dash_flush(AVFormatContext *s, int final, int stream) } } + if (!os->muxer_overhead) + os->muxer_overhead = ((int64_t) (range_length - os->total_pkt_size) * + 8 * AV_TIME_BASE) / + av_rescale_q(os->max_pts - os->start_pts, + st->time_base, AV_TIME_BASE_Q); + os->total_pkt_size = 0; + if (!os->bit_rate) { // calculate average bitrate of first segment int64_t bitrate = (int64_t) range_length * 8 * AV_TIME_BASE / av_rescale_q(os->max_pts - os->start_pts, st->time_base, AV_TIME_BASE_Q); - if (bitrate >= 0) { + if (bitrate >= 0) os->bit_rate = bitrate; - snprintf(os->bandwidth_str, sizeof(os->bandwidth_str), - " bandwidth=\"%d\"", os->bit_rate); - } } - add_segment(os, os->filename, os->start_pts, os->max_pts - os->start_pts, os->pos, range_length, index_length); + add_segment(os, os->filename, os->start_pts, os->max_pts - os->start_pts, os->pos, range_length, index_length, next_exp_index); av_log(s, AV_LOG_VERBOSE, "Representation %d media segment %d written to: %s\n", i, os->segment_index, os->full_path); os->pos += range_length; @@ -1256,6 +1295,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) DASHContext *c = s->priv_data; AVStream *st = s->streams[pkt->stream_index]; OutputStream *os = &c->streams[pkt->stream_index]; + int64_t seg_end_duration, elapsed_duration; int ret; ret = update_stream_extradata(s, os, st->codecpar); @@ -1282,11 +1322,31 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (os->first_pts == AV_NOPTS_VALUE) os->first_pts = pkt->pts; + os->last_pts = pkt->pts; + + if (!c->availability_start_time[0]) + format_date_now(c->availability_start_time, + sizeof(c->availability_start_time)); + + if (!os->availability_time_offset && pkt->duration) { + int64_t frame_duration = av_rescale_q(pkt->duration, st->time_base, + AV_TIME_BASE_Q); + os->availability_time_offset = ((double) c->seg_duration - + frame_duration) / AV_TIME_BASE; + } + + if (c->use_template && !c->use_timeline) { + elapsed_duration = pkt->pts - os->first_pts; + seg_end_duration = (int64_t) os->segment_index * c->seg_duration; + } else { + elapsed_duration = pkt->pts - os->start_pts; + seg_end_duration = c->seg_duration; + } if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && pkt->flags & AV_PKT_FLAG_KEY && os->packets_written && - av_compare_ts(pkt->pts - os->start_pts, st->time_base, - c->min_seg_duration, AV_TIME_BASE_Q) >= 0) { + av_compare_ts(elapsed_duration, st->time_base, + seg_end_duration, AV_TIME_BASE_Q) >= 0) { int64_t prev_duration = c->last_duration; c->last_duration = av_rescale_q(pkt->pts - os->start_pts, @@ -1323,6 +1383,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) else os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration); os->packets_written++; + os->total_pkt_size += pkt->size; if ((ret = ff_write_chained(os->ctx, 0, pkt, s, 0)) < 0) return ret; @@ -1426,7 +1487,10 @@ static const AVOption options[] = { { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, { "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E }, { "extra_window_size", "number of segments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E }, - { "min_seg_duration", "minimum segment duration (in microseconds)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX, E }, +#if FF_API_DASH_MIN_SEG_DURATION + { "min_seg_duration", "minimum segment duration (in microseconds) (will be deprecated)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX, E }, +#endif + { "seg_duration", "segment duration (in seconds, fractional value can be set)", OFFSET(seg_duration), AV_OPT_TYPE_DURATION, { .i64 = 5000000 }, 0, INT_MAX, E }, { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, @@ -1440,6 +1504,8 @@ static const AVOption options[] = { { "http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "streaming", "Enable/Disable streaming mode of output. Each frame will be moof fragment", OFFSET(streaming), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, + { "index_correction", "Enable/Disable segment index correction logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { NULL }, }; diff --git a/chromium/third_party/ffmpeg/libavformat/dsfdec.c b/chromium/third_party/ffmpeg/libavformat/dsfdec.c index 41538fd83c0..5e06fd63a58 100644 --- a/chromium/third_party/ffmpeg/libavformat/dsfdec.c +++ b/chromium/third_party/ffmpeg/libavformat/dsfdec.c @@ -26,6 +26,8 @@ typedef struct { uint64_t data_end; + uint64_t audio_size; + uint64_t data_size; } DSFContext; static int dsf_probe(AVProbeData *p) @@ -120,7 +122,7 @@ static int dsf_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - avio_skip(pb, 8); + dsf->audio_size = avio_rl64(pb) / 8 * st->codecpar->channels; st->codecpar->block_align = avio_rl32(pb); if (st->codecpar->block_align > INT_MAX / st->codecpar->channels) { avpriv_request_sample(s, "block_align overflow"); @@ -135,7 +137,9 @@ static int dsf_read_header(AVFormatContext *s) dsf->data_end = avio_tell(pb); if (avio_rl32(pb) != MKTAG('d', 'a', 't', 'a')) return AVERROR_INVALIDDATA; - dsf->data_end += avio_rl64(pb); + dsf->data_size = avio_rl64(pb) - 12; + dsf->data_end += dsf->data_size + 12; + s->internal->data_offset = avio_tell(pb); return 0; } @@ -151,6 +155,34 @@ static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; pkt->stream_index = 0; + if (dsf->data_size > dsf->audio_size) { + int last_packet = pos == (dsf->data_end - st->codecpar->block_align); + + if (last_packet) { + int64_t data_pos = pos - s->internal->data_offset; + int64_t packet_size = dsf->audio_size - data_pos; + int64_t skip_size = dsf->data_size - data_pos - packet_size; + uint8_t *dst; + int ch, ret; + + if (packet_size <= 0 || skip_size <= 0) + return AVERROR_INVALIDDATA; + + if (av_new_packet(pkt, packet_size) < 0) + return AVERROR(ENOMEM); + dst = pkt->data; + for (ch = 0; ch < st->codecpar->channels; ch++) { + ret = avio_read(pb, dst, packet_size / st->codecpar->channels); + if (ret < packet_size / st->codecpar->channels) + return AVERROR_EOF; + + dst += ret; + avio_skip(pb, skip_size / st->codecpar->channels); + } + + return 0; + } + } return av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codecpar->block_align)); } diff --git a/chromium/third_party/ffmpeg/libavformat/flacenc.c b/chromium/third_party/ffmpeg/libavformat/flacenc.c index b894f9ef614..617bccdc84a 100644 --- a/chromium/third_party/ffmpeg/libavformat/flacenc.c +++ b/chromium/third_party/ffmpeg/libavformat/flacenc.c @@ -21,10 +21,13 @@ #include "libavutil/channel_layout.h" #include "libavutil/opt.h" +#include "libavutil/pixdesc.h" #include "libavcodec/flac.h" #include "avformat.h" #include "avio_internal.h" #include "flacenc.h" +#include "id3v2.h" +#include "internal.h" #include "vorbiscomment.h" #include "libavcodec/bytestream.h" @@ -33,8 +36,15 @@ typedef struct FlacMuxerContext { const AVClass *class; int write_header; + int audio_stream_idx; + int waiting_pics; + /* audio packets are queued here until we get all the attached pictures */ + AVPacketList *queue, *queue_end; + /* updated streaminfo sent by the encoder at the end */ uint8_t *streaminfo; + + unsigned attached_types; } FlacMuxerContext; static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_bytes, @@ -74,36 +84,160 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, return 0; } -static int flac_write_header(struct AVFormatContext *s) +static int flac_write_picture(struct AVFormatContext *s, AVPacket *pkt) { - int ret; - int padding = s->metadata_header_padding; - AVCodecParameters *par = s->streams[0]->codecpar; - FlacMuxerContext *c = s->priv_data; - - if (!c->write_header) + FlacMuxerContext *c = s->priv_data; + AVIOContext *pb = s->pb; + const AVPixFmtDescriptor *pixdesc; + const CodecMime *mime = ff_id3v2_mime_tags; + AVDictionaryEntry *e; + const char *mimetype = NULL, *desc = ""; + const AVStream *st = s->streams[pkt->stream_index]; + int i, mimelen, desclen, type = 0; + + if (!pkt->data) return 0; - if (s->nb_streams > 1) { - av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); + while (mime->id != AV_CODEC_ID_NONE) { + if (mime->id == st->codecpar->codec_id) { + mimetype = mime->str; + break; + } + mime++; + } + if (!mimetype) { + av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot " + "write an attached picture.\n", st->index); + return AVERROR(EINVAL); + } + mimelen = strlen(mimetype); + + /* get the picture type */ + e = av_dict_get(st->metadata, "comment", NULL, 0); + for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) { + if (!av_strcasecmp(e->value, ff_id3v2_picture_types[i])) { + type = i; + break; + } + } + + if ((c->attached_types & (1 << type)) & 0x6) { + av_log(s, AV_LOG_ERROR, "Duplicate attachment for type '%s'\n", ff_id3v2_picture_types[type]); return AVERROR(EINVAL); } - if (par->codec_id != AV_CODEC_ID_FLAC) { - av_log(s, AV_LOG_ERROR, "unsupported codec\n"); + + if (type == 1 && (st->codecpar->codec_id != AV_CODEC_ID_PNG || + st->codecpar->width != 32 || + st->codecpar->height != 32)) { + av_log(s, AV_LOG_ERROR, "File icon attachment must be a 32x32 PNG"); return AVERROR(EINVAL); } + c->attached_types |= (1 << type); + + /* get the description */ + if ((e = av_dict_get(st->metadata, "title", NULL, 0))) + desc = e->value; + desclen = strlen(desc); + + avio_w8(pb, 0x06); + avio_wb24(pb, 4 + 4 + mimelen + 4 + desclen + 4 + 4 + 4 + 4 + 4 + pkt->size); + + avio_wb32(pb, type); + + avio_wb32(pb, mimelen); + avio_write(pb, mimetype, mimelen); + + avio_wb32(pb, desclen); + avio_write(pb, desc, desclen); + + avio_wb32(pb, st->codecpar->width); + avio_wb32(pb, st->codecpar->height); + if ((pixdesc = av_pix_fmt_desc_get(st->codecpar->format))) + avio_wb32(pb, av_get_bits_per_pixel(pixdesc)); + else + avio_wb32(pb, 0); + avio_wb32(pb, 0); + + avio_wb32(pb, pkt->size); + avio_write(pb, pkt->data, pkt->size); + return 0; +} + +static int flac_finish_header(struct AVFormatContext *s) +{ + int i, ret, padding = s->metadata_header_padding; if (padding < 0) padding = 8192; /* The FLAC specification states that 24 bits are used to represent the * size of a metadata block so we must clip this value to 2^24-1. */ padding = av_clip_uintp2(padding, 24); - ret = ff_flac_write_header(s->pb, par->extradata, - par->extradata_size, 0); + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + AVPacket *pkt = st->priv_data; + if (!pkt) + continue; + ret = flac_write_picture(s, pkt); + av_packet_unref(pkt); + if (ret < 0 && (s->error_recognition & AV_EF_EXPLODE)) + return ret; + } + + ret = flac_write_block_comment(s->pb, &s->metadata, !padding, + s->flags & AVFMT_FLAG_BITEXACT); if (ret) return ret; + /* The command line flac encoder defaults to placing a seekpoint + * every 10s. So one might add padding to allow that later + * but there seems to be no simple way to get the duration here. + * So just add the amount requested by the user. */ + if (padding) + flac_write_block_padding(s->pb, padding, 1); + + return 0; +} + +static int flac_init(struct AVFormatContext *s) +{ + AVCodecParameters *par; + FlacMuxerContext *c = s->priv_data; + int i; + + c->audio_stream_idx = -1; + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (c->audio_stream_idx >= 0 || st->codecpar->codec_id != AV_CODEC_ID_FLAC) { + av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one FLAC " + "audio stream is required.\n"); + return AVERROR(EINVAL); + } + par = s->streams[i]->codecpar; + c->audio_stream_idx = i; + } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) { + av_log(s, AV_LOG_WARNING, "Video stream #%d is not an attached picture. Ignoring\n", i); + continue; + } else if (st->codecpar->codec_id == AV_CODEC_ID_GIF) { + av_log(s, AV_LOG_ERROR, "GIF image support is not implemented.\n"); + return AVERROR_PATCHWELCOME; + } else if (!c->write_header) { + av_log(s, AV_LOG_ERROR, "Can't write attached pictures without a header.\n"); + return AVERROR(EINVAL); + } + c->waiting_pics++; + } else { + av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in FLAC.\n"); + return AVERROR(EINVAL); + } + } + if (c->audio_stream_idx < 0) { + av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); + return AVERROR(EINVAL); + } + /* add the channel layout tag */ if (par->channel_layout && !(par->channel_layout & ~0x3ffffULL) && @@ -121,18 +255,68 @@ static int flac_write_header(struct AVFormatContext *s) } } - ret = flac_write_block_comment(s->pb, &s->metadata, !padding, - s->flags & AVFMT_FLAG_BITEXACT); - if (ret) + return 0; +} + +static int flac_write_header(struct AVFormatContext *s) +{ + FlacMuxerContext *c = s->priv_data; + AVCodecParameters *par = s->streams[c->audio_stream_idx]->codecpar; + int ret; + + if (!c->write_header) + return 0; + + ret = ff_flac_write_header(s->pb, par->extradata, + par->extradata_size, 0); + if (ret < 0) return ret; - /* The command line flac encoder defaults to placing a seekpoint - * every 10s. So one might add padding to allow that later - * but there seems to be no simple way to get the duration here. - * So just add the amount requested by the user. */ - if (padding) - flac_write_block_padding(s->pb, padding, 1); + if (!c->waiting_pics) + ret = flac_finish_header(s); + + return ret; +} + +static int flac_write_audio_packet(struct AVFormatContext *s, AVPacket *pkt) +{ + FlacMuxerContext *c = s->priv_data; + uint8_t *streaminfo; + int streaminfo_size; + + /* check for updated streaminfo */ + streaminfo = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &streaminfo_size); + if (streaminfo && streaminfo_size == FLAC_STREAMINFO_SIZE) { + av_freep(&c->streaminfo); + + c->streaminfo = av_malloc(FLAC_STREAMINFO_SIZE); + if (!c->streaminfo) + return AVERROR(ENOMEM); + memcpy(c->streaminfo, streaminfo, FLAC_STREAMINFO_SIZE); + } + + if (pkt->size) + avio_write(s->pb, pkt->data, pkt->size); + return 0; +} +static int flac_queue_flush(AVFormatContext *s) +{ + FlacMuxerContext *c = s->priv_data; + AVPacket pkt; + int ret, write = 1; + + ret = flac_finish_header(s); + if (ret < 0) + write = 0; + + while (c->queue) { + ff_packet_list_get(&c->queue, &c->queue_end, &pkt); + if (write && (ret = flac_write_audio_packet(s, &pkt)) < 0) + write = 0; + av_packet_unref(&pkt); + } return ret; } @@ -142,7 +326,13 @@ static int flac_write_trailer(struct AVFormatContext *s) int64_t file_size; FlacMuxerContext *c = s->priv_data; uint8_t *streaminfo = c->streaminfo ? c->streaminfo : - s->streams[0]->codecpar->extradata; + s->streams[c->audio_stream_idx]->codecpar->extradata; + + if (c->waiting_pics) { + av_log(s, AV_LOG_WARNING, "No packets were sent for some of the " + "attached pictures.\n"); + flac_queue_flush(s); + } if (!c->write_header || !streaminfo) return 0; @@ -166,23 +356,48 @@ static int flac_write_trailer(struct AVFormatContext *s) static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) { FlacMuxerContext *c = s->priv_data; - uint8_t *streaminfo; - int streaminfo_size; + int ret; - /* check for updated streaminfo */ - streaminfo = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, - &streaminfo_size); - if (streaminfo && streaminfo_size == FLAC_STREAMINFO_SIZE) { - av_freep(&c->streaminfo); + if (pkt->stream_index == c->audio_stream_idx) { + if (c->waiting_pics) { + /* buffer audio packets until we get all the pictures */ + ret = ff_packet_list_put(&c->queue, &c->queue_end, pkt, FF_PACKETLIST_FLAG_REF_PACKET); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Out of memory in packet queue; skipping attached pictures\n"); + c->waiting_pics = 0; + ret = flac_queue_flush(s); + if (ret < 0) + return ret; + return flac_write_audio_packet(s, pkt); + } + } else + return flac_write_audio_packet(s, pkt); + } else { + AVStream *st = s->streams[pkt->stream_index]; - c->streaminfo = av_malloc(FLAC_STREAMINFO_SIZE); - if (!c->streaminfo) - return AVERROR(ENOMEM); - memcpy(c->streaminfo, streaminfo, FLAC_STREAMINFO_SIZE); + if (!c->waiting_pics || + !(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) + return 0; + + /* warn only once for each stream */ + if (st->nb_frames == 1) { + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + } + if (st->nb_frames >= 1) + return 0; + + st->priv_data = av_packet_clone(pkt); + if (!st->priv_data) + av_log(s, AV_LOG_ERROR, "Out of memory queueing an attached picture; skipping\n"); + c->waiting_pics--; + + /* flush the buffered audio packets */ + if (!c->waiting_pics && + (ret = flac_queue_flush(s)) < 0) + return ret; } - if (pkt->size) - avio_write(s->pb, pkt->data, pkt->size); return 0; } @@ -205,7 +420,8 @@ AVOutputFormat ff_flac_muxer = { .mime_type = "audio/x-flac", .extensions = "flac", .audio_codec = AV_CODEC_ID_FLAC, - .video_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_PNG, + .init = flac_init, .write_header = flac_write_header, .write_packet = flac_write_packet, .write_trailer = flac_write_trailer, diff --git a/chromium/third_party/ffmpeg/libavformat/format.c b/chromium/third_party/ffmpeg/libavformat/format.c index 75951938cf0..1c66afb7e6d 100644 --- a/chromium/third_party/ffmpeg/libavformat/format.c +++ b/chromium/third_party/ffmpeg/libavformat/format.c @@ -51,10 +51,9 @@ int av_match_ext(const char *filename, const char *extensions) AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type) { - AVOutputFormat *fmt = NULL, *fmt_found; -#if !FF_API_NEXT + const AVOutputFormat *fmt = NULL; + AVOutputFormat *fmt_found = NULL; void *i = 0; -#endif int score_max, score; /* specific test for image sequences */ @@ -66,15 +65,8 @@ AVOutputFormat *av_guess_format(const char *short_name, const char *filename, } #endif /* Find the proper file type. */ - fmt_found = NULL; score_max = 0; -#if FF_API_NEXT -FF_DISABLE_DEPRECATION_WARNINGS - while ((fmt = av_oformat_next(fmt))) -#else - while ((fmt = av_muxer_iterate(&i))) -#endif - { + while ((fmt = av_muxer_iterate(&i))) { score = 0; if (fmt->name && short_name && av_match_name(short_name, fmt->name)) score += 100; @@ -86,12 +78,9 @@ FF_DISABLE_DEPRECATION_WARNINGS } if (score > score_max) { score_max = score; - fmt_found = fmt; + fmt_found = (AVOutputFormat*)fmt; } } -#if FF_API_NEXT -FF_ENABLE_DEPRECATION_WARNINGS -#endif return fmt_found; } @@ -128,19 +117,11 @@ enum AVCodecID av_guess_codec(AVOutputFormat *fmt, const char *short_name, AVInputFormat *av_find_input_format(const char *short_name) { - AVInputFormat *fmt = NULL; -#if FF_API_NEXT -FF_DISABLE_DEPRECATION_WARNINGS - while ((fmt = av_iformat_next(fmt))) - if (av_match_name(short_name, fmt->name)) - return fmt; -FF_ENABLE_DEPRECATION_WARNINGS -#else + const AVInputFormat *fmt = NULL; void *i = 0; while ((fmt = av_demuxer_iterate(&i))) if (av_match_name(short_name, fmt->name)) - return fmt; -#endif + return (AVInputFormat*)fmt; return NULL; } @@ -148,7 +129,8 @@ AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, int *score_ret) { AVProbeData lpd = *pd; - AVInputFormat *fmt1 = NULL, *fmt; + const AVInputFormat *fmt1 = NULL; + AVInputFormat *fmt = NULL; int score, score_max = 0; void *i = 0; const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE]; @@ -175,7 +157,6 @@ AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, nodat = ID3_GREATER_PROBE; } - fmt = NULL; while ((fmt1 = av_demuxer_iterate(&i))) { if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2")) continue; @@ -210,7 +191,7 @@ AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened, } if (score > score_max) { score_max = score; - fmt = fmt1; + fmt = (AVInputFormat*)fmt1; } else if (score == score_max) fmt = NULL; } diff --git a/chromium/third_party/ffmpeg/libavformat/hls.c b/chromium/third_party/ffmpeg/libavformat/hls.c index c578bf86e3b..ffec1248189 100644 --- a/chromium/third_party/ffmpeg/libavformat/hls.c +++ b/chromium/third_party/ffmpeg/libavformat/hls.c @@ -202,11 +202,6 @@ typedef struct HLSContext { int64_t first_timestamp; int64_t cur_timestamp; AVIOInterruptCB *interrupt_callback; - char *referer; ///< holds HTTP referer set as an AVOption to the HTTP protocol context - char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context - char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context - char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context - char *http_proxy; ///< holds the address of the HTTP proxy server AVDictionary *avio_opts; int strict_std_compliance; char *allowed_extensions; @@ -216,14 +211,6 @@ typedef struct HLSContext { AVIOContext *playlist_pb; } HLSContext; -static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) -{ - int len = ff_get_line(s, buf, maxlen); - while (len > 0 && av_isspace(buf[len - 1])) - buf[--len] = '\0'; - return len; -} - static void free_segment_list(struct playlist *pls) { int i; @@ -275,10 +262,6 @@ static void free_playlist_list(HLSContext *c) av_free(pls); } av_freep(&c->playlists); - av_freep(&c->cookies); - av_freep(&c->user_agent); - av_freep(&c->headers); - av_freep(&c->http_proxy); c->n_playlists = 0; } @@ -601,14 +584,6 @@ static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url return 0; } -static void update_options(char **dest, const char *name, void *src) -{ - av_freep(dest); - av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest); - if (*dest && !strlen(*dest)) - av_freep(dest); -} - static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, const char *url) { @@ -692,12 +667,8 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, if (!(s->flags & AVFMT_FLAG_CUSTOM_IO)) av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies); - if (new_cookies) { - av_free(c->cookies); - c->cookies = new_cookies; - } - - av_dict_set(&opts, "cookies", c->cookies, 0); + if (new_cookies) + av_dict_set(&opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); } av_dict_free(&tmp); @@ -743,16 +714,8 @@ static int parse_playlist(HLSContext *c, const char *url, } if (!in) { -#if 1 AVDictionary *opts = NULL; - /* Some HLS servers don't like being sent the range header */ - av_dict_set(&opts, "seekable", "0", 0); - - // broker prior HTTP options that should be consistent across requests - av_dict_set(&opts, "user_agent", c->user_agent, 0); - av_dict_set(&opts, "cookies", c->cookies, 0); - av_dict_set(&opts, "headers", c->headers, 0); - av_dict_set(&opts, "http_proxy", c->http_proxy, 0); + av_dict_copy(&opts, c->avio_opts, 0); if (c->http_persistent) av_dict_set(&opts, "multiple_requests", "1", 0); @@ -766,18 +729,12 @@ static int parse_playlist(HLSContext *c, const char *url, c->playlist_pb = in; else close_in = 1; -#else - ret = open_in(c, &in, url); - if (ret < 0) - return ret; - close_in = 1; -#endif } if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) url = new_url; - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (strcmp(line, "#EXTM3U")) { ret = AVERROR_INVALIDDATA; goto fail; @@ -789,7 +746,7 @@ static int parse_playlist(HLSContext *c, const char *url, pls->type = PLS_TYPE_UNSPECIFIED; } while (!avio_feof(in)) { - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { is_variant = 1; memset(&variant_info, 0, sizeof(variant_info)); @@ -952,14 +909,8 @@ static struct segment *next_segment(struct playlist *pls) return pls->segments[n]; } -enum ReadFromURLMode { - READ_NORMAL, - READ_COMPLETE, -}; - static int read_from_url(struct playlist *pls, struct segment *seg, - uint8_t *buf, int buf_size, - enum ReadFromURLMode mode) + uint8_t *buf, int buf_size) { int ret; @@ -967,13 +918,7 @@ static int read_from_url(struct playlist *pls, struct segment *seg, if (seg->size >= 0) buf_size = FFMIN(buf_size, seg->size - pls->cur_seg_offset); - if (mode == READ_COMPLETE) { - ret = avio_read(pls->input, buf, buf_size); - if (ret != buf_size) - av_log(NULL, AV_LOG_ERROR, "Could not read complete segment.\n"); - } else - ret = avio_read(pls->input, buf, buf_size); - + ret = avio_read(pls->input, buf, buf_size); if (ret > 0) pls->cur_seg_offset += ret; @@ -1092,7 +1037,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, while (1) { /* see if we can retrieve enough data for ID3 header */ if (*len < ID3v2_HEADER_SIZE && buf_size >= ID3v2_HEADER_SIZE) { - bytes = read_from_url(pls, seg, buf + *len, ID3v2_HEADER_SIZE - *len, READ_COMPLETE); + bytes = read_from_url(pls, seg, buf + *len, ID3v2_HEADER_SIZE - *len); if (bytes > 0) { if (bytes == ID3v2_HEADER_SIZE - *len) @@ -1144,7 +1089,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, if (remaining > 0) { /* read the rest of the tag in */ - if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining, READ_COMPLETE) != remaining) + if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining) != remaining) break; id3_buf_pos += remaining; av_log(pls->ctx, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining); @@ -1158,7 +1103,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, /* re-fill buffer for the caller unless EOF */ if (*len >= 0 && (fill_buf || *len == 0)) { - bytes = read_from_url(pls, seg, buf + *len, buf_size - *len, READ_NORMAL); + bytes = read_from_url(pls, seg, buf + *len, buf_size - *len); /* ignore error if we already had some data */ if (bytes >= 0) @@ -1184,14 +1129,6 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, int ret; int is_http = 0; - // broker prior HTTP options that should be consistent across requests - av_dict_set(&opts, "user_agent", c->user_agent, 0); - av_dict_set(&opts, "referer", c->referer, 0); - av_dict_set(&opts, "cookies", c->cookies, 0); - av_dict_set(&opts, "headers", c->headers, 0); - av_dict_set(&opts, "http_proxy", c->http_proxy, 0); - av_dict_set(&opts, "seekable", "0", 0); - if (c->http_persistent) av_dict_set(&opts, "multiple_requests", "1", 0); @@ -1208,7 +1145,6 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, if (seg->key_type == KEY_NONE) { ret = open_url(pls->parent, in, seg->url, c->avio_opts, opts, &is_http); } else if (seg->key_type == KEY_AES_128) { - AVDictionary *opts2 = NULL; char iv[33], key[33], url[MAX_URL_SIZE]; if (strcmp(seg->key, pls->key_url)) { AVIOContext *pb = NULL; @@ -1233,14 +1169,10 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, else snprintf(url, sizeof(url), "crypto:%s", seg->url); - av_dict_copy(&opts2, c->avio_opts, 0); - av_dict_set(&opts2, "key", key, 0); - av_dict_set(&opts2, "iv", iv, 0); - - ret = open_url(pls->parent, in, url, opts2, opts, &is_http); - - av_dict_free(&opts2); + av_dict_set(&opts, "key", key, 0); + av_dict_set(&opts, "iv", iv, 0); + ret = open_url(pls->parent, in, url, c->avio_opts, opts, &is_http); if (ret < 0) { goto cleanup; } @@ -1318,7 +1250,7 @@ static int update_init_section(struct playlist *pls, struct segment *seg) av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size); ret = read_from_url(pls, seg->init_section, pls->init_sec_buf, - pls->init_sec_buf_size, READ_COMPLETE); + pls->init_sec_buf_size); ff_format_io_close(pls->parent, &pls->input); if (ret < 0) @@ -1513,7 +1445,7 @@ reload: } seg = current_segment(v); - ret = read_from_url(v, seg, buf, buf_size, READ_NORMAL); + ret = read_from_url(v, seg, buf, buf_size); if (ret > 0) { if (just_opened && v->is_id3_timestamped != 0) { /* Intercept ID3 tags here, elementary audio streams are required @@ -1661,7 +1593,7 @@ static int save_avio_options(AVFormatContext *s) { HLSContext *c = s->priv_data; static const char * const opts[] = { - "headers", "http_proxy", "user_agent", "user-agent", "cookies", "referer", NULL }; + "headers", "http_proxy", "user_agent", "user-agent", "cookies", "referer", "rw_timeout", NULL }; const char * const * opt = opts; uint8_t *buf; int ret = 0; @@ -1796,7 +1728,6 @@ static int hls_close(AVFormatContext *s) static int hls_read_header(AVFormatContext *s) { - void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb; HLSContext *c = s->priv_data; int ret = 0, i; int highest_cur_seq_no = 0; @@ -1809,29 +1740,15 @@ static int hls_read_header(AVFormatContext *s) c->first_timestamp = AV_NOPTS_VALUE; c->cur_timestamp = AV_NOPTS_VALUE; - if (u) { - // get the previous user agent & set back to null if string size is zero - update_options(&c->user_agent, "user_agent", u); - - // get the previous cookies & set back to null if string size is zero - update_options(&c->cookies, "cookies", u); - - // get the previous headers & set back to null if string size is zero - update_options(&c->headers, "headers", u); - - // get the previous http proxt & set back to null if string size is zero - update_options(&c->http_proxy, "http_proxy", u); - } - - if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0) - goto fail; - if ((ret = save_avio_options(s)) < 0) goto fail; /* Some HLS servers don't like being sent the range header */ av_dict_set(&c->avio_opts, "seekable", "0", 0); + if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0) + goto fail; + if (c->n_variants == 0) { av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); ret = AVERROR_EOF; diff --git a/chromium/third_party/ffmpeg/libavformat/hlsenc.c b/chromium/third_party/ffmpeg/libavformat/hlsenc.c index 7d9512b6648..c27a66ea795 100644 --- a/chromium/third_party/ffmpeg/libavformat/hlsenc.c +++ b/chromium/third_party/ffmpeg/libavformat/hlsenc.c @@ -75,6 +75,7 @@ typedef struct HLSSegment { int discont; int64_t pos; int64_t size; + unsigned var_stream_idx; char key_uri[LINE_BUFFER_SIZE + 1]; char iv_string[KEYSIZE*2 + 1]; @@ -106,6 +107,7 @@ typedef enum { } SegmentType; typedef struct VariantStream { + unsigned var_stream_idx; unsigned number; int64_t sequence; AVOutputFormat *oformat; @@ -171,6 +173,7 @@ typedef struct HLSContext { float time; // Set by a private option. float init_time; // Set by a private option. int max_nb_segments; // Set by a private option. + int hls_delete_threshold; // Set by a private option. #if FF_API_HLS_WRAP int wrap; // Set by a private option. #endif @@ -224,6 +227,7 @@ typedef struct HLSContext { int http_persistent; AVIOContext *m3u8_out; AVIOContext *sub_m3u8_out; + int64_t timeout; } HLSContext; static int mkdir_p(const char *path) { @@ -305,7 +309,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont av_dict_set(options, "user_agent", c->user_agent, 0); if (c->http_persistent) av_dict_set_int(options, "multiple_requests", 1, 0); - + if (c->timeout >= 0) + av_dict_set_int(options, "timeout", c->timeout, 0); } static void write_codec_attr(AVStream *st, VariantStream *vs) { @@ -441,6 +446,7 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, HLSSegment *segment, *previous_segment = NULL; float playlist_duration = 0.0f; int ret = 0, path_size, sub_path_size; + int segment_cnt = 0; char *dirname = NULL, *p, *sub_path; char *path = NULL; AVDictionary *options = NULL; @@ -454,14 +460,20 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, } segment = vs->old_segments; + segment_cnt = 0; while (segment) { playlist_duration -= segment->duration; previous_segment = segment; segment = previous_segment->next; + segment_cnt++; if (playlist_duration <= -previous_segment->duration) { previous_segment->next = NULL; break; } + if (segment_cnt >= hls->hls_delete_threshold) { + previous_segment->next = NULL; + break; + } } if (segment && !hls->use_localtime_mkdir) { @@ -476,9 +488,23 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, } p = (char *)av_basename(dirname); *p = '\0'; + } while (segment) { + char * r_dirname = dirname; + + /* if %v is present in the file's directory */ + if (av_stristr(dirname, "%v")) { + + if (replace_int_data_in_filename(&r_dirname, dirname, 'v', segment->var_stream_idx) < 1) { + ret = AVERROR(EINVAL); + goto fail; + } + av_free(dirname); + dirname = r_dirname; + } + av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n", segment->filename); path_size = (hls->use_localtime_mkdir ? 0 : strlen(dirname)) + strlen(segment->filename) + 1; @@ -678,14 +704,6 @@ static int hls_encryption_start(AVFormatContext *s) return 0; } -static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) -{ - int len = ff_get_line(s, buf, maxlen); - while (len > 0 && av_isspace(buf[len - 1])) - buf[--len] = '\0'; - return len; -} - static int hls_mux_init(AVFormatContext *s, VariantStream *vs) { AVDictionary *options = NULL; @@ -763,7 +781,11 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) if ((ret = avio_open_dyn_buf(&oc->pb)) < 0) return ret; - ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options); + if (byterange_mode) { + ret = hlsenc_io_open(s, &vs->out, vs->basename, &options); + } else { + ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options); + } av_dict_free(&options); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename); @@ -959,6 +981,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, if (!en) return AVERROR(ENOMEM); + en->var_stream_idx = vs->var_stream_idx; ret = sls_flags_filename_process(s, hls, vs, en, duration, pos, size); if (ret < 0) { return ret; @@ -1049,7 +1072,7 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs s->protocol_whitelist, s->protocol_blacklist)) < 0) return ret; - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (strcmp(line, "#EXTM3U")) { ret = AVERROR_INVALIDDATA; goto fail; @@ -1057,7 +1080,7 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs vs->discontinuity = 0; while (!avio_feof(in)) { - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { int64_t tmp_sequence = strtoll(ptr, NULL, 10); if (tmp_sequence < vs->sequence) @@ -1168,6 +1191,21 @@ static int get_relative_url(const char *master_url, const char *media_url, return 0; } +static int64_t get_stream_bit_rate(AVStream *stream) { + AVCPBProperties *props = (AVCPBProperties*)av_stream_get_side_data( + stream, + AV_PKT_DATA_CPB_PROPERTIES, + NULL + ); + + if (stream->codecpar->bit_rate) + return stream->codecpar->bit_rate; + else if (props) + return props->max_bitrate; + + return 0; +} + static int create_master_playlist(AVFormatContext *s, VariantStream * const input_vs) { @@ -1294,9 +1332,9 @@ static int create_master_playlist(AVFormatContext *s, bandwidth = 0; if (vid_st) - bandwidth += vid_st->codecpar->bit_rate; + bandwidth += get_stream_bit_rate(vid_st); if (aud_st) - bandwidth += aud_st->codecpar->bit_rate; + bandwidth += get_stream_bit_rate(aud_st); bandwidth += bandwidth / 10; ccgroup = NULL; @@ -1395,8 +1433,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { - ff_hls_write_init_file(hls->m3u8_out, vs->fmp4_init_filename, - hls->flags & HLS_SINGLE_FILE, en->size, en->pos); + ff_hls_write_init_file(hls->m3u8_out, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename, + hls->flags & HLS_SINGLE_FILE, vs->init_range_length, 0); } ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, @@ -1553,9 +1591,14 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) } if (c->key_info_file || c->encrypt) { + if (c->segment_type == SEGMENT_TYPE_FMP4) { + av_log(s, AV_LOG_ERROR, "Encrypted fmp4 not yet supported\n"); + return AVERROR_PATCHWELCOME; + } + if (c->key_info_file && c->encrypt) { av_log(s, AV_LOG_WARNING, "Cannot use both -hls_key_info_file and -hls_enc," - " will use -hls_key_info_file priority\n"); + " ignoring -hls_enc\n"); } if (!c->encrypt_started || (c->flags & HLS_PERIODIC_REKEY)) { @@ -1803,9 +1846,11 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) while (varstr = av_strtok(p, " \t", &saveptr1)) { p = NULL; - if (nb_varstreams < hls->nb_varstreams) - vs = &(hls->var_streams[nb_varstreams++]); - else + if (nb_varstreams < hls->nb_varstreams) { + vs = &(hls->var_streams[nb_varstreams]); + vs->var_stream_idx = nb_varstreams; + nb_varstreams++; + } else return AVERROR(EINVAL); q = varstr; @@ -1867,7 +1912,8 @@ static int parse_cc_stream_mapstring(AVFormatContext *s) { HLSContext *hls = s->priv_data; int nb_ccstreams; - char *p, *q, *saveptr1, *saveptr2, *ccstr, *keyval; + char *p, *q, *ccstr, *keyval; + char *saveptr1 = NULL, *saveptr2 = NULL; const char *val; ClosedCaptionsStream *ccs; @@ -1962,6 +2008,7 @@ static int update_variant_stream_info(AVFormatContext *s) { if (!hls->var_streams) return AVERROR(ENOMEM); + hls->var_streams[0].var_stream_idx = 0; hls->var_streams[0].nb_streams = s->nb_streams; hls->var_streams[0].streams = av_mallocz(sizeof(AVStream *) * hls->var_streams[0].nb_streams); @@ -2167,7 +2214,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base, - end_pts, AV_TIME_BASE_Q) >= 0) { + end_pts, AV_TIME_BASE_Q) >= 0) { int64_t new_start_pos; char *old_filename = NULL; int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); @@ -2175,23 +2222,32 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) av_write_frame(vs->avf, NULL); /* Flush any buffered data */ new_start_pos = avio_tell(vs->avf->pb); - vs->size = new_start_pos - vs->start_pos; + if (hls->segment_type != SEGMENT_TYPE_FMP4) { + vs->size = new_start_pos - vs->start_pos; + } else { + vs->size = new_start_pos; + } - if (!byterange_mode) { - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - if (!vs->init_range_length) { - avio_flush(oc->pb); - range_length = avio_close_dyn_buf(oc->pb, &buffer); - avio_write(vs->out, buffer, range_length); - vs->init_range_length = range_length; - avio_open_dyn_buf(&oc->pb); - vs->packets_written = 0; + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + if (!vs->init_range_length) { + avio_flush(oc->pb); + range_length = avio_close_dyn_buf(oc->pb, &buffer); + avio_write(vs->out, buffer, range_length); + vs->init_range_length = range_length; + avio_open_dyn_buf(&oc->pb); + vs->packets_written = 0; + vs->start_pos = range_length; + if (!byterange_mode) { ff_format_io_close(s, &vs->out); hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } - } else { + } + } else { + if (!byterange_mode) { hlsenc_io_close(s, &oc->pb, oc->url); } + } + if (!byterange_mode) { if (vs->vtt_avf) { hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->url); } @@ -2208,18 +2264,27 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } if (hls->segment_type == SEGMENT_TYPE_FMP4) { - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Failed to open file '%s'\n", - vs->avf->url); - return ret; - } - write_styp(vs->out); - ret = flush_dynbuf(vs, &range_length); - if (ret < 0) { - return ret; + if (hls->flags & HLS_SINGLE_FILE) { + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) { + av_free(old_filename); + return ret; + } + vs->size = range_length; + } else { + ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", + vs->avf->url); + return ret; + } + write_styp(vs->out); + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) { + return ret; + } + ff_format_io_close(s, &vs->out); } - ff_format_io_close(s, &vs->out); } old_filename = av_strdup(vs->avf->url); @@ -2227,15 +2292,21 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR(ENOMEM); } - ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size); - vs->start_pos = new_start_pos; - if (ret < 0) { - av_free(old_filename); - return ret; + if (vs->start_pos || hls->segment_type != SEGMENT_TYPE_FMP4) { + ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size); + vs->end_pts = pkt->pts; + vs->duration = 0; + if (ret < 0) { + av_free(old_filename); + return ret; + } } - vs->end_pts = pkt->pts; - vs->duration = 0; + if (hls->segment_type != SEGMENT_TYPE_FMP4) { + vs->start_pos = new_start_pos; + } else { + vs->start_pos += vs->size; + } vs->fmp4_init_mode = 0; if (hls->flags & HLS_SINGLE_FILE) { @@ -2286,79 +2357,85 @@ static int hls_write_trailer(struct AVFormatContext *s) for (i = 0; i < hls->nb_varstreams; i++) { vs = &hls->var_streams[i]; - oc = vs->avf; - vtt_oc = vs->vtt_avf; - old_filename = av_strdup(vs->avf->url); + oc = vs->avf; + vtt_oc = vs->vtt_avf; + old_filename = av_strdup(vs->avf->url); - if (!old_filename) { - return AVERROR(ENOMEM); - } - if ( hls->segment_type == SEGMENT_TYPE_FMP4) { - int range_length = 0; - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url); - goto failed; + if (!old_filename) { + return AVERROR(ENOMEM); } - write_styp(vs->out); - ret = flush_dynbuf(vs, &range_length); - if (ret < 0) { - goto failed; + if ( hls->segment_type == SEGMENT_TYPE_FMP4) { + int range_length = 0; + if (!(hls->flags & HLS_SINGLE_FILE)) { + ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url); + goto failed; + } + write_styp(vs->out); + } + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) { + goto failed; + } + ff_format_io_close(s, &vs->out); } - ff_format_io_close(s, &vs->out); - } failed: - av_write_trailer(oc); - if (oc->pb) { - vs->size = avio_tell(vs->avf->pb) - vs->start_pos; - if (hls->segment_type != SEGMENT_TYPE_FMP4) - ff_format_io_close(s, &oc->pb); + av_write_trailer(oc); + if (oc->pb) { + if (hls->segment_type != SEGMENT_TYPE_FMP4) { + vs->size = avio_tell(vs->avf->pb) - vs->start_pos; + } else { + vs->size = avio_tell(vs->avf->pb); + } + if (hls->segment_type != SEGMENT_TYPE_FMP4) + ff_format_io_close(s, &oc->pb); - if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) { - hls_rename_temp_file(s, oc); - av_free(old_filename); - old_filename = av_strdup(vs->avf->url); + if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) { + hls_rename_temp_file(s, oc); + av_free(old_filename); + old_filename = av_strdup(vs->avf->url); - if (!old_filename) { - return AVERROR(ENOMEM); + if (!old_filename) { + return AVERROR(ENOMEM); + } } - } - /* after av_write_trailer, then duration + 1 duration per packet */ - hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); - } + /* after av_write_trailer, then duration + 1 duration per packet */ + hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); + } - sls_flag_file_rename(hls, vs, old_filename); + sls_flag_file_rename(hls, vs, old_filename); - if (vtt_oc) { - if (vtt_oc->pb) - av_write_trailer(vtt_oc); - vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos; - ff_format_io_close(s, &vtt_oc->pb); - } - av_freep(&vs->basename); - av_freep(&vs->base_output_dirname); - avformat_free_context(oc); + if (vtt_oc) { + if (vtt_oc->pb) + av_write_trailer(vtt_oc); + vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos; + ff_format_io_close(s, &vtt_oc->pb); + } + av_freep(&vs->basename); + av_freep(&vs->base_output_dirname); + avformat_free_context(oc); - vs->avf = NULL; - hls_window(s, 1, vs); + vs->avf = NULL; + hls_window(s, 1, vs); - av_freep(&vs->fmp4_init_filename); - if (vtt_oc) { - av_freep(&vs->vtt_basename); - av_freep(&vs->vtt_m3u8_name); - avformat_free_context(vtt_oc); - } + av_freep(&vs->fmp4_init_filename); + if (vtt_oc) { + av_freep(&vs->vtt_basename); + av_freep(&vs->vtt_m3u8_name); + avformat_free_context(vtt_oc); + } - hls_free_segments(vs->segments); - hls_free_segments(vs->old_segments); - av_free(old_filename); - av_freep(&vs->m3u8_name); - av_freep(&vs->streams); - av_freep(&vs->agroup); - av_freep(&vs->ccgroup); - av_freep(&vs->baseurl); + hls_free_segments(vs->segments); + hls_free_segments(vs->old_segments); + av_free(old_filename); + av_freep(&vs->m3u8_name); + av_freep(&vs->streams); + av_freep(&vs->agroup); + av_freep(&vs->ccgroup); + av_freep(&vs->baseurl); } for (i = 0; i < hls->nb_ccstreams; i++) { @@ -2396,7 +2473,7 @@ static int hls_init(AVFormatContext *s) ret = update_variant_stream_info(s); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n", - ret); + ret); goto fail; } //TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present @@ -2432,7 +2509,7 @@ static int hls_init(AVFormatContext *s) ret = update_master_pl_info(s); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n", - ret); + ret); goto fail; } } @@ -2574,32 +2651,24 @@ static int hls_init(AVFormatContext *s) if (hls->segment_type == SEGMENT_TYPE_FMP4) { if (hls->nb_varstreams > 1) fmp4_init_filename_len += strlen(POSTFIX_PATTERN); - vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len); - if (!vs->fmp4_init_filename ) { - ret = AVERROR(ENOMEM); - goto fail; - } - av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename, - fmp4_init_filename_len); - - if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) { - ret = format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i); - if (ret < 0) + if (hls->flags & HLS_SINGLE_FILE) { + vs->fmp4_init_filename = av_strdup(vs->basename); + if (!vs->fmp4_init_filename) { + ret = AVERROR(ENOMEM); goto fail; - - fmp4_init_filename_len = strlen(vs->fmp4_init_filename) + 1; - vs->base_output_dirname = av_malloc(fmp4_init_filename_len); - if (!vs->base_output_dirname) { + } + } else { + vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len); + if (!vs->fmp4_init_filename ) { ret = AVERROR(ENOMEM); goto fail; } - av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename, + av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename, fmp4_init_filename_len); - } else { if (hls->nb_varstreams > 1) { ret = append_postfix(vs->fmp4_init_filename, fmp4_init_filename_len, i); if (ret < 0) - goto fail; + goto fail; } fmp4_init_filename_len = strlen(vs->m3u8_name) + @@ -2677,13 +2746,6 @@ static int hls_init(AVFormatContext *s) } } - if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) { - vs->fmp4_init_filename = av_strdup(vs->basename); - if (!vs->fmp4_init_filename) { - ret = AVERROR(ENOMEM); - goto fail; - } - } if ((ret = hls_mux_init(s, vs)) < 0) goto fail; @@ -2742,6 +2804,7 @@ static const AVOption options[] = { {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, {"hls_init_time", "set segment length in seconds at init list", OFFSET(init_time), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, FLT_MAX, E}, {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, + {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E}, {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, #if FF_API_HLS_WRAP @@ -2792,6 +2855,7 @@ static const AVOption options[] = { {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"master_pl_publish_rate", "Publish master play list every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E}, {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, + {"timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, { NULL }, }; diff --git a/chromium/third_party/ffmpeg/libavformat/hlsproto.c b/chromium/third_party/ffmpeg/libavformat/hlsproto.c index 2b19ed0cf67..e7ef2d88ea8 100644 --- a/chromium/third_party/ffmpeg/libavformat/hlsproto.c +++ b/chromium/third_party/ffmpeg/libavformat/hlsproto.c @@ -69,14 +69,6 @@ typedef struct HLSContext { int64_t last_load_time; } HLSContext; -static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) -{ - int len = ff_get_line(s, buf, maxlen); - while (len > 0 && av_isspace(buf[len - 1])) - buf[--len] = '\0'; - return len; -} - static void free_segment_list(HLSContext *s) { int i; @@ -122,7 +114,7 @@ static int parse_playlist(URLContext *h, const char *url) h->protocol_whitelist, h->protocol_blacklist)) < 0) return ret; - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (strcmp(line, "#EXTM3U")) { ret = AVERROR_INVALIDDATA; goto fail; @@ -131,7 +123,7 @@ static int parse_playlist(URLContext *h, const char *url) free_segment_list(s); s->finished = 0; while (!avio_feof(in)) { - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { struct variant_info info = {{0}}; is_variant = 1; diff --git a/chromium/third_party/ffmpeg/libavformat/http.c b/chromium/third_party/ffmpeg/libavformat/http.c index 344fd603cb9..aa6348f28bf 100644 --- a/chromium/third_party/ffmpeg/libavformat/http.c +++ b/chromium/third_party/ffmpeg/libavformat/http.c @@ -576,7 +576,11 @@ static int http_accept(URLContext *s, URLContext **c) goto fail; cc->hd = cl; cc->is_multi_client = 1; + return 0; fail: + if (c) { + ffurl_closep(c); + } return ret; } @@ -750,6 +754,9 @@ static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) { char *param, *next_param, *cstr, *back; + if (!set_cookie[0]) + return 0; + if (!(cstr = av_strdup(set_cookie))) return AVERROR(EINVAL); @@ -757,6 +764,8 @@ static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) back = &cstr[strlen(cstr)-1]; while (strchr(WHITESPACES, *back)) { *back='\0'; + if (back == cstr) + break; back--; } @@ -802,7 +811,7 @@ static int parse_cookie(HTTPContext *s, const char *p, AVDictionary **cookies) // if the cookie has already expired ignore it if (av_timegm(&new_tm) < av_gettime() / 1000000) { av_dict_free(&new_params); - return -1; + return 0; } // only replace an older cookie with the same name @@ -1010,7 +1019,8 @@ static int process_line(URLContext *h, char *line, int line_count, /** * Create a string containing cookie values for use as a HTTP cookie header * field value for a particular path and domain from the cookie values stored in - * the HTTP protocol context. The cookie string is stored in *cookies. + * the HTTP protocol context. The cookie string is stored in *cookies, and may + * be NULL if there are no valid cookies. * * @return a negative value if an error condition occurred, 0 otherwise */ @@ -1020,15 +1030,20 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, // cookie strings will look like Set-Cookie header field values. Multiple // Set-Cookie fields will result in multiple values delimited by a newline int ret = 0; - char *cookie, *set_cookies = av_strdup(s->cookies), *next = set_cookies; - - if (!set_cookies) return AVERROR(EINVAL); + char *cookie, *set_cookies, *next; // destroy any cookies in the dictionary. av_dict_free(&s->cookie_dict); + if (!s->cookies) + return 0; + + next = set_cookies = av_strdup(s->cookies); + if (!next) + return AVERROR(ENOMEM); + *cookies = NULL; - while ((cookie = av_strtok(next, "\n", &next))) { + while ((cookie = av_strtok(next, "\n", &next)) && !ret) { AVDictionary *cookie_params = NULL; AVDictionaryEntry *cookie_entry, *e; @@ -1038,23 +1053,19 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, // continue on to the next cookie if this one cannot be parsed if (parse_set_cookie(cookie, &cookie_params)) - continue; + goto skip_cookie; // if the cookie has no value, skip it cookie_entry = av_dict_get(cookie_params, "", NULL, AV_DICT_IGNORE_SUFFIX); - if (!cookie_entry || !cookie_entry->value) { - av_dict_free(&cookie_params); - continue; - } + if (!cookie_entry || !cookie_entry->value) + goto skip_cookie; // if the cookie has expired, don't add it if ((e = av_dict_get(cookie_params, "expires", NULL, 0)) && e->value) { struct tm tm_buf = {0}; if (!parse_set_cookie_expiry_time(e->value, &tm_buf)) { - if (av_timegm(&tm_buf) < av_gettime() / 1000000) { - av_dict_free(&cookie_params); - continue; - } + if (av_timegm(&tm_buf) < av_gettime() / 1000000) + goto skip_cookie; } } @@ -1062,42 +1073,32 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, if ((e = av_dict_get(cookie_params, "domain", NULL, 0)) && e->value) { // find the offset comparison is on the min domain (b.com, not a.b.com) int domain_offset = strlen(domain) - strlen(e->value); - if (domain_offset < 0) { - av_dict_free(&cookie_params); - continue; - } + if (domain_offset < 0) + goto skip_cookie; // match the cookie domain - if (av_strcasecmp(&domain[domain_offset], e->value)) { - av_dict_free(&cookie_params); - continue; - } + if (av_strcasecmp(&domain[domain_offset], e->value)) + goto skip_cookie; } // ensure this cookie matches the path e = av_dict_get(cookie_params, "path", NULL, 0); - if (!e || av_strncasecmp(path, e->value, strlen(e->value))) { - av_dict_free(&cookie_params); - continue; - } + if (!e || av_strncasecmp(path, e->value, strlen(e->value))) + goto skip_cookie; // cookie parameters match, so copy the value if (!*cookies) { - if (!(*cookies = av_asprintf("%s=%s", cookie_entry->key, cookie_entry->value))) { - ret = AVERROR(ENOMEM); - break; - } + *cookies = av_asprintf("%s=%s", cookie_entry->key, cookie_entry->value); } else { char *tmp = *cookies; - size_t str_size = strlen(cookie_entry->key) + strlen(cookie_entry->value) + strlen(*cookies) + 4; - if (!(*cookies = av_malloc(str_size))) { - ret = AVERROR(ENOMEM); - av_free(tmp); - break; - } - snprintf(*cookies, str_size, "%s; %s=%s", tmp, cookie_entry->key, cookie_entry->value); + *cookies = av_asprintf("%s; %s=%s", tmp, cookie_entry->key, cookie_entry->value); av_free(tmp); } + if (!*cookies) + ret = AVERROR(ENOMEM); + + skip_cookie: + av_dict_free(&cookie_params); } av_free(set_cookies); @@ -1622,6 +1623,18 @@ static int http_shutdown(URLContext *h, int flags) ((flags & AVIO_FLAG_READ) && s->chunked_post && s->listen)) { ret = ffurl_write(s->hd, footer, sizeof(footer) - 1); ret = ret > 0 ? 0 : ret; + /* flush the receive buffer when it is write only mode */ + if (!(flags & AVIO_FLAG_READ)) { + char buf[1024]; + int read_ret; + s->hd->flags |= AVIO_FLAG_NONBLOCK; + read_ret = ffurl_read(s->hd, buf, sizeof(buf)); + s->hd->flags &= ~AVIO_FLAG_NONBLOCK; + if (read_ret < 0 && read_ret != AVERROR(EAGAIN)) { + av_log(h, AV_LOG_ERROR, "URL read error: %d\n", read_ret); + ret = read_ret; + } + } s->end_chunked_post = 1; } diff --git a/chromium/third_party/ffmpeg/libavformat/img2dec.c b/chromium/third_party/ffmpeg/libavformat/img2dec.c index f3f52c83b35..ffbc9a66d82 100644 --- a/chromium/third_party/ffmpeg/libavformat/img2dec.c +++ b/chromium/third_party/ffmpeg/libavformat/img2dec.c @@ -323,7 +323,8 @@ int ff_img_read_header(AVFormatContext *s1) if (s1->pb) { int probe_buffer_size = 2048; uint8_t *probe_buffer = av_realloc(NULL, probe_buffer_size + AVPROBE_PADDING_SIZE); - AVInputFormat *fmt = NULL; + const AVInputFormat *fmt = NULL; + void *fmt_iter = NULL; AVProbeData pd = { 0 }; if (!probe_buffer) @@ -340,7 +341,7 @@ int ff_img_read_header(AVFormatContext *s1) pd.buf_size = probe_buffer_size; pd.filename = s1->url; - while ((fmt = av_iformat_next(fmt))) { + while ((fmt = av_demuxer_iterate(&fmt_iter))) { if (fmt->read_header != ff_img_read_header || !fmt->read_probe || (fmt->flags & AVFMT_NOFILE) || diff --git a/chromium/third_party/ffmpeg/libavformat/internal.h b/chromium/third_party/ffmpeg/libavformat/internal.h index a020b1b417d..35826829250 100644 --- a/chromium/third_party/ffmpeg/libavformat/internal.h +++ b/chromium/third_party/ffmpeg/libavformat/internal.h @@ -300,6 +300,16 @@ void ff_put_v(AVIOContext *bc, uint64_t val); int ff_get_line(AVIOContext *s, char *buf, int maxlen); /** + * Same as ff_get_line but strip the white-space characters in the text tail + * + * @param s the read-only AVIOContext + * @param buf buffer to store the read line + * @param maxlen size of the buffer + * @return the length of the string written in the buffer + */ +int ff_get_chomp_line(AVIOContext *s, char *buf, int maxlen); + +/** * Read a whole line of text from AVIOContext to an AVBPrint buffer. Stop * reading after reaching a \\r, a \\n, a \\r\\n, a \\0 or EOF. The line * ending characters are NOT included in the buffer, but they are skipped on @@ -731,11 +741,44 @@ int ff_unlock_avformat(void); */ void ff_format_set_url(AVFormatContext *s, char *url); -#if FF_API_NEXT +#define FF_PACKETLIST_FLAG_REF_PACKET (1 << 0) /**< Create a new reference for the packet instead of + transferring the ownership of the existing one to the + list. */ + /** - * Register devices in deprecated format linked list. - */ + * Append an AVPacket to the list. + * + * @param head List head element + * @param tail List tail element + * @param pkt The packet being appended + * @param flags Any combination of FF_PACKETLIST_FLAG_* flags + * @return 0 on success, negative AVERROR value on failure. On failure, + the list is unchanged + */ +int ff_packet_list_put(AVPacketList **head, AVPacketList **tail, + AVPacket *pkt, int flags); + +/** + * Remove the oldest AVPacket in the list and return it. + * + * @note The pkt will be overwritten completely. The caller owns the + * packet and must unref it by itself. + * + * @param head List head element + * @param tail List tail element + * @param pkt Pointer to an initialized AVPacket struct + */ +int ff_packet_list_get(AVPacketList **head, AVPacketList **tail, + AVPacket *pkt); + +/** + * Wipe the list and unref all the packets in it. + * + * @param head List head element + * @param tail List tail element + */ +void ff_packet_list_free(AVPacketList **head, AVPacketList **tail); + void avpriv_register_devices(const AVOutputFormat * const o[], const AVInputFormat * const i[]); -#endif #endif /* AVFORMAT_INTERNAL_H */ diff --git a/chromium/third_party/ffmpeg/libavformat/isom.h b/chromium/third_party/ffmpeg/libavformat/isom.h index 4da34142f06..fb361125c96 100644 --- a/chromium/third_party/ffmpeg/libavformat/isom.h +++ b/chromium/third_party/ffmpeg/libavformat/isom.h @@ -27,6 +27,7 @@ #include <stddef.h> #include <stdint.h> +#include "libavutil/encryption_info.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/spherical.h" #include "libavutil/stereo3d.h" @@ -108,12 +109,26 @@ typedef struct MOVSbgp { unsigned int index; } MOVSbgp; +typedef struct MOVEncryptionIndex { + // Individual encrypted samples. If there are no elements, then the default + // settings will be used. + unsigned int nb_encrypted_samples; + AVEncryptionInfo **encrypted_samples; + + uint8_t* auxiliary_info_sizes; + size_t auxiliary_info_sample_count; + uint8_t auxiliary_info_default_size; + size_t* auxiliary_offsets; ///< Absolute seek position + size_t auxiliary_offsets_count; +} MOVEncryptionIndex; + typedef struct MOVFragmentStreamInfo { int id; int64_t sidx_pts; int64_t first_tfra_pts; int64_t tfdt_dts; int index_entry; + MOVEncryptionIndex *encryption_index; } MOVFragmentStreamInfo; typedef struct MOVFragmentIndexItem { @@ -215,15 +230,10 @@ typedef struct MOVStreamContext { int has_sidx; // If there is an sidx entry for this stream. struct { - int use_subsamples; - uint8_t* auxiliary_info; - uint8_t* auxiliary_info_end; - uint8_t* auxiliary_info_pos; - uint8_t auxiliary_info_default_size; - uint8_t* auxiliary_info_sizes; - size_t auxiliary_info_sizes_count; - int64_t auxiliary_info_index; struct AVAESCTR* aes_ctr; + unsigned int per_sample_iv_size; // Either 0, 8, or 16. + AVEncryptionInfo *default_encrypted_sample; + MOVEncryptionIndex *encryption_index; } cenc; } MOVStreamContext; diff --git a/chromium/third_party/ffmpeg/libavformat/ivfenc.c b/chromium/third_party/ffmpeg/libavformat/ivfenc.c index f591327a218..af803d59ee8 100644 --- a/chromium/third_party/ffmpeg/libavformat/ivfenc.c +++ b/chromium/third_party/ffmpeg/libavformat/ivfenc.c @@ -37,8 +37,9 @@ static int ivf_write_header(AVFormatContext *s) } par = s->streams[0]->codecpar; if (par->codec_type != AVMEDIA_TYPE_VIDEO || - !(par->codec_id == AV_CODEC_ID_VP8 || par->codec_id == AV_CODEC_ID_VP9 || - par->codec_id == AV_CODEC_ID_AV1)) { + !(par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP8 || + par->codec_id == AV_CODEC_ID_VP9)) { av_log(s, AV_LOG_ERROR, "Currently only VP8, VP9 and AV1 are supported!\n"); return AVERROR(EINVAL); } diff --git a/chromium/third_party/ffmpeg/libavformat/libopenmpt.c b/chromium/third_party/ffmpeg/libavformat/libopenmpt.c index 30c3d6e646f..0fff702a36f 100644 --- a/chromium/third_party/ffmpeg/libavformat/libopenmpt.c +++ b/chromium/third_party/ffmpeg/libavformat/libopenmpt.c @@ -218,6 +218,62 @@ static int read_seek_openmpt(AVFormatContext *s, int stream_idx, int64_t ts, int return 0; } +static int probe_openmpt_extension(AVProbeData *p) +{ + const char *ext; + if (p->filename) { + ext = strrchr(p->filename, '.'); + if (ext && strlen(ext + 1) > 0) { + ext++; /* skip '.' */ + if (openmpt_is_extension_supported(ext) == 1) + return AVPROBE_SCORE_EXTENSION; + } + } + return 0; +} + +static int read_probe_openmpt(AVProbeData *p) +{ +#if OPENMPT_API_VERSION_AT_LEAST(0,3,0) + int probe_result; + if (p->buf && p->buf_size > 0) { + probe_result = openmpt_probe_file_header_without_filesize( + OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, + p->buf, p->buf_size, + &openmpt_logfunc, NULL, NULL, NULL, NULL, NULL); + if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS) { + /* As probing here relies on code external to FFmpeg, do not return + * AVPROBE_SCORE_MAX in order to reduce the impact in the rare + * cases of false positives. + */ + return AVPROBE_SCORE_MIME + 1; + } else if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA) { + if (probe_openmpt_extension(p) > 0) { + return AVPROBE_SCORE_RETRY; + } else { + if (p->buf_size >= openmpt_probe_file_header_get_recommended_size()) { + /* We have already received the recommended amount of data + * and still cannot decide. Return a rather low score. + */ + return AVPROBE_SCORE_RETRY / 2; + } else { + /* The file extension is unknown and we have very few data + * bytes available. libopenmpt cannot decide anything here, + * and returning any score > 0 would result in successfull + * probing of random data. + */ + return 0; + } + } + } else if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE) { + return 0; + } + } +#endif + /* for older libopenmpt, fall back to file extension probing */ + return probe_openmpt_extension(p); +} + static const AVClass class_openmpt = { .class_name = "libopenmpt", .item_name = av_default_item_name, @@ -229,10 +285,15 @@ AVInputFormat ff_libopenmpt_demuxer = { .name = "libopenmpt", .long_name = NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"), .priv_data_size = sizeof(OpenMPTContext), + .read_probe = read_probe_openmpt, .read_header = read_header_openmpt, .read_packet = read_packet_openmpt, .read_close = read_close_openmpt, .read_seek = read_seek_openmpt, .priv_class = &class_openmpt, - .extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,stk,stm,ult,umx,wow,xm,xpk", +#if OPENMPT_API_VERSION_AT_LEAST(0,3,0) + .extensions = "669,amf,ams,dbm,digi,dmf,dsm,dtm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,stp,ult,umx,wow,xm,xpk", +#else + .extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,ult,umx,wow,xm,xpk", +#endif }; diff --git a/chromium/third_party/ffmpeg/libavformat/libsrt.c b/chromium/third_party/ffmpeg/libavformat/libsrt.c new file mode 100644 index 00000000000..0f9529d263a --- /dev/null +++ b/chromium/third_party/ffmpeg/libavformat/libsrt.c @@ -0,0 +1,546 @@ +/* + * 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 + * Haivision Open SRT (Secure Reliable Transport) protocol + */ + +#include <srt/srt.h> + +#include "libavutil/avassert.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/time.h" + +#include "avformat.h" +#include "internal.h" +#include "network.h" +#include "os_support.h" +#include "url.h" + +enum SRTMode { + SRT_MODE_CALLER = 0, + SRT_MODE_LISTENER = 1, + SRT_MODE_RENDEZVOUS = 2 +}; + +typedef struct SRTContext { + const AVClass *class; + int fd; + int eid; + int64_t rw_timeout; + int64_t listen_timeout; + int recv_buffer_size; + int send_buffer_size; + + int64_t maxbw; + int pbkeylen; + char *passphrase; + int mss; + int ffs; + int ipttl; + int iptos; + int64_t inputbw; + int oheadbw; + int64_t tsbpddelay; + int tlpktdrop; + int nakreport; + int64_t connect_timeout; + enum SRTMode mode; +} SRTContext; + +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +#define OFFSET(x) offsetof(SRTContext, x) +static const AVOption libsrt_options[] = { + { "rw_timeout", "Timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "listen_timeout", "Connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { "maxbw", "Maximum bandwidth (bytes per second) that the connection can use", OFFSET(maxbw), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "pbkeylen", "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)", OFFSET(pbkeylen), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 32, .flags = D|E }, + { "passphrase", "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto", OFFSET(passphrase), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, + { "mss", "The Maximum Segment Size", OFFSET(mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1500, .flags = D|E }, + { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { "ipttl", "IP Time To Live", OFFSET(ipttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .flags = D|E }, + { "iptos", "IP Type of Service", OFFSET(iptos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .flags = D|E }, + { "inputbw", "Estimated input stream rate", OFFSET(inputbw), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "oheadbw", "MaxBW ceiling based on % over input stream rate", OFFSET(oheadbw), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 100, .flags = D|E }, + { "tsbpddelay", "TsbPd receiver delay to absorb burst of missed packet retransmission", OFFSET(tsbpddelay), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "tlpktdrop", "Enable receiver pkt drop", OFFSET(tlpktdrop), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, .flags = D|E }, + { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, .flags = D|E }, + { "connect_timeout", "Connect timeout. Caller default: 3000, rendezvous (x 10)", OFFSET(connect_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "mode", "Connection mode (caller, listener, rendezvous)", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = SRT_MODE_CALLER }, SRT_MODE_CALLER, SRT_MODE_RENDEZVOUS, .flags = D|E, "mode" }, + { "caller", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_CALLER }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, + { "listener", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_LISTENER }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, + { "rendezvous", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_RENDEZVOUS }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, + { NULL } +}; + +static int libsrt_neterrno(URLContext *h) +{ + int err = srt_getlasterror(NULL); + av_log(h, AV_LOG_ERROR, "%s\n", srt_getlasterror_str()); + if (err == SRT_EASYNCRCV) + return AVERROR(EAGAIN); + return AVERROR_UNKNOWN; +} + +static int libsrt_socket_nonblock(int socket, int enable) +{ + int ret = srt_setsockopt(socket, 0, SRTO_SNDSYN, &enable, sizeof(enable)); + if (ret < 0) + return ret; + return srt_setsockopt(socket, 0, SRTO_RCVSYN, &enable, sizeof(enable)); +} + +static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write) +{ + int ret, len = 1; + int modes = write ? SRT_EPOLL_OUT : SRT_EPOLL_IN; + SRTSOCKET ready[1]; + + if (srt_epoll_add_usock(eid, fd, &modes) < 0) + return libsrt_neterrno(h); + if (write) { + ret = srt_epoll_wait(eid, 0, 0, ready, &len, POLLING_TIME, 0, 0, 0, 0); + } else { + ret = srt_epoll_wait(eid, ready, &len, 0, 0, POLLING_TIME, 0, 0, 0, 0); + } + if (ret < 0) { + if (srt_getlasterror(NULL) == SRT_ETIMEOUT) + ret = AVERROR(EAGAIN); + else + ret = libsrt_neterrno(h); + } else { + ret = 0; + } + if (srt_epoll_remove_usock(eid, fd) < 0) + return libsrt_neterrno(h); + return ret; +} + +/* TODO de-duplicate code from ff_network_wait_fd_timeout() */ + +static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) +{ + int ret; + int64_t wait_start = 0; + + while (1) { + if (ff_check_interrupt(int_cb)) + return AVERROR_EXIT; + ret = libsrt_network_wait_fd(h, eid, fd, write); + if (ret != AVERROR(EAGAIN)) + return ret; + if (timeout > 0) { + if (!wait_start) + wait_start = av_gettime_relative(); + else if (av_gettime_relative() - wait_start > timeout) + return AVERROR(ETIMEDOUT); + } + } +} + +static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int timeout) +{ + int ret; + int reuse = 1; + if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) { + av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n"); + } + ret = srt_bind(fd, addr, addrlen); + if (ret) + return libsrt_neterrno(h); + + ret = srt_listen(fd, 1); + if (ret) + return libsrt_neterrno(h); + + while ((ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback))) { + switch (ret) { + case AVERROR(ETIMEDOUT): + continue; + default: + return ret; + } + } + + ret = srt_accept(fd, NULL, NULL); + if (ret < 0) + return libsrt_neterrno(h); + if (libsrt_socket_nonblock(ret, 1) < 0) + av_log(h, AV_LOG_DEBUG, "libsrt_socket_nonblock failed\n"); + + return ret; +} + +static int libsrt_listen_connect(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next) +{ + int ret; + + if (libsrt_socket_nonblock(fd, 1) < 0) + av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); + + while ((ret = srt_connect(fd, addr, addrlen))) { + ret = libsrt_neterrno(h); + switch (ret) { + case AVERROR(EINTR): + if (ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; + continue; + case AVERROR(EINPROGRESS): + case AVERROR(EAGAIN): + ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback); + if (ret < 0) + return ret; + ret = srt_getlasterror(NULL); + srt_clearlasterror(); + if (ret != 0) { + char buf[128]; + ret = AVERROR(ret); + av_strerror(ret, buf, sizeof(buf)); + if (will_try_next) + av_log(h, AV_LOG_WARNING, + "Connection to %s failed (%s), trying next address\n", + h->filename, buf); + else + av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", + h->filename, buf); + } + default: + return ret; + } + } + return ret; +} + +static int libsrt_setsockopt(URLContext *h, int fd, SRT_SOCKOPT optname, const char * optnamestr, const void * optval, int optlen) +{ + if (srt_setsockopt(fd, 0, optname, optval, optlen) < 0) { + av_log(h, AV_LOG_ERROR, "failed to set option %s on socket: %s\n", optnamestr, srt_getlasterror_str()); + return AVERROR(EIO); + } + return 0; +} + +/* - The "POST" options can be altered any time on a connected socket. + They MAY have also some meaning when set prior to connecting; such + option is SRTO_RCVSYN, which makes connect/accept call asynchronous. + Because of that this option is treated special way in this app. */ +static int libsrt_set_options_post(URLContext *h, int fd) +{ + SRTContext *s = h->priv_data; + + if ((s->inputbw >= 0 && libsrt_setsockopt(h, fd, SRTO_INPUTBW, "SRTO_INPUTBW", &s->inputbw, sizeof(s->inputbw)) < 0) || + (s->oheadbw >= 0 && libsrt_setsockopt(h, fd, SRTO_OHEADBW, "SRTO_OHEADBW", &s->oheadbw, sizeof(s->oheadbw)) < 0)) { + return AVERROR(EIO); + } + return 0; +} + +/* - The "PRE" options must be set prior to connecting and can't be altered + on a connected socket, however if set on a listening socket, they are + derived by accept-ed socket. */ +static int libsrt_set_options_pre(URLContext *h, int fd) +{ + SRTContext *s = h->priv_data; + int yes = 1; + int tsbpddelay = s->tsbpddelay / 1000; + int connect_timeout = s->connect_timeout; + + if ((s->mode == SRT_MODE_RENDEZVOUS && libsrt_setsockopt(h, fd, SRTO_RENDEZVOUS, "SRTO_RENDEZVOUS", &yes, sizeof(yes)) < 0) || + (s->maxbw >= 0 && libsrt_setsockopt(h, fd, SRTO_MAXBW, "SRTO_MAXBW", &s->maxbw, sizeof(s->maxbw)) < 0) || + (s->pbkeylen >= 0 && libsrt_setsockopt(h, fd, SRTO_PBKEYLEN, "SRTO_PBKEYLEN", &s->pbkeylen, sizeof(s->pbkeylen)) < 0) || + (s->passphrase && libsrt_setsockopt(h, fd, SRTO_PASSPHRASE, "SRTO_PASSPHRASE", &s->passphrase, sizeof(s->passphrase)) < 0) || + (s->mss >= 0 && libsrt_setsockopt(h, fd, SRTO_MSS, "SRTO_MMS", &s->mss, sizeof(s->mss)) < 0) || + (s->ffs >= 0 && libsrt_setsockopt(h, fd, SRTO_FC, "SRTO_FC", &s->ffs, sizeof(s->ffs)) < 0) || + (s->ipttl >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTTL, "SRTO_UPTTL", &s->ipttl, sizeof(s->ipttl)) < 0) || + (s->iptos >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTOS, "SRTO_IPTOS", &s->iptos, sizeof(s->iptos)) < 0) || + (tsbpddelay >= 0 && libsrt_setsockopt(h, fd, SRTO_TSBPDDELAY, "SRTO_TSBPDELAY", &tsbpddelay, sizeof(tsbpddelay)) < 0) || + (s->tlpktdrop >= 0 && libsrt_setsockopt(h, fd, SRTO_TLPKTDROP, "SRTO_TLPKDROP", &s->tlpktdrop, sizeof(s->tlpktdrop)) < 0) || + (s->nakreport >= 0 && libsrt_setsockopt(h, fd, SRTO_NAKREPORT, "SRTO_NAKREPORT", &s->nakreport, sizeof(s->nakreport)) < 0) || + (connect_timeout >= 0 && libsrt_setsockopt(h, fd, SRTO_CONNTIMEO, "SRTO_CONNTIMEO", &connect_timeout, sizeof(connect_timeout)) <0 )) { + return AVERROR(EIO); + } + return 0; +} + + +static int libsrt_setup(URLContext *h, const char *uri, int flags) +{ + struct addrinfo hints = { 0 }, *ai, *cur_ai; + int port, fd = -1; + SRTContext *s = h->priv_data; + const char *p; + char buf[256]; + int ret; + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; + int open_timeout = 5000000; + int eid; + + eid = srt_epoll_create(); + if (eid < 0) + return libsrt_neterrno(h); + s->eid = eid; + + av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), + &port, path, sizeof(path), uri); + if (strcmp(proto, "srt")) + return AVERROR(EINVAL); + if (port <= 0 || port >= 65536) { + av_log(h, AV_LOG_ERROR, "Port missing in uri\n"); + return AVERROR(EINVAL); + } + p = strchr(uri, '?'); + if (p) { + if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { + s->rw_timeout = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { + s->listen_timeout = strtol(buf, NULL, 10); + } + } + if (s->rw_timeout >= 0) { + open_timeout = h->rw_timeout = s->rw_timeout; + } + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + snprintf(portstr, sizeof(portstr), "%d", port); + if (s->mode == SRT_MODE_LISTENER) + hints.ai_flags |= AI_PASSIVE; + ret = getaddrinfo(hostname[0] ? hostname : NULL, portstr, &hints, &ai); + if (ret) { + av_log(h, AV_LOG_ERROR, + "Failed to resolve hostname %s: %s\n", + hostname, gai_strerror(ret)); + return AVERROR(EIO); + } + + cur_ai = ai; + + restart: + + fd = srt_socket(cur_ai->ai_family, cur_ai->ai_socktype, 0); + if (fd < 0) { + ret = libsrt_neterrno(h); + goto fail; + } + + if ((ret = libsrt_set_options_pre(h, fd)) < 0) { + goto fail; + } + + /* Set the socket's send or receive buffer sizes, if specified. + If unspecified or setting fails, system default is used. */ + if (s->recv_buffer_size > 0) { + srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size)); + } + if (s->send_buffer_size > 0) { + srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size)); + } + if (s->mode == SRT_MODE_LISTENER) { + // multi-client + if ((ret = libsrt_listen(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, open_timeout / 1000)) < 0) + goto fail1; + fd = ret; + } else { + if (s->mode == SRT_MODE_RENDEZVOUS) { + ret = srt_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); + if (ret) + goto fail1; + } + + if ((ret = libsrt_listen_connect(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, + open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) { + if (ret == AVERROR_EXIT) + goto fail1; + else + goto fail; + } + } + if ((ret = libsrt_set_options_post(h, fd)) < 0) { + goto fail; + } + + h->is_streamed = 1; + s->fd = fd; + + freeaddrinfo(ai); + return 0; + + fail: + if (cur_ai->ai_next) { + /* Retry with the next sockaddr */ + cur_ai = cur_ai->ai_next; + if (fd >= 0) + srt_close(fd); + ret = 0; + goto restart; + } + fail1: + if (fd >= 0) + srt_close(fd); + freeaddrinfo(ai); + return ret; +} + +static int libsrt_open(URLContext *h, const char *uri, int flags) +{ + SRTContext *s = h->priv_data; + const char * p; + char buf[256]; + + if (srt_startup() < 0) { + return AVERROR_UNKNOWN; + } + + /* SRT options (srt/srt.h) */ + p = strchr(uri, '?'); + if (p) { + if (av_find_info_tag(buf, sizeof(buf), "maxbw", p)) { + s->maxbw = strtoll(buf, NULL, 0); + } + if (av_find_info_tag(buf, sizeof(buf), "pbkeylen", p)) { + s->pbkeylen = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) { + s->passphrase = av_strndup(buf, strlen(buf)); + } + if (av_find_info_tag(buf, sizeof(buf), "mss", p)) { + s->mss = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "ffs", p)) { + s->ffs = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "ipttl", p)) { + s->ipttl = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "iptos", p)) { + s->iptos = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "inputbw", p)) { + s->inputbw = strtoll(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "oheadbw", p)) { + s->oheadbw = strtoll(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "tsbpddelay", p)) { + s->tsbpddelay = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "tlpktdrop", p)) { + s->tlpktdrop = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "nakreport", p)) { + s->nakreport = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "connect_timeout", p)) { + s->connect_timeout = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "mode", p)) { + if (!strcmp(buf, "caller")) { + s->mode = SRT_MODE_CALLER; + } else if (!strcmp(buf, "listener")) { + s->mode = SRT_MODE_LISTENER; + } else if (!strcmp(buf, "rendezvous")) { + s->mode = SRT_MODE_RENDEZVOUS; + } else { + return AVERROR(EIO); + } + } + } + return libsrt_setup(h, uri, flags); +} + +static int libsrt_read(URLContext *h, uint8_t *buf, int size) +{ + SRTContext *s = h->priv_data; + int ret; + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 0, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + } + + ret = srt_recvmsg(s->fd, buf, size); + if (ret < 0) { + ret = libsrt_neterrno(h); + } + + return ret; +} + +static int libsrt_write(URLContext *h, const uint8_t *buf, int size) +{ + SRTContext *s = h->priv_data; + int ret; + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = libsrt_network_wait_fd_timeout(h, s->eid, s->fd, 1, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + } + + ret = srt_sendmsg(s->fd, buf, size, -1, 0); + if (ret < 0) { + ret = libsrt_neterrno(h); + } + + return ret; +} + +static int libsrt_close(URLContext *h) +{ + SRTContext *s = h->priv_data; + + srt_close(s->fd); + + srt_epoll_release(s->eid); + + srt_cleanup(); + + return 0; +} + +static int libsrt_get_file_handle(URLContext *h) +{ + SRTContext *s = h->priv_data; + return s->fd; +} + +static const AVClass libsrt_class = { + .class_name = "libsrt", + .item_name = av_default_item_name, + .option = libsrt_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const URLProtocol ff_libsrt_protocol = { + .name = "srt", + .url_open = libsrt_open, + .url_read = libsrt_read, + .url_write = libsrt_write, + .url_close = libsrt_close, + .url_get_file_handle = libsrt_get_file_handle, + .priv_data_size = sizeof(SRTContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, + .priv_data_class = &libsrt_class, +}; diff --git a/chromium/third_party/ffmpeg/libavformat/matroskadec.c b/chromium/third_party/ffmpeg/libavformat/matroskadec.c index 222fa046b22..5b8dba254eb 100644 --- a/chromium/third_party/ffmpeg/libavformat/matroskadec.c +++ b/chromium/third_party/ffmpeg/libavformat/matroskadec.c @@ -108,6 +108,7 @@ typedef struct EbmlList { typedef struct EbmlBin { int size; + AVBufferRef *buf; uint8_t *data; int64_t pos; } EbmlBin; @@ -342,9 +343,8 @@ typedef struct MatroskaDemuxContext { int64_t segment_start; /* the packet queue */ - AVPacket **packets; - int num_packets; - AVPacket *prev_pkt; + AVPacketList *queue; + AVPacketList *queue_end; int done; @@ -967,14 +967,19 @@ static int ebml_read_ascii(AVIOContext *pb, int size, char **str) */ static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin) { - av_fast_padded_malloc(&bin->data, &bin->size, length); - if (!bin->data) - return AVERROR(ENOMEM); + int ret; + + ret = av_buffer_realloc(&bin->buf, length + AV_INPUT_BUFFER_PADDING_SIZE); + if (ret < 0) + return ret; + memset(bin->buf->data + length, 0, AV_INPUT_BUFFER_PADDING_SIZE); + bin->data = bin->buf->data; bin->size = length; bin->pos = avio_tell(pb); if (avio_read(pb, bin->data, length) != length) { - av_freep(&bin->data); + av_buffer_unref(&bin->buf); + bin->data = NULL; bin->size = 0; return AVERROR(EIO); } @@ -1257,7 +1262,7 @@ static void ebml_free(EbmlSyntax *syntax, void *data) av_freep(data_off); break; case EBML_BIN: - av_freep(&((EbmlBin *) data_off)->data); + av_buffer_unref(&((EbmlBin *) data_off)->buf); break; case EBML_LEVEL1: case EBML_NEST: @@ -1366,7 +1371,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, return 0; pkt_size = isize + header_size; - pkt_data = av_malloc(pkt_size); + pkt_data = av_malloc(pkt_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!pkt_data) return AVERROR(ENOMEM); @@ -1378,7 +1383,8 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, case MATROSKA_TRACK_ENCODING_COMP_LZO: do { olen = pkt_size *= 3; - newpktdata = av_realloc(pkt_data, pkt_size + AV_LZO_OUTPUT_PADDING); + newpktdata = av_realloc(pkt_data, pkt_size + AV_LZO_OUTPUT_PADDING + + AV_INPUT_BUFFER_PADDING_SIZE); if (!newpktdata) { result = AVERROR(ENOMEM); goto failed; @@ -1403,7 +1409,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, zstream.avail_in = isize; do { pkt_size *= 3; - newpktdata = av_realloc(pkt_data, pkt_size); + newpktdata = av_realloc(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!newpktdata) { inflateEnd(&zstream); result = AVERROR(ENOMEM); @@ -1436,7 +1442,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, bzstream.avail_in = isize; do { pkt_size *= 3; - newpktdata = av_realloc(pkt_data, pkt_size); + newpktdata = av_realloc(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!newpktdata) { BZ2_bzDecompressEnd(&bzstream); result = AVERROR(ENOMEM); @@ -1463,6 +1469,8 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, return AVERROR_INVALIDDATA; } + memset(pkt_data + pkt_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + *buf = pkt_data; *buf_size = pkt_size; return 0; @@ -2041,12 +2049,13 @@ static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID * * by expanding/shifting the data by 4 bytes and storing the data * size at the start. */ if (ff_codec_get_id(codec_tags, AV_RL32(track->codec_priv.data))) { - uint8_t *p = av_realloc(track->codec_priv.data, - track->codec_priv.size + 4); - if (!p) - return AVERROR(ENOMEM); - memmove(p + 4, p, track->codec_priv.size); - track->codec_priv.data = p; + int ret = av_buffer_realloc(&track->codec_priv.buf, + track->codec_priv.size + 4 + AV_INPUT_BUFFER_PADDING_SIZE); + if (ret < 0) + return ret; + + track->codec_priv.data = track->codec_priv.buf->data; + memmove(track->codec_priv.data + 4, track->codec_priv.data, track->codec_priv.size); track->codec_priv.size += 4; AV_WB32(track->codec_priv.data, track->codec_priv.size); } @@ -2167,8 +2176,19 @@ static int matroska_parse_tracks(AVFormatContext *s) "Failed to decode codec private data\n"); } - if (codec_priv != track->codec_priv.data) - av_free(codec_priv); + if (codec_priv != track->codec_priv.data) { + av_buffer_unref(&track->codec_priv.buf); + if (track->codec_priv.data) { + track->codec_priv.buf = av_buffer_create(track->codec_priv.data, + track->codec_priv.size + AV_INPUT_BUFFER_PADDING_SIZE, + NULL, NULL, 0); + if (!track->codec_priv.buf) { + av_freep(&track->codec_priv.data); + track->codec_priv.size = 0; + return AVERROR(ENOMEM); + } + } + } } } @@ -2730,11 +2750,11 @@ fail: static int matroska_deliver_packet(MatroskaDemuxContext *matroska, AVPacket *pkt) { - if (matroska->num_packets > 0) { + if (matroska->queue) { MatroskaTrack *tracks = matroska->tracks.elem; MatroskaTrack *track; - memcpy(pkt, matroska->packets[0], sizeof(AVPacket)); - av_freep(&matroska->packets[0]); + + ff_packet_list_get(&matroska->queue, &matroska->queue_end, pkt); track = &tracks[pkt->stream_index]; if (track->has_palette) { uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); @@ -2745,20 +2765,6 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska, } track->has_palette = 0; } - if (matroska->num_packets > 1) { - void *newpackets; - memmove(&matroska->packets[0], &matroska->packets[1], - (matroska->num_packets - 1) * sizeof(AVPacket *)); - newpackets = av_realloc(matroska->packets, - (matroska->num_packets - 1) * - sizeof(AVPacket *)); - if (newpackets) - matroska->packets = newpackets; - } else { - av_freep(&matroska->packets); - matroska->prev_pkt = NULL; - } - matroska->num_packets--; return 0; } @@ -2770,16 +2776,7 @@ static int matroska_deliver_packet(MatroskaDemuxContext *matroska, */ static void matroska_clear_queue(MatroskaDemuxContext *matroska) { - matroska->prev_pkt = NULL; - if (matroska->packets) { - int n; - for (n = 0; n < matroska->num_packets; n++) { - av_packet_unref(matroska->packets[n]); - av_freep(&matroska->packets[n]); - } - av_freep(&matroska->packets); - matroska->num_packets = 0; - } + ff_packet_list_free(&matroska->queue, &matroska->queue_end); } static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, @@ -2950,13 +2947,10 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, while (track->audio.pkt_cnt) { int ret; - AVPacket *pkt = av_mallocz(sizeof(AVPacket)); - if (!pkt) - return AVERROR(ENOMEM); + AVPacket pktl, *pkt = &pktl; ret = av_new_packet(pkt, a); if (ret < 0) { - av_free(pkt); return ret; } memcpy(pkt->data, @@ -2966,7 +2960,11 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, track->audio.buf_timecode = AV_NOPTS_VALUE; pkt->pos = pos; pkt->stream_index = st->index; - dynarray_add(&matroska->packets, &matroska->num_packets, pkt); + ret = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0); + if (ret < 0) { + av_packet_unref(pkt); + return AVERROR(ENOMEM); + } } return 0; @@ -3019,7 +3017,7 @@ static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, goto fail; } - tmp = av_realloc(dst, dstlen + blocksize + 32); + tmp = av_realloc(dst, dstlen + blocksize + 32 + AV_INPUT_BUFFER_PADDING_SIZE); if (!tmp) { ret = AVERROR(ENOMEM); goto fail; @@ -3043,6 +3041,8 @@ static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, offset += blocksize + 32; } + memset(dst + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); + *pdst = dst; *size = dstlen; @@ -3053,6 +3053,30 @@ fail: return ret; } +static int matroska_parse_prores(MatroskaTrack *track, uint8_t *src, + uint8_t **pdst, int *size) +{ + uint8_t *dst = src; + int dstlen = *size; + + if (AV_RB32(&src[4]) != MKBETAG('i', 'c', 'p', 'f')) { + dst = av_malloc(dstlen + 8 + AV_INPUT_BUFFER_PADDING_SIZE); + if (!dst) + return AVERROR(ENOMEM); + + AV_WB32(dst, dstlen); + AV_WB32(dst + 4, MKBETAG('i', 'c', 'p', 'f')); + memcpy(dst + 8, src, dstlen); + memset(dst + 8 + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); + dstlen += 8; + } + + *pdst = dst; + *size = dstlen; + + return 0; +} + static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, @@ -3061,7 +3085,7 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, uint64_t duration, int64_t pos) { - AVPacket *pkt; + AVPacket pktl, *pkt = &pktl; uint8_t *id, *settings, *text, *buf; int id_len, settings_len, text_len; uint8_t *p, *q; @@ -3118,12 +3142,8 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, if (text_len <= 0) return AVERROR_INVALIDDATA; - pkt = av_mallocz(sizeof(*pkt)); - if (!pkt) - return AVERROR(ENOMEM); err = av_new_packet(pkt, text_len); if (err < 0) { - av_free(pkt); return err; } @@ -3135,7 +3155,6 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, id_len); if (!buf) { av_packet_unref(pkt); - av_free(pkt); return AVERROR(ENOMEM); } memcpy(buf, id, id_len); @@ -3147,7 +3166,6 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, settings_len); if (!buf) { av_packet_unref(pkt); - av_free(pkt); return AVERROR(ENOMEM); } memcpy(buf, settings, settings_len); @@ -3165,15 +3183,18 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, pkt->duration = duration; pkt->pos = pos; - dynarray_add(&matroska->packets, &matroska->num_packets, pkt); - matroska->prev_pkt = pkt; + err = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0); + if (err < 0) { + av_packet_unref(pkt); + return AVERROR(ENOMEM); + } return 0; } static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, - uint8_t *data, int pkt_size, + AVBufferRef *buf, uint8_t *data, int pkt_size, uint64_t timecode, uint64_t lace_duration, int64_t pos, int is_keyframe, uint8_t *additional, uint64_t additional_id, int additional_size, @@ -3181,8 +3202,8 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, { MatroskaTrackEncoding *encodings = track->encodings.elem; uint8_t *pkt_data = data; - int offset = 0, res; - AVPacket *pkt; + int res; + AVPacket pktl, *pkt = &pktl; if (encodings && !encodings->type && encodings->scope & 1) { res = matroska_decode_buffer(&pkt_data, &pkt_size, track); @@ -3203,34 +3224,33 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, pkt_data = wv_data; } - if (st->codecpar->codec_id == AV_CODEC_ID_PRORES && - AV_RB32(&data[4]) != MKBETAG('i', 'c', 'p', 'f')) - offset = 8; - - pkt = av_mallocz(sizeof(AVPacket)); - if (!pkt) { + if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) { + uint8_t *pr_data; + res = matroska_parse_prores(track, pkt_data, &pr_data, &pkt_size); + if (res < 0) { + av_log(matroska->ctx, AV_LOG_ERROR, + "Error parsing a prores block.\n"); + goto fail; + } if (pkt_data != data) av_freep(&pkt_data); - return AVERROR(ENOMEM); + pkt_data = pr_data; } - /* XXX: prevent data copy... */ - if (av_new_packet(pkt, pkt_size + offset) < 0) { - av_free(pkt); + + av_init_packet(pkt); + if (pkt_data != data) + pkt->buf = av_buffer_create(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE, + NULL, NULL, 0); + else + pkt->buf = av_buffer_ref(buf); + + if (!pkt->buf) { res = AVERROR(ENOMEM); goto fail; } - if (st->codecpar->codec_id == AV_CODEC_ID_PRORES && offset == 8) { - uint8_t *buf = pkt->data; - bytestream_put_be32(&buf, pkt_size); - bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f')); - } - - memcpy(pkt->data + offset, pkt_data, pkt_size); - - if (pkt_data != data) - av_freep(&pkt_data); - + pkt->data = pkt_data; + pkt->size = pkt_size; pkt->flags = is_keyframe; pkt->stream_index = st->index; @@ -3240,7 +3260,6 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, additional_size + 8); if (!side_data) { av_packet_unref(pkt); - av_free(pkt); return AVERROR(ENOMEM); } AV_WB64(side_data, additional_id); @@ -3253,7 +3272,6 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, 10); if (!side_data) { av_packet_unref(pkt); - av_free(pkt); return AVERROR(ENOMEM); } discard_padding = av_rescale_q(discard_padding, @@ -3281,8 +3299,11 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif - dynarray_add(&matroska->packets, &matroska->num_packets, pkt); - matroska->prev_pkt = pkt; + res = ff_packet_list_put(&matroska->queue, &matroska->queue_end, pkt, 0); + if (res < 0) { + av_packet_unref(pkt); + return AVERROR(ENOMEM); + } return 0; @@ -3292,7 +3313,7 @@ fail: return res; } -static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, +static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf, uint8_t *data, int size, int64_t pos, uint64_t cluster_time, uint64_t block_duration, int is_keyframe, uint8_t *additional, uint64_t additional_id, int additional_size, @@ -3414,7 +3435,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, if (res) goto end; } else { - res = matroska_parse_frame(matroska, track, st, data, lace_size[n], + res = matroska_parse_frame(matroska, track, st, buf, data, lace_size[n], timecode, lace_duration, pos, !n ? is_keyframe : 0, additional, additional_id, additional_size, @@ -3450,7 +3471,6 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska) memset(&matroska->current_cluster, 0, sizeof(MatroskaCluster)); matroska->current_cluster_num_blocks = 0; matroska->current_cluster_pos = avio_tell(matroska->ctx->pb); - matroska->prev_pkt = NULL; /* sizeof the ID which was already read */ if (matroska->current_id) matroska->current_cluster_pos -= 4; @@ -3478,7 +3498,7 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska) blocks[i].additional.data : NULL; if (!blocks[i].non_simple) blocks[i].duration = 0; - res = matroska_parse_block(matroska, blocks[i].bin.data, + res = matroska_parse_block(matroska, blocks[i].bin.buf, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, matroska->current_cluster.timecode, blocks[i].duration, is_keyframe, @@ -3503,7 +3523,6 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) if (!matroska->contains_ssa) return matroska_parse_cluster_incremental(matroska); pos = avio_tell(matroska->ctx->pb); - matroska->prev_pkt = NULL; if (matroska->current_id) pos -= 4; /* sizeof the ID which was already read */ res = ebml_parse(matroska, matroska_clusters, &cluster); @@ -3512,7 +3531,7 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) for (i = 0; i < blocks_list->nb_elem; i++) if (blocks[i].bin.size > 0 && blocks[i].bin.data) { int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1; - res = matroska_parse_block(matroska, blocks[i].bin.data, + res = matroska_parse_block(matroska, blocks[i].bin.buf, blocks[i].bin.data, blocks[i].bin.size, blocks[i].bin.pos, cluster.timecode, blocks[i].duration, is_keyframe, NULL, 0, 0, pos, @@ -3688,10 +3707,10 @@ static int webm_clusters_start_with_keyframe(AVFormatContext *s) matroska->current_id = 0; matroska_clear_queue(matroska); if (matroska_parse_cluster(matroska) < 0 || - matroska->num_packets <= 0) { + !matroska->queue) { break; } - pkt = matroska->packets[0]; + pkt = &matroska->queue->pkt; cluster_pos += cluster_length + 12; // 12 is the offset of the cluster id and length. if (!(pkt->flags & AV_PKT_FLAG_KEY)) { rv = 0; diff --git a/chromium/third_party/ffmpeg/libavformat/mov.c b/chromium/third_party/ffmpeg/libavformat/mov.c index abac7fe9bc4..64ed293a7c3 100644 --- a/chromium/third_party/ffmpeg/libavformat/mov.c +++ b/chromium/third_party/ffmpeg/libavformat/mov.c @@ -412,7 +412,11 @@ retry: int ret = mov_read_covr(c, pb, data_type, str_size); if (ret < 0) { av_log(c->fc, AV_LOG_ERROR, "Error parsing cover art.\n"); + return ret; } + atom.size -= str_size; + if (atom.size > 8) + goto retry; return ret; } else if (!key && c->found_hdlr_mdta && c->meta_keys) { uint32_t index = AV_RB32(&atom.type); @@ -1326,6 +1330,7 @@ static int update_frag_index(MOVContext *c, int64_t offset) frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE; frag_stream_info[i].first_tfra_pts = AV_NOPTS_VALUE; frag_stream_info[i].index_entry = -1; + frag_stream_info[i].encryption_index = NULL; } if (index < c->frag_index.nb_items) @@ -2614,6 +2619,12 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_finalize_stsd_codec(c, pb, st, sc); fail: + if (sc->extradata) { + int j; + for (j = 0; j < sc->stsd_count; j++) + av_freep(&sc->extradata[j]); + } + av_freep(&sc->extradata); av_freep(&sc->extradata_size); return ret; @@ -3282,7 +3293,7 @@ static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, uns FFMAX(min_size_needed, 2 * (*allocated_size)) : min_size_needed; - if((unsigned)(*ctts_count) + 1 >= UINT_MAX / sizeof(MOVStts)) + if((unsigned)(*ctts_count) >= UINT_MAX / sizeof(MOVStts) - 1) return -1; ctts_buf_new = av_fast_realloc(*ctts_data, allocated_size, requested_size); @@ -4623,7 +4634,7 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; } sc = st->priv_data; - if (sc->pseudo_stream_id + 1 != frag->stsd_id) + if (sc->pseudo_stream_id + 1 != frag->stsd_id && sc->pseudo_stream_id != -1) return 0; version = avio_r8(pb); avio_rb24(pb); /* flags */ @@ -5781,115 +5792,475 @@ static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } -static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom) +/** + * Gets the current encryption info and associated current stream context. If + * we are parsing a track fragment, this will return the specific encryption + * info for this fragment; otherwise this will return the global encryption + * info for the current stream. + */ +static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encryption_index, MOVStreamContext **sc) { + MOVFragmentStreamInfo *frag_stream_info; AVStream *st; - MOVStreamContext *sc; - size_t auxiliary_info_size; + int i; - if (c->decryption_key_len == 0 || c->fc->nb_streams < 1) - return 0; + frag_stream_info = get_current_frag_stream_info(&c->frag_index); + if (frag_stream_info) { + for (i = 0; i < c->fc->nb_streams; i++) { + if (c->fc->streams[i]->id == frag_stream_info->id) { + st = c->fc->streams[i]; + break; + } + } + if (i == c->fc->nb_streams) + return 0; + *sc = st->priv_data; - st = c->fc->streams[c->fc->nb_streams - 1]; - sc = st->priv_data; + if (!frag_stream_info->encryption_index) { + frag_stream_info->encryption_index = av_mallocz(sizeof(*frag_stream_info->encryption_index)); + if (!frag_stream_info->encryption_index) + return AVERROR(ENOMEM); + } + *encryption_index = frag_stream_info->encryption_index; + return 1; + } else { + // No current track fragment, using stream level encryption info. - if (sc->cenc.aes_ctr) { - av_log(c->fc, AV_LOG_ERROR, "duplicate senc atom\n"); - return AVERROR_INVALIDDATA; + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams - 1]; + *sc = st->priv_data; + + if (!(*sc)->cenc.encryption_index) { + (*sc)->cenc.encryption_index = av_mallocz(sizeof(*frag_stream_info->encryption_index)); + if (!(*sc)->cenc.encryption_index) + return AVERROR(ENOMEM); + } + + *encryption_index = (*sc)->cenc.encryption_index; + return 1; + } +} + +static int mov_read_sample_encryption_info(MOVContext *c, AVIOContext *pb, MOVStreamContext *sc, AVEncryptionInfo **sample, int use_subsamples) +{ + int i; + unsigned int subsample_count; + AVSubsampleEncryptionInfo *subsamples; + + *sample = av_encryption_info_clone(sc->cenc.default_encrypted_sample); + if (!*sample) + return AVERROR(ENOMEM); + + if (sc->cenc.per_sample_iv_size != 0) { + if (avio_read(pb, (*sample)->iv, sc->cenc.per_sample_iv_size) != sc->cenc.per_sample_iv_size) { + av_log(c->fc, AV_LOG_ERROR, "failed to read the initialization vector\n"); + av_encryption_info_free(*sample); + *sample = NULL; + return AVERROR_INVALIDDATA; + } + } + + if (use_subsamples) { + subsample_count = avio_rb16(pb); + av_free((*sample)->subsamples); + (*sample)->subsamples = av_mallocz_array(subsample_count, sizeof(*subsamples)); + if (!(*sample)->subsamples) { + av_encryption_info_free(*sample); + *sample = NULL; + return AVERROR(ENOMEM); + } + + for (i = 0; i < subsample_count && !pb->eof_reached; i++) { + (*sample)->subsamples[i].bytes_of_clear_data = avio_rb16(pb); + (*sample)->subsamples[i].bytes_of_protected_data = avio_rb32(pb); + } + + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "hit EOF while reading sub-sample encryption info\n"); + av_encryption_info_free(*sample); + *sample = NULL; + return AVERROR_INVALIDDATA; + } + (*sample)->subsample_count = subsample_count; + } + + return 0; +} + +static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVEncryptionInfo **encrypted_samples; + MOVEncryptionIndex *encryption_index; + MOVStreamContext *sc; + int use_subsamples, ret; + unsigned int sample_count, i, alloc_size = 0; + + ret = get_current_encryption_info(c, &encryption_index, &sc); + if (ret != 1) + return ret; + + if (encryption_index->nb_encrypted_samples) { + // This can happen if we have both saio/saiz and senc atoms. + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate encryption info in senc\n"); + return 0; } avio_r8(pb); /* version */ - sc->cenc.use_subsamples = avio_rb24(pb) & 0x02; /* flags */ + use_subsamples = avio_rb24(pb) & 0x02; /* flags */ - avio_rb32(pb); /* entries */ + sample_count = avio_rb32(pb); + if (sample_count >= INT_MAX / sizeof(*encrypted_samples)) + return AVERROR(ENOMEM); - if (atom.size < 8 || atom.size > FFMIN(INT_MAX, SIZE_MAX)) { - av_log(c->fc, AV_LOG_ERROR, "senc atom size %"PRId64" invalid\n", atom.size); - return AVERROR_INVALIDDATA; + for (i = 0; i < sample_count; i++) { + unsigned int min_samples = FFMIN(FFMAX(i + 1, 1024 * 1024), sample_count); + encrypted_samples = av_fast_realloc(encryption_index->encrypted_samples, &alloc_size, + min_samples * sizeof(*encrypted_samples)); + if (encrypted_samples) { + encryption_index->encrypted_samples = encrypted_samples; + + ret = mov_read_sample_encryption_info( + c, pb, sc, &encryption_index->encrypted_samples[i], use_subsamples); + } else { + ret = AVERROR(ENOMEM); + } + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "Hit EOF while reading senc\n"); + ret = AVERROR_INVALIDDATA; + } + + if (ret < 0) { + for (; i > 0; i--) + av_encryption_info_free(encryption_index->encrypted_samples[i - 1]); + av_freep(&encryption_index->encrypted_samples); + return ret; + } } + encryption_index->nb_encrypted_samples = sample_count; - /* save the auxiliary info as is */ - auxiliary_info_size = atom.size - 8; + return 0; +} - sc->cenc.auxiliary_info = av_malloc(auxiliary_info_size); - if (!sc->cenc.auxiliary_info) { +static int mov_parse_auxiliary_info(MOVContext *c, MOVStreamContext *sc, AVIOContext *pb, MOVEncryptionIndex *encryption_index) +{ + AVEncryptionInfo **sample, **encrypted_samples; + int64_t prev_pos; + size_t sample_count, sample_info_size, i; + int ret = 0; + unsigned int alloc_size = 0; + + if (encryption_index->nb_encrypted_samples) + return 0; + sample_count = encryption_index->auxiliary_info_sample_count; + if (encryption_index->auxiliary_offsets_count != 1) { + av_log(c->fc, AV_LOG_ERROR, "Multiple auxiliary info chunks are not supported\n"); + return AVERROR_PATCHWELCOME; + } + if (sample_count >= INT_MAX / sizeof(*encrypted_samples)) return AVERROR(ENOMEM); + + prev_pos = avio_tell(pb); + if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || + avio_seek(pb, encryption_index->auxiliary_offsets[0], SEEK_SET) != encryption_index->auxiliary_offsets[0]) { + av_log(c->fc, AV_LOG_INFO, "Failed to seek for auxiliary info, will only parse senc atoms for encryption info\n"); + goto finish; } - sc->cenc.auxiliary_info_end = sc->cenc.auxiliary_info + auxiliary_info_size; - sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info; - sc->cenc.auxiliary_info_index = 0; + for (i = 0; i < sample_count && !pb->eof_reached; i++) { + unsigned int min_samples = FFMIN(FFMAX(i + 1, 1024 * 1024), sample_count); + encrypted_samples = av_fast_realloc(encryption_index->encrypted_samples, &alloc_size, + min_samples * sizeof(*encrypted_samples)); + if (!encrypted_samples) { + ret = AVERROR(ENOMEM); + goto finish; + } + encryption_index->encrypted_samples = encrypted_samples; - if (avio_read(pb, sc->cenc.auxiliary_info, auxiliary_info_size) != auxiliary_info_size) { - av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info"); - return AVERROR_INVALIDDATA; + sample = &encryption_index->encrypted_samples[i]; + sample_info_size = encryption_index->auxiliary_info_default_size + ? encryption_index->auxiliary_info_default_size + : encryption_index->auxiliary_info_sizes[i]; + + ret = mov_read_sample_encryption_info(c, pb, sc, sample, sample_info_size > sc->cenc.per_sample_iv_size); + if (ret < 0) + goto finish; + } + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "Hit EOF while reading auxiliary info\n"); + ret = AVERROR_INVALIDDATA; + } else { + encryption_index->nb_encrypted_samples = sample_count; } - /* initialize the cipher */ - sc->cenc.aes_ctr = av_aes_ctr_alloc(); - if (!sc->cenc.aes_ctr) { - return AVERROR(ENOMEM); +finish: + avio_seek(pb, prev_pos, SEEK_SET); + if (ret < 0) { + for (; i > 0; i--) { + av_encryption_info_free(encryption_index->encrypted_samples[i - 1]); + } + av_freep(&encryption_index->encrypted_samples); + } + return ret; +} + +/** + * Tries to read the given number of bytes from the stream and puts it in a + * newly allocated buffer. This reads in small chunks to avoid allocating large + * memory if the file contains an invalid/malicious size value. + */ +static int mov_try_read_block(AVIOContext *pb, size_t size, uint8_t **data) +{ + const unsigned int block_size = 1024 * 1024; + uint8_t *buffer = NULL; + unsigned int alloc_size = 0, offset = 0; + while (offset < size) { + unsigned int new_size = + alloc_size >= INT_MAX - block_size ? INT_MAX : alloc_size + block_size; + uint8_t *new_buffer = av_fast_realloc(buffer, &alloc_size, new_size); + unsigned int to_read = FFMIN(size, alloc_size) - offset; + if (!new_buffer) { + av_free(buffer); + return AVERROR(ENOMEM); + } + buffer = new_buffer; + + if (avio_read(pb, buffer + offset, to_read) != to_read) { + av_free(buffer); + return AVERROR_INVALIDDATA; + } + offset += to_read; } - return av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key); + *data = buffer; + return 0; } static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - AVStream *st; + MOVEncryptionIndex *encryption_index; MOVStreamContext *sc; - size_t data_size; - int atom_header_size; - int flags; + int ret; + unsigned int sample_count; - if (c->decryption_key_len == 0 || c->fc->nb_streams < 1) - return 0; + ret = get_current_encryption_info(c, &encryption_index, &sc); + if (ret != 1) + return ret; - st = c->fc->streams[c->fc->nb_streams - 1]; - sc = st->priv_data; + if (encryption_index->nb_encrypted_samples) { + // This can happen if we have both saio/saiz and senc atoms. + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate encryption info in saiz\n"); + return 0; + } - if (sc->cenc.auxiliary_info_sizes || sc->cenc.auxiliary_info_default_size) { - av_log(c->fc, AV_LOG_ERROR, "duplicate saiz atom\n"); + if (encryption_index->auxiliary_info_sample_count) { + av_log(c->fc, AV_LOG_ERROR, "Duplicate saiz atom\n"); return AVERROR_INVALIDDATA; } - atom_header_size = 9; - avio_r8(pb); /* version */ - flags = avio_rb24(pb); + if (avio_rb24(pb) & 0x01) { /* flags */ + if (avio_rb32(pb) != sc->cenc.default_encrypted_sample->scheme) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saiz box with non-zero aux_info_type\n"); + return 0; + } + if (avio_rb32(pb) != 0) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saiz box with non-zero aux_info_type_parameter\n"); + return 0; + } + } - if ((flags & 0x01) != 0) { - atom_header_size += 8; + encryption_index->auxiliary_info_default_size = avio_r8(pb); + sample_count = avio_rb32(pb); + encryption_index->auxiliary_info_sample_count = sample_count; - avio_rb32(pb); /* info type */ - avio_rb32(pb); /* info type param */ + if (encryption_index->auxiliary_info_default_size == 0) { + ret = mov_try_read_block(pb, sample_count, &encryption_index->auxiliary_info_sizes); + if (ret < 0) { + av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info\n"); + return ret; + } } - sc->cenc.auxiliary_info_default_size = avio_r8(pb); - avio_rb32(pb); /* entries */ + if (encryption_index->auxiliary_offsets_count) { + return mov_parse_auxiliary_info(c, sc, pb, encryption_index); + } + + return 0; +} - if (atom.size <= atom_header_size) { +static int mov_read_saio(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + uint64_t *auxiliary_offsets; + MOVEncryptionIndex *encryption_index; + MOVStreamContext *sc; + int i, ret; + unsigned int version, entry_count, alloc_size = 0; + + ret = get_current_encryption_info(c, &encryption_index, &sc); + if (ret != 1) + return ret; + + if (encryption_index->nb_encrypted_samples) { + // This can happen if we have both saio/saiz and senc atoms. + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate encryption info in saio\n"); return 0; } - if (atom.size > FFMIN(INT_MAX, SIZE_MAX)) { - av_log(c->fc, AV_LOG_ERROR, "saiz atom auxiliary_info_sizes size %"PRId64" invalid\n", atom.size); + if (encryption_index->auxiliary_offsets_count) { + av_log(c->fc, AV_LOG_ERROR, "Duplicate saio atom\n"); return AVERROR_INVALIDDATA; } - /* save the auxiliary info sizes as is */ - data_size = atom.size - atom_header_size; + version = avio_r8(pb); /* version */ + if (avio_rb24(pb) & 0x01) { /* flags */ + if (avio_rb32(pb) != sc->cenc.default_encrypted_sample->scheme) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saio box with non-zero aux_info_type\n"); + return 0; + } + if (avio_rb32(pb) != 0) { + av_log(c->fc, AV_LOG_DEBUG, "Ignoring saio box with non-zero aux_info_type_parameter\n"); + return 0; + } + } - sc->cenc.auxiliary_info_sizes = av_malloc(data_size); - if (!sc->cenc.auxiliary_info_sizes) { + entry_count = avio_rb32(pb); + if (entry_count >= INT_MAX / sizeof(*auxiliary_offsets)) return AVERROR(ENOMEM); + + for (i = 0; i < entry_count && !pb->eof_reached; i++) { + unsigned int min_offsets = FFMIN(FFMAX(i + 1, 1024), entry_count); + auxiliary_offsets = av_fast_realloc( + encryption_index->auxiliary_offsets, &alloc_size, + min_offsets * sizeof(*auxiliary_offsets)); + if (!auxiliary_offsets) { + av_freep(&encryption_index->auxiliary_offsets); + return AVERROR(ENOMEM); + } + encryption_index->auxiliary_offsets = auxiliary_offsets; + + if (version == 0) { + encryption_index->auxiliary_offsets[i] = avio_rb32(pb); + } else { + encryption_index->auxiliary_offsets[i] = avio_rb64(pb); + } + if (c->frag_index.current >= 0) { + encryption_index->auxiliary_offsets[i] += c->fragment.base_data_offset; + } + } + + if (pb->eof_reached) { + av_log(c->fc, AV_LOG_ERROR, "Hit EOF while reading saio\n"); + av_freep(&encryption_index->auxiliary_offsets); + return AVERROR_INVALIDDATA; } - sc->cenc.auxiliary_info_sizes_count = data_size; + encryption_index->auxiliary_offsets_count = entry_count; - if (avio_read(pb, sc->cenc.auxiliary_info_sizes, data_size) != data_size) { - av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info sizes"); + if (encryption_index->auxiliary_info_sample_count) { + return mov_parse_auxiliary_info(c, sc, pb, encryption_index); + } + + return 0; +} + +static int mov_read_schm(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + if (sc->pseudo_stream_id != 0) { + av_log(c->fc, AV_LOG_ERROR, "schm boxes are only supported in first sample descriptor\n"); + return AVERROR_PATCHWELCOME; + } + + if (atom.size < 8) return AVERROR_INVALIDDATA; + + avio_rb32(pb); /* version and flags */ + + if (!sc->cenc.default_encrypted_sample) { + sc->cenc.default_encrypted_sample = av_encryption_info_alloc(0, 16, 16); + if (!sc->cenc.default_encrypted_sample) { + return AVERROR(ENOMEM); + } + } + + sc->cenc.default_encrypted_sample->scheme = avio_rb32(pb); + return 0; +} + +static int mov_read_tenc(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + unsigned int version, pattern, is_protected, iv_size; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + sc = st->priv_data; + + if (sc->pseudo_stream_id != 0) { + av_log(c->fc, AV_LOG_ERROR, "tenc atom are only supported in first sample descriptor\n"); + return AVERROR_PATCHWELCOME; + } + + if (!sc->cenc.default_encrypted_sample) { + sc->cenc.default_encrypted_sample = av_encryption_info_alloc(0, 16, 16); + if (!sc->cenc.default_encrypted_sample) { + return AVERROR(ENOMEM); + } + } + + if (atom.size < 20) + return AVERROR_INVALIDDATA; + + version = avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + + avio_r8(pb); /* reserved */ + pattern = avio_r8(pb); + + if (version > 0) { + sc->cenc.default_encrypted_sample->crypt_byte_block = pattern >> 4; + sc->cenc.default_encrypted_sample->skip_byte_block = pattern & 0xf; + } + + is_protected = avio_r8(pb); + if (is_protected && !sc->cenc.encryption_index) { + // The whole stream should be by-default encrypted. + sc->cenc.encryption_index = av_mallocz(sizeof(MOVEncryptionIndex)); + if (!sc->cenc.encryption_index) + return AVERROR(ENOMEM); + } + sc->cenc.per_sample_iv_size = avio_r8(pb); + if (sc->cenc.per_sample_iv_size != 0 && sc->cenc.per_sample_iv_size != 8 && + sc->cenc.per_sample_iv_size != 16) { + av_log(c->fc, AV_LOG_ERROR, "invalid per-sample IV size value\n"); + return AVERROR_INVALIDDATA; + } + if (avio_read(pb, sc->cenc.default_encrypted_sample->key_id, 16) != 16) { + av_log(c->fc, AV_LOG_ERROR, "failed to read the default key ID\n"); + return AVERROR_INVALIDDATA; + } + + if (is_protected && !sc->cenc.per_sample_iv_size) { + iv_size = avio_r8(pb); + if (iv_size != 8 && iv_size != 16) { + av_log(c->fc, AV_LOG_ERROR, "invalid default_constant_IV_size in tenc atom\n"); + return AVERROR_INVALIDDATA; + } + + if (avio_read(pb, sc->cenc.default_encrypted_sample->iv, iv_size) != iv_size) { + av_log(c->fc, AV_LOG_ERROR, "failed to read the default IV\n"); + return AVERROR_INVALIDDATA; + } } return 0; @@ -5932,108 +6303,114 @@ static int mov_read_dfla(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } -static int mov_seek_auxiliary_info(MOVContext *c, MOVStreamContext *sc, int64_t index) +static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size) { - size_t auxiliary_info_seek_offset = 0; - int i; - - if (sc->cenc.auxiliary_info_default_size) { - auxiliary_info_seek_offset = (size_t)sc->cenc.auxiliary_info_default_size * index; - } else if (sc->cenc.auxiliary_info_sizes) { - if (index > sc->cenc.auxiliary_info_sizes_count) { - av_log(c, AV_LOG_ERROR, "current sample %"PRId64" greater than the number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n", - index, sc->cenc.auxiliary_info_sizes_count); - return AVERROR_INVALIDDATA; - } - - for (i = 0; i < index; i++) { - auxiliary_info_seek_offset += sc->cenc.auxiliary_info_sizes[i]; - } - } + int i, ret; - if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info) { - av_log(c, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER" greater than auxiliary info size %"SIZE_SPECIFIER"\n", - auxiliary_info_seek_offset, (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info)); - return AVERROR_INVALIDDATA; + if (sample->scheme != MKBETAG('c','e','n','c') || sample->crypt_byte_block != 0 || sample->skip_byte_block != 0) { + av_log(c->fc, AV_LOG_ERROR, "Only the 'cenc' encryption scheme is supported\n"); + return AVERROR_PATCHWELCOME; } - sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info + auxiliary_info_seek_offset; - sc->cenc.auxiliary_info_index = index; - return 0; -} - -static int cenc_filter(MOVContext *c, MOVStreamContext *sc, int64_t index, uint8_t *input, int size) -{ - uint32_t encrypted_bytes; - uint16_t subsample_count; - uint16_t clear_bytes; - uint8_t* input_end = input + size; - int ret; + if (!sc->cenc.aes_ctr) { + /* initialize the cipher */ + sc->cenc.aes_ctr = av_aes_ctr_alloc(); + if (!sc->cenc.aes_ctr) { + return AVERROR(ENOMEM); + } - if (index != sc->cenc.auxiliary_info_index) { - ret = mov_seek_auxiliary_info(c, sc, index); + ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key); if (ret < 0) { return ret; } } - /* read the iv */ - if (AES_CTR_IV_SIZE > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) { - av_log(c->fc, AV_LOG_ERROR, "failed to read iv from the auxiliary info\n"); - return AVERROR_INVALIDDATA; - } - - av_aes_ctr_set_iv(sc->cenc.aes_ctr, sc->cenc.auxiliary_info_pos); - sc->cenc.auxiliary_info_pos += AES_CTR_IV_SIZE; + av_aes_ctr_set_full_iv(sc->cenc.aes_ctr, sample->iv); - if (!sc->cenc.use_subsamples) + if (!sample->subsample_count) { /* decrypt the whole packet */ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size); return 0; } - /* read the subsample count */ - if (sizeof(uint16_t) > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) { - av_log(c->fc, AV_LOG_ERROR, "failed to read subsample count from the auxiliary info\n"); - return AVERROR_INVALIDDATA; - } - - subsample_count = AV_RB16(sc->cenc.auxiliary_info_pos); - sc->cenc.auxiliary_info_pos += sizeof(uint16_t); - - for (; subsample_count > 0; subsample_count--) + for (i = 0; i < sample->subsample_count; i++) { - if (6 > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) { - av_log(c->fc, AV_LOG_ERROR, "failed to read subsample from the auxiliary info\n"); - return AVERROR_INVALIDDATA; - } - - /* read the number of clear / encrypted bytes */ - clear_bytes = AV_RB16(sc->cenc.auxiliary_info_pos); - sc->cenc.auxiliary_info_pos += sizeof(uint16_t); - encrypted_bytes = AV_RB32(sc->cenc.auxiliary_info_pos); - sc->cenc.auxiliary_info_pos += sizeof(uint32_t); - - if ((uint64_t)clear_bytes + encrypted_bytes > input_end - input) { + if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) { av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n"); return AVERROR_INVALIDDATA; } /* skip the clear bytes */ - input += clear_bytes; + input += sample->subsamples[i].bytes_of_clear_data; + size -= sample->subsamples[i].bytes_of_clear_data; /* decrypt the encrypted bytes */ - av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, encrypted_bytes); - input += encrypted_bytes; + av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, sample->subsamples[i].bytes_of_protected_data); + input += sample->subsamples[i].bytes_of_protected_data; + size -= sample->subsamples[i].bytes_of_protected_data; } - if (input < input_end) { + if (size > 0) { av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n"); return AVERROR_INVALIDDATA; } - sc->cenc.auxiliary_info_index++; + return 0; +} + +static int cenc_filter(MOVContext *mov, MOVStreamContext *sc, AVPacket *pkt, int current_index) +{ + MOVFragmentStreamInfo *frag_stream_info; + MOVEncryptionIndex *encryption_index; + AVEncryptionInfo *encrypted_sample; + int encrypted_index; + + frag_stream_info = get_current_frag_stream_info(&mov->frag_index); + encrypted_index = current_index; + encryption_index = NULL; + if (frag_stream_info) { + // Note this only supports encryption info in the first sample descriptor. + if (mov->fragment.stsd_id == 1) { + if (frag_stream_info->encryption_index) { + encrypted_index = current_index - frag_stream_info->index_entry; + encryption_index = frag_stream_info->encryption_index; + } else { + encryption_index = sc->cenc.encryption_index; + } + } + } else { + encryption_index = sc->cenc.encryption_index; + } + + if (encryption_index) { + if (encryption_index->auxiliary_info_sample_count && + !encryption_index->nb_encrypted_samples) { + av_log(mov->fc, AV_LOG_ERROR, "saiz atom found without saio\n"); + return AVERROR_INVALIDDATA; + } + if (encryption_index->auxiliary_offsets_count && + !encryption_index->nb_encrypted_samples) { + av_log(mov->fc, AV_LOG_ERROR, "saio atom found without saiz\n"); + return AVERROR_INVALIDDATA; + } + + if (!encryption_index->nb_encrypted_samples) { + // Full-sample encryption with default settings. + encrypted_sample = sc->cenc.default_encrypted_sample; + } else if (encrypted_index >= 0 && encrypted_index < encryption_index->nb_encrypted_samples) { + // Per-sample setting override. + encrypted_sample = encryption_index->encrypted_samples[encrypted_index]; + } else { + av_log(mov->fc, AV_LOG_ERROR, "Incorrect number of samples in encryption info\n"); + return AVERROR_INVALIDDATA; + } + + if (mov->decryption_key) { + return cenc_decrypt(mov, sc, encrypted_sample, pkt->data, pkt->size); + } + } + return 0; } @@ -6163,6 +6540,10 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('f','r','m','a'), mov_read_frma }, { MKTAG('s','e','n','c'), mov_read_senc }, { MKTAG('s','a','i','z'), mov_read_saiz }, +{ MKTAG('s','a','i','o'), mov_read_saio }, +{ MKTAG('s','c','h','m'), mov_read_schm }, +{ MKTAG('s','c','h','i'), mov_read_default }, +{ MKTAG('t','e','n','c'), mov_read_tenc }, { MKTAG('d','f','L','a'), mov_read_dfla }, { MKTAG('s','t','3','d'), mov_read_st3d }, /* stereoscopic 3D video box */ { MKTAG('s','v','3','d'), mov_read_sv3d }, /* spherical video box */ @@ -6548,6 +6929,18 @@ static int mov_read_timecode_track(AVFormatContext *s, AVStream *st) return 0; } +static void mov_free_encryption_index(MOVEncryptionIndex **index) { + int i; + if (!index || !*index) return; + for (i = 0; i < (*index)->nb_encrypted_samples; i++) { + av_encryption_info_free((*index)->encrypted_samples[i]); + } + av_freep(&(*index)->encrypted_samples); + av_freep(&(*index)->auxiliary_info_sizes); + av_freep(&(*index)->auxiliary_offsets); + av_freep(index); +} + static int mov_read_close(AVFormatContext *s) { MOVContext *mov = s->priv_data; @@ -6590,8 +6983,8 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->extradata); av_freep(&sc->extradata_size); - av_freep(&sc->cenc.auxiliary_info); - av_freep(&sc->cenc.auxiliary_info_sizes); + mov_free_encryption_index(&sc->cenc.encryption_index); + av_encryption_info_free(sc->cenc.default_encrypted_sample); av_aes_ctr_free(sc->cenc.aes_ctr); av_freep(&sc->stereo3d); @@ -6616,6 +7009,10 @@ static int mov_read_close(AVFormatContext *s) av_freep(&mov->bitrates); for (i = 0; i < mov->frag_index.nb_items; i++) { + MOVFragmentStreamInfo *frag = mov->frag_index.item[i].stream_info; + for (j = 0; j < mov->frag_index.item[i].nb_stream_info; j++) { + mov_free_encryption_index(&frag[j].encryption_index); + } av_freep(&mov->frag_index.item[i].stream_info); } av_freep(&mov->frag_index.item); @@ -7186,12 +7583,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) if (mov->aax_mode) aax_filter(pkt->data, pkt->size, mov); - if (sc->cenc.aes_ctr) { - ret = cenc_filter(mov, sc, current_index, pkt->data, pkt->size); - if (ret) { - return ret; - } - } + ret = cenc_filter(mov, sc, pkt, current_index); + if (ret < 0) + return ret; return 0; } diff --git a/chromium/third_party/ffmpeg/libavformat/movenc.c b/chromium/third_party/ffmpeg/libavformat/movenc.c index 5b1e66c8973..0b44fd66ea1 100644 --- a/chromium/third_party/ffmpeg/libavformat/movenc.c +++ b/chromium/third_party/ffmpeg/libavformat/movenc.c @@ -140,6 +140,15 @@ static int co64_required(const MOVTrack *track) return 0; } +static int rtp_hinting_needed(const AVStream *st) +{ + /* Add hint tracks for each real audio and video stream */ + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) + return 0; + return st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || + st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO; +} + /* Chunk offset atom */ static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track) { @@ -491,10 +500,7 @@ concatenate: if (info->num_blocks != 6) goto end; av_packet_unref(pkt); - ret = av_packet_ref(pkt, &info->pkt); - if (ret < 0) - goto end; - av_packet_unref(&info->pkt); + av_packet_move_ref(pkt, &info->pkt); info->num_blocks = 0; } ret = pkt->size; @@ -3027,7 +3033,7 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (ret < 0) return ret; - if (mov->mode & MODE_MP4) + if (mov->mode & (MODE_MP4|MODE_MOV)) mov_write_track_metadata(pb_buf, st, "name", "title"); if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { @@ -3416,6 +3422,51 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb, return size; } +static int mov_write_covr(AVIOContext *pb, AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + int64_t pos = 0; + int i, type; + + for (i = 0; i < s->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + AVStream *st = s->streams[i]; + + if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC) || + trk->cover_image.size <= 0) + continue; + + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MJPEG: + type = 0xD; + break; + case AV_CODEC_ID_PNG: + type = 0xE; + break; + case AV_CODEC_ID_BMP: + type = 0x1B; + break; + default: + av_log(s, AV_LOG_ERROR, "unsupported codec_id (0x%x) for cover", + st->codecpar->codec_id); + continue; + } + + if (!pos) { + pos = avio_tell(pb); + avio_wb32(pb, 0); + ffio_wfourcc(pb, "covr"); + } + avio_wb32(pb, 16 + trk->cover_image.size); + ffio_wfourcc(pb, "data"); + avio_wb32(pb, type); + avio_wb32(pb , 0); + avio_write(pb, trk->cover_image.data, trk->cover_image.size); + } + + return pos ? update_size(pb, pos) : 0; +} + /* iTunes meta data list */ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) @@ -3435,7 +3486,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, } mov_write_string_metadata(s, pb, "\251cmt", "comment" , 1); mov_write_string_metadata(s, pb, "\251gen", "genre" , 1); - mov_write_string_metadata(s, pb, "\251cpy", "copyright", 1); + mov_write_string_metadata(s, pb, "cprt", "copyright", 1); mov_write_string_metadata(s, pb, "\251grp", "grouping" , 1); mov_write_string_metadata(s, pb, "\251lyr", "lyrics" , 1); mov_write_string_metadata(s, pb, "desc", "description",1); @@ -3450,6 +3501,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1); mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1); mov_write_int8_metadata (s, pb, "cpil", "compilation", 1); + mov_write_covr(pb, s); mov_write_trkn_tag(pb, mov, s, 0); // track number mov_write_trkn_tag(pb, mov, s, 1); // disc number mov_write_tmpo_tag(pb, s); @@ -3947,6 +3999,8 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat } else { continue; } + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) + continue; props = (AVCPBProperties*)av_stream_get_side_data(track->st, AV_PKT_DATA_CPB_PROPERTIES, NULL); @@ -4560,6 +4614,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) + continue; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) has_video = 1; if (st->codecpar->codec_id == AV_CODEC_ID_H264) @@ -4708,6 +4764,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s) int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) + continue; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) video_streams_nb++; else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) @@ -4897,7 +4955,8 @@ static int mov_flush_fragment(AVFormatContext *s, int force) int buf_size, moov_size; for (i = 0; i < mov->nb_streams; i++) - if (!mov->tracks[i].entry) + if (!mov->tracks[i].entry && + (i >= s->nb_streams || !(s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC))) break; /* Don't write the initial moov unless all tracks have data */ if (i < mov->nb_streams && !force) @@ -5476,13 +5535,34 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s, static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) { + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk; + AVStream *st; + if (!pkt) { mov_flush_fragment(s, 1); return 1; + } + + st = s->streams[pkt->stream_index]; + trk = &mov->tracks[pkt->stream_index]; + + if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) { + int ret; + + if (st->nb_frames >= 1) { + if (st->nb_frames == 1) + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + return 0; + } + + if ((ret = av_packet_ref(&trk->cover_image, pkt)) < 0) + return ret; + + return 0; } else { int i; - MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; if (!pkt->size) return mov_write_single_packet(s, pkt); /* Passthrough. */ @@ -5729,7 +5809,8 @@ static void enable_tracks(AVFormatContext *s) AVStream *st = s->streams[i]; if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN || - st->codecpar->codec_type >= AVMEDIA_TYPE_NB) + st->codecpar->codec_type >= AVMEDIA_TYPE_NB || + st->disposition & AV_DISPOSITION_ATTACHED_PIC) continue; if (first[st->codecpar->codec_type] < 0) @@ -5772,6 +5853,7 @@ static void mov_free(AVFormatContext *s) av_freep(&mov->tracks[i].par); av_freep(&mov->tracks[i].cluster); av_freep(&mov->tracks[i].frag_info); + av_packet_unref(&mov->tracks[i].cover_image); if (mov->tracks[i].vos_len) av_freep(&mov->tracks[i].vos_data); @@ -5944,14 +6026,9 @@ static int mov_init(AVFormatContext *s) mov->chapter_track = mov->nb_streams++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Add hint tracks for each audio and video stream */ - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + for (i = 0; i < s->nb_streams; i++) + if (rtp_hinting_needed(s->streams[i])) mov->nb_streams++; - } - } } if ( mov->write_tmcd == -1 && (mov->mode == MODE_MOV || mov->mode == MODE_MP4) @@ -6082,6 +6159,10 @@ static int mov_init(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "VP9 only supported in MP4.\n"); return AVERROR(EINVAL); } + } else if (track->par->codec_id == AV_CODEC_ID_AV1) { + /* spec is not finished, so forbid for now */ + av_log(s, AV_LOG_ERROR, "AV1 muxing is currently not supported.\n"); + return AVERROR_PATCHWELCOME; } else if (track->par->codec_id == AV_CODEC_ID_VP8) { /* altref frames handling is not defined in the spec as of version v1.0, * so just forbid muxing VP8 streams altogether until a new version does */ @@ -6174,15 +6255,10 @@ static int mov_write_header(AVFormatContext *s) nb_tracks++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Add hint tracks for each audio and video stream */ hint_track = nb_tracks; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + for (i = 0; i < s->nb_streams; i++) + if (rtp_hinting_needed(s->streams[i])) nb_tracks++; - } - } } if (mov->mode == MODE_MOV || mov->mode == MODE_MP4) @@ -6260,11 +6336,8 @@ static int mov_write_header(AVFormatContext *s) return ret; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Initialize the hint tracks for each audio and video stream */ for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (rtp_hinting_needed(s->streams[i])) { if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0) return ret; hint_track++; diff --git a/chromium/third_party/ffmpeg/libavformat/movenc.h b/chromium/third_party/ffmpeg/libavformat/movenc.h index ca2a9c97226..c9b4072fb9d 100644 --- a/chromium/third_party/ffmpeg/libavformat/movenc.h +++ b/chromium/third_party/ffmpeg/libavformat/movenc.h @@ -132,6 +132,7 @@ typedef struct MOVTrack { uint32_t default_size; HintSampleQueue sample_queue; + AVPacket cover_image; AVIOContext *mdat_buf; int64_t data_offset; diff --git a/chromium/third_party/ffmpeg/libavformat/mp3enc.c b/chromium/third_party/ffmpeg/libavformat/mp3enc.c index 8479e2485bb..dd662f5473c 100644 --- a/chromium/third_party/ffmpeg/libavformat/mp3enc.c +++ b/chromium/third_party/ffmpeg/libavformat/mp3enc.c @@ -369,20 +369,18 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt) static int mp3_queue_flush(AVFormatContext *s) { MP3Context *mp3 = s->priv_data; - AVPacketList *pktl; + AVPacket pkt; int ret = 0, write = 1; ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding); mp3_write_xing(s); - while ((pktl = mp3->queue)) { - if (write && (ret = mp3_write_audio_packet(s, &pktl->pkt)) < 0) + while (mp3->queue) { + ff_packet_list_get(&mp3->queue, &mp3->queue_end, &pkt); + if (write && (ret = mp3_write_audio_packet(s, &pkt)) < 0) write = 0; - av_packet_unref(&pktl->pkt); - mp3->queue = pktl->next; - av_freep(&pktl); + av_packet_unref(&pkt); } - mp3->queue_end = NULL; return ret; } @@ -514,21 +512,14 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) if (pkt->stream_index == mp3->audio_stream_idx) { if (mp3->pics_to_write) { /* buffer audio packets until we get all the pictures */ - AVPacketList *pktl = av_mallocz(sizeof(*pktl)); + int ret = ff_packet_list_put(&mp3->queue, &mp3->queue_end, pkt, FF_PACKETLIST_FLAG_REF_PACKET); - if (!pktl || av_packet_ref(&pktl->pkt, pkt) < 0) { - av_freep(&pktl); + if (ret < 0) { av_log(s, AV_LOG_WARNING, "Not enough memory to buffer audio. Skipping picture streams\n"); mp3->pics_to_write = 0; mp3_queue_flush(s); return mp3_write_audio_packet(s, pkt); } - - if (mp3->queue_end) - mp3->queue_end->next = pktl; - else - mp3->queue = pktl; - mp3->queue_end = pktl; } else return mp3_write_audio_packet(s, pkt); } else { diff --git a/chromium/third_party/ffmpeg/libavformat/mpc8.c b/chromium/third_party/ffmpeg/libavformat/mpc8.c index f280faa5ccd..79e5f6a9ab6 100644 --- a/chromium/third_party/ffmpeg/libavformat/mpc8.c +++ b/chromium/third_party/ffmpeg/libavformat/mpc8.c @@ -297,7 +297,7 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } if(tag == TAG_STREAMEND) - return AVERROR(EIO); + return AVERROR_EOF; mpc8_handle_chunk(s, tag, pos, size); } return AVERROR_EOF; diff --git a/chromium/third_party/ffmpeg/libavformat/mpeg.c b/chromium/third_party/ffmpeg/libavformat/mpeg.c index abdc6a937cb..8ae4740920b 100644 --- a/chromium/third_party/ffmpeg/libavformat/mpeg.c +++ b/chromium/third_party/ffmpeg/libavformat/mpeg.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "mpeg.h" @@ -128,6 +129,7 @@ typedef struct MpegDemuxContext { int sofdec; int dvd; int imkh_cctv; + int raw_ac3; #if CONFIG_VOBSUB_DEMUXER AVFormatContext *sub_ctx; FFDemuxSubtitlesQueue q[32]; @@ -442,8 +444,24 @@ redo: } if (startcode == PRIVATE_STREAM_1) { + int ret = ffio_ensure_seekback(s->pb, 2); + + if (ret < 0) + return ret; + startcode = avio_r8(s->pb); - len--; + m->raw_ac3 = 0; + if (startcode == 0x0b) { + if (avio_r8(s->pb) == 0x77) { + startcode = 0x80; + m->raw_ac3 = 1; + avio_skip(s->pb, -2); + } else { + avio_skip(s->pb, -1); + } + } else { + len--; + } } if (len < 0) goto error_redo; @@ -486,14 +504,16 @@ redo: if (len < 4) goto skip; - /* audio: skip header */ - avio_r8(s->pb); - lpcm_header_len = avio_rb16(s->pb); - len -= 3; - if (startcode >= 0xb0 && startcode <= 0xbf) { - /* MLP/TrueHD audio has a 4-byte header */ + if (!m->raw_ac3) { + /* audio: skip header */ avio_r8(s->pb); - len--; + lpcm_header_len = avio_rb16(s->pb); + len -= 3; + if (startcode >= 0xb0 && startcode <= 0xbf) { + /* MLP/TrueHD audio has a 4-byte header */ + avio_r8(s->pb); + len--; + } } } @@ -568,7 +588,7 @@ redo: codec_id = AV_CODEC_ID_DTS; } else if (startcode >= 0xa0 && startcode <= 0xaf) { type = AVMEDIA_TYPE_AUDIO; - if (lpcm_header_len == 6 || startcode == 0xa1) { + if (lpcm_header_len >= 6 && startcode == 0xa1) { codec_id = AV_CODEC_ID_MLP; } else { codec_id = AV_CODEC_ID_PCM_DVD; diff --git a/chromium/third_party/ffmpeg/libavformat/options_table.h b/chromium/third_party/ffmpeg/libavformat/options_table.h index b8fa47c6fd3..7c4d84798e1 100644 --- a/chromium/third_party/ffmpeg/libavformat/options_table.h +++ b/chromium/third_party/ffmpeg/libavformat/options_table.h @@ -54,10 +54,10 @@ static const AVOption avformat_options[] = { {"fastseek", "fast but inaccurate seeks", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FAST_SEEK }, INT_MIN, INT_MAX, D, "fflags"}, {"latm", "enable RTP MP4A-LATM payload", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"}, {"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, -{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D}, {"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "fflags" }, {"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" }, {"autobsf", "add needed bsfs automatically", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, "fflags" }, +{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D}, {"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D}, {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D}, diff --git a/chromium/third_party/ffmpeg/libavformat/os_support.c b/chromium/third_party/ffmpeg/libavformat/os_support.c index 099d7b501f4..15cea7fa5b0 100644 --- a/chromium/third_party/ffmpeg/libavformat/os_support.c +++ b/chromium/third_party/ffmpeg/libavformat/os_support.c @@ -43,6 +43,7 @@ #include "network.h" +#if !HAVE_GETADDRINFO #if !HAVE_INET_ATON #include <stdlib.h> @@ -62,7 +63,6 @@ static int inet_aton(const char *str, struct in_addr *add) } #endif /* !HAVE_INET_ATON */ -#if !HAVE_GETADDRINFO int ff_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { diff --git a/chromium/third_party/ffmpeg/libavformat/pcm.c b/chromium/third_party/ffmpeg/libavformat/pcm.c index 806f91b6b13..767bbd045a9 100644 --- a/chromium/third_party/ffmpeg/libavformat/pcm.c +++ b/chromium/third_party/ffmpeg/libavformat/pcm.c @@ -28,13 +28,20 @@ int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt) { + AVCodecParameters *par = s->streams[0]->codecpar; int ret, size; - size= RAW_SAMPLES*s->streams[0]->codecpar->block_align; - if (size <= 0) + if (par->block_align <= 0) return AVERROR(EINVAL); - ret= av_get_packet(s->pb, pkt, size); + /* + * Compute read size to complete a read every 62ms. + * Clamp to RAW_SAMPLES if larger. + */ + size = FFMAX(par->sample_rate/25, 1); + size = FFMIN(size, RAW_SAMPLES) * par->block_align; + + ret = av_get_packet(s->pb, pkt, size); pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; diff --git a/chromium/third_party/ffmpeg/libavformat/protocols.c b/chromium/third_party/ffmpeg/libavformat/protocols.c index 669d74d5a86..ad95659795e 100644 --- a/chromium/third_party/ffmpeg/libavformat/protocols.c +++ b/chromium/third_party/ffmpeg/libavformat/protocols.c @@ -65,6 +65,7 @@ extern const URLProtocol ff_librtmpe_protocol; extern const URLProtocol ff_librtmps_protocol; extern const URLProtocol ff_librtmpt_protocol; extern const URLProtocol ff_librtmpte_protocol; +extern const URLProtocol ff_libsrt_protocol; extern const URLProtocol ff_libssh_protocol; extern const URLProtocol ff_libsmbclient_protocol; diff --git a/chromium/third_party/ffmpeg/libavformat/rmdec.c b/chromium/third_party/ffmpeg/libavformat/rmdec.c index fea71a2fc97..ac61723c66a 100644 --- a/chromium/third_party/ffmpeg/libavformat/rmdec.c +++ b/chromium/third_party/ffmpeg/libavformat/rmdec.c @@ -70,16 +70,10 @@ static int rm_read_close(AVFormatContext *s); static inline void get_strl(AVIOContext *pb, char *buf, int buf_size, int len) { - int i; - char *q, r; + int read = avio_get_str(pb, len, buf, buf_size); - q = buf; - for(i=0;i<len;i++) { - r = avio_r8(pb); - if (i < buf_size - 1) - *q++ = r; - } - if (buf_size > 0) *q = '\0'; + if (read > 0) + avio_skip(pb, len - read); } static void get_str8(AVIOContext *pb, char *buf, int buf_size) @@ -105,8 +99,10 @@ static void rm_read_metadata(AVFormatContext *s, AVIOContext *pb, int wide) for (i=0; i<FF_ARRAY_ELEMS(ff_rm_metadata); i++) { int len = wide ? avio_rb16(pb) : avio_r8(pb); - get_strl(pb, buf, sizeof(buf), len); - av_dict_set(&s->metadata, ff_rm_metadata[i], buf, 0); + if (len > 0) { + get_strl(pb, buf, sizeof(buf), len); + av_dict_set(&s->metadata, ff_rm_metadata[i], buf, 0); + } } } diff --git a/chromium/third_party/ffmpeg/libavformat/rtpenc_chain.c b/chromium/third_party/ffmpeg/libavformat/rtpenc_chain.c index f768fb002eb..e69fdc27cfb 100644 --- a/chromium/third_party/ffmpeg/libavformat/rtpenc_chain.c +++ b/chromium/third_party/ffmpeg/libavformat/rtpenc_chain.c @@ -101,7 +101,7 @@ int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s, return 0; fail: - av_free(rtpctx); + avformat_free_context(rtpctx); if (handle) ffurl_close(handle); return ret; diff --git a/chromium/third_party/ffmpeg/libavformat/segafilm.c b/chromium/third_party/ffmpeg/libavformat/segafilm.c index 1fdef50cc75..b0c6c419ced 100644 --- a/chromium/third_party/ffmpeg/libavformat/segafilm.c +++ b/chromium/third_party/ffmpeg/libavformat/segafilm.c @@ -239,7 +239,7 @@ static int film_read_header(AVFormatContext *s) } else { film->sample_table[i].stream = film->video_stream_index; film->sample_table[i].pts = AV_RB32(&scratch[8]) & 0x7FFFFFFF; - film->sample_table[i].keyframe = (scratch[8] & 0x80) ? 0 : 1; + film->sample_table[i].keyframe = (scratch[8] & 0x80) ? 0 : AVINDEX_KEYFRAME; video_frame_counter++; if (film->video_type) av_add_index_entry(s->streams[film->video_stream_index], @@ -270,6 +270,8 @@ static int film_read_packet(AVFormatContext *s, FilmDemuxContext *film = s->priv_data; AVIOContext *pb = s->pb; film_sample *sample; + film_sample *next_sample = NULL; + int next_sample_id; int ret = 0; if (film->current_sample >= film->sample_count) @@ -277,6 +279,20 @@ static int film_read_packet(AVFormatContext *s, sample = &film->sample_table[film->current_sample]; + /* Find the next sample from the same stream, assuming there is one; + * this is used to calculate the duration below */ + next_sample_id = film->current_sample + 1; + while (next_sample == NULL) { + if (next_sample_id >= film->sample_count) + break; + + next_sample = &film->sample_table[next_sample_id]; + if (next_sample->stream != sample->stream) { + next_sample = NULL; + next_sample_id++; + } + } + /* position the stream (will probably be there anyway) */ avio_seek(pb, sample->sample_offset, SEEK_SET); @@ -285,7 +301,11 @@ static int film_read_packet(AVFormatContext *s, ret = AVERROR(EIO); pkt->stream_index = sample->stream; + pkt->dts = sample->pts; pkt->pts = sample->pts; + pkt->flags |= sample->keyframe ? AV_PKT_FLAG_KEY : 0; + if (next_sample != NULL) + pkt->duration = next_sample->pts - sample->pts; film->current_sample++; diff --git a/chromium/third_party/ffmpeg/libavformat/segafilmenc.c b/chromium/third_party/ffmpeg/libavformat/segafilmenc.c new file mode 100644 index 00000000000..5b0d7e69e87 --- /dev/null +++ b/chromium/third_party/ffmpeg/libavformat/segafilmenc.c @@ -0,0 +1,398 @@ +/* + * Sega FILM Format (CPK) Muxer + * Copyright (C) 2003 The FFmpeg project + * Copyright (C) 2018 Misty De Meo + * + * 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 + * Sega FILM (.cpk) file muxer + * @author Misty De Meo <misty@brew.sh> + * + * @see For more information regarding the Sega FILM file format, visit: + * http://wiki.multimedia.cx/index.php?title=Sega_FILM + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "avio_internal.h" + +typedef struct FILMPacket { + int audio; + int keyframe; + int32_t pts; + int32_t duration; + int32_t size; + int32_t index; + struct FILMPacket *next; +} FILMPacket; + +typedef struct FILMOutputContext { + const AVClass *class; + int audio_index; + int video_index; + int64_t stab_pos; + FILMPacket *start; + FILMPacket *last; + int64_t packet_count; +} FILMOutputContext; + +static int film_write_packet_to_header(AVFormatContext *format_context, FILMPacket *pkt) +{ + AVIOContext *pb = format_context->pb; + /* The bits in these two 32-bit integers contain info about the contents of this sample */ + int32_t info1 = 0; + int32_t info2 = 0; + + if (pkt->audio) { + /* Always the same, carries no more information than "this is audio" */ + info1 = 0xFFFFFFFF; + info2 = 1; + } else { + info1 = pkt->pts; + info2 = pkt->duration; + /* The top bit being set indicates a key frame */ + if (pkt->keyframe) + info1 |= (1 << 31); + } + + /* Write the 16-byte sample info packet to the STAB chunk in the header */ + avio_wb32(pb, pkt->index); + avio_wb32(pb, pkt->size); + avio_wb32(pb, info1); + avio_wb32(pb, info2); + + return 0; +} + +static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt) +{ + FILMPacket *metadata; + AVIOContext *pb = format_context->pb; + FILMOutputContext *film = format_context->priv_data; + int encoded_buf_size = 0; + enum AVCodecID codec_id; + + /* Track the metadata used to write the header and add it to the linked list */ + metadata = av_mallocz(sizeof(FILMPacket)); + if (!metadata) + return AVERROR(ENOMEM); + metadata->audio = pkt->stream_index == film->audio_index; + metadata->keyframe = pkt->flags & AV_PKT_FLAG_KEY; + metadata->pts = pkt->pts; + metadata->duration = pkt->duration; + metadata->size = pkt->size; + if (film->last == NULL) { + metadata->index = 0; + } else { + metadata->index = film->last->index + film->last->size; + film->last->next = metadata; + } + metadata->next = NULL; + if (film->start == NULL) + film->start = metadata; + film->packet_count++; + film->last = metadata; + + codec_id = format_context->streams[pkt->stream_index]->codecpar->codec_id; + + /* Sega Cinepak has an extra two-byte header; write dummy data there, + * then adjust the cvid header to accommodate for the extra size */ + if (codec_id == AV_CODEC_ID_CINEPAK) { + encoded_buf_size = AV_RB24(&pkt->data[1]); + /* Already Sega Cinepak, so no need to reformat the packets */ + if (encoded_buf_size != pkt->size && (pkt->size % encoded_buf_size) != 0) { + avio_write(pb, pkt->data, pkt->size); + } else { + uint8_t padding[2] = {0, 0}; + /* In Sega Cinepak, the reported size in the Cinepak header is + * 8 bytes too short. However, the size in the STAB section of the header + * is correct, taking into account the extra two bytes. */ + AV_WB24(&pkt->data[1], pkt->size - 8 + 2); + metadata->size += 2; + + avio_write(pb, pkt->data, 10); + avio_write(pb, padding, 2); + avio_write(pb, &pkt->data[10], pkt->size - 10); + } + } else { + /* Other formats can just be written as-is */ + avio_write(pb, pkt->data, pkt->size); + } + + return 0; +} + +static int get_audio_codec_id(enum AVCodecID codec_id) +{ + /* 0 (PCM) and 2 (ADX) are the only known values */ + switch (codec_id) { + case AV_CODEC_ID_PCM_S8_PLANAR: + case AV_CODEC_ID_PCM_S16BE_PLANAR: + return 0; + break; + case AV_CODEC_ID_ADPCM_ADX: + return 2; + break; + default: + return -1; + } +} + +static int film_init(AVFormatContext *format_context) +{ + AVStream *audio = NULL; + FILMOutputContext *film = format_context->priv_data; + film->audio_index = -1; + film->video_index = -1; + film->stab_pos = 0; + film->packet_count = 0; + film->start = NULL; + film->last = NULL; + + for (int i = 0; i < format_context->nb_streams; i++) { + AVStream *st = format_context->streams[i]; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (film->audio_index > -1) { + av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n"); + return AVERROR(EINVAL); + } + film->audio_index = i; + audio = st; + } + + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (film->video_index > -1) { + av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n"); + return AVERROR(EINVAL); + } + film->video_index = i; + } + + if (film->video_index == -1) { + av_log(format_context, AV_LOG_ERROR, "No video stream present.\n"); + return AVERROR(EINVAL); + } + } + + if (audio != NULL && get_audio_codec_id(audio->codecpar->codec_id) < 0) { + av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int shift_data(AVFormatContext *format_context, int64_t shift_size) +{ + int ret = 0; + int64_t pos, pos_end = avio_tell(format_context->pb); + uint8_t *buf, *read_buf[2]; + int read_buf_id = 0; + int read_size[2]; + AVIOContext *read_pb; + + buf = av_malloc(shift_size * 2); + if (!buf) + return AVERROR(ENOMEM); + read_buf[0] = buf; + read_buf[1] = buf + shift_size; + + /* Write the header at the beginning of the file, shifting all content as necessary; + * based on the approach used by MOV faststart. */ + avio_flush(format_context->pb); + ret = format_context->io_open(format_context, &read_pb, format_context->url, AVIO_FLAG_READ, NULL); + if (ret < 0) { + av_log(format_context, AV_LOG_ERROR, "Unable to re-open %s output file to " + "write the header\n", format_context->url); + av_free(buf); + return ret; + } + + /* mark the end of the shift to up to the last data we wrote, and get ready + * for writing */ + pos_end = avio_tell(format_context->pb); + avio_seek(format_context->pb, shift_size, SEEK_SET); + + /* start reading at where the new header will be placed */ + avio_seek(read_pb, 0, SEEK_SET); + pos = avio_tell(read_pb); + +#define READ_BLOCK do { \ + read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], shift_size); \ + read_buf_id ^= 1; \ +} while (0) + + /* shift data by chunk of at most shift_size */ + READ_BLOCK; + do { + int n; + READ_BLOCK; + n = read_size[read_buf_id]; + if (n <= 0) + break; + avio_write(format_context->pb, read_buf[read_buf_id], n); + pos += n; + } while (pos < pos_end); + ff_format_io_close(format_context, &read_pb); + + av_free(buf); + return 0; +} + +static int film_write_header(AVFormatContext *format_context) +{ + int ret = 0; + int64_t sample_table_size, stabsize, headersize; + int8_t audio_codec; + AVIOContext *pb = format_context->pb; + FILMOutputContext *film = format_context->priv_data; + FILMPacket *prev, *packet; + AVStream *audio = NULL; + AVStream *video = NULL; + + /* Calculate how much we need to reserve for the header; + * this is the amount the rest of the data will be shifted up by. */ + sample_table_size = film->packet_count * 16; + stabsize = 16 + sample_table_size; + headersize = 16 + /* FILM header base */ + 32 + /* FDSC chunk */ + stabsize; + + ret = shift_data(format_context, headersize); + if (ret < 0) + return ret; + /* Seek back to the beginning to start writing the header now */ + avio_seek(pb, 0, SEEK_SET); + + if (film->audio_index > -1) + audio = format_context->streams[film->audio_index]; + if (film->video_index > -1) + video = format_context->streams[film->video_index]; + + if (audio != NULL) { + audio_codec = get_audio_codec_id(audio->codecpar->codec_id); + if (audio_codec < 0) { + av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n"); + return AVERROR(EINVAL); + } + } + + if (video->codecpar->format != AV_PIX_FMT_RGB24) { + av_log(format_context, AV_LOG_ERROR, "Pixel format must be rgb24.\n"); + return AVERROR(EINVAL); + } + + /* First, write the FILM header; this is very simple */ + + ffio_wfourcc(pb, "FILM"); + avio_wb32(pb, 48 + stabsize); + /* This seems to be okay to hardcode, since this muxer targets 1.09 features; + * videos produced by this muxer are readable by 1.08 and lower players. */ + ffio_wfourcc(pb, "1.09"); + /* I have no idea what this field does, might be reserved */ + avio_wb32(pb, 0); + + /* Next write the FDSC (file description) chunk */ + ffio_wfourcc(pb, "FDSC"); + avio_wb32(pb, 0x20); /* Size of FDSC chunk */ + + /* The only two supported codecs; raw video is rare */ + switch (video->codecpar->codec_id) { + case AV_CODEC_ID_CINEPAK: + ffio_wfourcc(pb, "cvid"); + break; + case AV_CODEC_ID_RAWVIDEO: + ffio_wfourcc(pb, "raw "); + break; + default: + av_log(format_context, AV_LOG_ERROR, "Incompatible video stream format.\n"); + return AVERROR(EINVAL); + } + + avio_wb32(pb, video->codecpar->height); + avio_wb32(pb, video->codecpar->width); + avio_w8(pb, 24); /* Bits per pixel - observed to always be 24 */ + + if (audio != NULL) { + avio_w8(pb, audio->codecpar->channels); /* Audio channels */ + avio_w8(pb, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */ + avio_w8(pb, audio_codec); /* Compression - 0 is PCM, 2 is ADX */ + avio_wb16(pb, audio->codecpar->sample_rate); /* Audio sampling rate */ + } else { + /* Set all these fields to 0 if there's no audio */ + avio_w8(pb, 0); + avio_w8(pb, 0); + avio_w8(pb, 0); + avio_wb16(pb, 0); + } + + /* I have no idea what this pair of fields does either, might be reserved */ + avio_wb32(pb, 0); + avio_wb16(pb, 0); + + /* Finally, write the STAB (sample table) chunk */ + ffio_wfourcc(pb, "STAB"); + avio_wb32(pb, 16 + (film->packet_count * 16)); + /* Framerate base frequency. Here we're assuming that the frame rate is even. + * In real world Sega FILM files, there are usually a couple of approaches: + * a) framerate base frequency is the same as the framerate, and ticks + * increment by 1 every frame, or + * b) framerate base frequency is a much larger number, and ticks + * increment by larger steps every frame. + * The latter occurs even in cases where the frame rate is even; for example, in + * Lunar: Silver Star Story, the base frequency is 600 and each frame, the ticks + * are incremented by 25 for an evenly spaced framerate of 24fps. */ + avio_wb32(pb, av_q2d(av_inv_q(video->time_base))); + + avio_wb32(pb, film->packet_count); + + avio_flush(pb); + + /* Finally, write out each packet's data to the header */ + packet = film->start; + while (packet != NULL) { + film_write_packet_to_header(format_context, packet); + prev = packet; + packet = packet->next; + av_freep(&prev); + } + + return 0; +} + +static const AVClass film_muxer_class = { + .class_name = "Sega FILM muxer", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_segafilm_muxer = { + .name = "film_cpk", + .long_name = NULL_IF_CONFIG_SMALL("Sega FILM / CPK"), + .extensions = "cpk", + .priv_data_size = sizeof(FILMOutputContext), + .audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, + .video_codec = AV_CODEC_ID_CINEPAK, + .init = film_init, + .write_trailer = film_write_header, + .write_packet = film_write_packet, + .priv_class = &film_muxer_class, +}; diff --git a/chromium/third_party/ffmpeg/libavformat/spdifenc.c b/chromium/third_party/ffmpeg/libavformat/spdifenc.c index 3a50aebbefd..9514ff8e10e 100644 --- a/chromium/third_party/ffmpeg/libavformat/spdifenc.c +++ b/chromium/third_party/ffmpeg/libavformat/spdifenc.c @@ -118,7 +118,8 @@ static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt) static const uint8_t eac3_repeat[4] = {6, 3, 2, 1}; int repeat = 1; - if ((pkt->data[4] & 0xc0) != 0xc0) /* fscod */ + int bsid = pkt->data[5] >> 3; + if (bsid > 10 && (pkt->data[4] & 0xc0) != 0xc0) /* fscod */ repeat = eac3_repeat[(pkt->data[4] & 0x30) >> 4]; /* numblkscod */ ctx->hd_buf = av_fast_realloc(ctx->hd_buf, &ctx->hd_buf_size, ctx->hd_buf_filled + pkt->size); diff --git a/chromium/third_party/ffmpeg/libavformat/tcp.c b/chromium/third_party/ffmpeg/libavformat/tcp.c index 8773493df1e..b0289f854fc 100644 --- a/chromium/third_party/ffmpeg/libavformat/tcp.c +++ b/chromium/third_party/ffmpeg/libavformat/tcp.c @@ -208,8 +208,10 @@ static int tcp_accept(URLContext *s, URLContext **c) return ret; cc = (*c)->priv_data; ret = ff_accept(sc->fd, sc->listen_timeout, s); - if (ret < 0) + if (ret < 0) { + ffurl_closep(c); return ret; + } cc->fd = ret; return 0; } diff --git a/chromium/third_party/ffmpeg/libavformat/tls_schannel.c b/chromium/third_party/ffmpeg/libavformat/tls_schannel.c index 9a6e0c92e37..f41b007773f 100644 --- a/chromium/third_party/ffmpeg/libavformat/tls_schannel.c +++ b/chromium/third_party/ffmpeg/libavformat/tls_schannel.c @@ -413,11 +413,13 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) ret = ffurl_read(s->tcp, c->enc_buf + c->enc_buf_offset, c->enc_buf_size - c->enc_buf_offset); - if (ret < 0) { + if (ret == AVERROR_EOF) { + c->connection_closed = 1; + ret = 0; + } else if (ret < 0) { av_log(h, AV_LOG_ERROR, "Unable to read from socket\n"); return ret; - } else if (ret == 0) - c->connection_closed = 1; + } c->enc_buf_offset += ret; } @@ -515,7 +517,7 @@ cleanup: if (ret == 0 && !c->connection_closed) ret = AVERROR(EAGAIN); - return ret < 0 ? ret : 0; + return ret < 0 ? ret : AVERROR_EOF; } static int tls_write(URLContext *h, const uint8_t *buf, int len) diff --git a/chromium/third_party/ffmpeg/libavformat/ttaenc.c b/chromium/third_party/ffmpeg/libavformat/ttaenc.c index add15873d04..d8e1136ead1 100644 --- a/chromium/third_party/ffmpeg/libavformat/ttaenc.c +++ b/chromium/third_party/ffmpeg/libavformat/ttaenc.c @@ -91,22 +91,13 @@ static int tta_write_header(AVFormatContext *s) static int tta_write_packet(AVFormatContext *s, AVPacket *pkt) { TTAMuxContext *tta = s->priv_data; - AVPacketList *pktl = av_mallocz(sizeof(*pktl)); int ret; - if (!pktl) - return AVERROR(ENOMEM); - - ret = av_packet_ref(&pktl->pkt, pkt); + ret = ff_packet_list_put(&tta->queue, &tta->queue_end, pkt, + FF_PACKETLIST_FLAG_REF_PACKET); if (ret < 0) { - av_free(pktl); return ret; } - if (tta->queue_end) - tta->queue_end->next = pktl; - else - tta->queue = pktl; - tta->queue_end = pktl; avio_wl32(tta->seek_table, pkt->size); tta->nb_samples += pkt->duration; @@ -131,16 +122,13 @@ static int tta_write_packet(AVFormatContext *s, AVPacket *pkt) static void tta_queue_flush(AVFormatContext *s) { TTAMuxContext *tta = s->priv_data; - AVPacketList *pktl; - - while (pktl = tta->queue) { - AVPacket *pkt = &pktl->pkt; - avio_write(s->pb, pkt->data, pkt->size); - av_packet_unref(pkt); - tta->queue = pktl->next; - av_free(pktl); + AVPacket pkt; + + while (tta->queue) { + ff_packet_list_get(&tta->queue, &tta->queue_end, &pkt); + avio_write(s->pb, pkt.data, pkt.size); + av_packet_unref(&pkt); } - tta->queue_end = NULL; } static int tta_write_trailer(AVFormatContext *s) diff --git a/chromium/third_party/ffmpeg/libavformat/unix.c b/chromium/third_party/ffmpeg/libavformat/unix.c index 4f01d14a938..38016dbafea 100644 --- a/chromium/third_party/ffmpeg/libavformat/unix.c +++ b/chromium/third_party/ffmpeg/libavformat/unix.c @@ -111,6 +111,8 @@ static int unix_read(URLContext *h, uint8_t *buf, int size) return ret; } ret = recv(s->fd, buf, size, 0); + if (!ret && s->type == SOCK_STREAM) + return AVERROR_EOF; return ret < 0 ? ff_neterrno() : ret; } diff --git a/chromium/third_party/ffmpeg/libavformat/utils.c b/chromium/third_party/ffmpeg/libavformat/utils.c index 259549476dc..5a6e252d433 100644 --- a/chromium/third_party/ffmpeg/libavformat/utils.c +++ b/chromium/third_party/ffmpeg/libavformat/utils.c @@ -444,8 +444,9 @@ static int init_input(AVFormatContext *s, const char *filename, s, 0, s->format_probesize); } -static int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, - AVPacketList **plast_pktl, int ref) +int ff_packet_list_put(AVPacketList **packet_buffer, + AVPacketList **plast_pktl, + AVPacket *pkt, int flags) { AVPacketList *pktl = av_mallocz(sizeof(AVPacketList)); int ret; @@ -453,12 +454,15 @@ static int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt, if (!pktl) return AVERROR(ENOMEM); - if (ref) { + if (flags & FF_PACKETLIST_FLAG_REF_PACKET) { if ((ret = av_packet_ref(&pktl->pkt, pkt)) < 0) { av_free(pktl); return ret; } } else { + // TODO: Adapt callers in this file so the line below can use + // av_packet_move_ref() to effectively move the reference + // to the list. pktl->pkt = *pkt; } @@ -485,9 +489,10 @@ int avformat_queue_attached_pictures(AVFormatContext *s) continue; } - ret = add_to_pktbuf(&s->internal->raw_packet_buffer, - &s->streams[i]->attached_pic, - &s->internal->raw_packet_buffer_end, 1); + ret = ff_packet_list_put(&s->internal->raw_packet_buffer, + &s->internal->raw_packet_buffer_end, + &s->streams[i]->attached_pic, + FF_PACKETLIST_FLAG_REF_PACKET); if (ret < 0) return ret; } @@ -867,13 +872,9 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) continue; } - if (!pkt->buf) { - AVPacket tmp = { 0 }; - err = av_packet_ref(&tmp, pkt); - if (err < 0) - return err; - *pkt = tmp; - } + err = av_packet_make_refcounted(pkt); + if (err < 0) + return err; if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) && (pkt->flags & AV_PKT_FLAG_CORRUPT)) { @@ -913,8 +914,9 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) if (!pktl && st->request_probe <= 0) return ret; - err = add_to_pktbuf(&s->internal->raw_packet_buffer, pkt, - &s->internal->raw_packet_buffer_end, 0); + err = ff_packet_list_put(&s->internal->raw_packet_buffer, + &s->internal->raw_packet_buffer_end, + pkt, 0); if (err) return err; s->internal->raw_packet_buffer_remaining_size -= pkt->size; @@ -1127,6 +1129,7 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, if (st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE || + st->cur_dts < INT_MIN + RELATIVE_TS_BASE || is_relative(dts)) return; @@ -1158,7 +1161,9 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, } if (st->start_time == AV_NOPTS_VALUE) { - st->start_time = pts; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || !(pkt->flags & AV_PKT_FLAG_DISCARD)) { + st->start_time = pts; + } if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base); } @@ -1410,14 +1415,17 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif } -static void free_packet_buffer(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end) +void ff_packet_list_free(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end) { - while (*pkt_buf) { - AVPacketList *pktl = *pkt_buf; - *pkt_buf = pktl->next; + AVPacketList *tmp = *pkt_buf; + + while (tmp) { + AVPacketList *pktl = tmp; + tmp = pktl->next; av_packet_unref(&pktl->pkt); av_freep(&pktl); } + *pkt_buf = NULL; *pkt_buf_end = NULL; } @@ -1464,6 +1472,22 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) if (!out_pkt.size) continue; + if (pkt->buf && out_pkt.data == pkt->data) { + /* reference pkt->buf only when out_pkt.data is guaranteed to point + * to data in it and not in the parser's internal buffer. */ + /* XXX: Ensure this is the case with all parsers when st->parser->flags + * is PARSER_FLAG_COMPLETE_FRAMES and check for that instead? */ + out_pkt.buf = av_buffer_ref(pkt->buf); + if (!out_pkt.buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + } else { + ret = av_packet_make_refcounted(&out_pkt); + if (ret < 0) + goto fail; + } + if (pkt->side_data) { out_pkt.side_data = pkt->side_data; out_pkt.side_data_elems = pkt->side_data_elems; @@ -1502,11 +1526,13 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) compute_pkt_fields(s, st, st->parser, &out_pkt, next_dts, next_pts); - ret = add_to_pktbuf(&s->internal->parse_queue, &out_pkt, - &s->internal->parse_queue_end, 1); - av_packet_unref(&out_pkt); - if (ret < 0) + ret = ff_packet_list_put(&s->internal->parse_queue, + &s->internal->parse_queue_end, + &out_pkt, 0); + if (ret < 0) { + av_packet_unref(&out_pkt); goto fail; + } } /* end of the stream => close and free the parser */ @@ -1520,9 +1546,9 @@ fail: return ret; } -static int read_from_packet_buffer(AVPacketList **pkt_buffer, - AVPacketList **pkt_buffer_end, - AVPacket *pkt) +int ff_packet_list_get(AVPacketList **pkt_buffer, + AVPacketList **pkt_buffer_end, + AVPacket *pkt) { AVPacketList *pktl; av_assert0(*pkt_buffer); @@ -1668,7 +1694,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } if (!got_packet && s->internal->parse_queue) - ret = read_from_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt); + ret = ff_packet_list_get(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt); if (ret >= 0) { AVStream *st = s->streams[pkt->stream_index]; @@ -1747,7 +1773,7 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) if (!genpts) { ret = s->internal->packet_buffer - ? read_from_packet_buffer(&s->internal->packet_buffer, + ? ff_packet_list_get(&s->internal->packet_buffer, &s->internal->packet_buffer_end, pkt) : read_frame_internal(s, pkt); if (ret < 0) @@ -1796,7 +1822,7 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) st = s->streams[next_pkt->stream_index]; if (!(next_pkt->pts == AV_NOPTS_VALUE && st->discard < AVDISCARD_ALL && next_pkt->dts != AV_NOPTS_VALUE && !eof)) { - ret = read_from_packet_buffer(&s->internal->packet_buffer, + ret = ff_packet_list_get(&s->internal->packet_buffer, &s->internal->packet_buffer_end, pkt); goto return_packet; } @@ -1811,8 +1837,9 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) return ret; } - ret = add_to_pktbuf(&s->internal->packet_buffer, pkt, - &s->internal->packet_buffer_end, 1); + ret = ff_packet_list_put(&s->internal->packet_buffer, + &s->internal->packet_buffer_end, + pkt, FF_PACKETLIST_FLAG_REF_PACKET); av_packet_unref(pkt); if (ret < 0) return ret; @@ -1839,9 +1866,9 @@ static void flush_packet_queue(AVFormatContext *s) { if (!s->internal) return; - free_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end); - free_packet_buffer(&s->internal->packet_buffer, &s->internal->packet_buffer_end); - free_packet_buffer(&s->internal->raw_packet_buffer, &s->internal->raw_packet_buffer_end); + ff_packet_list_free(&s->internal->parse_queue, &s->internal->parse_queue_end); + ff_packet_list_free(&s->internal->packet_buffer, &s->internal->packet_buffer_end); + ff_packet_list_free(&s->internal->raw_packet_buffer, &s->internal->raw_packet_buffer_end); s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; } @@ -2591,9 +2618,8 @@ static int has_duration(AVFormatContext *ic) static void update_stream_timings(AVFormatContext *ic) { int64_t start_time, start_time1, start_time_text, end_time, end_time1, end_time_text; - int64_t duration, duration1, filesize; + int64_t duration, duration1, duration_text, filesize; int i; - AVStream *st; AVProgram *p; start_time = INT64_MAX; @@ -2601,22 +2627,25 @@ static void update_stream_timings(AVFormatContext *ic) end_time = INT64_MIN; end_time_text = INT64_MIN; duration = INT64_MIN; + duration_text = INT64_MIN; + for (i = 0; i < ic->nb_streams; i++) { - st = ic->streams[i]; + AVStream *st = ic->streams[i]; + int is_text = st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || + st->codecpar->codec_type == AVMEDIA_TYPE_DATA; if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) { start_time1 = av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q); - if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { - if (start_time1 < start_time_text) - start_time_text = start_time1; - } else + if (is_text) + start_time_text = FFMIN(start_time_text, start_time1); + else start_time = FFMIN(start_time, start_time1); end_time1 = av_rescale_q_rnd(st->duration, st->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); if (end_time1 != AV_NOPTS_VALUE && (end_time1 > 0 ? start_time1 <= INT64_MAX - end_time1 : start_time1 >= INT64_MIN - end_time1)) { end_time1 += start_time1; - if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codecpar->codec_type == AVMEDIA_TYPE_DATA) + if (is_text) end_time_text = FFMAX(end_time_text, end_time1); else end_time = FFMAX(end_time, end_time1); @@ -2631,7 +2660,10 @@ static void update_stream_timings(AVFormatContext *ic) if (st->duration != AV_NOPTS_VALUE) { duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q); - duration = FFMAX(duration, duration1); + if (is_text) + duration_text = FFMAX(duration_text, duration1); + else + duration = FFMAX(duration, duration1); } } if (start_time == INT64_MAX || (start_time > start_time_text && start_time - start_time_text < AV_TIME_BASE)) @@ -2639,11 +2671,15 @@ static void update_stream_timings(AVFormatContext *ic) else if (start_time > start_time_text) av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream starttime %f\n", start_time_text / (float)AV_TIME_BASE); - if (end_time == INT64_MIN || (end_time < end_time_text && end_time_text - end_time < AV_TIME_BASE)) { + if (end_time == INT64_MIN || (end_time < end_time_text && end_time_text - (uint64_t)end_time < AV_TIME_BASE)) end_time = end_time_text; - } else if (end_time < end_time_text) { + else if (end_time < end_time_text) av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream endtime %f\n", end_time_text / (float)AV_TIME_BASE); - } + + if (duration == INT64_MIN || (duration < duration_text && duration_text - duration < AV_TIME_BASE)) + duration = duration_text; + else if (duration < duration_text) + av_log(ic, AV_LOG_VERBOSE, "Ignoring outlier non primary stream duration %f\n", duration_text / (float)AV_TIME_BASE); if (start_time != INT64_MAX) { ic->start_time = start_time; @@ -3741,8 +3777,9 @@ FF_ENABLE_DEPRECATION_WARNINGS pkt = &pkt1; if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { - ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt, - &ic->internal->packet_buffer_end, 0); + ret = ff_packet_list_put(&ic->internal->packet_buffer, + &ic->internal->packet_buffer_end, + pkt, 0); if (ret < 0) goto find_stream_info_err; } @@ -5060,11 +5097,94 @@ FF_ENABLE_DEPRECATION_WARNINGS if (s->programs[i]->id != prog_id) continue; - if (*endptr++ == ':') { - int stream_idx = strtol(endptr, NULL, 0); - return stream_idx >= 0 && - stream_idx < s->programs[i]->nb_stream_indexes && - st->index == s->programs[i]->stream_index[stream_idx]; + if (*endptr++ == ':') { // p:<id>:.... + if ( *endptr == 'a' || *endptr == 'v' || + *endptr == 's' || *endptr == 'd') { // p:<id>:<st_type>[:<index>] + enum AVMediaType type; + + switch (*endptr++) { + case 'v': type = AVMEDIA_TYPE_VIDEO; break; + case 'a': type = AVMEDIA_TYPE_AUDIO; break; + case 's': type = AVMEDIA_TYPE_SUBTITLE; break; + case 'd': type = AVMEDIA_TYPE_DATA; break; + default: av_assert0(0); + } + if (*endptr++ == ':') { // p:<id>:<st_type>:<index> + int stream_idx = strtol(endptr, NULL, 0), type_counter = 0; + for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) { + int stream_index = s->programs[i]->stream_index[j]; + if (st->index == s->programs[i]->stream_index[j]) { +#if FF_API_LAVF_AVCTX +FF_DISABLE_DEPRECATION_WARNINGS + return type_counter == stream_idx && + (type == st->codecpar->codec_type || + type == st->codec->codec_type); +FF_ENABLE_DEPRECATION_WARNINGS +#else + return type_counter == stream_idx && + type == st->codecpar->codec_type; +#endif + } +#if FF_API_LAVF_AVCTX +FF_DISABLE_DEPRECATION_WARNINGS + if (type == s->streams[stream_index]->codecpar->codec_type || + type == s->streams[stream_index]->codec->codec_type) + type_counter++; +FF_ENABLE_DEPRECATION_WARNINGS +#else + if (type == s->streams[stream_index]->codecpar->codec_type) + type_counter++; +#endif + } + return 0; + } else { // p:<id>:<st_type> + for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) + if (st->index == s->programs[i]->stream_index[j]) { +#if FF_API_LAVF_AVCTX +FF_DISABLE_DEPRECATION_WARNINGS + return type == st->codecpar->codec_type || + type == st->codec->codec_type; +FF_ENABLE_DEPRECATION_WARNINGS +#else + return type == st->codecpar->codec_type; +#endif + } + return 0; + } + + } else if ( *endptr == 'm') { // p:<id>:m:<metadata_spec> + AVDictionaryEntry *tag; + char *key, *val; + int ret = 0; + + if (*(++endptr) != ':') { + av_log(s, AV_LOG_ERROR, "Invalid stream specifier syntax, missing ':' sign after :m.\n"); + return AVERROR(EINVAL); + } + + val = strchr(++endptr, ':'); + key = val ? av_strndup(endptr, val - endptr) : av_strdup(endptr); + if (!key) + return AVERROR(ENOMEM); + + for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) + if (st->index == s->programs[i]->stream_index[j]) { + tag = av_dict_get(st->metadata, key, NULL, 0); + if (tag && (!val || !strcmp(tag->value, val + 1))) + ret = 1; + + break; + } + + av_freep(&key); + return ret; + + } else { // p:<id>:<index> + int stream_idx = strtol(endptr, NULL, 0); + return stream_idx >= 0 && + stream_idx < s->programs[i]->nb_stream_indexes && + st->index == s->programs[i]->stream_index[stream_idx]; + } } for (j = 0; j < s->programs[i]->nb_stream_indexes; j++) diff --git a/chromium/third_party/ffmpeg/libavformat/version.h b/chromium/third_party/ffmpeg/libavformat/version.h index e28a9e7dfec..dced716450b 100644 --- a/chromium/third_party/ffmpeg/libavformat/version.h +++ b/chromium/third_party/ffmpeg/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 10 +#define LIBAVFORMAT_VERSION_MINOR 13 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ @@ -94,6 +94,9 @@ #ifndef FF_API_NEXT #define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59) #endif +#ifndef FF_API_DASH_MIN_SEG_DURATION +#define FF_API_DASH_MIN_SEG_DURATION (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif #ifndef FF_API_R_FRAME_RATE diff --git a/chromium/third_party/ffmpeg/libavformat/vpcc.c b/chromium/third_party/ffmpeg/libavformat/vpcc.c index df08de59a6a..66d0df69e5e 100644 --- a/chromium/third_party/ffmpeg/libavformat/vpcc.c +++ b/chromium/third_party/ffmpeg/libavformat/vpcc.c @@ -67,8 +67,8 @@ static int get_vpx_video_full_range_flag(enum AVColorRange color_range) return color_range == AVCOL_RANGE_JPEG; } -int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, - AVCodecParameters *par) +int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, + VPCC *vpcc) { int profile = par->profile; int level = par->level == FF_LEVEL_UNKNOWN ? 0 : par->level; @@ -90,9 +90,28 @@ int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, } } - avio_w8(pb, profile); - avio_w8(pb, level); - avio_w8(pb, (bit_depth << 4) | (vpx_chroma_subsampling << 1) | vpx_video_full_range_flag); + vpcc->profile = profile; + vpcc->level = level; + vpcc->bitdepth = bit_depth; + vpcc->chroma_subsampling = vpx_chroma_subsampling; + vpcc->full_range_flag = vpx_video_full_range_flag; + + return 0; +} + +int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, + AVCodecParameters *par) +{ + VPCC vpcc; + int ret; + + ret = ff_isom_get_vpcc_features(s, par, &vpcc); + if (ret < 0) + return ret; + + avio_w8(pb, vpcc.profile); + avio_w8(pb, vpcc.level); + avio_w8(pb, (vpcc.bitdepth << 4) | (vpcc.chroma_subsampling << 1) | vpcc.full_range_flag); avio_w8(pb, par->color_primaries); avio_w8(pb, par->color_trc); avio_w8(pb, par->color_space); diff --git a/chromium/third_party/ffmpeg/libavformat/vpcc.h b/chromium/third_party/ffmpeg/libavformat/vpcc.h index 184e8579f15..d71ba05105a 100644 --- a/chromium/third_party/ffmpeg/libavformat/vpcc.h +++ b/chromium/third_party/ffmpeg/libavformat/vpcc.h @@ -32,6 +32,14 @@ #include "avformat.h" #include "libavcodec/avcodec.h" +typedef struct VPCC { + int profile; + int level; + int bitdepth; + int chroma_subsampling; + int full_range_flag; +} VPCC; + /** * Writes VP codec configuration to the provided AVIOContext. * @@ -44,4 +52,7 @@ int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par); +int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, + VPCC *vpcc); + #endif /* AVFORMAT_VPCC_H */ diff --git a/chromium/third_party/ffmpeg/libavformat/yuv4mpegdec.c b/chromium/third_party/ffmpeg/libavformat/yuv4mpegdec.c index ff0125e4cf3..8662a42a4c5 100644 --- a/chromium/third_party/ffmpeg/libavformat/yuv4mpegdec.c +++ b/chromium/third_party/ffmpeg/libavformat/yuv4mpegdec.c @@ -314,7 +314,16 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) static int yuv4_read_seek(AVFormatContext *s, int stream_index, int64_t pts, int flags) { - if (avio_seek(s->pb, pts * s->packet_size + s->internal->data_offset, SEEK_SET) < 0) + AVStream *st = s->streams[0]; + int64_t pos; + + pos = av_rescale_rnd(pts * s->packet_size, + st->time_base.num, + st->time_base.den * s->packet_size, + (flags & AVSEEK_FLAG_BACKWARD) ? AV_ROUND_DOWN : AV_ROUND_UP); + pos *= s->packet_size; + + if (avio_seek(s->pb, pos + s->internal->data_offset, SEEK_SET) < 0) return -1; return 0; } |