Browse code

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>

Richard authored on 2013/03/17 18:21:12
Showing 7 changed files
... ...
@@ -743,6 +743,7 @@ OBJS-$(CONFIG_PNG_PARSER)              += png_parser.o
743 743
 OBJS-$(CONFIG_MPEGAUDIO_PARSER)        += mpegaudio_parser.o \
744 744
                                           mpegaudiodecheader.o mpegaudiodata.o
745 745
 OBJS-$(CONFIG_MPEGVIDEO_PARSER)        += mpegvideo_parser.o    \
746
+                                          dvd_nav_parser.o \
746 747
                                           mpeg12.o mpeg12data.o
747 748
 OBJS-$(CONFIG_PNM_PARSER)              += pnm_parser.o pnm.o
748 749
 OBJS-$(CONFIG_RV30_PARSER)             += rv34_parser.o
... ...
@@ -515,6 +515,7 @@ void avcodec_register_all(void)
515 515
     REGISTER_PARSER(DNXHD,              dnxhd);
516 516
     REGISTER_PARSER(DVBSUB,             dvbsub);
517 517
     REGISTER_PARSER(DVDSUB,             dvdsub);
518
+    REGISTER_PARSER(DVD_NAV,            dvd_nav);
518 519
     REGISTER_PARSER(FLAC,               flac);
519 520
     REGISTER_PARSER(GSM,                gsm);
520 521
     REGISTER_PARSER(H261,               h261);
... ...
@@ -482,6 +482,8 @@ enum AVCodecID {
482 482
     AV_CODEC_ID_IDF        = MKBETAG( 0 ,'I','D','F'),
483 483
     AV_CODEC_ID_OTF        = MKBETAG( 0 ,'O','T','F'),
484 484
     AV_CODEC_ID_SMPTE_KLV  = MKBETAG('K','L','V','A'),
485
+    AV_CODEC_ID_DVD_NAV    = MKBETAG('D','N','A','V'),
486
+
485 487
 
486 488
     AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it
487 489
 
... ...
@@ -2525,6 +2525,12 @@ static const AVCodecDescriptor codec_descriptors[] = {
2525 2525
         .name      = "klv",
2526 2526
         .long_name = NULL_IF_CONFIG_SMALL("SMPTE 336M Key-Length-Value (KLV) metadata"),
2527 2527
     },
2528
+    {
2529
+        .id        = AV_CODEC_ID_DVD_NAV,
2530
+        .type      = AVMEDIA_TYPE_DATA,
2531
+        .name      = "dvd_nav_packet",
2532
+        .long_name = NULL_IF_CONFIG_SMALL("DVD Nav packet"),
2533
+    },
2528 2534
 
2529 2535
 };
2530 2536
 
2531 2537
new file mode 100644
... ...
@@ -0,0 +1,116 @@
0
+/*
1
+ * DVD navigation block parser for FFmpeg
2
+ * Copyright (c) 2013 The FFmpeg Project
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+#include "avcodec.h"
21
+#include "dsputil.h"
22
+#include "get_bits.h"
23
+#include "parser.h"
24
+
25
+#define PCI_SIZE  980
26
+#define DSI_SIZE 1018
27
+
28
+/* parser definition */
29
+typedef struct DVDNavParseContext {
30
+    uint32_t     lba;
31
+    uint8_t      buffer[PCI_SIZE+DSI_SIZE];
32
+    int          copied;
33
+} DVDNavParseContext;
34
+
35
+static av_cold int dvd_nav_parse_init(AVCodecParserContext *s)
36
+{
37
+    DVDNavParseContext *pc = s->priv_data;
38
+
39
+    pc->lba    = 0xFFFFFFFF;
40
+    pc->copied = 0;
41
+    return 0;
42
+}
43
+
44
+static int dvd_nav_parse(AVCodecParserContext *s,
45
+                         AVCodecContext *avctx,
46
+                         const uint8_t **poutbuf, int *poutbuf_size,
47
+                         const uint8_t *buf, int buf_size)
48
+{
49
+    DVDNavParseContext *pc1 = s->priv_data;
50
+    int lastPacket          = 0;
51
+    int valid               = 0;
52
+
53
+    s->pict_type = AV_PICTURE_TYPE_NONE;
54
+
55
+    avctx->time_base.num = 1;
56
+    avctx->time_base.den = 90000;
57
+
58
+    if (buf && buf_size) {
59
+        switch(buf[0]) {
60
+            case 0x00:
61
+                if (buf_size == PCI_SIZE) {
62
+                    /* PCI */
63
+                    uint32_t lba      = AV_RB32(&buf[0x01]);
64
+                    uint32_t startpts = AV_RB32(&buf[0x0D]);
65
+                    uint32_t endpts   = AV_RB32(&buf[0x11]);
66
+
67
+                    if (endpts > startpts) {
68
+                        pc1->lba    = lba;
69
+                        s->pts      = (int64_t)startpts;
70
+                        s->duration = endpts - startpts;
71
+
72
+                        memcpy(pc1->buffer, buf, PCI_SIZE);
73
+                        pc1->copied = PCI_SIZE;
74
+                        valid       = 1;
75
+                    }
76
+                }
77
+                break;
78
+
79
+            case 0x01:
80
+                if ((buf_size == DSI_SIZE) && (pc1->copied == PCI_SIZE)) {
81
+                    /* DSI */
82
+                    uint32_t lba = AV_RB32(&buf[0x05]);
83
+
84
+                    if (lba == pc1->lba) {
85
+                        memcpy(pc1->buffer + pc1->copied, buf, DSI_SIZE);
86
+                        lastPacket  = 1;
87
+                        valid       = 1;
88
+                    }
89
+                }
90
+                break;
91
+        }
92
+    }
93
+
94
+    if (!valid || lastPacket) {
95
+        pc1->copied = 0;
96
+        pc1->lba    = 0xFFFFFFFF;
97
+    }
98
+
99
+    if (lastPacket) {
100
+        *poutbuf      = pc1->buffer;
101
+        *poutbuf_size = sizeof(pc1->buffer);
102
+    } else {
103
+        *poutbuf      = NULL;
104
+        *poutbuf_size = 0;
105
+    }
106
+
107
+    return buf_size;
108
+}
109
+
110
+AVCodecParser ff_dvd_nav_parser = {
111
+    .codec_ids      = { AV_CODEC_ID_DVD_NAV },
112
+    .priv_data_size = sizeof(DVDNavParseContext),
113
+    .parser_init    = dvd_nav_parse_init,
114
+    .parser_parse   = dvd_nav_parse,
115
+};
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVCODEC_VERSION_MAJOR 55
32
-#define LIBAVCODEC_VERSION_MINOR  0
32
+#define LIBAVCODEC_VERSION_MINOR  1
33 33
 #define LIBAVCODEC_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
... ...
@@ -30,6 +30,7 @@
30 30
 
31 31
 #undef NDEBUG
32 32
 #include <assert.h>
33
+#include "libavutil/avassert.h"
33 34
 
34 35
 /*********************************************/
35 36
 /* demux code */
... ...
@@ -108,6 +109,7 @@ typedef struct MpegDemuxContext {
108 108
     int32_t header_state;
109 109
     unsigned char psm_es_type[256];
110 110
     int sofdec;
111
+    int dvd;
111 112
 #if CONFIG_VOBSUB_DEMUXER
112 113
     AVFormatContext *sub_ctx;
113 114
     FFDemuxSubtitlesQueue q;
... ...
@@ -247,21 +249,82 @@ static int mpegps_read_pes_header(AVFormatContext *s,
247 247
         goto redo;
248 248
     }
249 249
     if (startcode == PRIVATE_STREAM_2) {
250
-        len = avio_rb16(s->pb);
251 250
         if (!m->sofdec) {
252
-            while (len-- >= 6) {
253
-                if (avio_r8(s->pb) == 'S') {
254
-                    uint8_t buf[5];
255
-                    avio_read(s->pb, buf, sizeof(buf));
256
-                    m->sofdec = !memcmp(buf, "ofdec", 5);
257
-                    len -= sizeof(buf);
258
-                    break;
251
+            /* Need to detect whether this from a DVD or a 'Sofdec' stream */
252
+            int len = avio_rb16(s->pb);
253
+            int bytesread = 0;
254
+            uint8_t *ps2buf = av_malloc(len);
255
+
256
+            if (ps2buf) {
257
+                bytesread = avio_read(s->pb, ps2buf, len);
258
+
259
+                if (bytesread != len) {
260
+                    avio_skip(s->pb, len - bytesread);
261
+                } else {
262
+                    uint8_t *p = 0;
263
+                    if (len >= 6)
264
+                        p = memchr(ps2buf, 'S', len - 5);
265
+
266
+                    if (p)
267
+                        m->sofdec = !memcmp(p+1, "ofdec", 5);
268
+
269
+                    m->sofdec -= !m->sofdec;
270
+
271
+                    if (m->sofdec < 0) {
272
+                        if (len == 980  && ps2buf[0] == 0) {
273
+                            /* PCI structure? */
274
+                            uint32_t startpts = AV_RB32(ps2buf + 0x0d);
275
+                            uint32_t endpts = AV_RB32(ps2buf + 0x11);
276
+                            uint8_t hours = ((ps2buf[0x19] >> 4) * 10) + (ps2buf[0x19] & 0x0f);
277
+                            uint8_t mins  = ((ps2buf[0x1a] >> 4) * 10) + (ps2buf[0x1a] & 0x0f);
278
+                            uint8_t secs  = ((ps2buf[0x1b] >> 4) * 10) + (ps2buf[0x1b] & 0x0f);
279
+
280
+                            m->dvd = (hours <= 23 &&
281
+                                      mins  <= 59 &&
282
+                                      secs  <= 59 &&
283
+                                      (ps2buf[0x19] & 0x0f) < 10 &&
284
+                                      (ps2buf[0x1a] & 0x0f) < 10 &&
285
+                                      (ps2buf[0x1b] & 0x0f) < 10 &&
286
+                                      endpts >= startpts);
287
+                        } else if (len == 1018 && ps2buf[0] == 1) {
288
+                            /* DSI structure? */
289
+                            uint8_t hours = ((ps2buf[0x1d] >> 4) * 10) + (ps2buf[0x1d] & 0x0f);
290
+                            uint8_t mins  = ((ps2buf[0x1e] >> 4) * 10) + (ps2buf[0x1e] & 0x0f);
291
+                            uint8_t secs  = ((ps2buf[0x1f] >> 4) * 10) + (ps2buf[0x1f] & 0x0f);
292
+
293
+                            m->dvd = (hours <= 23 &&
294
+                                      mins  <= 59 &&
295
+                                      secs  <= 59 &&
296
+                                      (ps2buf[0x1d] & 0x0f) < 10 &&
297
+                                      (ps2buf[0x1e] & 0x0f) < 10 &&
298
+                                      (ps2buf[0x1f] & 0x0f) < 10);
299
+                        }
300
+                    }
259 301
                 }
302
+
303
+                av_free(ps2buf);
304
+
305
+                /* If this isn't a DVD packet or no memory
306
+                 * could be allocated, just ignore it.
307
+                 * If we did, move back to the start of the
308
+                 * packet (plus 'length' field) */
309
+                if (!m->dvd || avio_skip(s->pb, -(len + 2)) < 0) {
310
+                    /* Skip back failed.
311
+                     * This packet will be lost but that can't be helped
312
+                     * if we can't skip back
313
+                     */
314
+                    goto redo;
315
+                }
316
+            } else {
317
+                /* No memory */
318
+                avio_skip(s->pb, len);
319
+                goto redo;
260 320
             }
261
-            m->sofdec -= !m->sofdec;
321
+        } else if (!m->dvd) {
322
+            int len = avio_rb16(s->pb);
323
+            avio_skip(s->pb, len);
324
+            goto redo;
262 325
         }
263
-        avio_skip(s->pb, len);
264
-        goto redo;
265 326
     }
266 327
     if (startcode == PROGRAM_STREAM_MAP) {
267 328
         mpegps_psm_parse(m, s->pb);
... ...
@@ -271,7 +334,9 @@ static int mpegps_read_pes_header(AVFormatContext *s,
271 271
     /* find matching stream */
272 272
     if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
273 273
           (startcode >= 0x1e0 && startcode <= 0x1ef) ||
274
-          (startcode == 0x1bd) || (startcode == 0x1fd)))
274
+          (startcode == 0x1bd) ||
275
+          (startcode == PRIVATE_STREAM_2) ||
276
+          (startcode == 0x1fd)))
275 277
         goto redo;
276 278
     if (ppos) {
277 279
         *ppos = avio_tell(s->pb) - 4;
... ...
@@ -279,6 +344,8 @@ static int mpegps_read_pes_header(AVFormatContext *s,
279 279
     len = avio_rb16(s->pb);
280 280
     pts =
281 281
     dts = AV_NOPTS_VALUE;
282
+    if (startcode != PRIVATE_STREAM_2)
283
+    {
282 284
     /* stuffing */
283 285
     for(;;) {
284 286
         if (len < 1)
... ...
@@ -352,6 +419,7 @@ static int mpegps_read_pes_header(AVFormatContext *s,
352 352
     }
353 353
     else if( c!= 0xf )
354 354
         goto redo;
355
+    }
355 356
 
356 357
     if (startcode == PRIVATE_STREAM_1) {
357 358
         startcode = avio_r8(s->pb);
... ...
@@ -448,6 +516,9 @@ static int mpegps_read_packet(AVFormatContext *s,
448 448
         else
449 449
             request_probe= 1;
450 450
         type = AVMEDIA_TYPE_VIDEO;
451
+    } else if (startcode == PRIVATE_STREAM_2) {
452
+        type = AVMEDIA_TYPE_DATA;
453
+        codec_id = AV_CODEC_ID_DVD_NAV;
451 454
     } else if (startcode >= 0x1c0 && startcode <= 0x1df) {
452 455
         type = AVMEDIA_TYPE_AUDIO;
453 456
         codec_id = m->sofdec > 0 ? AV_CODEC_ID_ADPCM_ADX : AV_CODEC_ID_MP2;