summaryrefslogtreecommitdiff
path: root/libavformat/mpeg.c
diff options
context:
space:
mode:
authorRichard <peper03@yahoo.com>2013-03-17 10:21:12 +0100
committerMichael Niedermayer <michaelni@gmx.at>2013-03-17 16:24:34 +0100
commit9cde9f70ab482287d892cac20679f40a6673baa8 (patch)
tree1a0a5ccdada68cb448ec52a02f2617ddfd58908e /libavformat/mpeg.c
parent975fbd43addbf70c047167b761ddcf06e8083c14 (diff)
mpeg: Add passing DVD navigation packets (startcode 0x1bf) to caller to allow better playback handling of DVDs.
The two types of packets (PCI and DSI) are passed untouched but combined by the new codec ID AV_CODEC_ID_DVD_NAV. The first 980 bytes in the packet contain the PCI data. The next 1018 are the DSI data. Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavformat/mpeg.c')
-rw-r--r--libavformat/mpeg.c95
1 files changed, 83 insertions, 12 deletions
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
index 4eaffd8812..5387b092e4 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -30,6 +30,7 @@
#undef NDEBUG
#include <assert.h>
+#include "libavutil/avassert.h"
/*********************************************/
/* demux code */
@@ -108,6 +109,7 @@ typedef struct MpegDemuxContext {
int32_t header_state;
unsigned char psm_es_type[256];
int sofdec;
+ int dvd;
#if CONFIG_VOBSUB_DEMUXER
AVFormatContext *sub_ctx;
FFDemuxSubtitlesQueue q;
@@ -247,21 +249,82 @@ static int mpegps_read_pes_header(AVFormatContext *s,
goto redo;
}
if (startcode == PRIVATE_STREAM_2) {
- len = avio_rb16(s->pb);
if (!m->sofdec) {
- while (len-- >= 6) {
- if (avio_r8(s->pb) == 'S') {
- uint8_t buf[5];
- avio_read(s->pb, buf, sizeof(buf));
- m->sofdec = !memcmp(buf, "ofdec", 5);
- len -= sizeof(buf);
- break;
+ /* Need to detect whether this from a DVD or a 'Sofdec' stream */
+ int len = avio_rb16(s->pb);
+ int bytesread = 0;
+ uint8_t *ps2buf = av_malloc(len);
+
+ if (ps2buf) {
+ bytesread = avio_read(s->pb, ps2buf, len);
+
+ if (bytesread != len) {
+ avio_skip(s->pb, len - bytesread);
+ } else {
+ uint8_t *p = 0;
+ if (len >= 6)
+ p = memchr(ps2buf, 'S', len - 5);
+
+ if (p)
+ m->sofdec = !memcmp(p+1, "ofdec", 5);
+
+ m->sofdec -= !m->sofdec;
+
+ if (m->sofdec < 0) {
+ if (len == 980 && ps2buf[0] == 0) {
+ /* PCI structure? */
+ uint32_t startpts = AV_RB32(ps2buf + 0x0d);
+ uint32_t endpts = AV_RB32(ps2buf + 0x11);
+ uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f);
+ uint8_t mins = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f);
+ uint8_t secs = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f);
+
+ m->dvd = (hours <= 23 &&
+ mins <= 59 &&
+ secs <= 59 &&
+ (ps2buf[0x19] & 0x0f) < 10 &&
+ (ps2buf[0x1a] & 0x0f) < 10 &&
+ (ps2buf[0x1b] & 0x0f) < 10 &&
+ endpts >= startpts);
+ } else if (len == 1018 && ps2buf[0] == 1) {
+ /* DSI structure? */
+ uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f);
+ uint8_t mins = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f);
+ uint8_t secs = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f);
+
+ m->dvd = (hours <= 23 &&
+ mins <= 59 &&
+ secs <= 59 &&
+ (ps2buf[0x1d] & 0x0f) < 10 &&
+ (ps2buf[0x1e] & 0x0f) < 10 &&
+ (ps2buf[0x1f] & 0x0f) < 10);
+ }
+ }
}
+
+ av_free(ps2buf);
+
+ /* If this isn't a DVD packet or no memory
+ * could be allocated, just ignore it.
+ * If we did, move back to the start of the
+ * packet (plus 'length' field) */
+ if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) {
+ /* Skip back failed.
+ * This packet will be lost but that can't be helped
+ * if we can't skip back
+ */
+ goto redo;
+ }
+ } else {
+ /* No memory */
+ avio_skip(s->pb, len);
+ goto redo;
}
- m->sofdec -= !m->sofdec;
+ } else if (!m->dvd) {
+ int len = avio_rb16(s->pb);
+ avio_skip(s->pb, len);
+ goto redo;
}
- avio_skip(s->pb, len);
- goto redo;
}
if (startcode == PROGRAM_STREAM_MAP) {
mpegps_psm_parse(m, s->pb);
@@ -271,7 +334,9 @@ static int mpegps_read_pes_header(AVFormatContext *s,
/* find matching stream */
if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
(startcode >= 0x1e0 && startcode <= 0x1ef) ||
- (startcode == 0x1bd) || (startcode == 0x1fd)))
+ (startcode == 0x1bd) ||
+ (startcode == PRIVATE_STREAM_2) ||
+ (startcode == 0x1fd)))
goto redo;
if (ppos) {
*ppos = avio_tell(s->pb) - 4;
@@ -279,6 +344,8 @@ static int mpegps_read_pes_header(AVFormatContext *s,
len = avio_rb16(s->pb);
pts =
dts = AV_NOPTS_VALUE;
+ if (startcode != PRIVATE_STREAM_2)
+ {
/* stuffing */
for(;;) {
if (len < 1)
@@ -352,6 +419,7 @@ static int mpegps_read_pes_header(AVFormatContext *s,
}
else if( c!= 0xf )
goto redo;
+ }
if (startcode == PRIVATE_STREAM_1) {
startcode = avio_r8(s->pb);
@@ -448,6 +516,9 @@ static int mpegps_read_packet(AVFormatContext *s,
else
request_probe= 1;
type = AVMEDIA_TYPE_VIDEO;
+ } else if (startcode == PRIVATE_STREAM_2) {
+ type = AVMEDIA_TYPE_DATA;
+ codec_id = AV_CODEC_ID_DVD_NAV;
} else if (startcode >= 0x1c0 && startcode <= 0x1df) {
type = AVMEDIA_TYPE_AUDIO;
codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2;