aboutsummaryrefslogtreecommitdiffhomepage
path: root/demux/demux.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux.c')
-rw-r--r--demux/demux.c1006
1 files changed, 254 insertions, 752 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 339333d36c..1783f92c96 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -17,6 +17,7 @@
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#define DEMUX_PRIV(x) x
#include <stdio.h>
#include <stdlib.h>
@@ -46,8 +47,6 @@
#error MP_INPUT_BUFFER_PADDING_SIZE is too small!
#endif
-static void clear_parser(sh_audio_t *sh);
-
// Demuxer list
extern const struct demuxer_desc demuxer_desc_edl;
extern const struct demuxer_desc demuxer_desc_cue;
@@ -55,18 +54,9 @@ extern const demuxer_desc_t demuxer_desc_rawaudio;
extern const demuxer_desc_t demuxer_desc_rawvideo;
extern const demuxer_desc_t demuxer_desc_tv;
extern const demuxer_desc_t demuxer_desc_mf;
-extern const demuxer_desc_t demuxer_desc_avi;
-extern const demuxer_desc_t demuxer_desc_asf;
extern const demuxer_desc_t demuxer_desc_matroska;
extern const demuxer_desc_t demuxer_desc_lavf;
extern const demuxer_desc_t demuxer_desc_mng;
-extern const demuxer_desc_t demuxer_desc_mpeg_ps;
-extern const demuxer_desc_t demuxer_desc_mpeg_pes;
-extern const demuxer_desc_t demuxer_desc_mpeg_gxf;
-extern const demuxer_desc_t demuxer_desc_mpeg_es;
-extern const demuxer_desc_t demuxer_desc_mpeg4_es;
-extern const demuxer_desc_t demuxer_desc_h264_es;
-extern const demuxer_desc_t demuxer_desc_mpeg_ts;
extern const demuxer_desc_t demuxer_desc_libass;
extern const demuxer_desc_t demuxer_desc_subreader;
@@ -88,18 +78,9 @@ const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_matroska,
&demuxer_desc_lavf,
&demuxer_desc_subreader,
- &demuxer_desc_avi,
- &demuxer_desc_asf,
#ifdef CONFIG_MNG
&demuxer_desc_mng,
#endif
- &demuxer_desc_mpeg_ps,
- &demuxer_desc_mpeg_pes,
- &demuxer_desc_mpeg_gxf,
- &demuxer_desc_mpeg_es,
- &demuxer_desc_mpeg4_es,
- &demuxer_desc_h264_es,
- &demuxer_desc_mpeg_ts,
// auto-probe last, because it checks file-extensions only
&demuxer_desc_mf,
/* Please do not add any new demuxers here. If you want to implement a new
@@ -108,8 +89,31 @@ const demuxer_desc_t *const demuxer_list[] = {
NULL
};
+struct demux_stream {
+ int selected; // user wants packets from this stream
+ int eof; // end of demuxed stream? (true if all buffer empty)
+ int packs; // number of packets in buffer
+ int bytes; // total bytes of packets in buffer
+ struct demux_packet *head;
+ struct demux_packet *tail;
+};
+
static void add_stream_chapters(struct demuxer *demuxer);
+static void ds_free_packs(struct demux_stream *ds)
+{
+ demux_packet_t *dp = ds->head;
+ while (dp) {
+ demux_packet_t *dn = dp->next;
+ free_demux_packet(dp);
+ dp = dn;
+ }
+ ds->head = ds->tail = NULL;
+ ds->packs = 0; // !!!!!
+ ds->bytes = 0;
+ ds->eof = 0;
+}
+
static int packet_destroy(void *ptr)
{
struct demux_packet *dp = ptr;
@@ -217,408 +221,178 @@ struct demux_packet *demux_copy_packet(struct demux_packet *dp)
new->pts = dp->pts;
new->duration = dp->duration;
new->stream_pts = dp->stream_pts;
- new->pos = dp->pos;
- new->keyframe = dp->keyframe;
return new;
}
-static void free_demuxer_stream(struct demux_stream *ds)
-{
- ds_free_packs(ds);
- free(ds);
-}
-
-static struct demux_stream *new_demuxer_stream(struct demuxer *demuxer,
- enum stream_type type, int id)
-{
- demux_stream_t *ds = malloc(sizeof(demux_stream_t));
- *ds = (demux_stream_t) {
- .stream_type = type,
- .id = id,
- .demuxer = demuxer,
- .asf_seq = -1,
- };
- return ds;
-}
-
-/**
- * Get demuxer description structure for a given demuxer type
- *
- * @param file_format type of the demuxer
- * @return structure for the demuxer, NULL if not found
- */
-static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format)
-{
- int i;
-
- for (i = 0; demuxer_list[i]; i++)
- if (file_format == demuxer_list[i]->type)
- return demuxer_list[i];
-
- return NULL;
-}
-
-
-static demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type,
- int a_id, int v_id, int s_id, char *filename)
-{
- struct demuxer *d = talloc_zero(NULL, struct demuxer);
- d->stream = stream;
- d->stream_pts = MP_NOPTS_VALUE;
- d->reference_clock = MP_NOPTS_VALUE;
- d->movi_start = stream->start_pos;
- d->movi_end = stream->end_pos;
- d->seekable = 1;
- d->synced = 0;
- d->filepos = -1;
- d->audio = new_demuxer_stream(d, STREAM_AUDIO, a_id);
- d->video = new_demuxer_stream(d, STREAM_VIDEO, v_id);
- d->sub = new_demuxer_stream(d, STREAM_SUB, s_id);
- d->ds[STREAM_VIDEO] = d->video;
- d->ds[STREAM_AUDIO] = d->audio;
- d->ds[STREAM_SUB] = d->sub;
- d->type = type;
- d->opts = opts;
- if (type)
- if (!(d->desc = get_demuxer_desc_from_type(type)))
- mp_msg(MSGT_DEMUXER, MSGL_ERR,
- "BUG! Invalid demuxer type in new_demuxer(), "
- "big troubles ahead.\n");
- if (filename) // Filename hack for avs_check_file
- d->filename = strdup(filename);
- stream_seek(stream, stream->start_pos);
- return d;
-}
-
-static struct sh_stream *new_sh_stream_id(demuxer_t *demuxer,
- enum stream_type type,
- int stream_index,
- int demuxer_id)
+struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
{
- if (demuxer->num_streams > MAX_SH_STREAMS || stream_index > MAX_SH_STREAMS) {
+ if (demuxer->num_streams > MAX_SH_STREAMS) {
mp_msg(MSGT_DEMUXER, MSGL_WARN, "Too many streams.");
return NULL;
}
- struct sh_stream *sh = talloc_struct(demuxer, struct sh_stream, {
+ int demuxer_id = 0;
+ for (int n = 0; n < demuxer->num_streams; n++) {
+ if (demuxer->streams[n]->type == type)
+ demuxer_id++;
+ }
+
+ struct sh_stream *sh = talloc_ptrtype(demuxer, sh);
+ *sh = (struct sh_stream) {
.type = type,
.demuxer = demuxer,
.index = demuxer->num_streams,
.demuxer_id = demuxer_id, // may be overwritten by demuxer
- .stream_index = stream_index,
.opts = demuxer->opts,
- });
+ .ds = talloc_zero(sh, struct demux_stream),
+ };
MP_TARRAY_APPEND(demuxer, demuxer->streams, demuxer->num_streams, sh);
switch (sh->type) {
case STREAM_VIDEO: {
struct sh_video *sht = talloc_zero(demuxer, struct sh_video);
sht->gsh = sh;
sht->opts = sh->opts;
- sht->ds = demuxer->video;
sh->video = sht;
- demuxer->v_streams[sh->stream_index] = sht;
break;
}
case STREAM_AUDIO: {
struct sh_audio *sht = talloc_zero(demuxer, struct sh_audio);
sht->gsh = sh;
sht->opts = sh->opts;
- sht->ds = demuxer->audio;
sht->samplesize = 2;
sht->sample_format = AF_FORMAT_S16_NE;
sh->audio = sht;
- demuxer->a_streams[sh->stream_index] = sht;
break;
}
case STREAM_SUB: {
struct sh_sub *sht = talloc_zero(demuxer, struct sh_sub);
sht->gsh = sh;
sht->opts = sh->opts;
- sht->ds = demuxer->sub;
sh->sub = sht;
- demuxer->s_streams[sh->stream_index] = sht;
break;
}
default: assert(false);
}
- return sh;
-}
-// This is what "modern" demuxers are supposed to use.
-struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
-{
- int num = 0;
- for (int n = 0; n < demuxer->num_streams; n++) {
- if (demuxer->streams[n]->type == type)
- num++;
- }
- return new_sh_stream_id(demuxer, type, demuxer->num_streams, num);
-}
+ sh->ds->selected = demuxer->stream_autoselect;
-static void free_sh_stream(struct sh_stream *sh)
-{
+ return sh;
}
-sh_sub_t *new_sh_sub_sid(demuxer_t *demuxer, int id, int sid)
+static void free_sh_sub(sh_sub_t *sh)
{
- if (id > MAX_S_STREAMS - 1 || id < 0) {
- mp_msg(MSGT_DEMUXER, MSGL_WARN,
- "Requested sub stream id overflow (%d > %d)\n", id,
- MAX_S_STREAMS);
- return NULL;
- }
- if (demuxer->s_streams[id])
- mp_msg(MSGT_DEMUXER, MSGL_WARN, "Sub stream %i redefined\n", id);
- else {
- new_sh_stream_id(demuxer, STREAM_SUB, id, sid);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SUBTITLE_ID=%d\n", sid);
- }
- return demuxer->s_streams[id];
+ free(sh->extradata);
}
-struct sh_sub *new_sh_sub_sid_lang(struct demuxer *demuxer, int id, int sid,
- const char *lang)
+static void free_sh_audio(sh_audio_t *sh)
{
- struct sh_sub *sh = new_sh_sub_sid(demuxer, id, sid);
- if (lang && lang[0] && strcmp(lang, "und")) {
- sh->gsh->lang = talloc_strdup(sh, lang);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sid, lang);
- }
- return sh;
+ free(sh->wf);
+ free(sh->codecdata);
}
-static void free_sh_sub(sh_sub_t *sh)
+static void free_sh_video(sh_video_t *sh)
{
- mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_sub at %p\n", sh);
- free(sh->extradata);
- free_sh_stream(sh->gsh);
+ free(sh->bih);
}
-sh_audio_t *new_sh_audio_aid(demuxer_t *demuxer, int id, int aid)
+static void free_sh_stream(struct sh_stream *sh)
{
- if (id > MAX_A_STREAMS - 1 || id < 0) {
- mp_msg(MSGT_DEMUXER, MSGL_WARN,
- "Requested audio stream id overflow (%d > %d)\n", id,
- MAX_A_STREAMS);
- return NULL;
- }
- if (demuxer->a_streams[id]) {
- mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "WARNING: Audio stream header %d redefined.\n", id);
- } else {
- mp_tmsg(MSGT_DEMUXER, MSGL_V, "==> Found audio stream: %d\n", id);
- new_sh_stream_id(demuxer, STREAM_AUDIO, id, aid);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_ID=%d\n", aid);
+ ds_free_packs(sh->ds);
+
+ switch (sh->type) {
+ case STREAM_AUDIO: free_sh_audio(sh->audio); break;
+ case STREAM_VIDEO: free_sh_video(sh->video); break;
+ case STREAM_SUB: free_sh_sub(sh->sub); break;
+ default: abort();
}
- return demuxer->a_streams[id];
}
-static void free_sh_audio(demuxer_t *demuxer, int id)
+void free_demuxer(demuxer_t *demuxer)
{
- sh_audio_t *sh = demuxer->a_streams[id];
- demuxer->a_streams[id] = NULL;
- mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_audio at %p\n", sh);
- free(sh->wf);
- free(sh->codecdata);
- clear_parser(sh);
- free_sh_stream(sh->gsh);
+ if (demuxer->desc->close)
+ demuxer->desc->close(demuxer);
+ // free streams:
+ for (int n = 0; n < demuxer->num_streams; n++)
+ free_sh_stream(demuxer->streams[n]);
+ talloc_free(demuxer);
}
-sh_video_t *new_sh_video_vid(demuxer_t *demuxer, int id, int vid)
+static const char *stream_type_name(enum stream_type type)
{
- if (id > MAX_V_STREAMS - 1 || id < 0) {
- mp_msg(MSGT_DEMUXER, MSGL_WARN,
- "Requested video stream id overflow (%d > %d)\n", id,
- MAX_V_STREAMS);
- return NULL;
- }
- if (demuxer->v_streams[id])
- mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "WARNING: Video stream header %d redefined.\n", id);
- else {
- mp_tmsg(MSGT_DEMUXER, MSGL_V, "==> Found video stream: %d\n", id);
- new_sh_stream_id(demuxer, STREAM_VIDEO, id, vid);
- mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_ID=%d\n", vid);
+ switch (type) {
+ case STREAM_VIDEO: return "video";
+ case STREAM_AUDIO: return "audio";
+ case STREAM_SUB: return "sub";
+ default: return "unknown";
}
- return demuxer->v_streams[id];
}
-static void free_sh_video(sh_video_t *sh)
+static int count_packs(struct demuxer *demux, enum stream_type type)
{
- mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_video at %p\n", sh);
- free(sh->bih);
- free_sh_stream(sh->gsh);
+ int c = 0;
+ for (int n = 0; n < demux->num_streams; n++)
+ c += demux->streams[n]->type == type ? demux->streams[n]->ds->packs : 0;
+ return c;
}
-void free_demuxer(demuxer_t *demuxer)
+static int count_bytes(struct demuxer *demux, enum stream_type type)
{
- int i;
- mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing %s demuxer at %p\n",
- demuxer->desc->shortdesc, demuxer);
- if (demuxer->desc->close)
- demuxer->desc->close(demuxer);
- // free streams:
- for (i = 0; i < MAX_A_STREAMS; i++)
- if (demuxer->a_streams[i])
- free_sh_audio(demuxer, i);
- for (i = 0; i < MAX_V_STREAMS; i++)
- if (demuxer->v_streams[i])
- free_sh_video(demuxer->v_streams[i]);
- for (i = 0; i < MAX_S_STREAMS; i++)
- if (demuxer->s_streams[i])
- free_sh_sub(demuxer->s_streams[i]);
- // free demuxers:
- free_demuxer_stream(demuxer->audio);
- free_demuxer_stream(demuxer->video);
- free_demuxer_stream(demuxer->sub);
- free(demuxer->filename);
- talloc_free(demuxer);
+ int c = 0;
+ for (int n = 0; n < demux->num_streams; n++)
+ c += demux->streams[n]->type == type ? demux->streams[n]->ds->bytes : 0;
+ return c;
}
// Returns the same value as demuxer->fill_buffer: 1 ok, 0 EOF/not selected.
int demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream,
demux_packet_t *dp)
{
- if (!dp || !demuxer_stream_is_selected(demuxer, stream)) {
- free_demux_packet(dp);
+ struct demux_stream *ds = stream ? stream->ds : NULL;
+ if (!dp || !ds || !ds->selected) {
+ talloc_free(dp);
return 0;
- } else {
- ds_add_packet(demuxer->ds[stream->type], dp);
- return 1;
- }
-}
-
-void ds_add_packet(demux_stream_t *ds, demux_packet_t *dp)
-{
- // demux API can't handle 0-sized packets, but at least some vobsubs
- // generate them. Skipping them seems to work fine. Not skipping them will
- // stop demuxing with external vobsubs. See FATE sub/vobsub.{idx,sub} at
- // pts=185.91.
- if (dp->len == 0 && ds->stream_type == STREAM_SUB) {
- mp_dbg(MSGT_DEMUXER, MSGL_INFO, "Discarding empty subtitle packet.\n");
- free_demux_packet(dp);
- return;
}
- // append packet to DS stream:
- ++ds->packs;
+ ds->packs++;
ds->bytes += dp->len;
- if (ds->last) {
+ if (ds->tail) {
// next packet in stream
- ds->last->next = dp;
- ds->last = dp;
+ ds->tail->next = dp;
+ ds->tail = dp;
} else {
// first packet in stream
- ds->first = ds->last = dp;
+ ds->head = ds->tail = dp;
}
mp_dbg(MSGT_DEMUXER, MSGL_DBG2,
- "DEMUX: Append packet to %s, len=%d pts=%5.3f pos=%u [packs: A=%d V=%d]\n",
- (ds == ds->demuxer->audio) ? "d_audio" : "d_video", dp->len,
- dp->pts, (unsigned int) dp->pos, ds->demuxer->audio->packs,
- ds->demuxer->video->packs);
-}
-
-static void allocate_parser(AVCodecContext **avctx, AVCodecParserContext **parser, const char *format)
-{
- enum AVCodecID codec_id = mp_codec_to_av_codec_id(format);
-
- switch (codec_id) {
- case AV_CODEC_ID_AAC_LATM:
- case AV_CODEC_ID_AC3:
- case AV_CODEC_ID_EAC3:
- case AV_CODEC_ID_DTS:
- case AV_CODEC_ID_FLAC:
- case AV_CODEC_ID_MLP:
- case AV_CODEC_ID_MP3:
- case AV_CODEC_ID_MP2:
- case AV_CODEC_ID_TRUEHD:
- *avctx = avcodec_alloc_context3(NULL);
- if (!*avctx)
- return;
- *parser = av_parser_init(codec_id);
- if (!*parser)
- av_freep(avctx);
- break;
- default: ;
- }
-}
-
-static void get_parser(sh_audio_t *sh, AVCodecContext **avctx, AVCodecParserContext **parser)
-{
- *avctx = NULL;
- *parser = NULL;
-
- if (!sh || !sh->needs_parsing)
- return;
-
- *avctx = sh->avctx;
- *parser = sh->parser;
- if (*parser)
- return;
-
- allocate_parser(avctx, parser, sh->gsh->codec);
- sh->avctx = *avctx;
- sh->parser = *parser;
-}
-
-int ds_parse(demux_stream_t *ds, uint8_t **buffer, int *len, double pts, int64_t pos)
-{
- AVCodecContext *avctx;
- AVCodecParserContext *parser;
- get_parser(ds->sh, &avctx, &parser);
- if (!parser)
- return *len;
- return av_parser_parse2(parser, avctx, buffer, len, *buffer, *len, pts, pts, pos);
-}
-
-static void clear_parser(sh_audio_t *sh)
-{
- av_parser_close(sh->parser);
- sh->parser = NULL;
- av_freep(&sh->avctx);
-}
-
-void ds_clear_parser(demux_stream_t *ds)
-{
- if (!ds->sh)
- return;
- clear_parser(ds->sh);
-}
-
-void ds_read_packet(demux_stream_t *ds, stream_t *stream, int len,
- double pts, int64_t pos, bool keyframe)
-{
- demux_packet_t *dp = new_demux_packet(len);
- len = stream_read(stream, dp->buffer, len);
- resize_demux_packet(dp, len);
- dp->pts = pts;
- dp->pos = pos;
- dp->keyframe = keyframe;
- // append packet to DS stream:
- ds_add_packet(ds, dp);
+ "DEMUX: Append packet to %s, len=%d pts=%5.3f pos=%"PRIu64" "
+ "[packs: A=%d V=%d S=%d]\n", stream_type_name(stream->type),
+ dp->len, dp->pts, dp->pos, count_packs(demuxer, STREAM_AUDIO),
+ count_packs(demuxer, STREAM_VIDEO), count_packs(demuxer, STREAM_SUB));
+ return 1;
}
static bool demux_check_queue_full(demuxer_t *demux)
{
- int apacks = demux->audio ? demux->audio->packs : 0;
- int abytes = demux->audio ? demux->audio->bytes : 0;
- int vpacks = demux->video ? demux->video->packs : 0;
- int vbytes = demux->video ? demux->video->bytes : 0;
+ for (int n = 0; n < demux->num_streams; n++) {
+ struct sh_stream *sh = demux->streams[n];
+ if (sh->ds->packs > MAX_PACKS || sh->ds->bytes > MAX_PACK_BYTES)
+ goto overflow;
+ }
+ return false;
- if (apacks < MAX_PACKS && abytes < MAX_PACK_BYTES &&
- vpacks < MAX_PACKS && vbytes < MAX_PACK_BYTES)
- return false;
+overflow:
if (!demux->warned_queue_overflow) {
mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many packets in the demuxer "
"packet queue (video: %d packets in %d bytes, audio: %d "
- "packets in %d bytes).\n", vpacks, vbytes, apacks, abytes);
+ "packets in %d bytes, sub: %d packets in %d bytes).\n",
+ count_packs(demux, STREAM_VIDEO), count_bytes(demux, STREAM_VIDEO),
+ count_packs(demux, STREAM_AUDIO), count_bytes(demux, STREAM_AUDIO),
+ count_packs(demux, STREAM_SUB), count_bytes(demux, STREAM_SUB));
mp_tmsg(MSGT_DEMUXER, MSGL_HINT, "Maybe you are playing a non-"
- "interleaved stream/file or the codec failed?\nFor AVI files, "
- "try to force non-interleaved mode with the "
- "--demuxer=avi --avi-ni options.\n");
+ "interleaved stream/file or the codec failed?\n");
}
-
demux->warned_queue_overflow = true;
-
return true;
}
@@ -626,51 +400,19 @@ static bool demux_check_queue_full(demuxer_t *demux)
// 0 = EOF or no stream found or invalid type
// 1 = successfully read a packet
-int demux_fill_buffer(demuxer_t *demux, demux_stream_t *ds)
+static int demux_fill_buffer(demuxer_t *demux)
{
- // Note: parameter 'ds' can be NULL!
- return demux->desc->fill_buffer ? demux->desc->fill_buffer(demux, ds) : 0;
+ return demux->desc->fill_buffer ? demux->desc->fill_buffer(demux) : 0;
}
-// return value:
-// 0 = EOF
-// 1 = successful
-int ds_fill_buffer(demux_stream_t *ds)
-{
- demuxer_t *demux = ds->demuxer;
- if (ds->current)
- free_demux_packet(ds->current);
- ds->current = NULL;
- mp_dbg(MSGT_DEMUXER, MSGL_DBG3, "ds_fill_buffer (%s) called\n",
- ds == demux->audio ? "d_audio" : ds == demux->video ? "d_video" :
- ds == demux->sub ? "d_sub" : "unknown");
+static void ds_get_packets(struct sh_stream *sh)
+{
+ struct demux_stream *ds = sh->ds;
+ demuxer_t *demux = sh->demuxer;
+ mp_dbg(MSGT_DEMUXER, MSGL_DBG3, "ds_get_packets (%s) called\n",
+ stream_type_name(sh->type));
while (1) {
- int apacks = demux->audio ? demux->audio->packs : 0;
- int vpacks = demux->video ? demux->video->packs : 0;
- if (ds->packs) {
- demux_packet_t *p = ds->first;
- // copy useful data:
- ds->buffer = p->buffer;
- ds->buffer_pos = 0;
- ds->buffer_size = p->len;
- ds->pos = p->pos;
- ds->dpos += p->len; // !!!
- ++ds->pack_no;
- if (p->pts != MP_NOPTS_VALUE) {
- ds->pts = p->pts;
- ds->pts_bytes = 0;
- }
- ds->pts_bytes += p->len; // !!!
- if (p->stream_pts != MP_NOPTS_VALUE)
- demux->stream_pts = p->stream_pts;
- ds->keyframe = p->keyframe;
- // unlink packet:
- ds->bytes -= p->len;
- ds->current = p;
- ds->first = p->next;
- if (!ds->first)
- ds->last = NULL;
- --ds->packs;
+ if (ds->head) {
/* The code below can set ds->eof to 1 when another stream runs
* out of buffer space. That makes sense because in that situation
* the calling code should not count on being able to demux more
@@ -679,205 +421,76 @@ int ds_fill_buffer(demux_stream_t *ds)
* despite the eof flag then it's better to clear it to avoid
* weird behavior. */
ds->eof = 0;
- ds->fill_count = 0;
- return 1;
+ return;
}
- // avoid buffering too far ahead in e.g. badly interleaved files
- // or when one stream is shorter, without breaking large audio
- // delay with well interleaved files.
- // This needs to be enough for at least 1 second of packets
- // since libavformat mov demuxer does not try to interleave
- // with more than 1s precision.
- if (ds->fill_count > 80)
- break;
if (demux_check_queue_full(demux))
break;
- if (!demux_fill_buffer(demux, ds)) {
- mp_dbg(MSGT_DEMUXER, MSGL_DBG2,
- "ds_fill_buffer()->demux_fill_buffer() failed\n");
+ if (!demux_fill_buffer(demux))
break; // EOF
- }
-
- struct sh_video *sh_video = demux->video->sh;
-
- if (sh_video && sh_video->gsh->attached_picture) {
- if (demux->audio)
- ds->fill_count += demux->audio->packs - apacks;
- if (demux->video && demux->video->packs > vpacks)
- ds->fill_count++;
- }
}
- ds->buffer_pos = ds->buffer_size = 0;
- ds->buffer = NULL;
- mp_msg(MSGT_DEMUXER, MSGL_V,
- "ds_fill_buffer: EOF reached (stream: %s) \n",
- ds == demux->audio ? "audio" : "video");
+ mp_msg(MSGT_DEMUXER, MSGL_V, "ds_get_packets: EOF reached (stream: %s)\n",
+ stream_type_name(sh->type));
ds->eof = 1;
- return 0;
}
-int demux_read_data(demux_stream_t *ds, unsigned char *mem, int len)
-{
- int x;
- int bytes = 0;
- while (len > 0) {
- x = ds->buffer_size - ds->buffer_pos;
- if (x == 0) {
- if (!ds_fill_buffer(ds))
- return bytes;
- } else {
- if (x > len)
- x = len;
- if (mem)
- memcpy(mem + bytes, &ds->buffer[ds->buffer_pos], x);
- bytes += x;
- len -= x;
- ds->buffer_pos += x;
- }
- }
- return bytes;
-}
+// Read a packet from the given stream. The returned packet belongs to the
+// caller, who has to free it with talloc_free(). Might block. Returns NULL
+// on EOF.
+struct demux_packet *demux_read_packet(struct sh_stream *sh)
+{
+ struct demux_stream *ds = sh ? sh->ds : NULL;
+ if (ds) {
+ ds_get_packets(sh);
+ struct demux_packet *pkt = ds->head;
+ if (pkt) {
+ ds->head = pkt->next;
+ pkt->next = NULL;
+ if (!ds->head)
+ ds->tail = NULL;
+ ds->bytes -= pkt->len;
+ ds->packs--;
-/**
- * \brief read data until the given 3-byte pattern is encountered, up to maxlen
- * \param mem memory to read data into, may be NULL to discard data
- * \param maxlen maximum number of bytes to read
- * \param read number of bytes actually read
- * \param pattern pattern to search for (lowest 8 bits are ignored)
- * \return whether pattern was found
- */
-int demux_pattern_3(demux_stream_t *ds, unsigned char *mem, int maxlen,
- int *read, uint32_t pattern)
-{
- register uint32_t head = 0xffffff00;
- register uint32_t pat = pattern & 0xffffff00;
- int total_len = 0;
- do {
- register unsigned char *ds_buf = &ds->buffer[ds->buffer_size];
- int len = ds->buffer_size - ds->buffer_pos;
- register long pos = -len;
- if (unlikely(pos >= 0)) { // buffer is empty
- ds_fill_buffer(ds);
- continue;
- }
- do {
- head |= ds_buf[pos];
- head <<= 8;
- } while (++pos && head != pat);
- len += pos;
- if (total_len + len > maxlen)
- len = maxlen - total_len;
- len = demux_read_data(ds, mem ? &mem[total_len] : NULL, len);
- total_len += len;
- } while ((head != pat || total_len < 3) && total_len < maxlen && !ds->eof);
- if (read)
- *read = total_len;
- return total_len >= 3 && head == pat;
-}
-
-void ds_free_packs(demux_stream_t *ds)
-{
- demux_packet_t *dp = ds->first;
- while (dp) {
- demux_packet_t *dn = dp->next;
- free_demux_packet(dp);
- dp = dn;
- }
- if (ds->asf_packet) {
- // free unfinished .asf fragments:
- free_demux_packet(ds->asf_packet);
- ds->asf_packet = NULL;
- }
- ds->first = ds->last = NULL;
- ds->packs = 0; // !!!!!
- ds->bytes = 0;
- if (ds->current)
- free_demux_packet(ds->current);
- ds->current = NULL;
- ds->buffer = NULL;
- ds->buffer_pos = ds->buffer_size;
- ds->pts = MP_NOPTS_VALUE;
- ds->pts_bytes = 0;
-}
-
-int ds_get_packet(demux_stream_t *ds, unsigned char **start)
-{
- int len;
- if (ds->buffer_pos >= ds->buffer_size) {
- if (!ds_fill_buffer(ds)) {
- // EOF
- *start = NULL;
- return -1;
+ if (pkt->stream_pts != MP_NOPTS_VALUE)
+ sh->demuxer->stream_pts = pkt->stream_pts;
+
+ return pkt;
}
}
- len = ds->buffer_size - ds->buffer_pos;
- *start = &ds->buffer[ds->buffer_pos];
- ds->buffer_pos += len;
- return len;
+ return NULL;
}
-int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts)
+// Return the pts of the next packet that demux_read_packet() would return.
+// Might block. Sometimes used to force a packet read, without removing any
+// packets from the queue.
+double demux_get_next_pts(struct sh_stream *sh)
{
- int len;
- *pts = MP_NOPTS_VALUE;
- len = ds_get_packet(ds, start);
- if (len < 0)
- return len;
- // Return pts unless this read starts from the middle of a packet
- if (len == ds->buffer_pos)
- *pts = ds->current->pts;
- return len;
+ if (sh) {
+ ds_get_packets(sh);
+ if (sh->ds->head)
+ return sh->ds->head->pts;
+ }
+ return MP_NOPTS_VALUE;
}
-struct demux_packet *ds_get_packet_sub(demux_stream_t *ds)
+// Return whether a packet is queued. Never blocks, never forces any reads.
+bool demux_has_packet(struct sh_stream *sh)
{
- if (ds->buffer_pos >= ds->buffer_size) {
- if (!ds->packs)
- return NULL; // no sub
- if (!ds_fill_buffer(ds))
- return NULL; // EOF
- }
- if (ds->buffer_pos < ds->buffer_size) {
- ds->current->buffer += ds->buffer_pos;
- ds->buffer_size -= ds->buffer_pos;
- }
- ds->buffer_pos = ds->buffer_size;
- return ds->current;
+ return sh && sh->ds->head;
}
-struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last)
+// Same as demux_has_packet, but to be called internally by demuxers, as
+// opposed to the user of the demuxer.
+bool demuxer_stream_has_packets_queued(struct demuxer *d, struct sh_stream *stream)
{
- if (!repeat_last)
- ds_fill_buffer(ds);
- // This shouldn't get used together with partial reads
- // However, some old demuxers return parsed packets with an offset in
- // -correct-pts mode (at least mpegts).
- // Not all old demuxers will actually work.
- if (ds->buffer_pos < ds->buffer_size) {
- ds->current->buffer += ds->buffer_pos;
- ds->buffer_size -= ds->buffer_pos;
- }
- ds->buffer_pos = ds->buffer_size;
- return ds->current;
+ return demux_has_packet(stream);
}
-double ds_get_next_pts(demux_stream_t *ds)
+// Return whether EOF was returned with an earlier packet read.
+bool demux_stream_eof(struct sh_stream *sh)
{
- demuxer_t *demux = ds->demuxer;
- // if we have not read from the "current" packet, consider it
- // as the next, otherwise we never get the pts for the first packet.
- while (!ds->first && (!ds->current || ds->buffer_pos)) {
- if (demux_check_queue_full(demux))
- return MP_NOPTS_VALUE;
- if (!demux_fill_buffer(demux, ds))
- return MP_NOPTS_VALUE;
- }
- // take pts from "current" if we never read from it.
- if (ds->current && !ds->buffer_pos)
- return ds->current->pts;
- return ds->first->pts;
+ return !sh || sh->ds->eof;
}
// ====================================================================
@@ -887,89 +500,60 @@ void demuxer_help(void)
int i;
mp_msg(MSGT_DEMUXER, MSGL_INFO, "Available demuxers:\n");
- mp_msg(MSGT_DEMUXER, MSGL_INFO, " demuxer: info: (comment)\n");
+ mp_msg(MSGT_DEMUXER, MSGL_INFO, " demuxer: info:\n");
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DEMUXERS\n");
for (i = 0; demuxer_list[i]; i++) {
- if (demuxer_list[i]->type >= DEMUXER_TYPE_END) // internal type
- continue;
- if (demuxer_list[i]->comment && strlen(demuxer_list[i]->comment))
- mp_msg(MSGT_DEMUXER, MSGL_INFO, "%10s %s (%s)\n",
- demuxer_list[i]->name, demuxer_list[i]->info,
- demuxer_list[i]->comment);
- else
- mp_msg(MSGT_DEMUXER, MSGL_INFO, "%10s %s\n",
- demuxer_list[i]->name, demuxer_list[i]->info);
+ mp_msg(MSGT_DEMUXER, MSGL_INFO, "%10s %s\n",
+ demuxer_list[i]->name, demuxer_list[i]->desc);
}
}
-
-/**
- * Get demuxer type for a given demuxer name
- *
- * @param demuxer_name string with demuxer name of demuxer number
- * @param force will be set if demuxer should be forced.
- * May be NULL.
- * @return DEMUXER_TYPE_xxx, -1 if error or not found
- */
-static int get_demuxer_type_from_name(char *demuxer_name, int *force)
-{
- if (!demuxer_name || !demuxer_name[0])
- return DEMUXER_TYPE_UNKNOWN;
- if (force)
- *force = demuxer_name[0] == '+';
- if (demuxer_name[0] == '+')
- demuxer_name = &demuxer_name[1];
- for (int i = 0; demuxer_list[i]; i++) {
- if (demuxer_list[i]->type >= DEMUXER_TYPE_END)
- // Can't select special demuxers from commandline
- continue;
- if (strcmp(demuxer_name, demuxer_list[i]->name) == 0)
- return demuxer_list[i]->type;
+static const char *d_level(enum demux_check level)
+{
+ switch (level) {
+ case DEMUX_CHECK_FORCE: return "force";
+ case DEMUX_CHECK_UNSAFE: return "unsafe";
+ case DEMUX_CHECK_REQUEST:return "request";
+ case DEMUX_CHECK_NORMAL: return "normal";
}
-
- return -1;
+ abort();
}
static struct demuxer *open_given_type(struct MPOpts *opts,
const struct demuxer_desc *desc,
- struct stream *stream, bool force,
- int audio_id, int video_id, int sub_id,
- char *filename,
- struct demuxer_params *params)
-{
- struct demuxer *demuxer;
- int fformat;
- mp_msg(MSGT_DEMUXER, MSGL_V, "Trying demuxer: %s\n", desc->name);
- demuxer = new_demuxer(opts, stream, desc->type, audio_id,
- video_id, sub_id, filename);
- demuxer->params = params;
- if (desc->check_file)
- fformat = desc->check_file(demuxer);
- else
- fformat = desc->type;
- if (force)
- fformat = desc->type;
- if (fformat == 0)
- goto fail;
- if (fformat == desc->type) {
+ struct stream *stream,
+ struct demuxer_params *params,
+ enum demux_check check)
+{
+ struct demuxer *demuxer = talloc_ptrtype(NULL, demuxer);
+ *demuxer = (struct demuxer) {
+ .desc = desc,
+ .type = desc->type,
+ .stream = stream,
+ .stream_pts = MP_NOPTS_VALUE,
+ .movi_start = stream->start_pos,
+ .movi_end = stream->end_pos,
+ .seekable = 1,
+ .accurate_seek = true,
+ .filepos = -1,
+ .opts = opts,
+ .filename = talloc_strdup(demuxer, stream->url),
+ };
+ demuxer->params = params; // temporary during open()
+ stream_seek(stream, stream->start_pos);
+
+ mp_msg(MSGT_DEMUXER, MSGL_V, "Trying demuxer: %s (force-level: %s)\n",
+ desc->name, d_level(check));
+
+ int ret = demuxer->desc->open(demuxer, check);
+ if (ret >= 0) {
+ demuxer->params = NULL;
if (demuxer->filetype)
mp_tmsg(MSGT_DEMUXER, MSGL_INFO, "Detected file format: %s (%s)\n",
- demuxer->filetype, desc->shortdesc);
+ demuxer->filetype, desc->desc);
else
mp_tmsg(MSGT_DEMUXER, MSGL_INFO, "Detected file format: %s\n",
- desc->shortdesc);
- if (demuxer->desc->open) {
- struct demuxer *demux2 = demuxer->desc->open(demuxer);
- if (!demux2) {
- mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "Opening as detected format "
- "\"%s\" failed.\n", desc->shortdesc);
- goto fail;
- }
- /* At least demux_mov can return a demux_demuxers instance
- * from open() instead of the original fed in. */
- demuxer = demux2;
- }
- demuxer->file_format = fformat;
+ desc->desc);
if (stream_manages_timeline(demuxer->stream)) {
// Incorrect, but fixes some behavior with DVD/BD
demuxer->ts_resets_possible = false;
@@ -980,135 +564,73 @@ static struct demuxer *open_given_type(struct MPOpts *opts,
demuxer_sort_chapters(demuxer);
demux_info_update(demuxer);
return demuxer;
- } else {
- // demux_mov can return playlist instead of mov
- if (fformat == DEMUXER_TYPE_PLAYLIST)
- return demuxer; // handled in mplayer.c
- /* Internal MPEG PS demuxer check can return other MPEG subtypes
- * which don't have their own checks; recurse to try opening as
- * the returned type instead. */
- free_demuxer(demuxer);
- desc = get_demuxer_desc_from_type(fformat);
- if (!desc) {
- mp_msg(MSGT_DEMUXER, MSGL_ERR,
- "BUG: recursion to nonexistent file format\n");
- return NULL;
- }
- return open_given_type(opts, desc, stream, false, audio_id,
- video_id, sub_id, filename, params);
}
- fail:
+
free_demuxer(demuxer);
return NULL;
}
-struct demuxer *demux_open_withparams(struct MPOpts *opts,
- struct stream *stream, int file_format,
- char *force_format, int audio_id,
- int video_id, int sub_id, char *filename,
- struct demuxer_params *params)
+static const int d_normal[] = {DEMUX_CHECK_NORMAL, DEMUX_CHECK_UNSAFE, -1};
+static const int d_request[] = {DEMUX_CHECK_REQUEST, -1};
+static const int d_force[] = {DEMUX_CHECK_FORCE, -1};
+
+struct demuxer *demux_open(struct stream *stream, char *force_format,
+ struct demuxer_params *params, struct MPOpts *opts)
{
- struct demuxer *demuxer = NULL;
- const struct demuxer_desc *desc;
+ const int *check_levels = d_normal;
+ const struct demuxer_desc *check_desc = NULL;
- int force = 0;
- int demuxer_type;
- if ((demuxer_type = get_demuxer_type_from_name(force_format, &force)) < 0) {
- mp_msg(MSGT_DEMUXER, MSGL_ERR, "Demuxer %s does not exist.\n",
- force_format);
- return NULL;
- }
- if (demuxer_type)
- file_format = demuxer_type;
-
- // Some code (e.g. dvd stuff, network code, or extension.c) explicitly
- // request certain file formats. The list of formats are always handled by
- // libavformat.
- // Maybe attempts should be made to convert the mplayer format to the libav
- // format, instead of reyling on libav to auto-detect the stream's format
- // correctly.
- switch (file_format) {
- //case DEMUXER_TYPE_MPEG_PS:
- //case DEMUXER_TYPE_MPEG_TS:
- case DEMUXER_TYPE_Y4M:
- case DEMUXER_TYPE_NSV:
- case DEMUXER_TYPE_AAC:
- case DEMUXER_TYPE_MPC:
- file_format = DEMUXER_TYPE_LAVF;
- }
+ if (!force_format)
+ force_format = opts->demuxer_name;
+ if (!force_format)
+ force_format = stream->demuxer;
- // If somebody requested a demuxer check it
- if (file_format) {
- desc = get_demuxer_desc_from_type(file_format);
- if (!desc)
- // should only happen with obsolete -demuxer 99 numeric format
+ if (force_format && force_format[0]) {
+ check_levels = d_request;
+ if (force_format[0] == '+') {
+ force_format += 1;
+ check_levels = d_force;
+ }
+ for (int n = 0; demuxer_list[n]; n++) {
+ if (strcmp(demuxer_list[n]->name, force_format) == 0)
+ check_desc = demuxer_list[n];
+ }
+ if (!check_desc) {
+ mp_msg(MSGT_DEMUXER, MSGL_ERR, "Demuxer %s does not exist.\n",
+ force_format);
return NULL;
- return open_given_type(opts, desc, stream, force, audio_id,
- video_id, sub_id, filename, params);
- }
-
- // Test demuxers with safe file checks
- for (int i = 0; (desc = demuxer_list[i]); i++) {
- if (desc->safe_check) {
- demuxer = open_given_type(opts, desc, stream, false, audio_id,
- video_id, sub_id, filename, params);
- if (demuxer)
- return demuxer;
}
}
- // Ok. We're over the stable detectable fileformats, the next ones are
- // a bit fuzzy. So by default (extension_parsing==1) try extension-based
- // detection first:
- if (filename && opts->extension_parsing == 1) {
- desc = get_demuxer_desc_from_type(demuxer_type_by_filename(filename));
- if (desc)
- demuxer = open_given_type(opts, desc, stream, false, audio_id,
- video_id, sub_id, filename, params);
- if (demuxer)
- return demuxer;
- }
-
- // Finally try detection for demuxers with unsafe checks
- for (int i = 0; (desc = demuxer_list[i]); i++) {
- if (!desc->safe_check && desc->check_file) {
- demuxer = open_given_type(opts, desc, stream, false, audio_id,
- video_id, sub_id, filename, params);
- if (demuxer)
- return demuxer;
+ // Test demuxers from first to last, one pass for each check_levels[] entry
+ for (int pass = 0; check_levels[pass] != -1; pass++) {
+ enum demux_check level = check_levels[pass];
+ for (int n = 0; demuxer_list[n]; n++) {
+ const struct demuxer_desc *desc = demuxer_list[n];
+ if (!check_desc || desc == check_desc) {
+ struct demuxer *demuxer = open_given_type(opts, desc, stream,
+ params, level);
+ if (demuxer)
+ return demuxer;
+ }
}
}
return NULL;
}
-struct demuxer *demux_open(struct MPOpts *opts, stream_t *vs, int file_format,
- int audio_id, int video_id, int sub_id,
- char *filename)
-{
- return demux_open_withparams(opts, vs, file_format, opts->demuxer_name,
- audio_id, video_id, sub_id, filename, NULL);
-}
-
void demux_flush(demuxer_t *demuxer)
{
- ds_free_packs(demuxer->video);
- ds_free_packs(demuxer->audio);
- ds_free_packs(demuxer->sub);
+ for (int n = 0; n < demuxer->num_streams; n++)
+ ds_free_packs(demuxer->streams[n]->ds);
+ demuxer->warned_queue_overflow = false;
}
int demux_seek(demuxer_t *demuxer, float rel_seek_secs, float audio_delay,
int flags)
{
if (!demuxer->seekable) {
- if (demuxer->file_format == DEMUXER_TYPE_AVI)
- mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in raw AVI streams. (Index required, try with the -idx switch.)\n");
-#ifdef CONFIG_TV
- else if (demuxer->file_format == DEMUXER_TYPE_TV)
- mp_tmsg(MSGT_SEEK, MSGL_WARN, "TV input is not seekable! (Seeking will probably be for changing channels ;)\n");
-#endif
- else
- mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in this file.\n");
+ mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in this file.\n");
return 0;
}
@@ -1117,10 +639,6 @@ int demux_seek(demuxer_t *demuxer, float rel_seek_secs, float audio_delay,
// clear demux buffers:
demux_flush(demuxer);
- demuxer->video->eof = 0;
- demuxer->audio->eof = 0;
- demuxer->sub->eof = 0;
- demuxer->warned_queue_overflow = false;
/* HACK: assume any demuxer used with these streams can cope with
* the stream layer suddenly seeking to a different position under it
@@ -1267,48 +785,32 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
{
assert(!stream || stream->type == type);
- // don't flush buffers if stream is already selected
- if (stream && demuxer_stream_is_selected(demuxer, stream))
- return;
-
- int old_id = demuxer->ds[type]->id;
-
- // legacy
- int index = stream ? stream->stream_index : -2;
- if (type == STREAM_AUDIO) {
- if (demux_control(demuxer, DEMUXER_CTRL_SWITCH_AUDIO, &index)
- == DEMUXER_CTRL_NOTIMPL)
- demuxer->audio->id = index;
- } else if (type == STREAM_VIDEO) {
- if (demux_control(demuxer, DEMUXER_CTRL_SWITCH_VIDEO, &index)
- == DEMUXER_CTRL_NOTIMPL)
- demuxer->video->id = index;
- } else if (type == STREAM_SUB) {
- demuxer->ds[type]->id = index;
- } else {
- abort();
- }
-
- int new_id = demuxer->ds[type]->id;
- void *new = NULL;
- if (new_id >= 0) {
- switch (type) {
- case STREAM_VIDEO: new = demuxer->v_streams[new_id]; break;
- case STREAM_AUDIO: new = demuxer->a_streams[new_id]; break;
- case STREAM_SUB: new = demuxer->s_streams[new_id]; break;
- }
+ for (int n = 0; n < demuxer->num_streams; n++) {
+ struct sh_stream *cur = demuxer->streams[n];
+ if (cur->type == type)
+ demuxer_select_track(demuxer, cur, cur == stream);
}
- demuxer->ds[type]->sh = new;
+}
- if (old_id != new_id) {
- ds_free_packs(demuxer->ds[type]);
+void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
+ bool selected)
+{
+ // don't flush buffers if stream is already selected / unselected
+ if (stream->ds->selected != selected) {
+ stream->ds->selected = selected;
+ ds_free_packs(stream->ds);
demux_control(demuxer, DEMUXER_CTRL_SWITCHED_TRACKS, NULL);
}
}
+void demuxer_enable_autoselect(struct demuxer *demuxer)
+{
+ demuxer->stream_autoselect = true;
+}
+
bool demuxer_stream_is_selected(struct demuxer *d, struct sh_stream *stream)
{
- return stream && d->ds[stream->type]->id == stream->stream_index;
+ return stream && stream->ds->selected;
}
int demuxer_add_attachment(demuxer_t *demuxer, struct bstr name,