diff options
Diffstat (limited to 'demux/demux.c')
-rw-r--r-- | demux/demux.c | 3506 |
1 files changed, 2414 insertions, 1092 deletions
diff --git a/demux/demux.c b/demux/demux.c index 8a1e9b0b82..5997a96ed6 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -15,29 +15,31 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <assert.h> -#include <unistd.h> +#include <float.h> #include <limits.h> -#include <pthread.h> -#include <stdint.h> - #include <math.h> - -#include <sys/types.h> +#include <stdatomic.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include "cache.h" #include "config.h" #include "options/m_config.h" #include "options/m_option.h" #include "mpv_talloc.h" +#include "common/av_common.h" #include "common/msg.h" #include "common/global.h" #include "common/recorder.h" +#include "common/stats.h" +#include "misc/charset_conv.h" #include "misc/thread_tools.h" -#include "osdep/atomic.h" #include "osdep/timer.h" #include "osdep/threads.h" @@ -56,16 +58,14 @@ extern const demuxer_desc_t demuxer_desc_mf; extern const demuxer_desc_t demuxer_desc_matroska; extern const demuxer_desc_t demuxer_desc_lavf; extern const demuxer_desc_t demuxer_desc_playlist; +extern const demuxer_desc_t demuxer_desc_disc; extern const demuxer_desc_t demuxer_desc_rar; extern const demuxer_desc_t demuxer_desc_libarchive; extern const demuxer_desc_t demuxer_desc_null; extern const demuxer_desc_t demuxer_desc_timeline; -/* Please do not add any new demuxers here. If you want to implement a new - * demuxer, add it to libavformat, except for wrappers around external - * libraries and demuxers requiring binary support. */ - -const demuxer_desc_t *const demuxer_list[] = { +static const demuxer_desc_t *const demuxer_list[] = { + &demuxer_desc_disc, &demuxer_desc_edl, &demuxer_desc_cue, &demuxer_desc_rawaudio, @@ -81,39 +81,43 @@ const demuxer_desc_t *const demuxer_list[] = { NULL }; -struct demux_opts { - int enable_cache; - int64_t max_bytes; - int64_t max_bytes_bw; - double min_secs; - int force_seekable; - double min_secs_cache; - int access_references; - int seekable_cache; - int create_ccs; - char *record_file; -}; - #define OPT_BASE_STRUCT struct demux_opts -#define MAX_BYTES MPMIN(INT64_MAX, SIZE_MAX / 2) +static bool get_demux_sub_opts(int index, const struct m_sub_options **sub); const struct m_sub_options demux_conf = { .opts = (const struct m_option[]){ - OPT_CHOICE("cache", enable_cache, 0, - ({"no", 0}, {"auto", -1}, {"yes", 1})), - OPT_DOUBLE("demuxer-readahead-secs", min_secs, M_OPT_MIN, .min = 0), - // (The MAX_BYTES sizes may not be accurate because the max field is - // of double type.) - OPT_BYTE_SIZE("demuxer-max-bytes", max_bytes, 0, 0, MAX_BYTES), - OPT_BYTE_SIZE("demuxer-max-back-bytes", max_bytes_bw, 0, 0, MAX_BYTES), - OPT_FLAG("force-seekable", force_seekable, 0), - OPT_DOUBLE("cache-secs", min_secs_cache, M_OPT_MIN, .min = 0), - OPT_FLAG("access-references", access_references, 0), - OPT_CHOICE("demuxer-seekable-cache", seekable_cache, 0, - ({"auto", -1}, {"no", 0}, {"yes", 1})), - OPT_FLAG("sub-create-cc-track", create_ccs, 0), - OPT_STRING("stream-record", record_file, 0), + {"cache", OPT_CHOICE(enable_cache, + {"no", 0}, {"auto", -1}, {"yes", 1})}, + {"cache-on-disk", OPT_BOOL(disk_cache)}, + {"demuxer-readahead-secs", OPT_DOUBLE(min_secs), M_RANGE(0, DBL_MAX)}, + {"demuxer-hysteresis-secs", OPT_DOUBLE(hyst_secs), M_RANGE(0, DBL_MAX)}, + {"demuxer-max-bytes", OPT_BYTE_SIZE(max_bytes), + M_RANGE(0, M_MAX_MEM_BYTES)}, + {"demuxer-max-back-bytes", OPT_BYTE_SIZE(max_bytes_bw), + M_RANGE(0, M_MAX_MEM_BYTES)}, + {"demuxer-donate-buffer", OPT_BOOL(donate_fw)}, + {"force-seekable", OPT_BOOL(force_seekable)}, + {"cache-secs", OPT_DOUBLE(min_secs_cache), M_RANGE(0, DBL_MAX)}, + {"access-references", OPT_BOOL(access_references)}, + {"demuxer-seekable-cache", OPT_CHOICE(seekable_cache, + {"auto", -1}, {"no", 0}, {"yes", 1})}, + {"index", OPT_CHOICE(index_mode, {"default", 1}, {"recreate", 0})}, + {"mf-fps", OPT_DOUBLE(mf_fps)}, + {"mf-type", OPT_STRING(mf_type)}, + {"sub-create-cc-track", OPT_BOOL(create_ccs)}, + {"stream-record", OPT_STRING(record_file)}, + {"video-backward-overlap", OPT_CHOICE(video_back_preroll, {"auto", -1}), + M_RANGE(0, 1024)}, + {"audio-backward-overlap", OPT_CHOICE(audio_back_preroll, {"auto", -1}), + M_RANGE(0, 1024)}, + {"video-backward-batch", OPT_INT(back_batch[STREAM_VIDEO]), + M_RANGE(0, 1024)}, + {"audio-backward-batch", OPT_INT(back_batch[STREAM_AUDIO]), + M_RANGE(0, 1024)}, + {"demuxer-backward-playback-step", OPT_DOUBLE(back_seek_size), + M_RANGE(0, DBL_MAX)}, + {"metadata-codepage", OPT_STRING(meta_cp)}, {0} }, .size = sizeof(struct demux_opts), @@ -121,30 +125,43 @@ const struct m_sub_options demux_conf = { .enable_cache = -1, // auto .max_bytes = 150 * 1024 * 1024, .max_bytes_bw = 50 * 1024 * 1024, + .donate_fw = true, .min_secs = 1.0, - .min_secs_cache = 10.0 * 60 * 60, + .min_secs_cache = 1000.0 * 60 * 60, .seekable_cache = -1, - .access_references = 1, + .index_mode = 1, + .mf_fps = 1.0, + .access_references = true, + .video_back_preroll = -1, + .audio_back_preroll = -1, + .back_seek_size = 60, + .back_batch = { + [STREAM_VIDEO] = 1, + [STREAM_AUDIO] = 10, + }, + .meta_cp = "auto", }, + .get_sub_options = get_demux_sub_opts, }; struct demux_internal { struct mp_log *log; + struct mpv_global *global; + struct stats_ctx *stats; - struct demux_opts *opts; + bool can_cache; // not a slave demuxer; caching makes sense + bool can_record; // stream recording is allowed // The demuxer runs potentially in another thread, so we keep two demuxer // structs; the real demuxer can access the shadow struct only. struct demuxer *d_thread; // accessed by demuxer impl. (producer) struct demuxer *d_user; // accessed by player (consumer) - bool owns_stream; - // The lock protects the packet queues (struct demux_stream), // and the fields below. - pthread_mutex_t lock; - pthread_cond_t wakeup; - pthread_t thread; + mp_mutex lock; + mp_cond wakeup; + mp_thread thread; // -- All the following fields are protected by lock. @@ -157,33 +174,51 @@ struct demux_internal { struct sh_stream **streams; int num_streams; - // If non-NULL, a _selected_ stream which is used for global (timed) - // metadata. It will be an arbitrary stream that is hopefully not sparse - // (i.e. not a subtitle stream). This is needed because due to variable - // interleaving multiple streams won't agree whether timed metadata is in - // effect yet at the same time position. - struct demux_stream *master_stream; + char *meta_charset; + + // If non-NULL, a stream which is used for global (timed) metadata. It will + // be an arbitrary stream, which hopefully will happen to work. + struct sh_stream *metadata_stream; int events; + struct demux_cache *cache; + bool warned_queue_overflow; - bool last_eof; // last actual global EOF status - bool eof; // whether we're in EOF state (reset for retry) - bool idle; - bool autoselect; + bool eof; // whether we're in EOF state double min_secs; + double hyst_secs; // stop reading till there's hyst_secs remaining + bool hyst_active; size_t max_bytes; size_t max_bytes_bw; bool seekable_cache; - - // At least one decoder actually requested data since init or the last seek. - // Do this to allow the decoder thread to select streams before starting. + bool using_network_cache_opts; + char *record_filename; + + // Whether the demuxer thread should prefetch packets. This is set to false + // if EOF was reached or the demuxer cache is full. This is also important + // in the initial state: the decoder thread needs to select streams before + // the first packet is read, so this is set to true by packet reading only. + // Reset to false again on EOF or if prefetching is done. bool reading; - // Set if we know that we are at the start of the file. This is used to + // Set if we just performed a seek, without reading packets yet. Used to // avoid a redundant initial seek after enabling streams. We could just // allow it, but to avoid buggy seeking affecting normal playback, we don't. - bool initial_state; + bool after_seek; + // Set in addition to after_seek if we think we seeked to the start of the + // file (or if the demuxer was just opened). + bool after_seek_to_start; + + // Demuxing backwards. Since demuxer implementations don't support this + // directly, it is emulated by seeking backwards for every packet run. Also, + // packets between keyframes are demuxed forwards (you can't decode that + // stuff otherwise), which adds complexity on top of it. + bool back_demuxing; + + // For backward demuxing: + bool need_back_seek; // back-step seek needs to be triggered + bool back_any_need_recheck; // at least 1 ds->back_need_recheck set bool tracks_switched; // thread needs to inform demuxer of this @@ -198,18 +233,14 @@ struct demux_internal { double ts_offset; // timestamp offset to apply to everything - void (*run_fn)(void *); // if non-NULL, function queued to be run on - void *run_fn_arg; // the thread as run_fn(run_fn_arg) - // (sorted by least recent use: index 0 is least recently used) struct demux_cached_range **ranges; int num_ranges; size_t total_bytes; // total sum of packet data buffered - size_t fw_bytes; // sum of forward packet data in current_range - // Range from which decoder is reading, and to which demuxer is appending. - // This is never NULL. This is always ranges[num_ranges - 1]. + // This is normally never NULL. This is always ranges[num_ranges - 1]. + // This is can be NULL during initialization or deinitialization. struct demux_cached_range *current_range; double highest_av_pts; // highest non-subtitle PTS seen - for duration @@ -221,14 +252,33 @@ struct demux_internal { // Cached state. int64_t stream_size; int64_t last_speed_query; + double speed_query_prev_sample; uint64_t bytes_per_second; int64_t next_cache_update; - // Updated during init only. - char *stream_base_filename; + + // demux user state (user thread, somewhat similar to reader/decoder state) + double last_playback_pts; // last playback_pts from demux_update() + bool force_metadata_update; + int cached_metadata_index; // speed up repeated lookups + + struct mp_recorder *dumper; + int dumper_status; + + bool owns_stream; // -- Access from demuxer thread only bool enable_recording; struct mp_recorder *recorder; + int64_t slave_unbuffered_read_bytes; // value repoted from demuxer impl. + int64_t hack_unbuffered_read_bytes; // for demux_get_bytes_read_hack() + int64_t cache_unbuffered_read_bytes; // for demux_reader_state.bytes_per_second + int64_t byte_level_seeks; // for demux_reader_state.byte_level_seeks +}; + +struct timed_metadata { + double pts; + struct mp_tags *tags; + bool from_stream; }; // A continuous range of cached packets for all enabled streams. @@ -244,9 +294,26 @@ struct demux_cached_range { bool is_bof; // set if the file begins with this range bool is_eof; // set if the file ends with this range + + struct timed_metadata **metadata; + int num_metadata; }; -#define MAX_INDEX_ENTRIES 16 +#define QUEUE_INDEX_SIZE_MASK(queue) ((queue)->index_size - 1) + +// Access the idx-th entry in the given demux_queue. +// Requirement: idx >= 0 && idx < queue->num_index +#define QUEUE_INDEX_ENTRY(queue, idx) \ + ((queue)->index[((queue)->index0 + (idx)) & QUEUE_INDEX_SIZE_MASK(queue)]) + +// Don't index packets whose timestamps that are within the last index entry by +// this amount of time (it's better to seek them manually). +#define INDEX_STEP_SIZE 1.0 + +struct index_entry { + double pts; + struct demux_packet *pkt; +}; // A continuous list of cached packets for a single stream/range. There is one // for each stream and range. Also contains some state for use during demuxing @@ -258,17 +325,18 @@ struct demux_queue { struct demux_packet *head; struct demux_packet *tail; - struct demux_packet *next_prune_target; // cached value for faster pruning + uint64_t tail_cum_pos; // cumulative size including tail packet bool correct_dts; // packet DTS is strictly monotonically increasing bool correct_pos; // packet pos is strictly monotonically increasing int64_t last_pos; // for determining correct_pos + int64_t last_pos_fixup; // for filling in unset dp->pos values double last_dts; // for determining correct_dts double last_ts; // timestamp of the last packet added to queue // for incrementally determining seek PTS range - double keyframe_pts, keyframe_end_pts; struct demux_packet *keyframe_latest; + struct demux_packet *keyframe_first; // cached value of first KF packet // incrementally maintained seek range, possibly invalid double seek_start, seek_end; @@ -277,11 +345,11 @@ struct demux_queue { bool is_bof; // started demuxing at beginning of file bool is_eof; // received true EOF here - // incomplete index to somewhat speed up seek operations - // the entries in index[] must be in packet queue append/removal order - int num_index; // valid index[] entries - double index_distance; // minimum keyframe distance to add index element - struct demux_packet *index[MAX_INDEX_ENTRIES]; + // Complete index, though it may skip some entries to reduce density. + struct index_entry *index; // ring buffer + size_t index_size; // size of index[] (0 or a power of 2) + size_t index0; // first index entry + size_t num_index; // number of index entries (wraps on index_size) }; struct demux_stream { @@ -299,7 +367,7 @@ struct demux_stream { bool eager; // try to keep at least 1 packet queued // if false, this stream is disabled, or passively // read (like subtitles) - bool still_image; // stream has still video images + bool still_image; // stream consists of multiple sparse still images bool refreshing; // finding old position after track switches bool eof; // end of demuxed stream? (true if no more packets) @@ -315,53 +383,81 @@ struct demux_stream { double last_br_ts; // timestamp of last packet bitrate was calculated size_t last_br_bytes; // summed packet sizes since last bitrate calculation double bitrate; - size_t fw_packs; // number of packets in buffer (forward) - size_t fw_bytes; // total bytes of packets in buffer (forward) struct demux_packet *reader_head; // points at current decoder position bool skip_to_keyframe; bool attached_picture_added; bool need_wakeup; // call wakeup_cb on next reader_head state change + double force_read_until;// eager=false streams (subs): force read-ahead + + // For demux_internal.dumper. Currently, this is used only temporarily + // during blocking dumping. + struct demux_packet *dump_pos; // for refresh seeks: pos/dts of last packet returned to reader int64_t last_ret_pos; double last_ret_dts; + // Backwards demuxing. + bool back_need_recheck; // flag for incremental find_backward_restart_pos work + // pos/dts of the previous keyframe packet returned; always valid if back- + // demuxing is enabled, and back_restart_eof/back_restart_next are false. + int64_t back_restart_pos; + double back_restart_dts; + bool back_restart_eof; // restart position is at EOF; overrides pos/dts + bool back_restart_next; // restart before next keyframe; overrides above + bool back_restarting; // searching keyframe before restart pos + // Current PTS lower bound for back demuxing. + double back_seek_pos; + // pos/dts of the packet to resume demuxing from when another stream caused + // a seek backward to get more packets. reader_head will be reset to this + // packet as soon as it's encountered again. + int64_t back_resume_pos; + double back_resume_dts; + bool back_resuming; // resuming mode (above fields are valid/used) + // Set to true if the first packet (keyframe) of a range was returned. + bool back_range_started; + // Number of KF packets at start of range yet to return. -1 is used for BOF. + int back_range_count; + // Number of KF packets yet to return that are marked as preroll. + int back_range_preroll; + // Static packet preroll count. + int back_preroll; + // for closed captions (demuxer_feed_caption) struct sh_stream *cc; bool ignore_eof; // ignore stream in underrun detection - - // timed metadata - struct mp_packet_tags *tags_demux; // demuxer state (last updated metadata) - struct mp_packet_tags *tags_reader; // reader state (last returned packet) - struct mp_packet_tags *tags_init; // global state at start of demuxing -}; - -// "Snapshot" of the tag state. Refcounted to avoid a copy per packet. -struct mp_packet_tags { - mp_atomic_int64 refcount; - struct mp_tags *demux; // demuxer global tags (normal thing) - struct mp_tags *stream; // byte stream tags (ICY crap) - struct mp_tags *sh; // per sh_stream tags (e.g. OGG) }; -// Return "a", or if that is NOPTS, return "def". -#define PTS_OR_DEF(a, def) ((a) == MP_NOPTS_VALUE ? (def) : (a)) -// If one of the values is NOPTS, always pick the other one. -#define MP_PTS_MIN(a, b) MPMIN(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a)) -#define MP_PTS_MAX(a, b) MPMAX(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a)) - -#define MP_ADD_PTS(a, b) ((a) == MP_NOPTS_VALUE ? (a) : ((a) + (b))) - +static void switch_to_fresh_cache_range(struct demux_internal *in); static void demuxer_sort_chapters(demuxer_t *demuxer); -static void *demux_thread(void *pctx); +static MP_THREAD_VOID demux_thread(void *pctx); static void update_cache(struct demux_internal *in); +static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp); +static struct demux_packet *advance_reader_head(struct demux_stream *ds); +static bool queue_seek(struct demux_internal *in, double seek_pts, int flags, + bool clear_back_state); +static struct demux_packet *compute_keyframe_times(struct demux_packet *pkt, + double *out_kf_min, + double *out_kf_max); +static void find_backward_restart_pos(struct demux_stream *ds); +static struct demux_packet *find_seek_target(struct demux_queue *queue, + double pts, int flags); +static void prune_old_packets(struct demux_internal *in); +static void dumper_close(struct demux_internal *in); +static void demux_convert_tags_charset(struct demuxer *demuxer); + +static uint64_t get_forward_buffered_bytes(struct demux_stream *ds) +{ + if (!ds->reader_head) + return 0; + return ds->queue->tail_cum_pos - ds->reader_head->cum_pos; +} #if 0 // very expensive check for redundant cached queue state static void check_queue_consistency(struct demux_internal *in) { - size_t total_bytes = 0; - size_t total_fw_bytes = 0; + uint64_t total_bytes = 0; assert(in->current_range && in->num_ranges > 0); assert(in->current_range == in->ranges[in->num_ranges - 1]); @@ -378,21 +474,21 @@ static void check_queue_consistency(struct demux_internal *in) assert(queue->range == range); size_t fw_bytes = 0; - size_t fw_packs = 0; bool is_forward = false; bool kf_found = false; - bool npt_found = false; - int next_index = 0; + bool kf1_found = false; + size_t next_index = 0; + uint64_t queue_total_bytes = 0; for (struct demux_packet *dp = queue->head; dp; dp = dp->next) { is_forward |= dp == queue->ds->reader_head; kf_found |= dp == queue->keyframe_latest; - npt_found |= dp == queue->next_prune_target; + kf1_found |= dp == queue->keyframe_first; size_t bytes = demux_packet_estimate_total_size(dp); total_bytes += bytes; + queue_total_bytes += bytes; if (is_forward) { fw_bytes += bytes; - fw_packs += 1; assert(range == in->current_range); assert(queue->ds->queue == queue); } @@ -401,33 +497,39 @@ static void check_queue_consistency(struct demux_internal *in) if (!dp->next) assert(queue->tail == dp); - if (next_index < queue->num_index && queue->index[next_index] == dp) + if (next_index < queue->num_index && + QUEUE_INDEX_ENTRY(queue, next_index).pkt == dp) next_index += 1; } if (!queue->head) assert(!queue->tail); assert(next_index == queue->num_index); + uint64_t queue_total_bytes2 = 0; + if (queue->head) + queue_total_bytes2 = queue->tail_cum_pos - queue->head->cum_pos; + + assert(queue_total_bytes == queue_total_bytes2); + // If the queue is currently used... if (queue->ds->queue == queue) { // ...reader_head and others must be in the queue. assert(is_forward == !!queue->ds->reader_head); assert(kf_found == !!queue->keyframe_latest); + uint64_t fw_bytes2 = get_forward_buffered_bytes(queue->ds); + assert(fw_bytes == fw_bytes2); } - assert(npt_found == !!queue->next_prune_target); + assert(kf1_found == !!queue->keyframe_first); - total_fw_bytes += fw_bytes; - - if (range == in->current_range) { - assert(queue->ds->fw_bytes == fw_bytes); - assert(queue->ds->fw_packs == fw_packs); - } else { - assert(fw_bytes == 0 && fw_packs == 0); + if (range != in->current_range) { + assert(fw_bytes == 0); } if (queue->keyframe_latest) assert(queue->keyframe_latest->keyframe); + + total_bytes += queue->index_size * sizeof(struct index_entry); } // Invariant needed by pruning; violation has worse effects than just @@ -437,71 +539,9 @@ static void check_queue_consistency(struct demux_internal *in) } assert(in->total_bytes == total_bytes); - assert(in->fw_bytes == total_fw_bytes); } #endif -void mp_packet_tags_unref(struct mp_packet_tags *tags) -{ - if (tags) { - if (atomic_fetch_add(&tags->refcount, -1) == 1) { - talloc_free(tags->sh); - talloc_free(tags->demux); - talloc_free(tags->stream); - talloc_free(tags); - } - } -} - -void mp_packet_tags_setref(struct mp_packet_tags **dst, struct mp_packet_tags *src) -{ - if (src) - atomic_fetch_add(&src->refcount, 1); - mp_packet_tags_unref(*dst); - *dst = src; -} - -static struct mp_tags *tags_dup_or_null(struct mp_tags *t) -{ - return t ? mp_tags_dup(NULL, t) : talloc_zero(NULL, struct mp_tags); -} - -// Return a "deep" copy. If tags==NULL, allocate a new one. -static struct mp_packet_tags *mp_packet_tags_copy(struct mp_packet_tags *tags) -{ - struct mp_packet_tags *new = talloc_ptrtype(NULL, new); - *new = (struct mp_packet_tags){ - .refcount = ATOMIC_VAR_INIT(1), - .demux = tags_dup_or_null(tags ? tags->demux : NULL), - .stream = tags_dup_or_null(tags ? tags->stream : NULL), - .sh = tags_dup_or_null(tags ? tags->sh : NULL), - }; - return new; -} - -// Force a copy if refcount != 1. -// (refcount==1 means we're the unambiguous owner.) -// If *tags==NULL, allocate a blank one. -static void mp_packet_tags_make_writable(struct mp_packet_tags **tags) -{ - if (*tags && atomic_load(&(*tags)->refcount) == 1) - return; - struct mp_packet_tags *new = mp_packet_tags_copy(*tags); - mp_packet_tags_unref(*tags); - *tags = new; -} - -static void recompute_buffers(struct demux_stream *ds) -{ - ds->fw_packs = 0; - ds->fw_bytes = 0; - - for (struct demux_packet *dp = ds->reader_head; dp; dp = dp->next) { - ds->fw_bytes += demux_packet_estimate_total_size(dp); - ds->fw_packs++; - } -} - // (this doesn't do most required things for a switch, like updating ds->queue) static void set_current_range(struct demux_internal *in, struct demux_cached_range *range) @@ -518,7 +558,32 @@ static void set_current_range(struct demux_internal *in, MP_TARRAY_APPEND(in, in->ranges, in->num_ranges, range); } -// Refresh range->seek_start/end. +static void prune_metadata(struct demux_cached_range *range) +{ + int first_needed = 0; + + if (range->seek_start == MP_NOPTS_VALUE) { + first_needed = range->num_metadata; + } else { + for (int n = 0; n < range->num_metadata ; n++) { + if (range->metadata[n]->pts > range->seek_start) + break; + first_needed = n; + } + } + + // Always preserve the last entry. + first_needed = MPMIN(first_needed, range->num_metadata - 1); + + // (Could make this significantly more efficient for large first_needed, + // however that might be very rare and even then it might not matter.) + for (int n = 0; n < first_needed; n++) { + talloc_free(range->metadata[0]); + MP_TARRAY_REMOVE_AT(range->metadata, range->num_metadata, 0); + } +} + +// Refresh range->seek_start/end. Idempotent. static void update_seek_ranges(struct demux_cached_range *range) { range->seek_start = range->seek_end = MP_NOPTS_VALUE; @@ -549,7 +614,9 @@ static void update_seek_ranges(struct demux_cached_range *range) range->is_bof &= queue->is_bof; bool empty = queue->is_eof && !queue->head; - if (queue->seek_start >= queue->seek_end && !empty) + if (queue->seek_start >= queue->seek_end && !empty && + !(queue->seek_start == queue->seek_end && + queue->seek_start != MP_NOPTS_VALUE)) goto broken; } } @@ -559,19 +626,30 @@ static void update_seek_ranges(struct demux_cached_range *range) if (range->is_bof) range->seek_start = min_start_pts; - // Sparse stream behavior is not very clearly defined, but usually we don't - // want it to restrict the range of other streams, unless + // Sparse (subtitle) stream behavior is not very clearly defined, but + // usually we don't want it to restrict the range of other streams. For + // example, if there are subtitle packets at position 5 and 10 seconds, and + // the demuxer demuxed the other streams until position 7 seconds, the seek + // range end position is 7. + // Assume that reading a non-sparse (audio/video) packet gets all sparse + // packets that are needed before that non-sparse packet. // This is incorrect in any of these cases: // - sparse streams only (it's unknown how to determine an accurate range) // - if sparse streams have non-keyframe packets (we set queue->last_pruned // to the start of the pruned keyframe range - we'd need the end or so) - // We also assume that ds->eager equals to a stream being sparse (usually - // true, except if only sparse streams are selected). + // We also assume that ds->eager equals to a stream not being sparse + // (usually true, except if only sparse streams are selected). // We also rely on the fact that the demuxer position will always be ahead // of the seek_end for audio/video, because they need to prefetch at least - // 1 packet to detect the end of a keyframe range. This means that we're - // relatively guaranteed to have all sparse (subtitle) packets within the - // seekable range. + // 1 packet to detect the end of a keyframe range. This means that there's + // a relatively high guarantee to have all sparse (subtitle) packets within + // the seekable range. + // As a consequence, the code _never_ checks queue->seek_end for a sparse + // queue, as the end of it is implied by the highest PTS of a non-sparse + // stream (i.e. the latest demuxer position). + // On the other hand, if a sparse packet was pruned, and that packet has + // a higher PTS than seek_start for non-sparse queues, that packet is + // missing. So the range's seek_start needs to be adjusted accordingly. for (int n = 0; n < range->num_streams; n++) { struct demux_queue *queue = range->streams[n]; if (queue->ds->selected && !queue->ds->eager && @@ -585,31 +663,36 @@ static void update_seek_ranges(struct demux_cached_range *range) } } - if (range->seek_start >= range->seek_end) + if (range->seek_start >= range->seek_end && !(range->is_bof && range->is_eof)) goto broken; + prune_metadata(range); return; broken: range->seek_start = range->seek_end = MP_NOPTS_VALUE; + prune_metadata(range); } -// Remove queue->head from the queue. Does not update in->fw_bytes/in->fw_packs. +// Remove queue->head from the queue. static void remove_head_packet(struct demux_queue *queue) { struct demux_packet *dp = queue->head; assert(queue->ds->reader_head != dp); - if (queue->next_prune_target == dp) - queue->next_prune_target = NULL; + if (queue->keyframe_first == dp) + queue->keyframe_first = NULL; if (queue->keyframe_latest == dp) queue->keyframe_latest = NULL; queue->is_bof = false; - queue->ds->in->total_bytes -= demux_packet_estimate_total_size(dp); + uint64_t end_pos = dp->next ? dp->next->cum_pos : queue->tail_cum_pos; + queue->ds->in->total_bytes -= end_pos - dp->cum_pos; - if (queue->num_index && queue->index[0] == dp) - MP_TARRAY_REMOVE_AT(queue->index, queue->num_index, 0); + if (queue->num_index && queue->index[queue->index0].pkt == dp) { + queue->index0 = (queue->index0 + 1) & QUEUE_INDEX_SIZE_MASK(queue); + queue->num_index -= 1; + } queue->head = dp->next; if (!queue->head) @@ -618,32 +701,44 @@ static void remove_head_packet(struct demux_queue *queue) talloc_free(dp); } +static void free_index(struct demux_queue *queue) +{ + struct demux_stream *ds = queue->ds; + struct demux_internal *in = ds->in; + + in->total_bytes -= queue->index_size * sizeof(queue->index[0]); + queue->index_size = 0; + queue->index0 = 0; + queue->num_index = 0; + TA_FREEP(&queue->index); +} + static void clear_queue(struct demux_queue *queue) { struct demux_stream *ds = queue->ds; struct demux_internal *in = ds->in; + if (queue->head) + in->total_bytes -= queue->tail_cum_pos - queue->head->cum_pos; + + free_index(queue); + struct demux_packet *dp = queue->head; while (dp) { struct demux_packet *dn = dp->next; - in->total_bytes -= demux_packet_estimate_total_size(dp); assert(ds->reader_head != dp); talloc_free(dp); dp = dn; } queue->head = queue->tail = NULL; - queue->next_prune_target = NULL; + queue->keyframe_first = NULL; queue->keyframe_latest = NULL; queue->seek_start = queue->seek_end = queue->last_pruned = MP_NOPTS_VALUE; - queue->num_index = 0; - queue->index_distance = 1.0; - queue->correct_dts = queue->correct_pos = true; queue->last_pos = -1; queue->last_ts = queue->last_dts = MP_NOPTS_VALUE; - queue->keyframe_latest = NULL; - queue->keyframe_pts = queue->keyframe_end_pts = MP_NOPTS_VALUE; + queue->last_pos_fixup = -1; queue->is_eof = false; queue->is_bof = false; @@ -654,6 +749,11 @@ static void clear_cached_range(struct demux_internal *in, { for (int n = 0; n < range->num_streams; n++) clear_queue(range->streams[n]); + + for (int n = 0; n < range->num_metadata; n++) + talloc_free(range->metadata[n]); + range->num_metadata = 0; + update_seek_ranges(range); } @@ -661,17 +761,26 @@ static void clear_cached_range(struct demux_internal *in, // ranges. static void free_empty_cached_ranges(struct demux_internal *in) { - assert(in->current_range && in->num_ranges > 0); - assert(in->current_range == in->ranges[in->num_ranges - 1]); - while (1) { struct demux_cached_range *worst = NULL; - for (int n = in->num_ranges - 2; n >= 0; n--) { + int end = in->num_ranges - 1; + + // (Not set during early init or late destruction.) + if (in->current_range) { + assert(in->current_range && in->num_ranges > 0); + assert(in->current_range == in->ranges[in->num_ranges - 1]); + end -= 1; + } + + for (int n = end; n >= 0; n--) { struct demux_cached_range *range = in->ranges[n]; if (range->seek_start == MP_NOPTS_VALUE || !in->seekable_cache) { clear_cached_range(in, range); MP_TARRAY_REMOVE_AT(in->ranges, in->num_ranges, n); + for (int i = 0; i < range->num_streams; i++) + talloc_free(range->streams[i]); + talloc_free(range); } else { if (!worst || (range->seek_end - range->seek_start < worst->seek_end - worst->seek_start)) @@ -679,7 +788,7 @@ static void free_empty_cached_ranges(struct demux_internal *in) } } - if (in->num_ranges <= MAX_SEEK_RANGES) + if (in->num_ranges <= MAX_SEEK_RANGES || !worst) break; clear_cached_range(in, worst); @@ -688,15 +797,13 @@ static void free_empty_cached_ranges(struct demux_internal *in) static void ds_clear_reader_queue_state(struct demux_stream *ds) { - ds->in->fw_bytes -= ds->fw_bytes; ds->reader_head = NULL; - ds->fw_bytes = 0; - ds->fw_packs = 0; ds->eof = false; ds->need_wakeup = true; } -static void ds_clear_reader_state(struct demux_stream *ds) +static void ds_clear_reader_state(struct demux_stream *ds, + bool clear_back_state) { ds_clear_reader_queue_state(ds); @@ -707,6 +814,34 @@ static void ds_clear_reader_state(struct demux_stream *ds) ds->attached_picture_added = false; ds->last_ret_pos = -1; ds->last_ret_dts = MP_NOPTS_VALUE; + ds->force_read_until = MP_NOPTS_VALUE; + + if (clear_back_state) { + ds->back_restart_pos = -1; + ds->back_restart_dts = MP_NOPTS_VALUE; + ds->back_restart_eof = false; + ds->back_restart_next = ds->in->back_demuxing; + ds->back_restarting = ds->in->back_demuxing && ds->eager; + ds->back_seek_pos = MP_NOPTS_VALUE; + ds->back_resume_pos = -1; + ds->back_resume_dts = MP_NOPTS_VALUE; + ds->back_resuming = false; + ds->back_range_started = false; + ds->back_range_count = 0; + ds->back_range_preroll = 0; + } +} + +// called locked, from user thread only +static void clear_reader_state(struct demux_internal *in, + bool clear_back_state) +{ + for (int n = 0; n < in->num_streams; n++) + ds_clear_reader_state(in->streams[n]->ds, clear_back_state); + in->warned_queue_overflow = false; + in->d_user->filepos = -1; // implicitly synchronized + in->blocked = false; + in->need_back_seek = false; } // Call if the observed reader state on this stream somehow changes. The wakeup @@ -721,7 +856,7 @@ static void wakeu |