1
mirror of https://github.com/mpv-player/mpv synced 2024-11-18 21:16:10 +01:00
mpv/demux/demux_disc.c

347 lines
11 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <assert.h>
#include "common/common.h"
#include "common/msg.h"
#include "stream/stream.h"
#include "demux.h"
#include "stheader.h"
#include "video/csputils.h"
struct priv {
struct demuxer *slave;
// streams[slave_stream_index] == our_stream
struct sh_stream **streams;
int num_streams;
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
// This contains each DVD sub stream, or NULL. Needed because DVD packets
// can come arbitrarily late in the MPEG stream, so the slave demuxer
// might add the streams only later.
struct sh_stream *dvd_subs[32];
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
// Used to rewrite the raw MPEG timestamps to playback time.
struct {
double base_time; // playback display start time of current segment
double base_dts; // packet DTS that maps to base_time
double last_dts; // DTS of previously demuxed packet
} pts[STREAM_TYPE_COUNT];
double seek_pts;
bool seek_reinit; // needs reinit after seek
};
static void reselect_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
for (int n = 0; n < MPMIN(p->slave->num_streams, p->num_streams); n++) {
if (p->streams[n]) {
demuxer_select_track(p->slave, p->slave->streams[n],
demux_stream_is_selected(p->streams[n]));
}
}
}
static void get_disc_lang(struct stream *stream, struct sh_stream *sh)
{
struct stream_lang_req req = {.type = sh->type, .id = sh->demuxer_id};
if (stream->uncached_type == STREAMTYPE_DVD && sh->type == STREAM_SUB)
req.id = req.id & 0x1F; // mpeg ID to index
stream_control(stream, STREAM_CTRL_GET_LANG, &req);
if (req.name[0])
sh->lang = talloc_strdup(sh, req.name);
}
static void add_dvd_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct stream *stream = demuxer->stream;
if (stream->uncached_type != STREAMTYPE_DVD)
return;
struct stream_dvd_info_req info;
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
for (int n = 0; n < MPMIN(32, info.num_subs); n++) {
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
if (!sh)
break;
sh->demuxer_id = n + 0x20;
sh->codec = "dvd_subtitle";
get_disc_lang(stream, sh);
// p->streams _must_ match with p->slave->streams, so we can't add
// it yet - it has to be done when the real stream appears, which
// could be right on start, or any time later.
p->dvd_subs[n] = sh;
// emulate the extradata
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
csp.int_bits_in = 8;
csp.int_bits_out = 8;
float cmatrix[3][4];
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
char *s = talloc_strdup(sh, "");
s = talloc_asprintf_append(s, "palette: ");
for (int i = 0; i < 16; i++) {
int color = info.palette[i];
int c[3] = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff};
mp_map_int_color(cmatrix, 8, c);
color = (c[2] << 16) | (c[1] << 8) | c[0];
if (i != 0)
s = talloc_asprintf_append(s, ", ");
s = talloc_asprintf_append(s, "%06x", color);
}
s = talloc_asprintf_append(s, "\n");
sh->sub->extradata = s;
sh->sub->extradata_len = strlen(s);
}
}
}
static void add_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
for (int n = p->num_streams; n < p->slave->num_streams; n++) {
struct sh_stream *src = p->slave->streams[n];
if (src->sub) {
struct sh_stream *sub = NULL;
if (src->demuxer_id >= 0x20 && src->demuxer_id <= 0x3F)
sub = p->dvd_subs[src->demuxer_id - 0x20];
if (sub) {
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sub);
continue;
}
}
struct sh_stream *sh = new_sh_stream(demuxer, src->type);
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
// Copy all stream fields that might be relevant
sh->codec = talloc_strdup(sh, src->codec);
sh->format = src->format;
sh->lav_headers = src->lav_headers;
if (sh && src->video) {
double ar;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
== STREAM_OK)
src->video->aspect = ar;
}
if (sh && src->audio)
sh->audio = src->audio;
}
reselect_streams(demuxer);
}
static void d_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
{
struct priv *p = demuxer->priv;
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
demux_seek(p->slave, rel_seek_secs, flags);
return;
}
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
double pts = p->seek_pts;
if (flags & SEEK_ABSOLUTE)
pts = 0.0f;
if (flags & SEEK_FACTOR) {
double tmp = 0;
stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
pts += tmp * rel_seek_secs;
} else {
pts += rel_seek_secs;
}
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
MP_VERBOSE(demuxer, "seek to: %f\n", pts);
stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, &pts);
demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
p->seek_pts = pts;
p->seek_reinit = true;
}
static void reset_pts(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
double base;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CURRENT_TIME, &base) < 1)
base = 0;
MP_VERBOSE(demuxer, "reset to time: %f\n", base);
for (int n = 0; n < STREAM_TYPE_COUNT; n++) {
p->pts[n].base_dts = p->pts[n].last_dts = MP_NOPTS_VALUE;
p->pts[n].base_time = base;
}
p->seek_reinit = false;
}
static int d_fill_buffer(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
struct demux_packet *pkt = demux_read_any_packet(p->slave);
if (!pkt)
return 0;
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
if (p->seek_reinit)
reset_pts(demuxer);
add_streams(demuxer);
if (pkt->stream >= p->num_streams) { // out of memory?
talloc_free(pkt);
return 0;
}
struct sh_stream *sh = p->streams[pkt->stream];
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
if (!demux_stream_is_selected(sh)) {
talloc_free(pkt);
return 1;
}
int t = sh->type;
// Subtitle timestamps are not continuous, so the heuristic below can't be
// applied. Instead, use the video stream as reference.
if (t == STREAM_SUB)
t = STREAM_VIDEO;
MP_TRACE(demuxer, "ipts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
if (sh->type == STREAM_SUB) {
if (p->pts[t].base_dts == MP_NOPTS_VALUE)
MP_WARN(demuxer, "subtitle packet along PTS reset, report a bug\n");
} else if (pkt->dts != MP_NOPTS_VALUE) {
if (p->pts[t].base_dts == MP_NOPTS_VALUE)
p->pts[t].base_dts = p->pts[t].last_dts = pkt->dts;
if (pkt->dts < p->pts[t].last_dts || pkt->dts > p->pts[t].last_dts + 0.5)
{
MP_VERBOSE(demuxer, "PTS discontinuity on stream %d\n", sh->type);
p->pts[t].base_time += p->pts[t].last_dts - p->pts[t].base_dts;
p->pts[t].base_dts = p->pts[t].last_dts = pkt->dts - pkt->duration;
}
p->pts[t].last_dts = pkt->dts;
}
if (p->pts[t].base_dts != MP_NOPTS_VALUE) {
double delta = -p->pts[t].base_dts + p->pts[t].base_time;
if (pkt->pts != MP_NOPTS_VALUE)
pkt->pts += delta;
if (pkt->dts != MP_NOPTS_VALUE)
pkt->dts += delta;
}
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
MP_TRACE(demuxer, "opts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
if (pkt->pts != MP_NOPTS_VALUE)
p->seek_pts = pkt->pts;
demux_add_packet(sh, pkt);
return 1;
}
static void add_stream_chapters(struct demuxer *demuxer)
{
int num = 0;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &num) < 1)
return;
for (int n = 0; n < num; n++) {
double p = n;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_CHAPTER_TIME, &p) < 1)
continue;
demuxer_add_chapter(demuxer, bstr0(""), p * 1e9, 0, 0);
}
}
static int d_open(demuxer_t *demuxer, enum demux_check check)
{
struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
if (check != DEMUX_CHECK_FORCE)
return -1;
char *demux = "+lavf";
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA)
demux = "+rawaudio";
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
reset_pts(demuxer);
p->slave = demux_open(demuxer->stream, demux, NULL, demuxer->global);
if (!p->slave)
return -1;
// So that we don't miss initial packets of delayed subtitle streams.
p->slave->stream_select_default = true;
dvd, bluray: handle playback display time handling differently This is a pretty big change. Instead of doing a half-hearted passthrough of the playback timestamp, we attempt to rewrite the raw MPEG timestamps such that they match with the playback time. We add the offset between raw start timestamp and playback time to the packet timestamps. This is the easy part; but the problem is with timestamp resets. We simply detect timestamp discontinuities by checking whether they are more than 500ms apart (large enough for all video faster than 2 FPS and audio with reasonable framesizes/samplerates), and adjust the timestamp offset accordingly. This should work pretty well. There may be some problems with subtitles. If the first packet after a timestamp reset is a subtitle instead of video, it will fail. Also, selecting multiple audio or video streams won't work (but mpv doesn't allow selecting several anyway). Trying to demux subtitles with no video stream enabled will probably fail. Untested with Bluray, because I have no Bluray sample. Background: libdvdnav/libdvdread/libbluray make this relatively hard. They return a raw MPEG (PS/TS) byte stream, and additionally to that provide a function to retrieve the current "playback" time. The playback time is what should be displayed to the user, while the MPEG timestamps can be completely different. Even worse, the MPEG timestamps can reset. Since we use the libavformat demuxer (instead of parsing the MPEG packets in the DVD/BD code), it's hard to associate between these timestamps. As a result, the time display is special cased in the playloop, and of low quality (updates only all 1 or 2 seconds, sometimes is incorrect). The fact that the stream cache can be between demuxer and the stream source makes things worse. All the libs seem to provide an event that tells whether timestamps are resetting. But since this signalling is byte based, it's hard to connect it to the demuxed MPEG packets. It might be possible to create some sort of table mapping file positions to discontinuities and new timestamps. (For simplicity, this table could be 2 entries large, sufficient to catch all discontinuities if the distance between them is larger than the total buffering.)
2014-07-06 19:03:12 +02:00
demuxer->start_time = p->pts[STREAM_VIDEO].base_time;
// Incorrect, but fixes some behavior
demuxer->ts_resets_possible = false;
// Doesn't work, because stream_pts is a "guess".
demuxer->accurate_seek = false;
// Can be seekable even if the stream isn't.
demuxer->seekable = true;
add_dvd_streams(demuxer);
add_streams(demuxer);
add_stream_chapters(demuxer);
return 0;
}
static void d_close(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
free_demuxer(p->slave);
}
static int d_control(demuxer_t *demuxer, int cmd, void *arg)
{
struct priv *p = demuxer->priv;
switch (cmd) {
case DEMUXER_CTRL_GET_TIME_LENGTH: {
double len;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) < 1)
break;
*(double *)arg = len;
return DEMUXER_CTRL_OK;
}
case DEMUXER_CTRL_SWITCHED_TRACKS:
reselect_streams(demuxer);
return DEMUXER_CTRL_OK;
}
return demux_control(p->slave, cmd, arg);
}
const demuxer_desc_t demuxer_desc_disc = {
.name = "disc",
.desc = "CD/DVD/BD wrapper",
.fill_buffer = d_fill_buffer,
.open = d_open,
.close = d_close,
.seek = d_seek,
.control = d_control,
.type = DEMUXER_TYPE_DISC,
};