diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 364 | ||||
-rw-r--r-- | demux/demux.h | 9 | ||||
-rw-r--r-- | demux/demux_lavf.c | 18 | ||||
-rw-r--r-- | demux/packet.c | 2 | ||||
-rw-r--r-- | demux/packet.h | 1 |
5 files changed, 200 insertions, 194 deletions
diff --git a/demux/demux.c b/demux/demux.c index eec91efc90..017f6c3cf7 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -173,12 +173,9 @@ 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; + // 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; @@ -247,6 +244,11 @@ struct demux_internal { uint64_t bytes_per_second; int64_t next_cache_update; + // 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 + // -- Access from demuxer thread only bool enable_recording; struct mp_recorder *recorder; @@ -255,6 +257,12 @@ struct demux_internal { int64_t cache_unbuffered_read_bytes; // for demux_reader_state.bytes_per_second }; +struct timed_metadata { + double pts; + struct mp_tags *tags; + bool from_stream; +}; + // A continuous range of cached packets for all enabled streams. // (One demux_queue for each known stream.) struct demux_cached_range { @@ -268,6 +276,9 @@ 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 QUEUE_INDEX_SIZE_MASK(queue) ((queue)->index_size - 1) @@ -392,19 +403,6 @@ struct demux_stream { // 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) }; static void switch_to_fresh_cache_range(struct demux_internal *in); @@ -512,56 +510,6 @@ static void check_queue_consistency(struct demux_internal *in) } #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; -} - // (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) @@ -578,6 +526,31 @@ static void set_current_range(struct demux_internal *in, MP_TARRAY_APPEND(in, in->ranges, in->num_ranges, range); } +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. static void update_seek_ranges(struct demux_cached_range *range) { @@ -659,10 +632,12 @@ static void update_seek_ranges(struct demux_cached_range *range) if (range->seek_start >= range->seek_end) 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. @@ -741,6 +716,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); } @@ -845,26 +825,17 @@ static void update_stream_selection_state(struct demux_internal *in, bool any_av_streams = false; bool any_streams = false; - struct demux_stream *master = NULL; for (int n = 0; n < in->num_streams; n++) { struct demux_stream *s = in->streams[n]->ds; s->still_image = s->sh->still_image; s->eager = s->selected && !s->sh->attached_picture; - if (s->eager && !s->still_image) { + if (s->eager && !s->still_image) any_av_streams |= s->type != STREAM_SUB; - if (!master || - (master->type == STREAM_VIDEO && s->type == STREAM_AUDIO)) - { - master = s; - } - } any_streams |= s->selected; } - in->master_stream = master; - // Subtitles are only eagerly read if there are no other eagerly read // streams. if (any_av_streams) { @@ -942,14 +913,6 @@ struct sh_stream *demux_alloc_sh_stream(enum stream_type type) return sh; } -static void ds_destroy(void *ptr) -{ - struct demux_stream *ds = ptr; - mp_packet_tags_unref(ds->tags_init); - mp_packet_tags_unref(ds->tags_reader); - mp_packet_tags_unref(ds->tags_demux); -} - // Add a new sh_stream to the demuxer. Note that as soon as the stream has been // added, it must be immutable, and must not be released (this will happen when // the demuxer is destroyed). @@ -969,7 +932,6 @@ static void demux_add_sh_stream_locked(struct demux_internal *in, .global_correct_dts = true, .global_correct_pos = true, }; - talloc_set_destructor(sh->ds, ds_destroy); struct demux_stream *ds = sh->ds; @@ -998,11 +960,6 @@ static void demux_add_sh_stream_locked(struct demux_internal *in, update_stream_selection_state(in, sh->ds); - mp_packet_tags_make_writable(&sh->ds->tags_init); - mp_tags_replace(sh->ds->tags_init->demux, in->d_thread->metadata); - mp_tags_replace(sh->ds->tags_init->sh, sh->tags); - mp_packet_tags_setref(&sh->ds->tags_reader, sh->ds->tags_init); - switch (ds->type) { case STREAM_AUDIO: ds->back_preroll = in->opts->audio_back_preroll; @@ -1021,6 +978,12 @@ static void demux_add_sh_stream_locked(struct demux_internal *in, break; } + if (!ds->sh->attached_picture) { + // Typically this is used for webradio, so any stream will do. + if (!in->metadata_stream) + in->metadata_stream = sh; + } + in->events |= DEMUX_EVENT_STREAMS; if (in->wakeup_cb) in->wakeup_cb(in->wakeup_cb_ctx); @@ -1036,35 +999,6 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh) pthread_mutex_unlock(&in->lock); } -static void ds_modify_demux_tags(struct demux_stream *ds) -{ - if (!ds->tags_demux) - mp_packet_tags_setref(&ds->tags_demux, ds->tags_init); - mp_packet_tags_make_writable(&ds->tags_demux); -} - -// Update sh->tags (lazily). This must be called by demuxers which update -// stream tags after init. (sh->tags can be accessed by the playback thread, -// which means the demuxer thread cannot write or read it directly.) -// Before init is finished, sh->tags can still be accessed freely. -// Ownership of tags goes to the function. -void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh, - struct mp_tags *tags) -{ - struct demux_internal *in = demuxer->in; - assert(demuxer == in->d_thread); - struct demux_stream *ds = sh->ds; - assert(ds); // stream must have been added - - pthread_mutex_lock(&in->lock); - - ds_modify_demux_tags(ds); - mp_tags_replace(ds->tags_demux->sh, tags); - talloc_free(tags); - - pthread_mutex_unlock(&in->lock); -} - // Return a stream with the given index. Since streams can only be added during // the lifetime of the demuxer, it is guaranteed that an index within the valid // range [0, demux_get_num_stream()) always returns a valid sh_stream pointer, @@ -1778,6 +1712,14 @@ static void attempt_range_joining(struct demux_internal *in) ds->refreshing = ds->selected; } + struct demux_cached_range *current = in->current_range; + + for (int n = 0; n < next->num_metadata; n++) { + MP_TARRAY_APPEND(current, current->metadata, current->num_metadata, + next->metadata[n]); + } + next->num_metadata = 0; + update_seek_ranges(in->current_range); // Move demuxing position to after the current range. @@ -1904,7 +1846,6 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp) dp->stream = stream->index; dp->next = NULL; - mp_packet_tags_setref(&dp->metadata, ds->tags_demux); // (keep in mind that even if the reader went out of data, the queue is not // necessarily empty due to the backbuffer) @@ -2463,18 +2404,6 @@ static int dequeue_packet(struct demux_stream *ds, struct demux_packet **res) pkt->end = MP_ADD_PTS(pkt->end, in->ts_offset); } - // Apply timed metadata when packet is returned to user. - // (The tags_init thing is a microopt. to not do refcounting for sane files.) - struct mp_packet_tags *metadata = pkt->metadata; - if (!metadata) - metadata = ds->tags_init; - if (metadata != ds->tags_reader) { - mp_packet_tags_setref(&ds->tags_reader, metadata); - in->events |= DEMUX_EVENT_METADATA; - if (in->wakeup_cb) - in->wakeup_cb(in->wakeup_cb_ctx); - } - prune_old_packets(in); *res = pkt; return 1; @@ -2689,6 +2618,65 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src) dst->metadata = mp_tags_dup(dst, src->metadata); } +// Update metadata after initialization. If sh==NULL, it's global metadata, +// otherwise it's bound to the stream. If pts==NOPTS, use the highest known pts +// in the stream. Caller retains ownership of tags ptr. Called locked. +static void add_timed_metadata(struct demux_internal *in, struct mp_tags *tags, + struct sh_stream *sh, double pts) +{ + struct demux_cached_range *r = in->current_range; + if (!r) + return; + + // We don't expect this, nor do we find it useful. + if (sh && sh != in->metadata_stream) + return; + + if (pts == MP_NOPTS_VALUE) { + for (int n = 0; n < r->num_streams; n++) + pts = MP_PTS_MAX(pts, r->streams[n]->last_ts); + + // Tends to happen when doing the initial icy update. + if (pts == MP_NOPTS_VALUE) + pts = in->d_thread->start_time; + } + + struct timed_metadata *tm = talloc_zero(NULL, struct timed_metadata); + *tm = (struct timed_metadata){ + .pts = pts, + .tags = mp_tags_dup(tm, tags), + .from_stream = !!sh, + }; + MP_TARRAY_APPEND(r, r->metadata, r->num_metadata, tm); +} + +// This is called by demuxer implementations if sh->tags changed. Note that +// sh->tags itself is never actually changed (it's immutable, because sh->tags +// can be accessed by the playback thread, and there is no synchronization). +// pts is the time at/after which the metadata becomes effective. You're +// supposed to call this ordered by time, and only while a packet is being +// read. +// Ownership of tags goes to the function. +void demux_stream_tags_changed(struct demuxer *demuxer, struct sh_stream *sh, + struct mp_tags *tags, double pts) +{ + struct demux_internal *in = demuxer->in; + assert(demuxer == in->d_thread); + struct demux_stream *ds = sh ? sh->ds : NULL; + assert(!sh || ds); // stream must have been added + + pthread_mutex_lock(&in->lock); + + if (pts == MP_NOPTS_VALUE) { + MP_WARN(in, "Discarding timed metadata without timestamp.\n"); + } else { + add_timed_metadata(in, tags, sh, pts); + } + talloc_free(tags); + + pthread_mutex_unlock(&in->lock); +} + // This is called by demuxer implementations if demuxer->metadata changed. // (It will be propagated to the user as timed metadata.) void demux_metadata_changed(demuxer_t *demuxer) @@ -2697,27 +2685,17 @@ void demux_metadata_changed(demuxer_t *demuxer) struct demux_internal *in = demuxer->in; pthread_mutex_lock(&in->lock); - - for (int n = 0; n < in->num_streams; n++) { - struct demux_stream *ds = in->streams[n]->ds; - ds_modify_demux_tags(ds); - mp_tags_replace(ds->tags_demux->demux, demuxer->metadata); - } - + add_timed_metadata(in, demuxer->metadata, NULL, MP_NOPTS_VALUE); pthread_mutex_unlock(&in->lock); } // Called locked, with user demuxer. -static void update_final_metadata(demuxer_t *demuxer) +static void update_final_metadata(demuxer_t *demuxer, struct timed_metadata *tm) { assert(demuxer == demuxer->in->d_user); struct demux_internal *in = demuxer->in; - struct mp_packet_tags *tags = - in->master_stream ? in->master_stream->tags_reader : NULL; - - if (tags) - mp_tags_replace(demuxer->metadata, tags->demux); + struct mp_tags *dyn_tags = NULL; // Often for useful audio-only files, which have metadata in the audio track // metadata instead of the main metadata, but can also have cover art @@ -2734,16 +2712,54 @@ static void update_final_metadata(demuxer_t *demuxer) astream_id = n; } } - if (vstreams == 0 && astreams == 1) - mp_tags_merge(demuxer->metadata, in->streams[astream_id]->tags); - if (tags) - mp_tags_merge(demuxer->metadata, tags->stream); + // Use the metadata_stream tags only if this really seems to be an audio- + // only stream. Otherwise it will happen too often that "uninteresting" + // stream metadata will trash the actual file tags. + if (vstreams == 0 && astreams == 1 && + in->streams[astream_id] == in->metadata_stream) + { + dyn_tags = in->metadata_stream->tags; + if (tm && tm->from_stream) + dyn_tags = tm->tags; + } + + // Global metadata updates. + if (tm && !tm->from_stream) + dyn_tags = tm->tags; + + if (dyn_tags) + mp_tags_merge(demuxer->metadata, dyn_tags); +} + +static struct timed_metadata *lookup_timed_metadata(struct demux_internal *in, + double pts) +{ + struct demux_cached_range *r = in->current_range; + + if (!r || !r->num_metadata || pts == MP_NOPTS_VALUE) + return NULL; + + int start = 1; + int i = in->cached_metadata_index; + if (i >= 0 && i < r->num_metadata && r->metadata[i]->pts <= pts) + start = i + 1; + + in->cached_metadata_index = r->num_metadata - 1; + for (int n = start; n < r->num_metadata; n++) { + if (r->metadata[n]->pts >= pts) { + in->cached_metadata_index = n - 1; + break; + } + } + + return r->metadata[in->cached_metadata_index]; } // Called by the user thread (i.e. player) to update metadata and other things // from the demuxer thread. -void demux_update(demuxer_t *demuxer) +// The pts parameter is the current playback position. +void demux_update(demuxer_t *demuxer, double pts) { assert(demuxer == demuxer->in->d_user); struct demux_internal *in = demuxer->in; @@ -2752,14 +2768,26 @@ void demux_update(demuxer_t *demuxer) update_cache(in); pthread_mutex_lock(&in->lock); + + pts = MP_ADD_PTS(pts, -in->ts_offset); + + struct timed_metadata *prev = lookup_timed_metadata(in, in->last_playback_pts); + struct timed_metadata *cur = lookup_timed_metadata(in, pts); + if (prev != cur || in->force_metadata_update) { + in->force_metadata_update = false; + update_final_metadata(demuxer, cur); + demuxer->events |= DEMUX_EVENT_METADATA; + } + + in->last_playback_pts = pts; + demuxer->events |= in->events; in->events = 0; - if (demuxer->events & DEMUX_EVENT_METADATA) - update_final_metadata(demuxer); if (demuxer->events & (DEMUX_EVENT_METADATA | DEMUX_EVENT_STREAMS)) demux_update_replaygain(demuxer); if (demuxer->events & DEMUX_EVENT_DURATION) demuxer->duration = in->duration; + pthread_mutex_unlock(&in->lock); } @@ -2817,19 +2845,6 @@ static void demux_init_ccs(struct demuxer *demuxer, struct demux_opts *opts) pthread_mutex_unlock(&in->lock); } -// Each stream contains a copy of the global demuxer metadata, but this might -// be outdated if a stream gets added and then metadata does get set during -// early init. -static void fixup_metadata(struct demux_internal *in) -{ - for (int n = 0; n < in->num_streams; n++) { - struct demux_stream *ds = in->streams[n]->ds; - mp_packet_tags_make_writable(&ds->tags_init); - mp_tags_replace(ds->tags_init->demux, in->d_thread->metadata); - mp_packet_tags_setref(&ds->tags_reader, ds->tags_init); - } -} - // Return whether "heavy" caching on this stream is enabled. By default, this // corresponds to whether the source stream is considered in the network. The // only effect should be adjusting display behavior (of cache stats etc.), and @@ -2922,9 +2937,7 @@ static struct demuxer *open_given_type(struct mpv_global *global, demux_copy(in->d_user, in->d_thread); in->duration = in->d_thread->duration; demuxer_sort_chapters(demuxer); - fixup_metadata(in); in->events = DEMUX_EVENT_ALL; - demux_update(demuxer); int seekable = opts->seekable_cache; if (demux_is_network_cached(demuxer)) { in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache); @@ -2953,6 +2966,8 @@ static struct demuxer *open_given_type(struct mpv_global *global, } switch_to_fresh_cache_range(in); + demux_update(demuxer, MP_NOPTS_VALUE); + demuxer = sub ? sub : demuxer; // Let this demuxer free demuxer->stream. Timeline sub-demuxers can // share a stream, and in these cases the demux_timeline instance @@ -3127,6 +3142,9 @@ static void switch_current_range(struct demux_internal *in, // No point in keeping any junk (especially if old current_range is empty). free_empty_cached_ranges(in); + + // The change detection doesn't work across ranges. + in->force_metadata_update = true; } // Search for the entry with the highest index with entry.pts <= pts true. @@ -3659,11 +3677,7 @@ static void update_cache(struct demux_internal *in) in->stream_size = stream_size; if (stream_metadata) { - for (int n = 0; n < in->num_streams; n++) { - struct demux_stream *ds = in->streams[n]->ds; - ds_modify_demux_tags(ds); - mp_tags_replace(ds->tags_demux->stream, stream_metadata); - } + add_timed_metadata(in, stream_metadata, NULL, MP_NOPTS_VALUE); talloc_free(stream_metadata); } diff --git a/demux/demux.h b/demux/demux.h index 4cbb33b588..99acc1f015 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -284,12 +284,12 @@ int demuxer_add_attachment(struct demuxer *demuxer, char *name, char *type, void *data, size_t data_size); int demuxer_add_chapter(demuxer_t *demuxer, char *name, double pts, uint64_t demuxer_id); -void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh, - struct mp_tags *tags); +void demux_stream_tags_changed(struct demuxer *demuxer, struct sh_stream *sh, + struct mp_tags *tags, double pts); void demux_close_stream(struct demuxer *demuxer); void demux_metadata_changed(demuxer_t *demuxer); -void demux_update(demuxer_t *demuxer); +void demux_update(demuxer_t *demuxer, double playback_pts); bool demux_is_network_cached(demuxer_t *demuxer); @@ -306,7 +306,4 @@ bool demux_matroska_uid_cmp(struct matroska_segment_uid *a, const char *stream_type_name(enum stream_type type); -void mp_packet_tags_unref(struct mp_packet_tags *tags); -void mp_packet_tags_setref(struct mp_packet_tags **dst, struct mp_packet_tags *src); - #endif /* MPLAYER_DEMUXER_H */ diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 9626bdf918..881d38b4df 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -864,16 +864,6 @@ static void update_metadata(demuxer_t *demuxer) priv->avfc->event_flags = 0; demux_metadata_changed(demuxer); } - - for (int n = 0; n < priv->num_streams; n++) { - AVStream *st = priv->streams[n]->sh ? priv->avfc->streams[n] : NULL; - if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) { - st->event_flags = 0; - struct mp_tags *tags = talloc_zero(NULL, struct mp_tags); - mp_tags_copy_from_av_dictionary(tags, st->metadata); - demux_set_stream_tags(demuxer, priv->streams[n]->sh, tags); - } - } } static int interrupt_cb(void *ctx) @@ -1214,6 +1204,14 @@ static bool demux_lavf_read_packet(struct demuxer *demux, } } + if (st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) { + st->event_flags = 0; + struct mp_tags *tags = talloc_zero(NULL, struct mp_tags); + mp_tags_copy_from_av_dictionary(tags, st->metadata); + double pts = MP_PTS_OR_DEF(dp->pts, dp->dts); + demux_stream_tags_changed(demux, stream, tags, pts); + } + *mp_pkt = dp; return true; } diff --git a/demux/packet.c b/demux/packet.c index f2291c21bb..18082f642e 100644 --- a/demux/packet.c +++ b/demux/packet.c @@ -35,7 +35,6 @@ static void packet_destroy(void *ptr) { struct demux_packet *dp = ptr; av_packet_unref(dp->avpacket); - mp_packet_tags_unref(dp->metadata); } // This actually preserves only data and side data, not PTS/DTS/pos/etc. @@ -133,7 +132,6 @@ void demux_packet_copy_attribs(struct demux_packet *dst, struct demux_packet *sr dst->back_preroll = src->back_preroll; dst->keyframe = src->keyframe; dst->stream = src->stream; - mp_packet_tags_setref(&dst->metadata, src->metadata); } struct demux_packet *demux_copy_packet(struct demux_packet *dp) diff --git a/demux/packet.h b/demux/packet.h index 92b0129934..9a99da116d 100644 --- a/demux/packet.h +++ b/demux/packet.h @@ -50,7 +50,6 @@ typedef struct demux_packet { struct AVPacket *avpacket; // keep the buffer allocation and sidedata uint64_t cum_pos; // demux.c internal: cumulative size until _start_ of pkt double kf_seek_pts; // demux.c internal: seek pts for keyframe range - struct mp_packet_tags *metadata; // timed metadata (demux.c internal) } demux_packet_t; struct AVBufferRef; |