From patchwork Wed Jan 5 14:04:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucien Murray-Pitts X-Patchwork-Id: 33087 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp753239iog; Wed, 5 Jan 2022 06:04:16 -0800 (PST) X-Google-Smtp-Source: ABdhPJyxRAClzPxNYIZ97T5mpjDKeAMSEmNAv2tB5uIE5sHalVW2t/QnoqKomEV4HFKuaxT+YnaG X-Received: by 2002:a05:6402:2789:: with SMTP id b9mr43382180ede.329.1641391456396; Wed, 05 Jan 2022 06:04:16 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641391456; cv=none; d=google.com; s=arc-20160816; b=bBEM97QVjqH0bfU9JswdNOp3Rz7xFjZ2BzEE7NXjlhhnTm1HquUIwEKQPXBjRdRxZ3 dHMcS8aQdyFRlFqXcYKCnMZA317JyihdqwAzgj9xASHsyDbL81bXXHQNK78ifxq8+yoK z4FZf/EpvUsmtZFzM448zvPOrNmye9d6T77B0bS2LLL9/4BA1CdHg9IgsmQOcFI3rxHd olJFLY814vvAU7PFnAwCkw0qX1jjiRj4Hnf9VSkvQCStfqROz9a9KqretS54nKl59w/P BTtgJUaZzvoBvuBx0NGP2KBmkw7bxBeT+Twk4DsenwWR8F46FNo9fvs7lLXO4k1l7dbc Czfw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:content-language:to:from:user-agent:mime-version :date:message-id:dkim-signature:delivered-to; bh=ZexUQUYWB1F3GHKQi3CThVWbwImSNwZ/fbtdM6b8xIM=; b=MRP3Tm26mtauQUFbYMGqTnTnrSYWnzlqYWk3DV404DNYVVDtBYgvCJh4inbSVeyHAH Dj0d2jgXQ6nTthjPOY/ODwB6yZd/3VM7hi/T+30+V4EH940OumY27QaQJM3Im2Q8f/WI Fof7hL0buLKipF7WT7cvYWTLf8o7C8sMKMgRI6wioVrg8QxyiGf0GUfhW5PkQfxAxUYH 95jMhfvWnRTHVKTvdR88yPR8bJp/SDMgHG9JCgtf7ZFnOO5oavnUqvRN8W7CK38VW9tw MUE4vX2noUEMa1uWOgseJFOuJoE/a7FfIKQmATRXee1Qe/pnk2bq2lwWm7vJpQ7sZpgM suBg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="XKC/1sQH"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id qb23si19473777ejc.37.2022.01.05.06.04.16; Wed, 05 Jan 2022 06:04:16 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b="XKC/1sQH"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 67B2E68AD8B; Wed, 5 Jan 2022 16:04:13 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5773D6881C2 for ; Wed, 5 Jan 2022 16:04:07 +0200 (EET) Received: by mail-pf1-f175.google.com with SMTP id c2so35289638pfc.1 for ; Wed, 05 Jan 2022 06:04:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:date:mime-version:user-agent:from:subject:to :content-language:content-transfer-encoding; bh=z19JZ6s5cqU45NEtr2S5b8Kq/7IeCWukFE3NE7gDqhk=; b=XKC/1sQH4SfJgdZ6G2WaQYbsVxWY+qw+HeatVBj1ntVXf1szhMnM5j7ygzXn+8YEj0 20SmA4S5WHXv1zArYWVOH6+oFMRBnuAcFSsyIz2jDs7b7KVq3VhZJ+5SUvtmh4n+77qJ dfBW9PIZJd/bBbc0Q9sv7NKYNJSQp+QqixvZIjIXRwn5afTW5SknBSvlU7PoPwM+BMk9 hucYrKZA90SnSSRG+bY+LdhbyjfSQxj5jifOFVqs8GwFYJg3fzvbNUdXaabEF+iUQ6UO 16D2I/dA3xZfgvC/kodqKNs/J2A/RrUI/h7v1+BVBDhHsm4x+8dKnLdvTwTWn5BD13dy xY/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:from :subject:to:content-language:content-transfer-encoding; bh=z19JZ6s5cqU45NEtr2S5b8Kq/7IeCWukFE3NE7gDqhk=; b=YpJd++MpYZpqxk0la9UEZzhRBEqJHsyLndB3jvUqTjhTVSAef1sERxy0HIWFVklwQV Ug+RkyR36/7kQEc/RtKRpPRjWb466HCi94+z483oUfj5NdmHkEmdCzXhPb+6n0NINLe4 o0eNy9hXvMC9W3cyDJIkrDjOUmLxTNT5PxtCwTkIl0Q1nm6QRm05Q3QfAdgSmqTdfmxX pEskRZK9K5PQgg8gxX4N/St2iyRWV9wmEM6T8gfs1G7JjHrv6jhke0PkC/qEvwrCqOla f5SaJHnEAR3LfRpeQdxZQI9z0P+41VoLkACjCJJq6Z0HP2xKCCqd4o+dydD8pr2AdEcO 3Dmw== X-Gm-Message-State: AOAM530X+Wk4A6ELKYBSFKfXjOxMNDuLbZkEjUrk4cTVw1/vz3k7hRwj 3dUx4OvBWTUc7qgdUvf7gZygnqYbupwE+D5y X-Received: by 2002:a05:6a00:2282:b0:4bb:dde0:63e5 with SMTP id f2-20020a056a00228200b004bbdde063e5mr44874949pfe.8.1641391444973; Wed, 05 Jan 2022 06:04:04 -0800 (PST) Received: from [192.168.2.144] (113x41x203x65.ap113.ftth.ucom.ne.jp. [113.41.203.65]) by smtp.gmail.com with ESMTPSA id s34sm48316768pfg.198.2022.01.05.06.04.03 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 05 Jan 2022 06:04:04 -0800 (PST) Message-ID: Date: Wed, 5 Jan 2022 23:04:02 +0900 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.4.1 From: Lucien Murray-Pitts To: ffmpeg-devel@ffmpeg.org Content-Language: en-US Subject: [FFmpeg-devel] [PATCH v2] Adds DVD protocol X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: UTvOAw6YpcHH Copies the existing Bluray protocol format to add DVD protocol support using libdvdnav. 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 of titles/chapters the tools/dvd2concat perl script should be used. Signed-off-by: Lucien Murray-Pitts --- configure | 4 + libavformat/Makefile | 1 + libavformat/dvd.c | 264 ++++++++++++++++++++++++++++++++++++++++ libavformat/protocols.c | 1 + 4 files changed, 270 insertions(+) create mode 100644 libavformat/dvd.c diff --git a/configure b/configure index 8392c26015..7f8b0046d2 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 @@ -3539,6 +3541,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" @@ -6526,6 +6529,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 c479ea998e..5fbba89e36 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..b3bc1b95e4 --- /dev/null +++ b/libavformat/dvd.c @@ -0,0 +1,264 @@ +/* + * DVD (libdvdnav) protocol based on BluRay (libbluray) protocol by Petri Hintukainen. + * + * Copyright (c) 2022 Lucien Murray-Pitts 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 + +#include "libavutil/avstring.h" +#include "libavformat/avformat.h" +#include "libavformat/url.h" +#include "libavutil/opt.h" + +#define DVD_PROTO_PREFIX "dvd:" + +typedef struct { + const AVClass *class; + + 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; + + // Gets the DVD DISK's region; confirms if disk is at least readable + if ( !dvdnav_get_region_mask( disk->dvdnav, ®ion_mask) ) { + av_log(h, AV_LOG_ERROR, "dvdnav_get_region_mask() failed\n"); + return -1; + } + + 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 ; + uint64_t _duration; + + uint32_t chcount = dvdnav_describe_title_chapters( disk->dvdnav, i, ×, &_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 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 ; + uint64_t _duration; + + uint32_t chcount = dvdnav_describe_title_chapters( disk->dvdnav, disk->playlist, ×, &_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); + } + + av_log(h, AV_LOG_INFO, " Seeking to: %05ld pts, start of chapter %05d\n", times[disk->chapter - 2], disk->chapter ); + dvdnav_time_search(disk->dvdnav, times[disk->chapter - 2] ); + + 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; + uint32_t curpos; + uint32_t len ; + + if (!disk || !disk->dvdnav) { + return AVERROR(EFAULT); + } + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + dvdnav_get_position_in_title(disk->dvdnav, &curpos, &len); + dvdnav_sector_search(disk->dvdnav, pos, whence); + return curpos; + + case AVSEEK_SIZE: + dvdnav_get_position_in_title(disk->dvdnav, &curpos, &len); + 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;