diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e4fccf1512..3c559dbb06 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -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 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 584446f136..1eaf2d3397 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -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); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index cbb64569c7..af6294968b 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -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 diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 673f9710b5..ded09c93e8 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -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 index 0000000000..4b03e399f3 --- /dev/null +++ b/libavcodec/dvd_nav_parser.c @@ -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, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 256a2e5500..5cf07bc31a 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -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, \ 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 +#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;