From patchwork Tue Jan 4 13:37:46 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: 33060 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp16989148iog; Tue, 4 Jan 2022 05:38:00 -0800 (PST) X-Google-Smtp-Source: ABdhPJx293H4obvOYM/89d8EyfLe6OgQJJgI+JM6aY6HPu8csxlhvq32nl700/RWWTAW7ORtf0bg X-Received: by 2002:a17:906:7305:: with SMTP id di5mr39322077ejc.255.1641303480707; Tue, 04 Jan 2022 05:38:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1641303480; cv=none; d=google.com; s=arc-20160816; b=uoxXpQGC5d2LyAwXfMEdZhFv+qJ/TD3tgU/qmK+QI+x0GHEwy3q8KQeOzIJUKg/NG/ WaNIcc/c7/sH0JmzVhAtJDTqY22/wpyRUNYkbEocQ5PllBeI0qnqJjM1m2W+kLlBwJ88 Md227ydgAXJxjHxi/91f5z6twWAd9D148MT4HyEr1vGKyZrkFhKYUr0ggtK0jPNQXJC1 Ln4bubchOGJMBg0MPaloyyh1VSm2sdtnpsmSE3f1usn53PhdMZRCs7BeDYRIVFnG2DZ/ FOVkCkB2FAx/f5O9Q9zOfnBbpo4RKoM04QQvJDBLRWGMOtvfsoGHfcl0NY3PXKfLf4Z5 pJkg== 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=039++KIK3oVpYLz1CimWlCVqrP8DPuGWEO1n4r3WNQk=; b=rNExRt6SOrsXjZIoUldEwdmouiDIyUXLvoZ292VeEd5cHc33HlqCTy0HJn/tsxfweQ HP/2tQdsgQ+GFhlw2v58AFh5apxQDKK7aZ3Zs+pWCShX8u1fXCcksnUJtMQYkqBycMem wUSFPEBnj8M+9Q2IbZcctb9eEb8meFl7rmqu9rzCWE5UoQm2JYgfU9IWo7gFDFRZKfRw +P564D/oUCAbu4Z8DLLyC/oJmkGa4NkMIL1RCjtvKx+RyEkTyRCGVgMQ1k+/A1eW45io oYgRwtSpyEvX3PjNPTLCp0+XGlM6Te1VVpKVFKT4RGD9kkcuTgbO/VvQZWrXLKD23MMJ aeZg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=Nz5cUddH; 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 q4si17571272edr.429.2022.01.04.05.38.00; Tue, 04 Jan 2022 05:38:00 -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=Nz5cUddH; 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 2620168B291; Tue, 4 Jan 2022 15:37:58 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 12FC9680CB2 for ; Tue, 4 Jan 2022 15:37:52 +0200 (EET) Received: by mail-pj1-f43.google.com with SMTP id iy13so31352346pjb.5 for ; Tue, 04 Jan 2022 05:37:51 -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=yVAwQHDMGTX2Vs/O6eGgzNvheibbOgBV41iqbjei6ss=; b=Nz5cUddHQxuIztFByaHRVScvEMfc/oruB5Hkdz0HvDC1C76EJiCxanlWLfOFHjsvPM xNFm4lm2RPffrxlH10HgHw8AO1HUFDFlOc3WF7pnOE+GmMqUywquGlvZX7SkC1riavRQ q+lVOW/flT5yWeXtOHnsdUeG7I2GH5hnvbChg2oPxcqSvlmovZ7WmhOiT3Myog6+aXta LwK+GnPM08fuvyJDF+/TLgj8WZKqLv+mcbInO4Ak1rAehJmAOAAZUwW4qm7IrGTr9EYq w/sj7xVci9O8tC5M8eImQsMoG4FnVtkLzT2xTrZi13t6+hnzb7E962h5/OEs4kbjd1Rt kZgQ== 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=yVAwQHDMGTX2Vs/O6eGgzNvheibbOgBV41iqbjei6ss=; b=Bf7F/6+6DP8HfVNI6cmmyMnYCLWT2eIYVghHQEYS7ot/yiGnyVHzu23XrGMCw43Nzb BxXL1KZYjxEEsILd4JJ/RJgSjNgDC8OCoxg3G4ZILA5jnpVy+elpPiNEwq8z50gT8EKo IevBk0D/++Je+S8UePDnJaYbNJRqA09nLYqi6jDDbVnYdx6S0rz/9FShdIwYmebHZCfl e9WefKXf56s9BmYU83M89ZVyz2C2LcJJnUUTtOjcazwc/0PQLim8wasAcEXiMIQ/uZ/b rnqUeINLhBsz61smnTJUp95M8lNcbyFDc4gLyNnpPxQkpTAxiNq4eruHxeEgJfVrtr0o fi/w== X-Gm-Message-State: AOAM53355BTjGBNolCNR40WdT3vibsg1CEOkdm2LWZdKbCftMQ8IXCB6 2nnNnplRc8BOAIH4Bum5+LCYwcVezzD/W2Wy X-Received: by 2002:a17:90b:30cc:: with SMTP id hi12mr60966459pjb.50.1641303469812; Tue, 04 Jan 2022 05:37:49 -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 u6sm42502091pja.32.2022.01.04.05.37.48 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 04 Jan 2022 05:37:49 -0800 (PST) Message-ID: <3f498c9a-7284-6bd2-eec2-147ea3136119@gmail.com> Date: Tue, 4 Jan 2022 22:37:46 +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] 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: F44KfjSQKe84 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 --- 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] @@ -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 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:" +#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, ®ion_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, ×, &_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 ; // FIXME: Should be freed + 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); + } + + 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;