summaryrefslogtreecommitdiffstats
path: root/demux/demux.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux.c')
-rw-r--r--demux/demux.c3506
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