OSDN Git Service

mpeg: Add passing DVD navigation packets (startcode 0x1bf) to caller to allow better...
authorRichard <peper03@yahoo.com>
Sun, 17 Mar 2013 09:21:12 +0000 (10:21 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 17 Mar 2013 15:24:34 +0000 (16:24 +0100)
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>
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/codec_desc.c
libavcodec/dvd_nav_parser.c [new file with mode: 0644]
libavcodec/version.h
libavformat/mpeg.c

index e4fccf1..3c559db 100644 (file)
@@ -743,6 +743,7 @@ OBJS-$(CONFIG_PNG_PARSER)              += png_parser.o
 OBJS-$(CONFIG_MPEGAUDIO_PARSER)        += mpegaudio_parser.o \
                                           mpegaudiodecheader.o mpegaudiodata.o
 OBJS-$(CONFIG_MPEGVIDEO_PARSER)        += mpegvideo_parser.o    \
+                                          dvd_nav_parser.o \
                                           mpeg12.o mpeg12data.o
 OBJS-$(CONFIG_PNM_PARSER)              += pnm_parser.o pnm.o
 OBJS-$(CONFIG_RV30_PARSER)             += rv34_parser.o
index 584446f..1eaf2d3 100644 (file)
@@ -515,6 +515,7 @@ void avcodec_register_all(void)
     REGISTER_PARSER(DNXHD,              dnxhd);
     REGISTER_PARSER(DVBSUB,             dvbsub);
     REGISTER_PARSER(DVDSUB,             dvdsub);
+    REGISTER_PARSER(DVD_NAV,            dvd_nav);
     REGISTER_PARSER(FLAC,               flac);
     REGISTER_PARSER(GSM,                gsm);
     REGISTER_PARSER(H261,               h261);
index cbb6456..af62949 100644 (file)
@@ -482,6 +482,8 @@ enum AVCodecID {
     AV_CODEC_ID_IDF        = MKBETAG( 0 ,'I','D','F'),
     AV_CODEC_ID_OTF        = MKBETAG( 0 ,'O','T','F'),
     AV_CODEC_ID_SMPTE_KLV  = MKBETAG('K','L','V','A'),
+    AV_CODEC_ID_DVD_NAV    = MKBETAG('D','N','A','V'),
+
 
     AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it
 
index 673f971..ded09c9 100644 (file)
@@ -2525,6 +2525,12 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .name      = "klv",
         .long_name = NULL_IF_CONFIG_SMALL("SMPTE 336M Key-Length-Value (KLV) metadata"),
     },
+    {
+        .id        = AV_CODEC_ID_DVD_NAV,
+        .type      = AVMEDIA_TYPE_DATA,
+        .name      = "dvd_nav_packet",
+        .long_name = NULL_IF_CONFIG_SMALL("DVD Nav packet"),
+    },
 
 };
 
diff --git a/libavcodec/dvd_nav_parser.c b/libavcodec/dvd_nav_parser.c
new file mode 100644 (file)
index 0000000..4b03e39
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * DVD navigation block parser for FFmpeg
+ * Copyright (c) 2013 The FFmpeg Project
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "avcodec.h"
+#include "dsputil.h"
+#include "get_bits.h"
+#include "parser.h"
+
+#define PCI_SIZE  980
+#define DSI_SIZE 1018
+
+/* parser definition */
+typedef struct DVDNavParseContext {
+    uint32_t     lba;
+    uint8_t      buffer[PCI_SIZE+DSI_SIZE];
+    int          copied;
+} DVDNavParseContext;
+
+static av_cold int dvd_nav_parse_init(AVCodecParserContext *s)
+{
+    DVDNavParseContext *pc = s->priv_data;
+
+    pc->lba    = 0xFFFFFFFF;
+    pc->copied = 0;
+    return 0;
+}
+
+static int dvd_nav_parse(AVCodecParserContext *s,
+                         AVCodecContext *avctx,
+                         const uint8_t **poutbuf, int *poutbuf_size,
+                         const uint8_t *buf, int buf_size)
+{
+    DVDNavParseContext *pc1 = s->priv_data;
+    int lastPacket          = 0;
+    int valid               = 0;
+
+    s->pict_type = AV_PICTURE_TYPE_NONE;
+
+    avctx->time_base.num = 1;
+    avctx->time_base.den = 90000;
+
+    if (buf && buf_size) {
+        switch(buf[0]) {
+            case 0x00:
+                if (buf_size == PCI_SIZE) {
+                    /* PCI */
+                    uint32_t lba      = AV_RB32(&buf[0x01]);
+                    uint32_t startpts = AV_RB32(&buf[0x0D]);
+                    uint32_t endpts   = AV_RB32(&buf[0x11]);
+
+                    if (endpts > startpts) {
+                        pc1->lba    = lba;
+                        s->pts      = (int64_t)startpts;
+                        s->duration = endpts - startpts;
+
+                        memcpy(pc1->buffer, buf, PCI_SIZE);
+                        pc1->copied = PCI_SIZE;
+                        valid       = 1;
+                    }
+                }
+                break;
+
+            case 0x01:
+                if ((buf_size == DSI_SIZE) && (pc1->copied == PCI_SIZE)) {
+                    /* DSI */
+                    uint32_t lba = AV_RB32(&buf[0x05]);
+
+                    if (lba == pc1->lba) {
+                        memcpy(pc1->buffer + pc1->copied, buf, DSI_SIZE);
+                        lastPacket  = 1;
+                        valid       = 1;
+                    }
+                }
+                break;
+        }
+    }
+
+    if (!valid || lastPacket) {
+        pc1->copied = 0;
+        pc1->lba    = 0xFFFFFFFF;
+    }
+
+    if (lastPacket) {
+        *poutbuf      = pc1->buffer;
+        *poutbuf_size = sizeof(pc1->buffer);
+    } else {
+        *poutbuf      = NULL;
+        *poutbuf_size = 0;
+    }
+
+    return buf_size;
+}
+
+AVCodecParser ff_dvd_nav_parser = {
+    .codec_ids      = { AV_CODEC_ID_DVD_NAV },
+    .priv_data_size = sizeof(DVDNavParseContext),
+    .parser_init    = dvd_nav_parse_init,
+    .parser_parse   = dvd_nav_parse,
+};
index 256a2e5..5cf07bc 100644 (file)
@@ -29,7 +29,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 55
-#define LIBAVCODEC_VERSION_MINOR  0
+#define LIBAVCODEC_VERSION_MINOR  1
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
index 4eaffd8..5387b09 100644 (file)
@@ -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;