PR #25384 opened by danog URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25384 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25384.patch
Opening a Master Playlist fetches every Media Playlist, and then the key and first segment of each, strictly one after another, each over a new connection. With several variants or renditions this takes seconds before the first packet. This adds `parallel_open`, the maximum number of playlists to load at once. Above 1, Media Playlist parsing and the opening of the per-playlist demuxers run in up to that many threads. Streams are still created in playlist order. The default (0) is unchanged and takes the original code path. Opening live streams with several audio renditions went from 1.8 s to 0.6 s, 4.0 s to 1.5 s and 1.7 s to 0.5 s in testing. - Shared state is protected by a mutex, and concurrent responses that set cookies are merged into the cookie jar. - The persistent playlist connection isn't used while threads run, and playlists with SAMPLE-AES segments are opened one after another. - With the option above 1, the `io_open`, `io_close2` and interrupt callbacks can be called from several threads; this is documented. - Checked with ThreadSanitizer, including over HTTP with cookies. - A FATE test opens a stream with two audio renditions and a subtitle playlist both ways and compares the result with the same reference. The first commit is a no-functional-change refactor. Kodi carries this as a patch in https://github.com/xbmc/xbmc/pull/30046; its maintainers asked for it to be submitted here instead. >From 2491dd254d064e871e67edbe6bd2c481a40f1b75 Mon Sep 17 00:00:00 2001 From: Daniil Gentili <[email protected]> Date: Thu, 22 Oct 2026 23:41:56 +0200 Subject: [PATCH 1/2] avformat/hls: factor out opening the demuxer of a playlist Move the code opening the demuxer of each playlist on its first segment from hls_read_header() to its own function. No functional change. Signed-off-by: Daniil Gentili <[email protected]> --- libavformat/hls.c | 339 ++++++++++++++++++++++++---------------------- 1 file changed, 176 insertions(+), 163 deletions(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index c68b5e88cf5..e36f6d30723 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -2469,6 +2469,181 @@ static int hls_close(AVFormatContext *s) return 0; } +/* Open the demuxer of one playlist on its first segment */ +static int open_playlist_demuxer(AVFormatContext *s, struct playlist *pls, + int64_t highest_cur_seq_no) +{ + HLSContext *c = s->priv_data; + const AVInputFormat *in_fmt = NULL; + char *url; + AVDictionary *options = NULL; + struct segment *seg = NULL; + uint8_t *buf; + int buf_size; + int ret = 0; + + /* + * If this is a live stream and this playlist looks like it is one segment + * behind, try to sync it up so that every substream starts at the same + * time position (so e.g. avformat_find_stream_info() will see packets from + * all active streams within the first few seconds). This is not very generic, + * though, as the sequence numbers are technically independent. + */ + if (!pls->finished && pls->cur_seq_no == highest_cur_seq_no - 1 && + highest_cur_seq_no < pls->start_seq_no + pls->n_segments) { + pls->cur_seq_no = highest_cur_seq_no; + } + + if (pls->is_subtitle) { + buf_size = strlen("WEBVTT\n"); + buf = av_memdup("WEBVTT\n", buf_size); + } else { + buf_size = INITIAL_BUFFER_SIZE; + buf = av_malloc(buf_size); + } + if (!buf) { + avformat_free_context(pls->ctx); + pls->ctx = NULL; + return AVERROR(ENOMEM); + } + + ffio_init_context(&pls->pb, buf, buf_size, 0, pls, + pls->is_subtitle ? NULL : read_data_continuous, NULL, NULL); + + /* + * If encryption scheme is SAMPLE-AES, try to read ID3 tags of + * external audio track that contains audio setup information + */ + seg = current_segment(pls); + if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 && + pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) { + uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN]; + if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) { + /* Fail if error was not end of file */ + if (ret != AVERROR_EOF) { + avformat_free_context(pls->ctx); + pls->ctx = NULL; + return ret; + } + } + ret = 0; + /* Reset reading */ + ff_format_io_close(pls->parent, &pls->input); + pls->input = NULL; + pls->input_read_done = 0; + ff_format_io_close(pls->parent, &pls->input_next); + pls->input_next = NULL; + pls->input_next_requested = 0; + pls->cur_seg_offset = 0; + pls->cur_init_section = NULL; + /* Reset EOF flag */ + pls->pb.pub.eof_reached = 0; + /* Clear any buffered data */ + pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer; + /* Reset the position */ + pls->pb.pub.pos = 0; + } + + /* + * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track, + * use that information to find the media format, otherwise probe input data + */ + seg = current_segment(pls); + if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped && + pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) { + av_assert1(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC || + pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 || + pls->audio_setup_info.codec_id == AV_CODEC_ID_EAC3); + // Keep this list in sync with ff_hls_senc_read_audio_setup_info() + in_fmt = av_find_input_format(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC ? "aac" : + pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 ? "ac3" : "eac3"); + } else { + pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4; + pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE; + pls->ctx->interrupt_callback = s->interrupt_callback; + url = av_strdup(pls->segments[0]->url); + ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0); + + for (int n = 0; n < pls->n_segments; n++) + if (ret >= 0) + ret = test_segment(s, in_fmt, pls, pls->segments[n]); + + if (ret < 0) { + /* Free the ctx - it isn't initialized properly at this point, + * so avformat_close_input shouldn't be called. If + * avformat_open_input fails below, it frees and zeros the + * context, so it doesn't need any special treatment like this. */ + av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url); + avformat_free_context(pls->ctx); + pls->ctx = NULL; + av_free(url); + return ret; + } + av_free(url); + } + + seg = current_segment(pls); + if (seg && seg->key_type == KEY_SAMPLE_AES) { + if (strstr(in_fmt->name, "mov")) { + char key[33]; + ff_data_to_hex(key, pls->key, sizeof(pls->key), 0); + av_dict_set(&options, "decryption_key", key, 0); + } else if (!c->crypto_ctx.aes_ctx) { + c->crypto_ctx.aes_ctx = av_aes_alloc(); + if (!c->crypto_ctx.aes_ctx) { + avformat_free_context(pls->ctx); + pls->ctx = NULL; + return AVERROR(ENOMEM); + } + } + } + + pls->ctx->pb = &pls->pb.pub; + pls->ctx->io_open = nested_io_open; + pls->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO; + + if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0) + return ret; + + av_dict_copy(&options, c->seg_format_opts, 0); + + ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options); + av_dict_free(&options); + if (ret < 0) + return ret; + + if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) { + if (av_match_name(pls->ctx->iformat->name, PACKED_AUDIO_FORMATS)) + ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra); + avformat_queue_attached_pictures(pls->ctx); + ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra); + ff_id3v2_free_extra_meta(&pls->id3_deferred_extra); + } + + if (pls->is_id3_timestamped == -1) + av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n"); + + /* + * For ID3 timestamped raw audio streams we need to detect the packet + * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(), + * but for other streams we can rely on our user calling avformat_find_stream_info() + * on us if they want to. + */ + if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) { + seg = current_segment(pls); + if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 && + pls->ctx->nb_streams == 1) + ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info); + else + ret = avformat_find_stream_info(pls->ctx, NULL); + + if (ret < 0) + return ret; + } + + return 0; +} + static int hls_read_header(AVFormatContext *s) { HLSContext *c = s->priv_data; @@ -2593,12 +2768,6 @@ static int hls_read_header(AVFormatContext *s) /* Open the demuxer for each playlist */ for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; - const AVInputFormat *in_fmt = NULL; - char *url; - AVDictionary *options = NULL; - struct segment *seg = NULL; - uint8_t *buf; - int buf_size; if (!(pls->ctx = avformat_alloc_context())) return AVERROR(ENOMEM); @@ -2610,165 +2779,9 @@ static int hls_read_header(AVFormatContext *s) pls->needed = 1; pls->parent = s; - /* - * If this is a live stream and this playlist looks like it is one segment - * behind, try to sync it up so that every substream starts at the same - * time position (so e.g. avformat_find_stream_info() will see packets from - * all active streams within the first few seconds). This is not very generic, - * though, as the sequence numbers are technically independent. - */ - if (!pls->finished && pls->cur_seq_no == highest_cur_seq_no - 1 && - highest_cur_seq_no < pls->start_seq_no + pls->n_segments) { - pls->cur_seq_no = highest_cur_seq_no; - } - - if (pls->is_subtitle) { - buf_size = strlen("WEBVTT\n"); - buf = av_memdup("WEBVTT\n", buf_size); - } else { - buf_size = INITIAL_BUFFER_SIZE; - buf = av_malloc(buf_size); - } - if (!buf) { - avformat_free_context(pls->ctx); - pls->ctx = NULL; - return AVERROR(ENOMEM); - } - - ffio_init_context(&pls->pb, buf, buf_size, 0, pls, - pls->is_subtitle ? NULL : read_data_continuous, NULL, NULL); - - /* - * If encryption scheme is SAMPLE-AES, try to read ID3 tags of - * external audio track that contains audio setup information - */ - seg = current_segment(pls); - if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 && - pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) { - uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN]; - if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) { - /* Fail if error was not end of file */ - if (ret != AVERROR_EOF) { - avformat_free_context(pls->ctx); - pls->ctx = NULL; - return ret; - } - } - ret = 0; - /* Reset reading */ - ff_format_io_close(pls->parent, &pls->input); - pls->input = NULL; - pls->input_read_done = 0; - ff_format_io_close(pls->parent, &pls->input_next); - pls->input_next = NULL; - pls->input_next_requested = 0; - pls->cur_seg_offset = 0; - pls->cur_init_section = NULL; - /* Reset EOF flag */ - pls->pb.pub.eof_reached = 0; - /* Clear any buffered data */ - pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer; - /* Reset the position */ - pls->pb.pub.pos = 0; - } - - /* - * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track, - * use that information to find the media format, otherwise probe input data - */ - seg = current_segment(pls); - if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped && - pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) { - av_assert1(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC || - pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 || - pls->audio_setup_info.codec_id == AV_CODEC_ID_EAC3); - // Keep this list in sync with ff_hls_senc_read_audio_setup_info() - in_fmt = av_find_input_format(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC ? "aac" : - pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 ? "ac3" : "eac3"); - } else { - pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4; - pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE; - pls->ctx->interrupt_callback = s->interrupt_callback; - url = av_strdup(pls->segments[0]->url); - ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0); - - for (int n = 0; n < pls->n_segments; n++) - if (ret >= 0) - ret = test_segment(s, in_fmt, pls, pls->segments[n]); - - if (ret < 0) { - /* Free the ctx - it isn't initialized properly at this point, - * so avformat_close_input shouldn't be called. If - * avformat_open_input fails below, it frees and zeros the - * context, so it doesn't need any special treatment like this. */ - av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url); - avformat_free_context(pls->ctx); - pls->ctx = NULL; - av_free(url); - return ret; - } - av_free(url); - } - - seg = current_segment(pls); - if (seg && seg->key_type == KEY_SAMPLE_AES) { - if (strstr(in_fmt->name, "mov")) { - char key[33]; - ff_data_to_hex(key, pls->key, sizeof(pls->key), 0); - av_dict_set(&options, "decryption_key", key, 0); - } else if (!c->crypto_ctx.aes_ctx) { - c->crypto_ctx.aes_ctx = av_aes_alloc(); - if (!c->crypto_ctx.aes_ctx) { - avformat_free_context(pls->ctx); - pls->ctx = NULL; - return AVERROR(ENOMEM); - } - } - } - - pls->ctx->pb = &pls->pb.pub; - pls->ctx->io_open = nested_io_open; - pls->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO; - - if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0) + if ((ret = open_playlist_demuxer(s, pls, highest_cur_seq_no)) < 0) return ret; - av_dict_copy(&options, c->seg_format_opts, 0); - - ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options); - av_dict_free(&options); - if (ret < 0) - return ret; - - if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) { - if (av_match_name(pls->ctx->iformat->name, PACKED_AUDIO_FORMATS)) - ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra); - avformat_queue_attached_pictures(pls->ctx); - ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra); - ff_id3v2_free_extra_meta(&pls->id3_deferred_extra); - } - - if (pls->is_id3_timestamped == -1) - av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n"); - - /* - * For ID3 timestamped raw audio streams we need to detect the packet - * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(), - * but for other streams we can rely on our user calling avformat_find_stream_info() - * on us if they want to. - */ - if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) { - seg = current_segment(pls); - if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 && - pls->ctx->nb_streams == 1) - ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info); - else - ret = avformat_find_stream_info(pls->ctx, NULL); - - if (ret < 0) - return ret; - } - pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER); /* Create new AVStreams for each stream in this playlist */ -- 2.54.0 >From 8ec233898aa13c94c22d0bcae92b275e43db0778 Mon Sep 17 00:00:00 2001 From: Daniil Gentili <[email protected]> Date: Thu, 22 Oct 2026 23:42:15 +0200 Subject: [PATCH 2/2] avformat/hls: add an option to open playlists in parallel When opening a Master Playlist, the demuxer fetches every Media Playlist, and then the key and first segment of each of them, strictly one after another, each over a new connection. With several variants or renditions this takes seconds before the first packet. Add parallel_open, the maximum number of playlists to load at once. Above 1, the Media Playlist parsing and the opening of the per-playlist demuxers run in up to that many threads, one per playlist. Streams are still created in playlist order, so the result is the same as with sequential opening, which stays the default and is unchanged. State shared between playlists is protected by a mutex, and the persistent playlist connection is not used while the threads run. If any segment is SAMPLE-AES, the per-playlist demuxers are opened one after another. Opening a live stream with 5 variants and 3 audio renditions went from 1.8 s to 0.6 s in testing, and two others from 4.0 s to 1.5 s and from 1.7 s to 0.5 s. Signed-off-by: Daniil Gentili <[email protected]> --- doc/demuxers.texi | 9 + libavformat/hls.c | 298 ++++++++++++++++++++++--- tests/fate/demux.mak | 23 ++ tests/ref/fate/hls-parallel-open-demux | 92 ++++++++ 4 files changed, 385 insertions(+), 37 deletions(-) create mode 100644 tests/ref/fate/hls-parallel-open-demux diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 76100551931..9432650edde 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -619,6 +619,15 @@ Set options for the demuxer of media segments using a list of key=value pairs se @item seg_max_retry Maximum number of times to reload a segment on error, useful when segment skip on network error is not desired. Default value is 0. + +@item parallel_open +Maximum number of Media Playlists to load at the same time when opening a +Master Playlist, each with its first segment and in its own thread. This +reduces the time to open streams with several variants or renditions. +With a value above 1, the @code{io_open}, @code{io_close2} and interrupt +callbacks of the format context can be called from several threads at +once, so they must be thread-safe. Default value is 0, which like 1 loads +the playlists one after another. @end table @section image2 diff --git a/libavformat/hls.c b/libavformat/hls.c index e36f6d30723..230758892e9 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -33,11 +33,13 @@ #include "libavutil/aes.h" #include "libavutil/avstring.h" #include "libavutil/avassert.h" +#include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/dict.h" +#include "libavutil/thread.h" #include "libavutil/time.h" #include "libavutil/tree.h" #include "avformat.h" @@ -281,6 +283,10 @@ typedef struct HLSContext { int http_multiple; int http_seekable; int seg_max_retry; + int parallel_open; + int in_parallel; + int lock_initialized; + AVMutex lock; AVIOContext *playlist_pb; HLSCryptoContext crypto_ctx; } HLSContext; @@ -804,6 +810,54 @@ static int is_native_http(AVIOContext *pb) #endif } +/* Protects state shared between playlists opened in parallel */ +static void hls_lock(HLSContext *c) +{ + if (c->in_parallel) + ff_mutex_lock(&c->lock); +} + +static void hls_unlock(HLSContext *c) +{ + if (c->in_parallel) + ff_mutex_unlock(&c->lock); +} + +static int has_line(const char *str, const char *line, size_t len) +{ + while (*str) { + size_t n = strcspn(str, "\n"); + if (n == len && !memcmp(str, line, len)) + return 1; + str += n + (str[n] == '\n'); + } + return 0; +} + +/* Parallel requests all start from the same cookie jar, so add the cookies + * a response set to the current jar instead of replacing it. */ +static char *merge_cookies(const char *jar, const char *cookies) +{ + AVBPrint bp; + char *ret; + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprintf(&bp, "%s", jar); + while (*cookies) { + size_t n = strcspn(cookies, "\n"); + if (n && !has_line(jar, cookies, n)) { + if (bp.len && bp.str[bp.len - 1] != '\n') + av_bprint_chars(&bp, '\n', 1); + av_bprint_append_data(&bp, cookies, n); + av_bprint_chars(&bp, '\n', 1); + } + cookies += n + (cookies[n] == '\n'); + } + if (av_bprint_finalize(&bp, &ret) < 0) + return NULL; + return ret; +} + static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, const char *url, AVDictionary **options) { @@ -870,7 +924,9 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5)) return AVERROR_INVALIDDATA; + hls_lock(c); av_dict_copy(&tmp, *opts, 0); + hls_unlock(c); av_dict_copy(&tmp, opts2, 0); if (is_http && c->http_persistent && is_native_http(*pb)) { @@ -883,7 +939,9 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, av_log(s, AV_LOG_WARNING, "keepalive request failed for '%s' with error: '%s' when opening url, retrying with new connection\n", url, av_err2str(ret)); + hls_lock(c); av_dict_copy(&tmp, *opts, 0); + hls_unlock(c); av_dict_copy(&tmp, opts2, 0); ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp); } @@ -898,8 +956,19 @@ 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_dict_set(opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); + if (new_cookies) { + const AVDictionaryEntry *jar; + + hls_lock(c); + if (c->in_parallel && (jar = av_dict_get(*opts, "cookies", NULL, 0))) { + char *merged = merge_cookies(jar->value, new_cookies); + av_free(new_cookies); + new_cookies = merged; + } + if (new_cookies) + av_dict_set(opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); + hls_unlock(c); + } } av_dict_free(&tmp); @@ -957,6 +1026,24 @@ static int test_segment(AVFormatContext *s, const AVInputFormat *in_fmt, struct return 0; } +static void update_seekability(HLSContext *c) +{ + c->ctx->ctx_flags = c->ctx->ctx_flags & ~(unsigned)AVFMTCTX_UNSEEKABLE; + if (!c->n_variants || !c->variants[0]->n_playlists || + !c->variants[0]->playlists[0]->n_segments) + c->ctx->ctx_flags |= AVFMTCTX_UNSEEKABLE; + + if (c->n_variants && c->variants[0]->n_playlists && + c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT && + !c->variants[0]->playlists[0]->finished) { + struct playlist *p = c->variants[0]->playlists[0]; + int64_t duration = 0; + for (int i = 0; i < p->n_segments; i++) + duration += p->segments[i]->duration; + c->ctx->duration = duration; + } +} + static int parse_playlist(HLSContext *c, const char *url, struct playlist *pls, AVIOContext *in) { @@ -984,7 +1071,7 @@ static int parse_playlist(HLSContext *c, const char *url, int time_offset_flag = 0; int finished = 0; - if (is_http && !in && c->http_persistent && c->playlist_pb) { + if (is_http && !in && c->http_persistent && !c->in_parallel && c->playlist_pb) { in = c->playlist_pb; ret = open_url_keepalive(c->ctx, &c->playlist_pb, url, NULL); if (ret == AVERROR_EXIT) { @@ -1000,7 +1087,9 @@ static int parse_playlist(HLSContext *c, const char *url, if (!in) { AVDictionary *opts = NULL; + hls_lock(c); av_dict_copy(&opts, c->avio_opts, 0); + hls_unlock(c); if (c->http_persistent) av_dict_set(&opts, "multiple_requests", "1", 0); @@ -1010,7 +1099,7 @@ static int parse_playlist(HLSContext *c, const char *url, if (ret < 0) return ret; - if (is_http && c->http_persistent && is_native_http(in)) + if (is_http && c->http_persistent && !c->in_parallel && is_native_http(in)) c->playlist_pb = in; else close_in = 1; @@ -1318,20 +1407,8 @@ fail: av_free(new_url); if (close_in) ff_format_io_close(c->ctx, &in); - c->ctx->ctx_flags = c->ctx->ctx_flags & ~(unsigned)AVFMTCTX_UNSEEKABLE; - if (!c->n_variants || !c->variants[0]->n_playlists || - !c->variants[0]->playlists[0]->n_segments) - c->ctx->ctx_flags |= AVFMTCTX_UNSEEKABLE; - - if (c->n_variants && c->variants[0]->n_playlists && - c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT && - !c->variants[0]->playlists[0]->finished) { - struct playlist *p = c->variants[0]->playlists[0]; - int64_t duration = 0; - for (int i = 0; i < p->n_segments; i++) - duration += p->segments[i]->duration; - c->ctx->duration = duration; - } + if (!c->in_parallel) + update_seekability(c); return ret; } @@ -1901,6 +1978,7 @@ static int read_data_continuous(void *opaque, uint8_t *buf, int buf_size) int ret; int just_opened = 0; int segment_retries = 0; + int http_multiple; struct segment *seg; if (c->http_persistent && v->input_read_done) { @@ -1925,7 +2003,10 @@ restart: if (ret) return ret; - if (c->http_multiple == 1 && v->input_next_requested) { + hls_lock(c); + http_multiple = c->http_multiple; + hls_unlock(c); + if (http_multiple == 1 && v->input_next_requested) { FFSWAP(AVIOContext *, v->input, v->input_next); FFSWAP(int64_t, v->input_end, v->input_next_end); v->cur_seg_offset = 0; @@ -1958,6 +2039,7 @@ restart: v->first_read_seq_no = v->cur_seq_no; } + hls_lock(c); if (c->http_multiple == -1) { uint8_t *http_version_opt = NULL; int r = av_opt_get(v->input, "http_version", AV_OPT_SEARCH_CHILDREN, &http_version_opt); @@ -1966,9 +2048,11 @@ restart: av_freep(&http_version_opt); } } + http_multiple = c->http_multiple; + hls_unlock(c); seg = next_segment(v); - if (c->http_multiple == 1 && !v->input_next_requested && + if (http_multiple == 1 && !v->input_next_requested && seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL) && !segment_continues(current_segment(v), seg, v->input_end)) { ret = open_input(c, v, seg, &v->input_next, &v->input_next_end); @@ -2006,7 +2090,9 @@ restart: /* The next segment follows within the requested range and is read * on from the same input. */ v->cur_seq_no++; + hls_lock(c); c->cur_seq_no = v->cur_seq_no; + hls_unlock(c); v->cur_seg_offset = 0; just_opened = 1; goto restart; @@ -2023,7 +2109,9 @@ restart: } v->cur_seq_no++; + hls_lock(c); c->cur_seq_no = v->cur_seq_no; + hls_unlock(c); goto restart; } @@ -2132,7 +2220,9 @@ restart: } ff_format_io_close(v->parent, &v->input); v->cur_seq_no++; + hls_lock(c); c->cur_seq_no = v->cur_seq_no; + hls_unlock(c); avformat_close_input(&v->ctx); @@ -2465,10 +2555,86 @@ static int hls_close(AVFormatContext *s) av_dict_free(&c->avio_opts); ff_format_io_close(c->ctx, &c->playlist_pb); + if (c->lock_initialized) + ff_mutex_destroy(&c->lock); return 0; } +typedef int (*playlist_job_func)(AVFormatContext *s, struct playlist *pls, int64_t arg); + +#if HAVE_THREADS +struct playlist_job { + AVFormatContext *s; + struct playlist *pls; + playlist_job_func func; + int64_t arg; + pthread_t thread; + int started; + int ret; +}; + +static void *playlist_job_thread(void *opaque) +{ + struct playlist_job *job = opaque; + job->ret = job->func(job->s, job->pls, job->arg); + return NULL; +} +#endif + +/* + * Run func on every playlist and store the results in rets. When + * c->in_parallel is set, up to c->parallel_open playlists are run at once, + * each in its own thread. + */ +static int run_playlist_jobs(AVFormatContext *s, playlist_job_func func, int64_t arg, int *rets) +{ + HLSContext *c = s->priv_data; + +#if HAVE_THREADS + if (c->in_parallel && c->n_playlists > 1) { + int max_jobs = FFMIN(c->parallel_open, c->n_playlists); + struct playlist_job *jobs = av_calloc(max_jobs, sizeof(*jobs)); + if (!jobs) + return AVERROR(ENOMEM); + + for (int start = 0; start < c->n_playlists; start += max_jobs) { + int n = FFMIN(max_jobs, c->n_playlists - start); + + for (int i = 0; i < n; i++) { + struct playlist_job *job = &jobs[i]; + job->s = s; + job->pls = c->playlists[start + i]; + job->func = func; + job->arg = arg; + job->started = !pthread_create(&job->thread, NULL, playlist_job_thread, job); + if (!job->started) + job->ret = func(s, job->pls, arg); + } + for (int i = 0; i < n; i++) { + if (jobs[i].started) + pthread_join(jobs[i].thread, NULL); + rets[start + i] = jobs[i].ret; + } + } + av_free(jobs); + return 0; + } +#endif + + for (int i = 0; i < c->n_playlists; i++) + rets[i] = func(s, c->playlists[i], arg); + return 0; +} + +static int parse_media_playlist(AVFormatContext *s, struct playlist *pls, int64_t unused) +{ + HLSContext *c = s->priv_data; + + pls->m3u8_hold_counters = 0; + return parse_playlist(c, pls->url, pls, NULL); +} + /* Open the demuxer of one playlist on its first segment */ static int open_playlist_demuxer(AVFormatContext *s, struct playlist *pls, int64_t highest_cur_seq_no) @@ -2482,6 +2648,9 @@ static int open_playlist_demuxer(AVFormatContext *s, struct playlist *pls, int buf_size; int ret = 0; + if (pls->n_segments == 0) + return 0; + /* * If this is a live stream and this playlist looks like it is one segment * behind, try to sync it up so that every substream starts at the same @@ -2588,8 +2757,11 @@ static int open_playlist_demuxer(AVFormatContext *s, struct playlist *pls, char key[33]; ff_data_to_hex(key, pls->key, sizeof(pls->key), 0); av_dict_set(&options, "decryption_key", key, 0); - } else if (!c->crypto_ctx.aes_ctx) { - c->crypto_ctx.aes_ctx = av_aes_alloc(); + } else { + hls_lock(c); + if (!c->crypto_ctx.aes_ctx) + c->crypto_ctx.aes_ctx = av_aes_alloc(); + hls_unlock(c); if (!c->crypto_ctx.aes_ctx) { avformat_free_context(pls->ctx); pls->ctx = NULL; @@ -2649,6 +2821,12 @@ static int hls_read_header(AVFormatContext *s) HLSContext *c = s->priv_data; int ret = 0, i; int64_t highest_cur_seq_no = 0; + int *rets = NULL; + int has_sample_aes = 0, parallel; + + if ((ret = ff_mutex_init(&c->lock, NULL))) + return AVERROR(ret); + c->lock_initialized = 1; c->ctx = s; c->interrupt_callback = &s->interrupt_callback; @@ -2690,20 +2868,33 @@ static int hls_read_header(AVFormatContext *s) av_log(s, AV_LOG_WARNING, "Empty playlist\n"); return AVERROR_EOF; } + rets = av_calloc(c->n_playlists, sizeof(*rets)); + if (!rets) + return AVERROR(ENOMEM); + /* If the playlist only contained playlists (Master Playlist), * parse each individual playlist. */ if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) { + c->in_parallel = HAVE_THREADS && c->parallel_open > 1; + ret = run_playlist_jobs(s, parse_media_playlist, 0, rets); + if (c->in_parallel) { + c->in_parallel = 0; + update_seekability(c); + } + if (ret < 0) + goto fail; + for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; - pls->m3u8_hold_counters = 0; - if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) { + if ((ret = rets[i]) < 0) { av_log(s, AV_LOG_WARNING, "parse_playlist error %s [%s]\n", av_err2str(ret), pls->url); pls->broken = 1; if (c->n_playlists > 1) continue; - return ret; + goto fail; } } + ret = 0; } for (i = 0; i < c->n_variants; i++) { @@ -2729,17 +2920,17 @@ static int hls_read_header(AVFormatContext *s) if (var->audio_group[0] && (ret = add_renditions_to_variant(c, var, AVMEDIA_TYPE_AUDIO, var->audio_group)) < 0) - return ret; + goto fail; if (var->video_group[0] && (ret = add_renditions_to_variant(c, var, AVMEDIA_TYPE_VIDEO, var->video_group)) < 0) - return ret; + goto fail; if (var->subtitles_group[0] && (ret = add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group)) < 0) - return ret; + goto fail; } if ((ret = add_renditions_to_playlists(c, AVMEDIA_TYPE_AUDIO)) < 0 || (ret = add_renditions_to_playlists(c, AVMEDIA_TYPE_VIDEO)) < 0) - return ret; + goto fail; /* Create a program for each variant */ for (i = 0; i < c->n_variants; i++) { @@ -2747,8 +2938,10 @@ static int hls_read_header(AVFormatContext *s) AVProgram *program; program = av_new_program(s, i); - if (!program) - return AVERROR(ENOMEM); + if (!program) { + ret = AVERROR(ENOMEM); + goto fail; + } av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0); } @@ -2769,8 +2962,10 @@ static int hls_read_header(AVFormatContext *s) for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; - if (!(pls->ctx = avformat_alloc_context())) - return AVERROR(ENOMEM); + if (!(pls->ctx = avformat_alloc_context())) { + ret = AVERROR(ENOMEM); + goto fail; + } if (pls->n_segments == 0) continue; @@ -2779,15 +2974,39 @@ static int hls_read_header(AVFormatContext *s) pls->needed = 1; pls->parent = s; - if ((ret = open_playlist_demuxer(s, pls, highest_cur_seq_no)) < 0) - return ret; + for (int n = 0; n < pls->n_segments; n++) + if (pls->segments[n]->key_type == KEY_SAMPLE_AES) + has_sample_aes = 1; + } + + /* Opening SAMPLE-AES playlists sets up decryption state shared by all + * playlists (c->crypto_ctx), so they are opened one after another. */ + parallel = HAVE_THREADS && c->parallel_open > 1 && !has_sample_aes; + if (parallel) { + c->in_parallel = 1; + ret = run_playlist_jobs(s, open_playlist_demuxer, highest_cur_seq_no, rets); + c->in_parallel = 0; + update_seekability(c); + if (ret < 0) + goto fail; + } + + for (i = 0; i < c->n_playlists; i++) { + struct playlist *pls = c->playlists[i]; + + if (pls->n_segments == 0) + continue; + + ret = parallel ? rets[i] : open_playlist_demuxer(s, pls, highest_cur_seq_no); + if (ret < 0) + goto fail; pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER); /* Create new AVStreams for each stream in this playlist */ ret = update_streams_from_subdemuxer(s, pls); if (ret < 0) - return ret; + goto fail; /* * Copy any metadata from playlist to main streams, but do not set @@ -2809,8 +3028,11 @@ static int hls_read_header(AVFormatContext *s) } update_noheader_flag(s); + ret = 0; - return 0; +fail: + av_free(rets); + return ret; } static int recheck_discard_flags(AVFormatContext *s, int first) @@ -3365,6 +3587,8 @@ static const AVOption hls_options[] = { OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS}, {"seg_max_retry", "Maximum number of times to reload a segment on error.", OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS}, + {"parallel_open", "Maximum number of playlists to load in parallel when opening (0 or 1 loads them one after another)", + OFFSET(parallel_open), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS}, {NULL} }; diff --git a/tests/fate/demux.mak b/tests/fate/demux.mak index d4ac666f810..5d2427e585e 100644 --- a/tests/fate/demux.mak +++ b/tests/fate/demux.mak @@ -233,6 +233,29 @@ fate-hls-shared-renditions-demux: tests/data/hls-shared-renditions.m3u8 fate-hls-shared-renditions-demux: CLEANFILES = tests/data/hls-shared-renditions*.m3u8 tests/data/hls-shared-renditions-*.ts tests/data/hls-shared-renditions-*.vtt fate-hls-shared-renditions-demux: CMD = ffprobe_demux $(TARGET_PATH)/tests/data/hls-shared-renditions.m3u8 -show_programs +tests/data/hls-parallel.m3u8: TAG = GEN +tests/data/hls-parallel.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data + $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< -nostdin \ + -f lavfi -i "aevalsrc=0:s=16000:d=2" -c:a mp2fixed -flags +bitexact -muxdelay 0 -muxpreload 0 \ + -f hls -hls_time 2 -hls_list_size 0 \ + -hls_segment_filename $(TARGET_PATH)/tests/data/hls-parallel-a%d.ts \ + $(TARGET_PATH)/tests/data/hls-parallel-a.m3u8 2>/dev/null + $(Q)$(TARGET_EXEC) $(TARGET_PATH)/$< -nostdin \ + -f lavfi -i "aevalsrc=0:s=32000:d=2" -c:a mp2fixed -flags +bitexact -muxdelay 0 -muxpreload 0 \ + -f hls -hls_time 2 -hls_list_size 0 \ + -hls_segment_filename $(TARGET_PATH)/tests/data/hls-parallel-b%d.ts \ + $(TARGET_PATH)/tests/data/hls-parallel-b.m3u8 2>/dev/null + $(Q)printf '#EXTM3U\n#EXT-X-TARGETDURATION:2\n#EXTINF:2,\nhls-parallel-s0.vtt\n#EXT-X-ENDLIST\n' > tests/data/hls-parallel-s.m3u8 + $(Q)printf 'WEBVTT\n\n00:00.500 --> 00:01.500\nfirst\n' > tests/data/hls-parallel-s0.vtt + $(Q)printf '#EXTM3U\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a",NAME="German",LANGUAGE="de",URI="hls-parallel-b.m3u8"\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="s",NAME="English",LANGUAGE="en",URI="hls-parallel-s.m3u8"\n#EXT-X-STREAM-INF:BANDWIDTH=64000,AUDIO="a",SUBTITLES="s"\nhls-parallel-a.m3u8\n' > $@ + +FATE_HLS_PARALLEL-$(call FILTERDEMDECENCMUX, AEVALSRC ARESAMPLE, HLS MPEGTS WEBVTT, MP3FLOAT PCM_F64LE, MP2FIXED, HLS MPEGTS, LAVFI_INDEV MPEGAUDIO_PARSER) += fate-hls-parallel-open-demux fate-hls-parallel-open-sequential-demux +FATE_FFMPEG_FFPROBE += $(FATE_HLS_PARALLEL-yes) +fate-hls-parallel-open-demux fate-hls-parallel-open-sequential-demux: tests/data/hls-parallel.m3u8 +fate-hls-parallel-open-demux: CMD = ffprobe_demux $(TARGET_PATH)/tests/data/hls-parallel.m3u8 -show_programs -parallel_open 4 +fate-hls-parallel-open-sequential-demux: CMD = ffprobe_demux $(TARGET_PATH)/tests/data/hls-parallel.m3u8 -show_programs +fate-hls-parallel-open-sequential-demux: REF = $(SRC_PATH)/tests/ref/fate/hls-parallel-open-demux + FATE_SAMPLES_DEMUX-$(call PARSERDEM, JPEGXS, IMAGE_JPEGXS_PIPE, CONCAT_PROTOCOL) += fate-jxs-concat-demux fate-jxs-concat-demux: CMD = framecrc "-i concat:$(TARGET_SAMPLES)/jxs/lena.jxs|$(TARGET_SAMPLES)/jxs/lena.jxs -c:v copy" diff --git a/tests/ref/fate/hls-parallel-open-demux b/tests/ref/fate/hls-parallel-open-demux new file mode 100644 index 00000000000..522b52fc50a --- /dev/null +++ b/tests/ref/fate/hls-parallel-open-demux @@ -0,0 +1,92 @@ +packet|codec_type=audio|stream_index=0|pts=0|pts_time=0.000000|dts=0|dts_time=0.000000|duration=3240|duration_time=0.036000|size=1728|pos=564|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=0|pts_time=0.000000|dts=0|dts_time=0.000000|duration=N/A|duration_time=N/A|size=1440|pos=564|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=3240|pts_time=0.036000|dts=3240|dts_time=0.036000|duration=3240|duration_time=0.036000|size=1728|pos=2444|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=6480|pts_time=0.072000|dts=6480|dts_time=0.072000|duration=3240|duration_time=0.036000|size=1728|pos=4324|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=6480|pts_time=0.072000|dts=6480|dts_time=0.072000|duration=6480|duration_time=0.072000|size=1440|pos=2068|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=9720|pts_time=0.108000|dts=9720|dts_time=0.108000|duration=3240|duration_time=0.036000|size=1728|pos=6204|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=12960|pts_time=0.144000|dts=12960|dts_time=0.144000|duration=3240|duration_time=0.036000|size=1728|pos=8084|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=12960|pts_time=0.144000|dts=12960|dts_time=0.144000|duration=6480|duration_time=0.072000|size=1440|pos=3572|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=16200|pts_time=0.180000|dts=16200|dts_time=0.180000|duration=3240|duration_time=0.036000|size=1728|pos=9964|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=19440|pts_time=0.216000|dts=19440|dts_time=0.216000|duration=3240|duration_time=0.036000|size=1728|pos=11844|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=19440|pts_time=0.216000|dts=19440|dts_time=0.216000|duration=6480|duration_time=0.072000|size=1440|pos=5076|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=22680|pts_time=0.252000|dts=22680|dts_time=0.252000|duration=3240|duration_time=0.036000|size=1728|pos=13724|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=25920|pts_time=0.288000|dts=25920|dts_time=0.288000|duration=3240|duration_time=0.036000|size=1728|pos=15604|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=25920|pts_time=0.288000|dts=25920|dts_time=0.288000|duration=6480|duration_time=0.072000|size=1440|pos=6580|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=29160|pts_time=0.324000|dts=29160|dts_time=0.324000|duration=3240|duration_time=0.036000|size=1728|pos=17484|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=32400|pts_time=0.360000|dts=32400|dts_time=0.360000|duration=3240|duration_time=0.036000|size=1728|pos=19364|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=32400|pts_time=0.360000|dts=32400|dts_time=0.360000|duration=6480|duration_time=0.072000|size=1440|pos=8084|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=35640|pts_time=0.396000|dts=35640|dts_time=0.396000|duration=3240|duration_time=0.036000|size=1728|pos=21244|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=38880|pts_time=0.432000|dts=38880|dts_time=0.432000|duration=3240|duration_time=0.036000|size=1728|pos=23124|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=38880|pts_time=0.432000|dts=38880|dts_time=0.432000|duration=6480|duration_time=0.072000|size=1440|pos=9588|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=42120|pts_time=0.468000|dts=42120|dts_time=0.468000|duration=3240|duration_time=0.036000|size=1728|pos=25004|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=45360|pts_time=0.504000|dts=45360|dts_time=0.504000|duration=3240|duration_time=0.036000|size=1728|pos=26884|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=45360|pts_time=0.504000|dts=45360|dts_time=0.504000|duration=6480|duration_time=0.072000|size=1440|pos=11092|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=48600|pts_time=0.540000|dts=48600|dts_time=0.540000|duration=3240|duration_time=0.036000|size=1728|pos=28764|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=51840|pts_time=0.576000|dts=51840|dts_time=0.576000|duration=3240|duration_time=0.036000|size=1728|pos=30644|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=51840|pts_time=0.576000|dts=51840|dts_time=0.576000|duration=6480|duration_time=0.072000|size=1440|pos=12596|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=55080|pts_time=0.612000|dts=55080|dts_time=0.612000|duration=3240|duration_time=0.036000|size=1728|pos=32524|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=58320|pts_time=0.648000|dts=58320|dts_time=0.648000|duration=3240|duration_time=0.036000|size=1728|pos=34404|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=58320|pts_time=0.648000|dts=58320|dts_time=0.648000|duration=6480|duration_time=0.072000|size=1440|pos=14100|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=61560|pts_time=0.684000|dts=61560|dts_time=0.684000|duration=3240|duration_time=0.036000|size=1728|pos=36284|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=64800|pts_time=0.720000|dts=64800|dts_time=0.720000|duration=3240|duration_time=0.036000|size=1728|pos=38164|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=64800|pts_time=0.720000|dts=64800|dts_time=0.720000|duration=6480|duration_time=0.072000|size=1440|pos=15604|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=68040|pts_time=0.756000|dts=68040|dts_time=0.756000|duration=3240|duration_time=0.036000|size=1728|pos=40044|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=71280|pts_time=0.792000|dts=71280|dts_time=0.792000|duration=3240|duration_time=0.036000|size=1728|pos=41924|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=71280|pts_time=0.792000|dts=71280|dts_time=0.792000|duration=6480|duration_time=0.072000|size=1440|pos=17108|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=74520|pts_time=0.828000|dts=74520|dts_time=0.828000|duration=3240|duration_time=0.036000|size=1728|pos=43804|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=77760|pts_time=0.864000|dts=77760|dts_time=0.864000|duration=3240|duration_time=0.036000|size=1728|pos=45684|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=77760|pts_time=0.864000|dts=77760|dts_time=0.864000|duration=6480|duration_time=0.072000|size=1440|pos=18612|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=81000|pts_time=0.900000|dts=81000|dts_time=0.900000|duration=3240|duration_time=0.036000|size=1728|pos=47564|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=84240|pts_time=0.936000|dts=84240|dts_time=0.936000|duration=3240|duration_time=0.036000|size=1728|pos=49444|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=84240|pts_time=0.936000|dts=84240|dts_time=0.936000|duration=6480|duration_time=0.072000|size=1440|pos=20116|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=87480|pts_time=0.972000|dts=87480|dts_time=0.972000|duration=3240|duration_time=0.036000|size=1728|pos=51324|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=90720|pts_time=1.008000|dts=90720|dts_time=1.008000|duration=3240|duration_time=0.036000|size=1728|pos=53204|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=90720|pts_time=1.008000|dts=90720|dts_time=1.008000|duration=6480|duration_time=0.072000|size=1440|pos=21620|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=93960|pts_time=1.044000|dts=93960|dts_time=1.044000|duration=3240|duration_time=0.036000|size=1728|pos=55084|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=97200|pts_time=1.080000|dts=97200|dts_time=1.080000|duration=3240|duration_time=0.036000|size=1728|pos=56964|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=97200|pts_time=1.080000|dts=97200|dts_time=1.080000|duration=6480|duration_time=0.072000|size=1440|pos=23124|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=100440|pts_time=1.116000|dts=100440|dts_time=1.116000|duration=3240|duration_time=0.036000|size=1728|pos=58844|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=103680|pts_time=1.152000|dts=103680|dts_time=1.152000|duration=3240|duration_time=0.036000|size=1728|pos=60724|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=103680|pts_time=1.152000|dts=103680|dts_time=1.152000|duration=6480|duration_time=0.072000|size=1440|pos=24628|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=106920|pts_time=1.188000|dts=106920|dts_time=1.188000|duration=3240|duration_time=0.036000|size=1728|pos=62604|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=110160|pts_time=1.224000|dts=110160|dts_time=1.224000|duration=3240|duration_time=0.036000|size=1728|pos=64484|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=110160|pts_time=1.224000|dts=110160|dts_time=1.224000|duration=6480|duration_time=0.072000|size=1440|pos=26132|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=113400|pts_time=1.260000|dts=113400|dts_time=1.260000|duration=3240|duration_time=0.036000|size=1728|pos=66364|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=116640|pts_time=1.296000|dts=116640|dts_time=1.296000|duration=3240|duration_time=0.036000|size=1728|pos=68244|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=116640|pts_time=1.296000|dts=116640|dts_time=1.296000|duration=6480|duration_time=0.072000|size=1440|pos=27636|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=119880|pts_time=1.332000|dts=119880|dts_time=1.332000|duration=3240|duration_time=0.036000|size=1728|pos=70124|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=123120|pts_time=1.368000|dts=123120|dts_time=1.368000|duration=3240|duration_time=0.036000|size=1728|pos=72004|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=123120|pts_time=1.368000|dts=123120|dts_time=1.368000|duration=6480|duration_time=0.072000|size=1440|pos=29140|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=126360|pts_time=1.404000|dts=126360|dts_time=1.404000|duration=3240|duration_time=0.036000|size=1728|pos=73884|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=129600|pts_time=1.440000|dts=129600|dts_time=1.440000|duration=3240|duration_time=0.036000|size=1728|pos=75764|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=129600|pts_time=1.440000|dts=129600|dts_time=1.440000|duration=6480|duration_time=0.072000|size=1440|pos=30644|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=132840|pts_time=1.476000|dts=132840|dts_time=1.476000|duration=3240|duration_time=0.036000|size=1728|pos=77644|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=136080|pts_time=1.512000|dts=136080|dts_time=1.512000|duration=3240|duration_time=0.036000|size=1728|pos=79524|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=136080|pts_time=1.512000|dts=136080|dts_time=1.512000|duration=6480|duration_time=0.072000|size=1440|pos=32148|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=139320|pts_time=1.548000|dts=139320|dts_time=1.548000|duration=3240|duration_time=0.036000|size=1728|pos=81404|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=142560|pts_time=1.584000|dts=142560|dts_time=1.584000|duration=3240|duration_time=0.036000|size=1728|pos=83284|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=142560|pts_time=1.584000|dts=142560|dts_time=1.584000|duration=6480|duration_time=0.072000|size=1440|pos=33652|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=145800|pts_time=1.620000|dts=145800|dts_time=1.620000|duration=3240|duration_time=0.036000|size=1728|pos=85164|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=149040|pts_time=1.656000|dts=149040|dts_time=1.656000|duration=3240|duration_time=0.036000|size=1728|pos=87044|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=149040|pts_time=1.656000|dts=149040|dts_time=1.656000|duration=6480|duration_time=0.072000|size=1440|pos=35156|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=152280|pts_time=1.692000|dts=152280|dts_time=1.692000|duration=3240|duration_time=0.036000|size=1728|pos=88924|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=155520|pts_time=1.728000|dts=155520|dts_time=1.728000|duration=3240|duration_time=0.036000|size=1728|pos=90804|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=155520|pts_time=1.728000|dts=155520|dts_time=1.728000|duration=6480|duration_time=0.072000|size=1440|pos=36660|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=158760|pts_time=1.764000|dts=158760|dts_time=1.764000|duration=3240|duration_time=0.036000|size=1728|pos=92684|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=162000|pts_time=1.800000|dts=162000|dts_time=1.800000|duration=3240|duration_time=0.036000|size=1728|pos=94564|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=162000|pts_time=1.800000|dts=162000|dts_time=1.800000|duration=6480|duration_time=0.072000|size=1440|pos=38164|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=165240|pts_time=1.836000|dts=165240|dts_time=1.836000|duration=3240|duration_time=0.036000|size=1728|pos=96444|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=168480|pts_time=1.872000|dts=168480|dts_time=1.872000|duration=3240|duration_time=0.036000|size=1728|pos=98324|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=168480|pts_time=1.872000|dts=168480|dts_time=1.872000|duration=6480|duration_time=0.072000|size=1440|pos=39668|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=171720|pts_time=1.908000|dts=171720|dts_time=1.908000|duration=3240|duration_time=0.036000|size=1728|pos=100204|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=174960|pts_time=1.944000|dts=174960|dts_time=1.944000|duration=3240|duration_time=0.036000|size=1728|pos=102084|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=2|pts=174960|pts_time=1.944000|dts=174960|dts_time=1.944000|duration=6480|duration_time=0.072000|size=1440|pos=41172|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +packet|codec_type=audio|stream_index=0|pts=178200|pts_time=1.980000|dts=178200|dts_time=1.980000|duration=3240|duration_time=0.036000|size=1728|pos=103964|flags=K__|data_hash=CRC32:9e22ed1d|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192 +program|program_id=0|program_num=0|nb_streams=3|pmt_pid=0|pcr_pid=0|tag:variant_bitrate=64000 +stream|index=0|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[3][0][0][0]|codec_tag=0x0003|mime_codec_string=mp4a.40.33|sample_fmt=fltp|sample_rate=32000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|id=0x0|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=384000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=56|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000|tag:language=de|tag :comment=German +stream|index=1|codec_name=webvtt|profile=unknown|level=-99|codec_type=subtitle|codec_tag_string=[0][0][0][0]|codec_tag=0x0000|width=N/A|height=N/A|id=0x1|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/1000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=N/A|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000|tag:language=en|tag:comment=English +stream|index=2|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[4][0][0][0]|codec_tag=0x0004|mime_codec_string=mp4a.40.33|sample_fmt=unknown|sample_rate=16000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|id=0x2|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=160000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=28|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000 +stream|index=0|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[3][0][0][0]|codec_tag=0x0003|mime_codec_string=mp4a.40.33|sample_fmt=fltp|sample_rate=32000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|id=0x0|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=384000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=56|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000|tag:language=de|tag :comment=German +stream|index=1|codec_name=webvtt|profile=unknown|level=-99|codec_type=subtitle|codec_tag_string=[0][0][0][0]|codec_tag=0x0000|width=N/A|height=N/A|id=0x1|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/1000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=N/A|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000|tag:language=en|tag:comment=English +stream|index=2|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[4][0][0][0]|codec_tag=0x0004|mime_codec_string=mp4a.40.33|sample_fmt=unknown|sample_rate=16000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|id=0x2|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=160000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=28|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000 +format|filename=hls-parallel.m3u8|nb_streams=3|nb_programs=1|nb_stream_groups=0|format_name=hls|start_time=0.000000|duration=2.016000|size=273|bit_rate=1083|probe_score=100 -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
