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