summaryrefslogtreecommitdiffstats
path: root/demux/demux.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux.c')
-rw-r--r--demux/demux.c290
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;
+}