summaryrefslogtreecommitdiffstats
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",
-