diff options
Diffstat (limited to 'demux/demux.c')
-rw-r--r-- | demux/demux.c | 290 |
1 files changed, 230 insertions, 60 deletions
diff --git a/demux/demux.c b/demux/demux.c index e1bb960e78..c783b0cbc2 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -40,7 +40,8 @@ #include "audio/format.h" -#include "libavcodec/avcodec.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 @@ -66,6 +67,8 @@ 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; /* 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 @@ -79,8 +82,12 @@ const demuxer_desc_t *const demuxer_list[] = { #ifdef CONFIG_TV &demuxer_desc_tv, #endif +#ifdef CONFIG_LIBASS + &demuxer_desc_libass, +#endif &demuxer_desc_matroska, &demuxer_desc_lavf, + &demuxer_desc_subreader, &demuxer_desc_avi, &demuxer_desc_asf, #ifdef CONFIG_MNG @@ -101,6 +108,16 @@ const demuxer_desc_t *const demuxer_list[] = { NULL }; +static void add_stream_chapters(struct demuxer *demuxer); + +static int packet_destroy(void *ptr) +{ + struct demux_packet *dp = ptr; + talloc_free(dp->avpacket); + free(dp->allocation); + return 0; +} + static struct demux_packet *create_packet(size_t len) { if (len > 1000000000) { @@ -108,16 +125,14 @@ static struct demux_packet *create_packet(size_t len) "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->buffer = NULL; - dp->avpacket = NULL; + struct demux_packet *dp = talloc(NULL, struct demux_packet); + talloc_set_destructor(dp, packet_destroy); + *dp = (struct demux_packet) { + .len = len, + .pts = MP_NOPTS_VALUE, + .duration = -1, + .stream_pts = MP_NOPTS_VALUE, + }; return dp; } @@ -130,6 +145,7 @@ struct demux_packet *new_demux_packet(size_t len) abort(); } memset(dp->buffer + len, 0, MP_INPUT_BUFFER_PADDING_SIZE); + dp->allocation = dp->buffer; return dp; } @@ -155,6 +171,7 @@ void resize_demux_packet(struct demux_packet *dp, size_t len) "over 1 GB!\n"); abort(); } + assert(dp->allocation); dp->buffer = realloc(dp->buffer, len + MP_INPUT_BUFFER_PADDING_SIZE); if (!dp->buffer) { mp_msg(MSGT_DEMUXER, MSGL_FATAL, "Memory allocation failure!\n"); @@ -162,15 +179,47 @@ void resize_demux_packet(struct demux_packet *dp, size_t len) } memset(dp->buffer + len, 0, MP_INPUT_BUFFER_PADDING_SIZE); dp->len = len; + dp->allocation = dp->buffer; } void free_demux_packet(struct demux_packet *dp) { - if (dp->avpacket) - talloc_free(dp->avpacket); - else - free(dp->buffer); - free(dp); + talloc_free(dp); +} + +static int destroy_avpacket(void *pkt) +{ + av_free_packet(pkt); + return 0; +} + +struct demux_packet *demux_copy_packet(struct demux_packet *dp) +{ + struct demux_packet *new = NULL; + // No av_copy_packet() in Libav +#if LIBAVCODEC_VERSION_MICRO >= 100 + if (dp->avpacket) { + assert(dp->buffer == dp->avpacket->data); + assert(dp->len == dp->avpacket->size); + AVPacket *newavp = talloc_zero(NULL, AVPacket); + talloc_set_destructor(newavp, destroy_avpacket); + av_init_packet(newavp); + if (av_copy_packet(newavp, dp->avpacket) < 0) + abort(); + new = new_demux_packet_fromdata(newavp->data, newavp->size); + new->avpacket = newavp; + } +#endif + if (!new) { + new = new_demux_packet(dp->len); + memcpy(new->buffer, dp->buffer, new->len); + } + 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) @@ -210,8 +259,8 @@ static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format) } -demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, - int a_id, int v_id, int s_id, char *filename) +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; @@ -222,8 +271,8 @@ demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, 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->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; @@ -308,10 +357,6 @@ struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type) static void free_sh_stream(struct sh_stream *sh) { - if (sh->lav_headers) { - avcodec_close(sh->lav_headers); - av_free(sh->lav_headers); - } } sh_sub_t *new_sh_sub_sid(demuxer_t *demuxer, int id, int sid) @@ -428,13 +473,16 @@ void free_demuxer(demuxer_t *demuxer) talloc_free(demuxer); } -void demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream, - demux_packet_t *dp) +// 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 (!demuxer_stream_is_selected(demuxer, stream)) { + if (!dp || !demuxer_stream_is_selected(demuxer, stream)) { free_demux_packet(dp); + return 0; } else { ds_add_packet(demuxer->ds[stream->type], dp); + return 1; } } @@ -581,7 +629,7 @@ static bool demux_check_queue_full(demuxer_t *demux) 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 demux->desc->fill_buffer ? demux->desc->fill_buffer(demux, ds) : 0; } // return value: @@ -739,8 +787,7 @@ void ds_free_packs(demux_stream_t *ds) } if (ds->asf_packet) { // free unfinished .asf fragments: - free(ds->asf_packet->buffer); - free(ds->asf_packet); + free_demux_packet(ds->asf_packet); ds->asf_packet = NULL; } ds->first = ds->last = NULL; @@ -784,28 +831,34 @@ int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts) return len; } -int ds_get_packet_sub(demux_stream_t *ds, unsigned char **start) +struct demux_packet *ds_get_packet_sub(demux_stream_t *ds) { - int len; if (ds->buffer_pos >= ds->buffer_size) { - *start = NULL; if (!ds->packs) - return -1; // no sub + return NULL; // no sub if (!ds_fill_buffer(ds)) - return -1; // EOF + return NULL; // EOF } - len = ds->buffer_size - ds->buffer_pos; - *start = &ds->buffer[ds->buffer_pos]; - ds->buffer_pos += len; - return len; + 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; } 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); + // 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; } @@ -917,12 +970,15 @@ static struct demuxer *open_given_type(struct MPOpts *opts, 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; + if (stream_manages_timeline(demuxer->stream)) { + // Incorrect, but fixes some behavior with DVD/BD + demuxer->ts_resets_possible = false; + // Doesn't work, because stream_pts is a "guess". + demuxer->accurate_seek = false; + } + add_stream_chapters(demuxer); demuxer_sort_chapters(demuxer); + demux_info_update(demuxer); return demuxer; } else { // demux_mov can return playlist instead of mov @@ -1071,7 +1127,7 @@ int demux_seek(demuxer_t *demuxer, float rel_seek_secs, float audio_delay, * (nothing actually implements DEMUXER_CTRL_RESYNC now). */ struct stream *stream = demuxer->stream; - if (stream->type == STREAMTYPE_DVD) { + if (stream_manages_timeline(stream)) { double pts; if (flags & SEEK_ABSOLUTE) @@ -1174,6 +1230,18 @@ char *demux_info_get(demuxer_t *demuxer, const char *opt) return NULL; } +void demux_info_update(struct demuxer *demuxer) +{ + demux_control(demuxer, DEMUXER_CTRL_UPDATE_INFO, NULL); + // Take care of stream metadata as well + char **meta; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_METADATA, &meta) > 0) { + for (int n = 0; meta[n + 0]; n += 2) + demux_info_add(demuxer, meta[n + 0], meta[n + 1]); + talloc_free(meta); + } +} + int demux_control(demuxer_t *demuxer, int cmd, void *arg) { @@ -1199,6 +1267,10 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type, { assert(!stream || stream->type == type); + // don't flush buffers if stream is already selected + if (stream && demuxer_stream_is_selected(demuxer, stream)) + return; + int old_id = demuxer->ds[type]->id; // legacy @@ -1283,13 +1355,26 @@ int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name, .original_index = demuxer->num_chapters, .start = start, .end = end, - .name = name.len ? bstrdup0(demuxer, name) - : talloc_strdup(demuxer, mp_gtext("unknown")), + .name = name.len ? bstrdup0(demuxer, name) : NULL, }; MP_TARRAY_APPEND(demuxer, demuxer->chapters, demuxer->num_chapters, new); return 0; } +static void add_stream_chapters(struct demuxer *demuxer) +{ + if (demuxer->num_chapters) + return; + int num_chapters = demuxer_chapter_count(demuxer); + for (int n = 0; n < num_chapters; n++) { + double p = n; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p) + != STREAM_OK) + return; + demuxer_add_chapter(demuxer, bstr0(""), p * 1e9, 0); + } +} + /** * \brief demuxer_seek_chapter() seeks to a chapter in two possible ways: * either using the demuxer->chapters structure set by the demuxer @@ -1301,22 +1386,22 @@ int demuxer_add_chapter(demuxer_t *demuxer, struct bstr name, int demuxer_seek_chapter(demuxer_t *demuxer, int chapter, double *seek_pts) { - int ris; - - if (!demuxer->num_chapters || !demuxer->chapters) { - demux_flush(demuxer); + int ris = STREAM_UNSUPPORTED; + if (demuxer->num_chapters == 0) ris = stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_CHAPTER, &chapter); - if (ris != STREAM_UNSUPPORTED) - demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); + + if (ris != STREAM_UNSUPPORTED) { + demux_flush(demuxer); + demux_control(demuxer, DEMUXER_CTRL_RESYNC, NULL); // exit status may be ok, but main() doesn't have to seek itself // (because e.g. dvds depend on sectors, not on pts) *seek_pts = -1.0; - return ris != STREAM_UNSUPPORTED ? chapter : -1; - } else { // chapters structure is set in the demuxer + return chapter; + } else { if (chapter >= demuxer->num_chapters) return -1; if (chapter < 0) @@ -1355,12 +1440,10 @@ char *demuxer_chapter_name(demuxer_t *demuxer, int chapter) return NULL; } -float demuxer_chapter_time(demuxer_t *demuxer, int chapter, float *end) +double demuxer_chapter_time(demuxer_t *demuxer, int chapter) { if (demuxer->num_chapters && demuxer->chapters && chapter >= 0 && chapter < demuxer->num_chapters) { - if (end) - *end = demuxer->chapters[chapter].end / 1e9; return demuxer->chapters[chapter].start / 1e9; } return -1.0; @@ -1378,6 +1461,27 @@ int demuxer_chapter_count(demuxer_t *demuxer) return demuxer->num_chapters; } +double demuxer_get_time_length(struct demuxer *demuxer) +{ + double len; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) > 0) + return len; + // <= 0 means DEMUXER_CTRL_NOTIMPL or DEMUXER_CTRL_DONTKNOW + if (demux_control(demuxer, DEMUXER_CTRL_GET_TIME_LENGTH, &len) > 0) + return len; + return -1; +} + +double demuxer_get_start_time(struct demuxer *demuxer) +{ + double time; + if (stream_control(demuxer->stream, STREAM_CTRL_GET_START_TIME, &time) > 0) + return time; + if (demux_control(demuxer, DEMUXER_CTRL_GET_START_TIME, &time) > 0) + return time; + return 0; +} + int demuxer_angles_count(demuxer_t *demuxer) { int ris, angles = -1; @@ -1416,3 +1520,69 @@ int demuxer_set_angle(demuxer_t *demuxer, int angle) return angle; } + +static int packet_sort_compare(const void *p1, const void *p2) +{ + struct demux_packet *c1 = *(struct demux_packet **)p1; + struct demux_packet *c2 = *(struct demux_packet **)p2; + + if (c1->pts > c2->pts) + return 1; + else if (c1->pts < c2->pts) + return -1; + return 0; +} + +void demux_packet_list_sort(struct demux_packet **pkts, int num_pkts) +{ + qsort(pkts, num_pkts, sizeof(struct demux_packet *), packet_sort_compare); +} + +void demux_packet_list_seek(struct demux_packet **pkts, int num_pkts, + int *current, float rel_seek_secs, int flags) +{ + double ref_time = 0; + if (*current >= 0 && *current < num_pkts) { + ref_time = pkts[*current]->pts; + } else if (*current == num_pkts && num_pkts > 0) { + ref_time = pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration; + } + + if (flags & SEEK_ABSOLUTE) + ref_time = 0; + + if (flags & SEEK_FACTOR) { + ref_time += demux_packet_list_duration(pkts, num_pkts) * rel_seek_secs; + } else { + ref_time += rel_seek_secs; + } + + // Could do binary search, but it's probably not worth the complexity. + int last_index = 0; + for (int n = 0; n < num_pkts; n++) { + if (pkts[n]->pts > ref_time) + break; + last_index = n; + } + *current = last_index; +} + +double demux_packet_list_duration(struct demux_packet **pkts, int num_pkts) +{ + if (num_pkts > 0) + return pkts[num_pkts - 1]->pts + pkts[num_pkts - 1]->duration; + return 0; +} + +struct demux_packet *demux_packet_list_fill(struct demux_packet **pkts, + int num_pkts, int *current) +{ + if (*current < 0) + *current = 0; + if (*current >= num_pkts) + return NULL; + struct demux_packet *new = talloc(NULL, struct demux_packet); + *new = *pkts[*current]; + *current += 1; + return new; +} |