diff options
Diffstat (limited to 'demux/demux.c')
-rw-r--r-- | demux/demux.c | 1435 |
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) + 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 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 + 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) { + |