summaryrefslogtreecommitdiffstats
path: root/demux/demux.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-11-05 17:02:04 +0100
committerwm4 <wm4@nowhere>2012-11-12 20:06:14 +0100
commitd4bdd0473d6f43132257c9fb3848d829755167a3 (patch)
tree8021c2f7da1841393c8c832105e20cd527826d6c /demux/demux.c
parentbd48deba77bd5582c5829d6fe73a7d2571088aba (diff)
downloadmpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.bz2
mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.xz
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code.
Diffstat (limited to 'demux/demux.c')
-rw-r--r--demux/demux.c1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/demux/demux.c b/demux/demux.c
new file mode 100644
index 0000000000..3673fd06e3
--- /dev/null
+++ b/demux/demux.c
@@ -0,0 +1,1435 @@
+/*
+ * DEMUXER v2.5
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "config.h"
+#include "options.h"
+#include "talloc.h"
+#include "mp_msg.h"
+#include "m_config.h"
+
+#include "stream/stream.h"
+#include "demuxer.h"
+#include "stheader.h"
+#include "mf.h"
+
+#include "libaf/format.h"
+
+#include "libavcodec/avcodec.h"
+#if MP_INPUT_BUFFER_PADDING_SIZE < FF_INPUT_BUFFER_PADDING_SIZE
+#error MP_INPUT_BUFFER_PADDING_SIZE is too small!
+#endif
+
+static void clear_parser(sh_common_t *sh);
+
+// Demuxer list
+extern const struct demuxer_desc demuxer_desc_edl;
+extern const struct demuxer_desc demuxer_desc_cue;
+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_gif;
+extern const demuxer_desc_t demuxer_desc_lavf;
+extern const demuxer_desc_t demuxer_desc_lavf_preferred;
+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;
+
+/* Please do not add any new demuxers here. If you want to implement a new
+ * demuxer, add it to libavformat, except for wrappers around external
+ * libraries and demuxers requiring binary support. */
+
+const demuxer_desc_t *const demuxer_list[] = {
+ &demuxer_desc_edl,
+ &demuxer_desc_cue,
+ &demuxer_desc_rawaudio,
+ &demuxer_desc_rawvideo,
+#ifdef CONFIG_TV
+ &demuxer_desc_tv,
+#endif
+ &demuxer_desc_mf,
+ &demuxer_desc_lavf_preferred,
+ &demuxer_desc_avi,
+ &demuxer_desc_asf,
+ &demuxer_desc_matroska,
+#ifdef CONFIG_GIF
+ &demuxer_desc_gif,
+#endif
+ &demuxer_desc_lavf,
+#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,
+ /* Please do not add any new demuxers here. If you want to implement a new
+ * demuxer, add it to libavformat, except for wrappers around external
+ * libraries and demuxers requiring binary support. */
+ NULL
+};
+
+static struct demux_packet *create_packet(size_t len)
+{
+ if (len > 1000000000) {
+ mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Attempt to allocate demux packet "
+ "over 1 GB!\n");
+ abort();
+ }
+ struct demux_packet *dp = malloc(sizeof(struct demux_packet));
+ dp->len = len;
+ dp->next = NULL;
+ dp->pts = MP_NOPTS_VALUE;
+ dp->duration = -1;
+ dp->stream_pts = MP_NOPTS_VALUE;
+ dp->pos = 0;
+ dp->keyframe = false;
+ dp->refcount = 1;
+ dp->master = NULL;
+ dp->buffer = NULL;
+ dp->avpacket = NULL;
+ return dp;
+}
+
+struct demux_packet *new_demux_packet(size_t len)
+{
+ struct demux_packet *dp = create_packet(len);
+ dp->buffer = malloc(len + MP_INPUT_BUFFER_PADDING_SIZE);
+ if (!dp->buffer) {
+ mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Memory allocation failure!\n");
+ abort();
+ }
+ memset(dp->buffer + len, 0, 8);
+ return dp;
+}
+
+// data must already have suitable padding
+struct demux_packet *new_demux_packet_fromdata(void *data, size_t len)
+{
+ struct demux_packet *dp = create_packet(len);
+ dp->buffer = data;
+ return dp;
+}
+
+void resize_demux_packet(struct demux_packet *dp, size_t len)
+{
+ if (len > 1000000000) {
+ mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Attempt to realloc demux packet "
+ "over 1 GB!\n");
+ abort();
+ }
+ dp->buffer = realloc(dp->buffer, len + MP_INPUT_BUFFER_PADDING_SIZE);
+ if (!dp->buffer) {
+ mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Memory allocation failure!\n");
+ abort();
+ }
+ memset(dp->buffer + len, 0, 8);
+ dp->len = len;
+}
+
+struct demux_packet *clone_demux_packet(struct demux_packet *pack)
+{
+ struct demux_packet *dp = malloc(sizeof(struct demux_packet));
+ while (pack->master)
+ pack = pack->master; // find the master
+ memcpy(dp, pack, sizeof(struct demux_packet));
+ dp->next = NULL;
+ dp->refcount = 0;
+ dp->master = pack;
+ pack->refcount++;
+ return dp;
+}
+
+void free_demux_packet(struct demux_packet *dp)
+{
+ if (dp->master == NULL) { //dp is a master packet
+ dp->refcount--;
+ if (dp->refcount == 0) {
+ if (dp->avpacket)
+ talloc_free(dp->avpacket);
+ else
+ free(dp->buffer);
+ free(dp);
+ }
+ return;
+ }
+ // dp is a clone:
+ free_demux_packet(dp->master);
+ free(dp);
+}
+
+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;
+}
+
+struct sh_stream *ds_gsh(struct demux_stream *ds)
+{
+ // Ideally ds would have a gsh field, but since all the old demuxers set
+ // ds->sh themselves and we don't want to change them, enjoy this hack.
+ if (!ds->sh)
+ return NULL;
+ switch (ds->stream_type) {
+ case STREAM_VIDEO: return ((struct sh_video *)ds->sh)->gsh;
+ case STREAM_AUDIO: return ((struct sh_audio *)ds->sh)->gsh;
+ case STREAM_SUB: return ((struct sh_sub *)ds->sh)->gsh;
+ }
+ assert(false);
+}
+
+/**
+ * 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;
+}
+
+
+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_VIDEO, a_id);
+ d->video = new_demuxer_stream(d, STREAM_AUDIO, 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;
+}
+
+const char *sh_sub_type2str(int type)
+{
+ switch (type) {
+ case 't': return "text";
+ case 'm': return "movtext";
+ case 'a': return "ass";
+ case 'v': return "vobsub";
+ case 'x': return "xsub";
+ case 'b': return "dvb";
+ case 'd': return "dvb-teletext";
+ case 'p': return "hdmv pgs";
+ }
+ return "unknown";
+}
+
+static struct sh_stream *new_sh_stream(demuxer_t *demuxer,
+ enum stream_type type,
+ int stream_index,
+ int tid)
+{
+ struct sh_stream *sh = talloc_struct(demuxer, struct sh_stream, {
+ .type = type,
+ .demuxer = demuxer,
+ .index = demuxer->num_streams,
+ .demuxer_id = tid, // may be overwritten by demuxer
+ .tid = tid,
+ .stream_index = stream_index,
+ .opts = demuxer->opts,
+ });
+ 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->vid = sh->tid;
+ sht->ds = demuxer->video;
+ sh->video = sht;
+ sh->common_header = (struct sh_common *) sht;
+ demuxer->v_streams[sh->stream_index] = sht;
+ break;
+ }
+ case STREAM_AUDIO: {
+ struct sh_audio *sht = talloc_zero(demuxer, struct sh_audio);
+ sht->aid = tid;
+ sht->ds = demuxer->audio;
+ sht->samplesize = 2;
+ sht->sample_format = AF_FORMAT_S16_NE;
+ sh->audio = sht;
+ sh->common_header = (struct sh_common *) sht;
+ demuxer->a_streams[sh->stream_index] = sht;
+ break;
+ }
+ case STREAM_SUB: {
+ struct sh_sub *sht = talloc_zero(demuxer, struct sh_sub);
+ sht->sid = tid;
+ sht->ds = demuxer->sub;
+ sh->sub = sht;
+ sh->common_header = (struct sh_common *) sht;
+ demuxer->s_streams[sh->stream_index] = sht;
+ break;
+ }
+ default: assert(false);
+ }
+ sh->common_header->opts = sh->opts;
+ sh->common_header->gsh = sh;
+ return sh;
+}
+
+sh_sub_t *new_sh_sub_sid(demuxer_t *demuxer, int id, int sid)
+{
+ 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(demuxer, STREAM_SUB, id, sid);
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SUBTITLE_ID=%d\n", sid);
+ }
+ return demuxer->s_streams[id];
+}
+
+struct sh_sub *new_sh_sub_sid_lang(struct demuxer *demuxer, int id, int sid,
+ const char *lang)
+{
+ struct sh_sub *sh = new_sh_sub_sid(demuxer, id, sid);
+ if (lang && lang[0] && strcmp(lang, "und")) {
+ sh->lang = talloc_strdup(sh, lang);
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SID_%d_LANG=%s\n", sid, lang);
+ }
+ return sh;
+}
+
+static void free_sh_sub(sh_sub_t *sh)
+{
+ mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_sub at %p\n", sh);
+ free(sh->extradata);
+ clear_parser((sh_common_t *)sh);
+ talloc_free(sh);
+}
+
+sh_audio_t *new_sh_audio_aid(demuxer_t *demuxer, int id, int aid)
+{
+ 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(demuxer, STREAM_AUDIO, id, aid);
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_ID=%d\n", aid);
+ }
+ return demuxer->a_streams[id];
+}
+
+static void free_sh_audio(demuxer_t *demuxer, int id)
+{
+ 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_common_t *)sh);
+ talloc_free(sh);
+}
+
+sh_video_t *new_sh_video_vid(demuxer_t *demuxer, int id, int vid)
+{
+ 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(demuxer, STREAM_VIDEO, id, vid);
+ mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_ID=%d\n", vid);
+ }
+ return demuxer->v_streams[id];
+}
+
+static void free_sh_video(sh_video_t *sh)
+{
+ mp_msg(MSGT_DEMUXER, MSGL_DBG2, "DEMUXER: freeing sh_video at %p\n", sh);
+ free(sh->bih);
+ clear_parser((sh_common_t *)sh);
+ talloc_free(sh);
+}
+
+void free_demuxer(demuxer_t *demuxer)
+{
+ 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);
+}
+
+
+void ds_add_packet(demux_stream_t *ds, demux_packet_t *dp)
+{
+ // append packet to DS stream:
+ ++ds->packs;
+ ds->bytes += dp->len;
+ if (ds->last) {
+ // next packet in stream
+ ds->last->next = dp;
+ ds->last = dp;
+ } else {
+ // first packet in stream
+ ds->first = ds->last = 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, unsigned format)
+{
+ enum CodecID codec_id = CODEC_ID_NONE;
+
+ switch (format) {
+ case MKTAG('M', 'P', '4', 'L'):
+ codec_id = CODEC_ID_AAC_LATM;
+ break;
+ case 0x2000:
+ case 0x332D6361:
+ case 0x332D4341:
+ case 0x20736D:
+ case MKTAG('s', 'a', 'c', '3'):
+ codec_id = CODEC_ID_AC3;
+ break;
+ case MKTAG('d', 'n', 'e', 't'):
+ // DNET/byte-swapped AC-3 - there is no parser for that yet
+ //codec_id = CODEC_ID_DNET;
+ break;
+ case MKTAG('E', 'A', 'C', '3'):
+ codec_id = CODEC_ID_EAC3;
+ break;
+ case 0x2001:
+ case 0x86:
+ codec_id = CODEC_ID_DTS;
+ break;
+ case MKTAG('f', 'L', 'a', 'C'):
+ codec_id = CODEC_ID_FLAC;
+ break;
+ case MKTAG('M', 'L', 'P', ' '):
+ codec_id = CODEC_ID_MLP;
+ break;
+ case 0x55:
+ case 0x5500736d:
+ case 0x55005354:
+ case MKTAG('.', 'm', 'p', '3'):
+ case MKTAG('M', 'P', '3', ' '):
+ case MKTAG('L', 'A', 'M', 'E'):
+ codec_id = CODEC_ID_MP3;
+ break;
+ case 0x50:
+ case 0x5000736d:
+ case MKTAG('.', 'm', 'p', '2'):
+ case MKTAG('.', 'm', 'p', '1'):
+ codec_id = CODEC_ID_MP2;
+ break;
+ case MKTAG('T', 'R', 'H', 'D'):
+ codec_id = CODEC_ID_TRUEHD;
+ break;
+ }
+ if (codec_id != CODEC_ID_NONE) {
+ *avctx = avcodec_alloc_context3(NULL);
+ if (!*avctx)
+ return;
+ *parser = av_parser_init(codec_id);
+ if (!*parser)
+ av_freep(avctx);
+ }
+}
+
+static void get_parser(sh_common_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->format);
+ sh->avctx = *avctx;
+ sh->parser = *parser;
+}
+
+int ds_parse(demux_stream_t *ds, uint8_t **buffer, int *len, double pts, off_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_common_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, off_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);
+}
+
+// return value:
+// 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)
+{
+ // Note: parameter 'ds' can be NULL!
+ return demux->desc->fill_buffer(demux, ds);
+}
+
+// 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");
+ while (1) {
+ 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;
+ /* 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
+ * packets from this stream.
+ * If however the situation improves and we're called again
+ * despite the eof flag then it's better to clear it to avoid
+ * weird behavior. */
+ ds->eof = 0;
+ return 1;
+ }
+
+#define MaybeNI _("Maybe you are playing a non-interleaved stream/file or the codec failed?\n" \
+ "For AVI files, try to force non-interleaved mode with the --demuxer=avi --avi-ni options.\n")
+
+ if (demux->audio->packs >= MAX_PACKS
+ || demux->audio->bytes >= MAX_PACK_BYTES) {
+ mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many audio packets in the buffer: (%d in %d bytes).\n",
+ demux->audio->packs, demux->audio->bytes);
+ mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI);
+ break;
+ }
+ if (demux->video->packs >= MAX_PACKS
+ || demux->video->bytes >= MAX_PACK_BYTES) {
+ mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many video packets in the buffer: (%d in %d bytes).\n",
+ demux->video->packs, demux->video->bytes);
+ mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI);
+ break;
+ }
+ if (!demux_fill_buffer(demux, ds)) {
+ mp_dbg(MSGT_DEMUXER, MSGL_DBG2,
+ "ds_fill_buffer()->demux_fill_buffer() failed\n");
+ break; // EOF
+ }
+ }
+ 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");
+ 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;
+}
+
+/**
+ * \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(ds->asf_packet->buffer);
+ free(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;
+ }
+ }
+ len = ds->buffer_size - ds->buffer_pos;
+ *start = &ds->buffer[ds->buffer_pos];
+ ds->buffer_pos += len;
+ return len;
+}
+
+int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts)
+{
+ 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;
+}
+
+int ds_get_packet_sub(demux_stream_t *ds, unsigned char **start)
+{
+ int len;
+ if (ds->buffer_pos >= ds->buffer_size) {
+ *start = NULL;
+ if (!ds->packs)
+ return -1; // no sub
+ if (!ds_fill_buffer(ds))
+ return -1; // EOF
+ }
+ len = ds->buffer_size - ds->buffer_pos;
+ *start = &ds->buffer[ds->buffer_pos];
+ ds->buffer_pos += len;
+ return len;
+}
+
+struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last)
+{
+ // This shouldn't get used together with partial reads
+ assert(ds->buffer_pos == 0 || ds->buffer_pos >= ds->buffer_size);
+ if (!repeat_last)
+ ds_fill_buffer(ds);
+ ds->buffer_pos = ds->buffer_size;
+ return ds->current;
+}
+
+double ds_get_next_pts(demux_stream_t *ds)
+{
+ 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->audio->packs >= MAX_PACKS
+ || demux->audio->bytes >= MAX_PACK_BYTES) {
+ mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many audio packets in the buffer: (%d in %d bytes).\n",
+ demux->audio->packs, demux->audio->bytes);
+ mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI);
+ return MP_NOPTS_VALUE;
+ }
+ if (demux->video->packs >= MAX_PACKS
+ || demux->video->bytes >= MAX_PACK_BYTES) {
+ mp_tmsg(MSGT_DEMUXER, MSGL_ERR, "\nToo many video packets in the buffer: (%d in %d bytes).\n",
+ demux->video->packs, demux->video->bytes);
+ mp_tmsg(MSGT_DEMUXER, MSGL_HINT, MaybeNI);
+ 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;
+}
+
+// ====================================================================
+
+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_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);
+ }
+}
+
+
+/**
+ * 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;
+ }
+
+ return -1;
+}
+
+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;
+ 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) {
+ if (demuxer->filetype)
+ mp_tmsg(MSGT_DEMUXER, MSGL_INFO, "Detected file format: %s (%s)\n",
+ demuxer->filetype, desc->shortdesc);
+ 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;
+ opts->correct_pts = opts->user_correct_pts;
+ if (opts->correct_pts < 0)
+ opts->correct_pts =
+ demux_control(demuxer, DEMUXER_CTRL_CORRECT_PTS,
+ NULL) == DEMUXER_CTRL_OK;
+ 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)
+{
+ struct demuxer *demuxer = NULL;
+ const struct demuxer_desc *desc;
+
+ 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)