diff mbox series

[FFmpeg-devel] Adds DVD protocol

Message ID 3f498c9a-7284-6bd2-eec2-147ea3136119@gmail.com
State New
Headers show
Series [FFmpeg-devel] Adds DVD protocol | expand

Checks

Context Check Description
andriy/configurex86 warning Failed to apply patch
andriy/configureppc warning Failed to apply patch

Commit Message

Lucien Murray-Pitts Jan. 4, 2022, 1:37 p.m. UTC
Copies the Bluray protocol but uses libdvdnav to add simple DVD protocol 
support.    Since title selection is mandatory, ffprobe cant provide 
information for the complete disk but a single title only.  Chapter 
information for probe will also be missing.

To see a complete disk catalog the tools/dvd2concat perl script should 
be used

Signed-off-by: Lucien Murray-Pitts <lucien.murraypitts@gmail.com>
---
  configure               |   4 +
  libavformat/Makefile    |   1 +
  libavformat/dvd.c       | 268 ++++++++++++++++++++++++++++++++++++++++
  libavformat/protocols.c |   1 +
  4 files changed, 274 insertions(+)
  create mode 100644 libavformat/dvd.c

Comments

Michael Niedermayer Jan. 5, 2022, 12:02 p.m. UTC | #1
On Tue, Jan 04, 2022 at 10:37:46PM +0900, Lucien Murray-Pitts wrote:
> Copies the Bluray protocol but uses libdvdnav to add simple DVD protocol
> support.    Since title selection is mandatory, ffprobe cant provide
> information for the complete disk but a single title only.  Chapter
> information for probe will also be missing.
> 
> To see a complete disk catalog the tools/dvd2concat perl script should be
> used
> 
> Signed-off-by: Lucien Murray-Pitts <lucien.murraypitts@gmail.com>
> ---
>  configure               |   4 +
>  libavformat/Makefile    |   1 +
>  libavformat/dvd.c       | 268 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/protocols.c |   1 +
>  4 files changed, 274 insertions(+)
>  create mode 100644 libavformat/dvd.c
> 
> diff --git a/configure b/configure
> index 6ad70b9f7b..46216ea785 100755
> --- a/configure
> +++ b/configure
> @@ -230,6 +230,7 @@ External library support:
>    --enable-libdavs2        enable AVS2 decoding via libdavs2 [no]
>    --enable-libdc1394       enable IIDC-1394 grabbing using libdc1394
>                             and libraw1394 [no]
> +  --enable-libdvdnav       enable DVD reading using libdvdnav [no]
> --enable-libfdk-aac      enable AAC de/encoding via libfdk-aac [no]
>    --enable-libflite        enable flite (voice synthesis) support via
> libflite [no]
>    --enable-libfontconfig   enable libfontconfig, useful for drawtext filter
> [no]

error: corrupt patch at line 20

[...]
Lucien Murray-Pitts Jan. 5, 2022, 12:22 p.m. UTC | #2
On 1/5/2022 9:02 PM, Michael Niedermayer wrote:
> On Tue, Jan 04, 2022 at 10:37:46PM +0900, Lucien Murray-Pitts wrote:
>> Copies the Bluray protocol but uses libdvdnav to add simple DVD protocol
>>
> error: corrupt patch at line 20
>
> [...]
>
Thank you - I just noticed today via the automated patchwork, thanks for 
catching it too -- maybe something to do with generation of 
eml/thunderbird now picks up eml by itself rather than by the x-unset 
patch (maybe it is doing something) - I will retry to submit.
diff mbox series

Patch

diff --git a/configure b/configure
index 6ad70b9f7b..46216ea785 100755
--- a/configure
+++ b/configure
@@ -230,6 +230,7 @@  External library support:
    --enable-libdavs2        enable AVS2 decoding via libdavs2 [no]
    --enable-libdc1394       enable IIDC-1394 grabbing using libdc1394
                             and libraw1394 [no]
+  --enable-libdvdnav       enable DVD reading using libdvdnav [no] 
                      --enable-libfdk-aac      enable AAC de/encoding 
via libfdk-aac [no]
    --enable-libflite        enable flite (voice synthesis) support via 
libflite [no]
    --enable-libfontconfig   enable libfontconfig, useful for drawtext 
filter [no]
@@ -1822,6 +1823,7 @@  EXTERNAL_LIBRARY_LIST="
      libdav1d
      libdc1394
      libdrm
+    libdvdnav
      libflite
      libfontconfig
      libfreetype
@@ -3532,6 +3534,7 @@  xv_outdev_deps="xlib_xv xlib_x11 xlib_xext"
  # protocols
  async_protocol_deps="threads"
  bluray_protocol_deps="libbluray"
+dvd_protocol_deps="libdvdnav"
  ffrtmpcrypt_protocol_conflict="librtmp_protocol"
  ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls"
  ffrtmpcrypt_protocol_select="tcp_protocol"
@@ -6519,6 +6522,7 @@  enabled libdav1d          && require_pkg_config 
libdav1d "dav1d >= 0.5.0" "dav1d
  enabled libdavs2          && require_pkg_config libdavs2 "davs2 >= 
1.6.0" davs2.h davs2_decoder_open
  enabled libdc1394         && require_pkg_config libdc1394 libdc1394-2 
dc1394/dc1394.h dc1394_new
  enabled libdrm            && require_pkg_config libdrm libdrm 
xf86drm.h drmGetVersion
+enabled libdvdnav         && require_pkg_config libdvdnav dvdnav 
dvdnav/dvdnav.h dvdnav_open
  enabled libfdk_aac        && { check_pkg_config libfdk_aac fdk-aac 
"fdk-aac/aacenc_lib.h" aacEncOpen ||
                                 { require libfdk_aac 
fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac &&
                                   warn "using libfdk without 
pkg-config"; } }
diff --git a/libavformat/Makefile b/libavformat/Makefile
index e31b248ac0..fea05d7886 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -627,6 +627,7 @@  OBJS-$(CONFIG_CONCAT_PROTOCOL)           += concat.o
  OBJS-$(CONFIG_CONCATF_PROTOCOL)          += concat.o
  OBJS-$(CONFIG_CRYPTO_PROTOCOL)           += crypto.o
  OBJS-$(CONFIG_DATA_PROTOCOL)             += data_uri.o
+OBJS-$(CONFIG_DVD_PROTOCOL)              += dvd.o
  OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL)      += rtmpcrypt.o rtmpdigest.o 
rtmpdh.o
  OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL)       += rtmphttp.o
  OBJS-$(CONFIG_FILE_PROTOCOL)             += file.o
diff --git a/libavformat/dvd.c b/libavformat/dvd.c
new file mode 100644
index 0000000000..9c65241238
--- /dev/null
+++ b/libavformat/dvd.c
@@ -0,0 +1,268 @@ 
+/*
+ * DVD (libdvdnav) protocol based on BluRay (libbluray) protocol by 
Petri Hintukainen.
+ *
+ * Copyright (c) 2022 Lucien Murray-Pitts <lucien.murraypitts <at> 
gmail.com>
+ *
+ * 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
+ */
+
+
+/* REFERENCES: 
https://code.videolan.org/videolan/libdvdnav/-/blob/master/src/dvdnav/dvdnav.h
+ * 
https://code.videolan.org/videolan/libdvdnav/-/blob/master/examples/menus.c
+ *
+ * EXAMPLE USE:
+ *   Choose Title 4, map 1/2/3 (video/audio/subtitles), copy the 
subtitles as is into mkv
+ *   ./ffmpeg -playlist 4 -i dvd:"/mnt/MURDER_SHE_WROTE_TS.S10D1" -map 
0:1 -map 0:2 -map 0:3 -vb 20M  -codec:s copy  remuxed-dvd.mkv
+ *
+ *   Probe for info
+ *   ./ffprobe -loglevel trace -fdebug 8 -playlist 19 -i 
dvd:"/mnt/MURDER_SHE_WROTE_TS.S10D1" -threads 0 -v warning -print_format 
json -show_streams -show_chapters -show_format
+ */
+#include <dvdnav/dvdnav.h>
+
+#include "libavutil/avstring.h"
+#include "libavformat/avformat.h"
+#include "libavformat/url.h"
+#include "libavutil/opt.h"
+
+#define DVD_PROTO_PREFIX     "dvd:"
+#define MIN_PLAYLIST_LENGTH     180     /* 3 min */
+
+typedef struct {
+    const AVClass *class;
+
+    // LMP:
+    dvdnav_t *dvdnav;
+
+    int playlist;
+    int angle;
+    int chapter;
+    /*int region;*/
+} DVDContext;
+
+#define OFFSET(x) offsetof(DVDContext, x)
+static const AVOption options[] = {
+{"playlist", "DVD title to play", OFFSET(playlist), AV_OPT_TYPE_INT, { 
.i64=-1 }, -1,  99999, AV_OPT_FLAG_DECODING_PARAM },
+{"angle",    "DVD Video stream angle", OFFSET(angle), AV_OPT_TYPE_INT, 
{ .i64=0 },   0,   0xfe, AV_OPT_FLAG_DECODING_PARAM },
+{"chapter",  "DVD Chapter to play", OFFSET(chapter),  AV_OPT_TYPE_INT, 
{ .i64=1 },   1, 0xfffe, AV_OPT_FLAG_DECODING_PARAM },
+/*{"region",   "dvd player region code (1 = region A, 2 = region B, 4 = 
region C)", OFFSET(region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 3, 
AV_OPT_FLAG_DECODING_PARAM },*/
+{NULL}
+};
+
+static const AVClass dvd_context_class = {
+    .class_name     = "dvd",
+    .item_name      = av_default_item_name,
+    .option         = options,
+    .version        = LIBAVUTIL_VERSION_INT,
+};
+
+
+static int check_disc_info(URLContext *h)
+{
+    DVDContext *disk = h->priv_data;
+    +    int32_t region_mask;
+
+    if ( !dvdnav_get_region_mask( disk->dvdnav, &region_mask) ) {
+        av_log(h, AV_LOG_ERROR, "dvdnav_get_region_mask() failed\n");
+        return -1;
+    }
+
+    // Gets the DVD DISK's region
+    av_log(h, AV_LOG_INFO, "dvdnav_get_region_mask() got %i\n", 
region_mask);
+
+    return 0;
+}
+
+static int dvd_close(URLContext *h)
+{
+    DVDContext *disk = h->priv_data;
+    if (disk->dvdnav) {
+        dvdnav_close( disk->dvdnav );
+    }
+
+    return 0;
+}
+
+static int dvd_open(URLContext *h, const char *path, int flags)
+{
+    DVDContext *disk = h->priv_data;
+    int num_title_idx;
+    const char *diskname = path;
+
+    av_strstart(path, DVD_PROTO_PREFIX, &diskname);
+
+    if ( dvdnav_open(&disk->dvdnav, diskname) != DVDNAV_STATUS_OK ) {
+        av_log(h, AV_LOG_ERROR, "dvdnav_open() failed, problem opening 
DVD folder/drive\n");
+        return AVERROR(EIO);
+    }
+
+    /* check if disc can be played */
+    if (check_disc_info(h) < 0) {
+        return AVERROR(EIO);
+    }
+
+    /* setup player registers */
+    /* region code has no effect without menus
+    if (disk->region > 0 && disk->region < 5) {
+        av_log(h, AV_LOG_INFO, "setting region code to %d (%c)\n", 
disk->region, 'A' + (disk->region - 1));
+    }
+    */
+
+    /* load title list */
+    dvdnav_get_number_of_titles(disk->dvdnav, &num_title_idx);
+    if (num_title_idx < 1) {
+        av_log(h, AV_LOG_ERROR, "no usable dvd titles found on disk\n");
+        return AVERROR(EIO);
+    }
+
+    av_log(h, AV_LOG_INFO, "%d usable dvd titles\n", num_title_idx);
+
+    /* if playlist was not given, select longest playlist */
+    if (disk->playlist < 0) {
+        uint64_t duration = 0;
+        int i;
+        for (i = 1; i <= num_title_idx; i++) {
+            uint64_t *times ; // FIXME: Should be freed
+            uint64_t _duration;
+
+            uint32_t chcount = dvdnav_describe_title_chapters( 
disk->dvdnav, i, &times, &_duration );
+            if( chcount==0 ) continue;
+
+            // Title complete length
+            av_log(h, AV_LOG_INFO, "title #%05d (%d:%02d:%02d)\n",
+                   i,
+                   ((int)(_duration / 90000) / 3600),
+                   ((int)(_duration / 90000) % 3600) / 60,
+                   ((int)(_duration / 90000) % 60));
+
+            // Display Chapter times
+            for( int tidx=0 ; tidx<chcount; tidx++ ) {
+                if(times==0) break;
+
+                if(times[tidx]==0) break;
+                av_log(h, AV_LOG_INFO, "  chapter %05d (%d:%02d:%02d)\n",
+                   tidx+1,
+                   ((int)(times[tidx] / 90000) / 3600),
+                   ((int)(times[tidx] / 90000) % 3600) / 60,
+                   ((int)(times[tidx] / 90000) % 60));
+            }
+
+            if (_duration > duration) {
+                disk->playlist = i;
+                duration = _duration;
+            }
+
+            free(times);
+        }
+    }
+    av_log(h, AV_LOG_INFO, "selected title# %05d\n", disk->playlist);
+
+    /* select playlist */
+    if ( dvdnav_title_play(disk->dvdnav, disk->playlist) <= 0) {
+        av_log(h, AV_LOG_ERROR, "dvdnav_title_play(%05d) failed, title 
doesnt exist or is corrupted.\n", disk->playlist);
+        return AVERROR(EIO);
+    }
+
+
+    /* select angle; note angles may appear during video stream and not 
at the start  */
+    if (disk->angle >= 0) {
+        if( dvdnav_angle_change(disk->dvdnav, disk->angle) ) {
+            av_log(h, AV_LOG_ERROR, "selected angle doesnt exist\n");
+        }
+
+    }
+
+
+    /* select chapter */  +    if (disk->chapter > 1) {
+        uint64_t *times ; // FIXME: Should be freed
+        uint64_t _duration;
+
+        uint32_t chcount = dvdnav_describe_title_chapters( 
disk->dvdnav, disk->playlist, &times, &_duration );
+        if(chcount == 0) {
+            av_log(h, AV_LOG_ERROR, "Title %05d description failed, 
title doesnt exist or is corrupted.\n", disk->playlist);
+            return AVERROR(EIO);
+        }
+
+        if(disk->chapter > chcount) {
+            av_log(h, AV_LOG_ERROR, "Chapter %05d is out of range of 
1..%05d for title %05d.\n", disk->chapter, chcount, disk->playlist);
+            return AVERROR(EIO);
+        }
+
+        dvdnav_time_search(disk->dvdnav, times[disk->chapter - 1] );
+
+        av_log(h, AV_LOG_INFO, "selected title# %05d; chapter %05d\n", 
disk->playlist, disk->chapter);
+
+        free(times);
+    }
+
+    return 0;
+}
+
+static int dvd_read(URLContext *h, unsigned char *buf, int size)
+{
+    DVDContext *disk = h->priv_data;
+    int32_t event;
+    int len;
+
+    if (!disk || !disk->dvdnav) {
+        return AVERROR(EFAULT);
+    }
+
+    dvdnav_get_next_block(disk->dvdnav, (uint8_t *)buf, &event, &len);
+
+    return len == 0 ? AVERROR_EOF : len;
+}
+
+static int64_t dvd_seek(URLContext *h, int64_t pos, int whence)
+{
+    DVDContext *disk = h->priv_data;
+
+    if (!disk || !disk->dvdnav) {
+        return AVERROR(EFAULT);
+    }
+
+    switch (whence) {
+    case SEEK_SET:
+        return dvdnav_sector_search(disk->dvdnav, pos, SEEK_SET);
+    case SEEK_CUR:
+        return dvdnav_sector_search(disk->dvdnav, pos, SEEK_CUR);
+    case SEEK_END:
+        return dvdnav_sector_search(disk->dvdnav, pos, SEEK_END);
+
+    case AVSEEK_SIZE:
+        uint32_t _pos, len ;
+        dvdnav_sector_search(disk->dvdnav, 0, SEEK_END);
+        dvdnav_get_position( disk->dvdnav, &_pos, &len);
+        dvdnav_sector_search(disk->dvdnav, 0, SEEK_SET);
+        return len ;
+    }
+
+    av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence);
+    return AVERROR(EINVAL);
+}
+
+
+const URLProtocol ff_dvd_protocol = {
+    .name            = "dvd",
+    .url_close       = dvd_close,
+    .url_open        = dvd_open,
+    .url_read        = dvd_read,
+    .url_seek        = dvd_seek,
+    .priv_data_size  = sizeof(DVDContext),
+    .priv_data_class = &dvd_context_class,
+};
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 948fae411f..4cbcedb5c8 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -30,6 +30,7 @@  extern const URLProtocol ff_concat_protocol;
  extern const URLProtocol ff_concatf_protocol;
  extern const URLProtocol ff_crypto_protocol;
  extern const URLProtocol ff_data_protocol;
+extern const URLProtocol ff_dvd_protocol;
  extern const URLProtocol ff_ffrtmpcrypt_protocol;
  extern const URLProtocol ff_ffrtmphttp_protocol;
  extern const URLProtocol ff_file_protocol;