1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-29 02:22:19 +00:00
mpv/demux/demux.h

322 lines
11 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEMUXER_H
#define MPLAYER_DEMUXER_H
#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "misc/bstr.h"
#include "common/common.h"
#include "common/tags.h"
#include "packet.h"
#include "stheader.h"
enum demux_ctrl {
DEMUXER_CTRL_SWITCHED_TRACKS = 1,
DEMUXER_CTRL_REPLACE_STREAM,
};
demux: support multiple seekable cached ranges Until now, the demuxer cache was limited to a single range. Extend this to multiple range. Should be useful for slow network streams. This commit changes a lot in the internal demuxer cache logic, so there's a lot of room for bugs and regressions. The logic without demuxer cache is mostly untouched, but also involved with the code changes. Or in other words, this commit probably fucks up shit. There are two things which makes multiple cached ranges rather hard: 1. the need to resume the demuxer at the end of a cached range when seeking to it 2. joining two adjacent ranges when the lowe range "grows" into it (and resuming the demuxer at the end of the new joined range) "Resuming" the demuxer means that we perform a low level seek to the end of a cached range, and properly append new packets to it, without adding packets multiple times or creating holes due to missing packets. Since audio and video never line up exactly, there is no clean "cut" possible, at which you could resume the demuxer cleanly (for 1.) or which you could use to detect that two ranges are perfectly adjacent (for 2.). The way how the demuxer interleaves multiple streams is also unpredictable. Typically you will have to expect that it randomly allows one of the streams to be ahead by a bit, and so on. To deal with this, we have heuristics in place to detect when one packet equals or is "behind" a packet that was demuxed earlier. We reuse the refresh seek logic (used to "reread" packets into the demuxer cache when enabling a track), which checks for certain packet invariants. Currently, it observes whether either the raw packet position, or the packet DTS is strictly monotonically increasing. If none of them are true, we discard old ranges when creating a new one. This heavily depends on the file format and the demuxer behavior. For example, not all file formats have DTS, and the packet position can be unset due to libavformat not always setting it (e.g. when parsers are used). At the same time, we must deal with all the complicated state used to track prefetching and seek ranges. In some complicated corner cases, we just give up and discard other seek ranges, even if the previously mentioned packet invariants are fulfilled. To handle joining, we're being particularly dumb, and require a small overlap to be confident that two ranges join perfectly. (This could be done incrementally with as little overlap as 1 packet, but corner cases would eat us: each stream needs to be joined separately, and the cache pruning logic could remove overlapping packets for other streams again.) Another restriction is that switching the cached range will always trigger an asynchronous low level seek to resume demuxing at the new range. Some users might find this annoying. Dealing with interleaved subtitles is not fully handled yet. It will clamp the seekable range to where subtitle packets are.
2017-11-09 08:53:46 +00:00
#define MAX_SEEK_RANGES 10
struct demux_seek_range {
double start, end;
};
struct demux_reader_state {
bool eof, underrun, idle;
double ts_duration;
double ts_reader; // approx. timerstamp of decoder position
double ts_end; // approx. timestamp of end of buffered range
int64_t total_bytes;
int64_t fw_bytes;
double seeking; // current low level seek target, or NOPTS
int low_level_seeks; // number of started low level seeks
double ts_last; // approx. timestamp of demuxer position
uint64_t bytes_per_second; // low level statistics
// Positions that can be seeked to without incurring the latency of a low
// level seek.
int num_seek_ranges;
struct demux_seek_range seek_ranges[MAX_SEEK_RANGES];
};
#define SEEK_FACTOR (1 << 1) // argument is in range [0,1]
#define SEEK_FORWARD (1 << 2) // prefer later time if not exact
// (if unset, prefer earlier time)
#define SEEK_CACHED (1 << 3) // allow packet cache seeks only
#define SEEK_HR (1 << 5) // hr-seek (this is a weak hint only)
// Strictness of the demuxer open format check.
// demux.c will try by default: NORMAL, UNSAFE (in this order)
// Using "-demuxer format" will try REQUEST
// Using "-demuxer +format" will try FORCE
// REQUEST can be used as special value for raw demuxers which have no file
// header check; then they should fail if check!=FORCE && check!=REQUEST.
//
// In general, the list is sorted from weakest check to normal check.
// You can use relation operators to compare the check level.
enum demux_check {
DEMUX_CHECK_FORCE, // force format if possible
DEMUX_CHECK_UNSAFE, // risky/fuzzy detection
DEMUX_CHECK_REQUEST,// requested by user or stream implementation
DEMUX_CHECK_NORMAL, // normal, safe detection
};
enum demux_event {
DEMUX_EVENT_INIT = 1 << 0, // complete (re-)initialization
DEMUX_EVENT_STREAMS = 1 << 1, // a stream was added
DEMUX_EVENT_METADATA = 1 << 2, // metadata or stream_metadata changed
DEMUX_EVENT_DURATION = 1 << 3, // duration updated
DEMUX_EVENT_ALL = 0xFFFF,
};
struct demuxer;
struct timeline;
/**
* Demuxer description structure
*/
typedef struct demuxer_desc {
const char *name; // Demuxer name, used with -demuxer switch
const char *desc; // Displayed to user
// Return 0 on success, otherwise -1
int (*open)(struct demuxer *demuxer, enum demux_check check);
// The following functions are all optional
// Try to read a packet. Return false on EOF. If true is returned, the
// demuxer may set *pkt to a new packet (the reference goes to the caller).
// If *pkt is NULL (the value when this function is called), the call
// will be repeated.
bool (*read_packet)(struct demuxer *demuxer, struct demux_packet **pkt);
void (*close)(struct demuxer *demuxer);
void (*seek)(struct demuxer *demuxer, double rel_seek_secs, int flags);
int (*control)(struct demuxer *demuxer, int cmd, void *arg);
// See timeline.c
void (*load_timeline)(struct timeline *tl);
} demuxer_desc_t;
typedef struct demux_chapter
{
int original_index;
double pts;
struct mp_tags *metadata;
uint64_t demuxer_id; // for mapping to internal demuxer data structures
} demux_chapter_t;
struct demux_edition {
uint64_t demuxer_id;
bool default_edition;
struct mp_tags *metadata;
};
struct matroska_segment_uid {
unsigned char segment[16];
uint64_t edition;
};
struct matroska_data {
struct matroska_segment_uid uid;
// Ordered chapter information if any
struct matroska_chapter {
uint64_t start;
uint64_t end;
bool has_segment_uid;
struct matroska_segment_uid uid;
char *name;
} *ordered_chapters;
int num_ordered_chapters;
};
struct replaygain_data {
float track_gain;
float track_peak;
float album_gain;
float album_peak;
};
typedef struct demux_attachment
{
char *name;
char *type;
void *data;
unsigned int data_size;
} demux_attachment_t;
struct demuxer_params {
char *force_format;
int matroska_num_wanted_uids;
struct matroska_segment_uid *matroska_wanted_uids;
int matroska_wanted_segment;
bool *matroska_was_valid;
Rewrite ordered chapters and timeline stuff This uses a different method to piece segments together. The old approach basically changes to a new file (with a new start offset) any time a segment ends. This meant waiting for audio/video end on segment end, and then changing to the new segment all at once. It had a very weird impact on the playback core, and some things (like truly gapless segment transitions, or frame backstepping) just didn't work. The new approach adds the demux_timeline pseudo-demuxer, which presents an uniform packet stream from the many segments. This is pretty similar to how ordered chapters are implemented everywhere else. It also reminds of the FFmpeg concat pseudo-demuxer. The "pure" version of this approach doesn't work though. Segments can actually have different codec configurations (different extradata), and subtitles are most likely broken too. (Subtitles have multiple corner cases which break the pure stream-concatenation approach completely.) To counter this, we do two things: - Reinit the decoder with each segment. We go as far as allowing concatenating files with completely different codecs for the sake of EDL (which also uses the timeline infrastructure). A "lighter" approach would try to make use of decoder mechanism to update e.g. the extradata, but that seems fragile. - Clip decoded data to segment boundaries. This is equivalent to normal playback core mechanisms like hr-seek, but now the playback core doesn't need to care about these things. These two mechanisms are equivalent to what happened in the old implementation, except they don't happen in the playback core anymore. In other words, the playback core is completely relieved from timeline implementation details. (Which honestly is exactly what I'm trying to do here. I don't think ordered chapter behavior deserves improvement, even if it's bad - but I want to get it out from the playback core.) There is code duplication between audio and video decoder common code. This is awful and could be shareable - but this will happen later. Note that the audio path has some code to clip audio frames for the purpose of codec preroll/gapless handling, but it's not shared as sharing it would cause more pain than it would help.
2016-02-15 20:04:07 +00:00
struct timeline *timeline;
bool disable_timeline;
bstr init_fragment;
bool skip_lavf_probing;
bool does_not_own_stream; // if false, stream is free'd on demux_free()
bool stream_record; // if true, enable stream recording if option is set
// -- demux_open_url() only
int stream_flags;
// result
bool demuxer_failed;
};
typedef struct demuxer {
const demuxer_desc_t *desc; ///< Demuxer description structure
2011-05-04 20:12:55 +00:00
const char *filetype; // format name when not identified by demuxer (libavformat)
int64_t filepos; // input stream current pos.
int64_t filesize;
char *filename; // same as stream->url
bool seekable;
bool partially_seekable; // true if _maybe_ seekable; implies seekable=true
double start_time;
double duration; // -1 if unknown
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
// The file data was fully read, and there is no need to keep the stream
// open, keep the cache active, or to run the demuxer thread. Generating
// packets is not slow either (unlike e.g. libavdevice pseudo-demuxers).
// Typical examples: text subtitles, playlists
bool fully_read;
bool is_network; // opened directly from a network stream
bool access_references; // allow opening other files/URLs
// Bitmask of DEMUX_EVENT_*
int events;
struct demux_edition *editions;
int num_editions;
int edition;
struct demux_chapter *chapters;
int num_chapters;
struct demux_attachment *attachments;
int num_attachments;
struct matroska_data matroska_data;
// If the file is a playlist file
struct playlist *playlist;
struct mp_tags *metadata;
void *priv; // demuxer-specific internal data
struct mpv_global *global;
struct mp_log *log, *glog;
struct demuxer_params *params;
// internal to demux.c
struct demux_internal *in;
// Triggered when ending demuxing forcefully. Usually bound to the stream too.
struct mp_cancel *cancel;
// Demuxer thread only.
uint64_t total_unbuffered_read_bytes;
// Since the demuxer can run in its own thread, and the stream is not
// thread-safe, only the demuxer is allowed to access the stream directly.
// Also note that the stream can get replaced if fully_read is set.
struct stream *stream;
} demuxer_t;
void demux_free(struct demuxer *demuxer);
void demux_cancel_and_free(struct demuxer *demuxer);
struct demux_free_async_state;
struct demux_free_async_state *demux_free_async(struct demuxer *demuxer);
void demux_free_async_force(struct demux_free_async_state *state);
bool demux_free_async_finish(struct demux_free_async_state *state);
void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp);
struct demux_packet *demux_read_packet(struct sh_stream *sh);
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt);
bool demux_stream_is_selected(struct sh_stream *stream);
bool demux_has_packet(struct sh_stream *sh);
void demux_set_stream_wakeup_cb(struct sh_stream *sh,
void (*cb)(void *ctx), void *ctx);
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
struct sh_stream *demux_get_stream(struct demuxer *demuxer, int index);
int demux_get_num_stream(struct demuxer *demuxer);
struct sh_stream *demux_alloc_sh_stream(enum stream_type type);
void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh);
struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params,
struct mpv_global *global);
struct mp_cancel;
struct demuxer *demux_open_url(const char *url,
struct demuxer_params *params,
struct mp_cancel *cancel,
struct mpv_global *global);
void demux_start_thread(struct demuxer *demuxer);
void demux_stop_thread(struct demuxer *demuxer);
void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *ctx);
bool demux_cancel_test(struct demuxer *demuxer);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags);
void demux_set_ts_offset(struct demuxer *demuxer, double offset);
void demux_get_bitrate_stats(struct demuxer *demuxer, double *rates);
void demux_get_reader_state(struct demuxer *demuxer, struct demux_reader_state *r);
void demux_block_reading(struct demuxer *demuxer, bool block);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
double ref_pts, bool selected);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
void demuxer_help(struct mp_log *log);
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_metadata_changed(demuxer_t *demuxer);
void demux_update(demuxer_t *demuxer);
void demux_disable_cache(demuxer_t *demuxer);
bool demux_is_network_cached(demuxer_t *demuxer);
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id);
struct demux_chapter *demux_copy_chapter_data(struct demux_chapter *c, int num);
bool demux_matroska_uid_cmp(struct matroska_segment_uid *a,
struct matroska_segment_uid *b);
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 */