From patchwork Sat Jan 21 11:01:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Ionascu X-Patchwork-Id: 40085 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:3ca3:b0:b9:1511:ac2c with SMTP id b35csp1844891pzj; Sat, 21 Jan 2023 03:02:40 -0800 (PST) X-Google-Smtp-Source: AMrXdXvFfVKWhih/x8x8SnVxww0mfLyOM+pc+mNEN/ahoupKfjiHGWdoWTa06D/DchXjk6UAVzuv X-Received: by 2002:a17:906:1b4a:b0:84d:4e4e:2c7b with SMTP id p10-20020a1709061b4a00b0084d4e4e2c7bmr31511878ejg.30.1674298960121; Sat, 21 Jan 2023 03:02:40 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1674298960; cv=none; d=google.com; s=arc-20160816; b=axhsr0tGbe3FofjdeFqSXjvP3PWARxBoSWPIqCK/TIcrdfErCJbp7FFnu41uoMchSx uDTCeEuHyMWwJv9xXmhdY0wjaf1b2MdBQrzmyscB0dlxssxt2I3jAqS59mI++U/+W7O4 OLmYMIq9iv5faYu9mTc1R0p2CNf6D5zFdO8K7aUma+E5DK/RAOyxUz+DMpcqeMdP5U+g GxHlcY92+DsqJ/F1LcmJVHG/sUZ4zQbkSRuUG1MXEZHb8jYPgHMPfc0O6y+67HRjFbFW fMPvcvAFkAazC+F/RYLpBWjoedco/oaOSSTvxcnxfd8MH5QGLSHDjXDWdRzsCwm8mND0 kioQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=dVg9KFs1T4WMKoXs+e2EQqsxm7md9sG7RwFK/VHLzN0=; b=wAgxLd6MzJLXidY86Zy/Gl0hc3R8cMETauZysk/lnaHi1u0yzakigq13oMa8oGTeuI Sm2iAIfZsAMd1SGI7jAWVyhxwxHw4gzVhrVDUGC2gXsktDOdJ/zBh3Px1RtrWSfVpLat PdTKpTrF6ocQwGlf91zW/W/CmK35thLcc5w5HEDxidUwF3TaUFLqw2fPFUom4ZHrmjky 6t4+BgFWJM5OR3fExmzZL6NiSL1YzMQfK3lswJz1b9UFu/AaKRfqSReHwqiDstQVOzuW KZLmEMyYgG2JKZMCEZkDDgdCmkIyVxs39Z26PRSyWGHJEJwtiiDjqHS6GSCirMRvTl7K anOA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=KRHqlwYM; 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 cw18-20020a170906479200b00866ea828d03si28998737ejc.605.2023.01.21.03.02.39; Sat, 21 Jan 2023 03:02:40 -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=KRHqlwYM; 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 473CF68B274; Sat, 21 Jan 2023 13:02:35 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AABF668A23A for ; Sat, 21 Jan 2023 13:02:28 +0200 (EET) Received: by mail-wr1-f42.google.com with SMTP id r9so6880109wrw.4 for ; Sat, 21 Jan 2023 03:02:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=o/9v5mWdfRbDlvgJYGDkLX22ogilvd43WvdmwX/hfY0=; b=KRHqlwYMBgYrFKoMtbOStQ88R0wMnbpjZ/75WYrL9guzXbIBiw5eA/qikZYcSnyI0L ScaTGwAr9ExcxEV1w91DAUsA33PkC6FgHbuWbhlW0Oyzxe18ivjBMEWCOlqSsch/gPwS Fi9qkfZ0wnqvqP0NK8WJ7VwGFFK4JByeGRdANQdN7940Z5f9pQ3EifIYPgjMymqXnSDP by9ehuZhoDZsHm0puIwTPWme/n/bI2yYRa4mlPpzywKbpbIBn83eylbrCv5z64Ayt5HG wuEJU5MKlGdL3mqZq2s1EWGZ2+DR0ulyRlBWY1bRxcAV5orKyJQprvUsUXvayocxso9n ZPDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=o/9v5mWdfRbDlvgJYGDkLX22ogilvd43WvdmwX/hfY0=; b=5XPf9XzUCKyn6zkK2b+eBbKpQr43MLB3yWKD64IGJ80gz+sxcGS0oAqDKh0uI+jjBH 45zj5NyY39fOlFEh+oxx9Yj9whU+enUjp0N0dnsEE5wYa+mZdyvTy0z71HqHpBn1OKgp Gpj/l91Wzf2mEOM9Kks0PDGLpJe7MOYAMMh69kn2OhDl8XcM39pv5fPuPNqCp8816i8c u0njqKKXIAOXBMOT4DwRLLtHEtaJM3SXBVUa4hl/TJ31Spf5M5yPR9gxEoS+sEKTPOaR h7DX2HOCRR9mmstJMoUbd7bF+mjt9k5/fd59bO4aWi9KNiXplRvtx5SH7lve2rQfK+2f IhnQ== X-Gm-Message-State: AFqh2koFvlJ/aqK/eSJreo/HQl8QWP57VoC6ZCOSaTfMNLltyR9TN383 dda0xjWPt17QQQIWa4j4ArPPNOLL7TvepiZp X-Received: by 2002:adf:f0c8:0:b0:2bf:9656:d1a8 with SMTP id x8-20020adff0c8000000b002bf9656d1a8mr3027221wro.32.1674298947282; Sat, 21 Jan 2023 03:02:27 -0800 (PST) Received: from localhost.localdomain (host109-147-184-255.range109-147.btcentralplus.com. [109.147.184.255]) by smtp.gmail.com with ESMTPSA id f2-20020adff982000000b002bde537721dsm19921483wrr.20.2023.01.21.03.02.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 21 Jan 2023 03:02:26 -0800 (PST) From: Stanislav Ionascu To: ffmpeg-devel@ffmpeg.org Date: Sat, 21 Jan 2023 11:01:40 +0000 Message-Id: <20230121110139.27074-1-stanislav.ionascu@gmail.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: References: MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v5] avformat/dvd: new dvd:// protocol for reading dvd folder/images 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 Cc: Stanislav Ionascu Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: HAEVOW41gNUg dvd:// protocol uses libdvdread for opening folders and/or disc-images, it then either identifies the longest title-set, or uses the title-set parameter. After opening the dvd, libdvdread-protocol will read and output all VOBs, in the cell and pack sequence defined by the program-chain. The (split)-VOB content that is streamed from the folder/disc by libdvdread, is given to ffmpeg probed demuxer/decoder, which decodes and presents the frames. The chapter and language stream information is present in the side-files (BUP/IFO), which are parsed for identifying the correct main video-title-set, but not attached to the stream information. Signed-off-by: Stanislav Ionascu --- configure | 5 + doc/protocols.texi | 61 ++++++++ libavformat/Makefile | 1 + libavformat/dvd.c | 327 ++++++++++++++++++++++++++++++++++++++++ libavformat/protocols.c | 1 + 5 files changed, 395 insertions(+) create mode 100644 libavformat/dvd.c diff --git a/configure b/configure index df69d39669..4dcd1f1484 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-libdvdread enable DVD reading using libdvdread [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] @@ -1814,6 +1815,7 @@ EXTERNAL_LIBRARY_LIST=" libdav1d libdc1394 libdrm + libdvdread libflite libfontconfig libfreetype @@ -3557,6 +3559,7 @@ xv_outdev_deps="xlib_xv xlib_x11 xlib_xext" # protocols async_protocol_deps="threads" bluray_protocol_deps="libbluray" +dvd_protocol_deps="libdvdread" ffrtmpcrypt_protocol_conflict="librtmp_protocol" ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls" ffrtmpcrypt_protocol_select="tcp_protocol" @@ -6568,6 +6571,8 @@ 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 libdvdread && enable dvdread +enabled dvdread && require_pkg_config libdvdread dvdread "dvdread/dvd_reader.h" DVDOpen2 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/doc/protocols.texi b/doc/protocols.texi index 21ae6181a0..ec5be9c6e5 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -275,6 +275,67 @@ For example, to convert a GIF file given inline with @command{ffmpeg}: ffmpeg -i "data:image/gif;base64,R0lGODdhCAAIAMIEAAAAAAAA//8AAP//AP///////////////ywAAAAACAAIAAADF0gEDLojDgdGiJdJqUX02iB4E8Q9jUMkADs=" smiley.png @end example +@section dvd + +Reads DVD titles from image or folder backups. + +After opening the dvd, the protocol will read and output the VOBs, +associated with the longest or requested @command{title}, in the cell +and pack sequence defined by the program-chain. It will stop, when reaching +the end of the @command{title}. + +@subsection Limitations + +There are some limitations in the stream information that this protocol +provides: +@itemize @bullet +@item +Some of the streams, like subtitles, are discovered by @command{ffmpeg} while +parsing the DVD stream, thus large enough @command{probesize} and @command{analyzeduration} +are needed to discover those while probing or remuxing. +@item +The protocol does not handle the presentation or decoding timestamps, as decoding is done by +the probed decoder, VOB. And so it might be necessary to generate the presentation +timestamps for formats that need them via @command{-fflag +genpts}. +@item +The chapter and language stream information is present in the side-files (BUP/IFO), +that are parsed for identifying the correct main video-title-set, but are not attached +to the stream information. +@end itemize + +The accepted options are: +@table @option + +@item angle +DVD angle + +@item title +DVD title to read (VIDEO_TS/VTS_??_??.mpls) + +@end table + +@subsection Examples + +Read longest title from DVD mounted to /mnt/dvd: +@example +dvd:/mnt/dvd +@end example + +Read longest title from DVD image backup /mnt/nfs/dvd.iso: +@example +dvd:/mnt/nfs/dvd.iso +@end example + +Read title 6 and angle 1 from DVD mounted to /mnt/dvd: +@example +-title 6 -angle 1 dvd:/mnt/dvd +@end example + +Remux longest title from DVD mounted to /mnt/dvd into Matroska container, while skipping the unsupported @code{dvd_nav_packet} stream: +@example +-fflags +genpts -probesize 20M -analyzeduration 20M dvd:/mnt/dvd -map 0:0 -map -0:0 -codec copy out.mkv +@end example + @section fd File descriptor access protocol. diff --git a/libavformat/Makefile b/libavformat/Makefile index fa71ec12f7..b58ea0f4ac 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -644,6 +644,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..7a70189c69 --- /dev/null +++ b/libavformat/dvd.c @@ -0,0 +1,327 @@ +/* + * DVD (libdvdread) protocol + * Copyright (c) 2023 Stan Ionascu + * + * 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 +#include +#include + +#include "libavformat/avformat.h" +#include "libavformat/url.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" + +#define DVD_PROTO_PREFIX "dvd:" +#define DVD_SECTOR_SIZE 2048 + +typedef struct { + const AVClass *class; + + dvd_reader_t *dvd; + dvd_logger_cb dvd_logger; + + int title_nr; + int angle_nr; + dvd_file_t *dvd_title_file; + ifo_handle_t *vmg_ifo; + ifo_handle_t *vts_ifo; + pgc_t *current_pgc; + int current_cell; + + int start_sector; + int current_sector; + int end_sector; + +} DvdProtocolContext; + +static int bcd2int(int bcd) +{ + return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f)); +} + +static void dvd_logger_ff(void *opaque, dvd_logger_level_t dvd_level, const char *fmt, va_list args) +{ + int level = AV_LOG_TRACE; + if (dvd_level == DVD_LOGGER_LEVEL_ERROR) { + level = AV_LOG_ERROR; + } else if (dvd_level == DVD_LOGGER_LEVEL_WARN) { + level = AV_LOG_WARNING; + } else if (dvd_level == DVD_LOGGER_LEVEL_INFO) { + level = AV_LOG_INFO; + } else if (dvd_level == DVD_LOGGER_LEVEL_DEBUG) { + level = AV_LOG_DEBUG; + } + av_vlog(opaque, level, fmt, args); + /* add new line */ + av_log(opaque, level, "\n"); +} + +static int ff_dvd_get_title_set_length(ifo_handle_t *vts_ifo, tt_srpt_t *tt_srpt, int title_nr) +{ + /* reindex 1 => 0 */ + int vts_title_number = tt_srpt->title[title_nr].vts_ttn - 1; + int pgc_number = vts_ifo->vts_ptt_srpt->title[vts_title_number].ptt[0].pgcn; + dvd_time_t *playback_time = &vts_ifo->vts_pgcit->pgci_srp[pgc_number - 1].pgc->playback_time; + + return (bcd2int(playback_time->hour) * 3600 + + bcd2int(playback_time->minute) * 60 + + bcd2int(playback_time->second)) * 1000; +} + +static void ff_dvd_set_program_chain_info(DvdProtocolContext *ctx, int title_nr, int ptt_nr) +{ + /* reindex 1 => 0 */ + int vts_title_number = ctx->vmg_ifo->tt_srpt->title[title_nr].vts_ttn - 1; + ptt_info_t *ptt = &ctx->vts_ifo->vts_ptt_srpt->title[vts_title_number].ptt[ptt_nr]; + int pgc_id = ptt->pgcn; + int pgn = ptt->pgn; + + ctx->current_pgc = ctx->vts_ifo->vts_pgcit->pgci_srp[pgc_id - 1].pgc; + ctx->current_cell = ctx->current_pgc->program_map[pgn - 1] - 1; + + /* change angle */ + if(ctx->current_pgc->cell_playback[ctx->current_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK ) { + ctx->current_cell += ctx->angle_nr; + } + + ctx->current_sector = ctx->start_sector = ctx->current_pgc->cell_playback[ctx->current_cell].first_sector; + ctx->end_sector = ctx->current_pgc->cell_playback[ctx->current_cell].last_sector; +} + +static int ff_dvd_get_next_cell(DvdProtocolContext *ctx) +{ + int next_cell = ctx->current_cell; + + /* fast-forward until last-cell */ + if (ctx->current_pgc->cell_playback[next_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK) { + ctx->current_cell += ctx->angle_nr; + while (next_cell < ctx->current_pgc->nr_of_cells && + ctx->current_pgc->cell_playback[next_cell].block_type != BLOCK_MODE_LAST_CELL) { + next_cell++; + } + } + + /* take next cell */ + next_cell++; + if (next_cell >= ctx->current_pgc->nr_of_cells) { + return -1; + } + + return next_cell; +} + +static int dvd_url_close(URLContext *h) +{ + DvdProtocolContext *ctx = h->priv_data; + ifoClose(ctx->vts_ifo); + ifoClose(ctx->vmg_ifo); + DVDCloseFile(ctx->dvd_title_file); + DVDClose(ctx->dvd); + return 0; +} + +static int dvd_url_open(URLContext *h, const char *path, int flags) +{ + DvdProtocolContext *ctx = h->priv_data; + const char *dvd_path = path; + ifo_handle_t *vmg_ifo, *vts_ifo; + tt_srpt_t *tt_srpt; + int num_vts, num_titles; + int longest_title_nr; + int64_t longest_title_length_ms = 0; + int vts_nr; + int title_set_nr; + char disc_volume_id[32]; + + ctx->vmg_ifo = ctx->vts_ifo = NULL; + ctx->dvd_logger.pf_log = &dvd_logger_ff; + + /* strip protocol from path */ + av_strstart(path, DVD_PROTO_PREFIX, &dvd_path); + + /* open dvd folder or disc */ + if ((ctx->dvd = DVDOpen2(h, &ctx->dvd_logger, dvd_path)) == NULL) { + av_log(h, AV_LOG_ERROR, "DVDOpen(%s) failed\n", dvd_path); + return AVERROR(EIO); + } + + if (DVDUDFVolumeInfo(ctx->dvd, &disc_volume_id[0], 32, NULL, 0) == 0) { + av_log(h, AV_LOG_INFO, "opened disc-volume-id: %s\n", disc_volume_id); + } + + /* read toc */ + if ((vmg_ifo = ifoOpen(ctx->dvd, 0)) == NULL) { + av_log(h, AV_LOG_ERROR, "ifoOpen(0) failed\n"); + return AVERROR(EIO); + } + + /* read titles */ + num_vts = vmg_ifo->vts_atrt->nr_of_vtss; + num_titles = vmg_ifo->tt_srpt->nr_of_srpts; + av_log(h, AV_LOG_INFO, "there are %d usable titles\n", num_titles); + + /* get title-set info(s) */ + tt_srpt = vmg_ifo->tt_srpt; + if (ctx->title_nr >= num_titles) { + av_log(h, AV_LOG_ERROR, "invalid title id %d\n", ctx->title_nr); + return AVERROR(EINVAL); + } + + for (vts_nr = 1; vts_nr <= num_vts; vts_nr++) { + int title_nr; + for (title_nr = 0; title_nr < num_titles; title_nr++) { + int title_length_ms; + + if (tt_srpt->title[title_nr].title_set_nr != vts_nr) { + continue; + } + + /* describe the title(s) info */ + if ((vts_ifo = ifoOpen(ctx->dvd, vts_nr)) == NULL) { + av_log(h, AV_LOG_ERROR, "ifoOpen(%d) failed\n", title_nr); + return AVERROR(ENOMEM); + } + + /* skip if video title set or program chain info is missing */ + if (vts_ifo->vtsi_mat == NULL || vts_ifo->vts_pgcit == NULL) { + ifoClose(vts_ifo); + av_log(h, AV_LOG_TRACE, "skip title %d as no vts or pgc info is present\n", + title_nr); + continue; + } + + /* skip if vts_ttn is incorrect */ + if (tt_srpt->title[title_nr].vts_ttn < 1 || + tt_srpt->title[title_nr].vts_ttn > num_titles) { + ifoClose(vts_ifo); + av_log(h, AV_LOG_WARNING, "skip title %d as vts_ttn is out of bounds\n", + title_nr); + continue; + } + + title_length_ms = ff_dvd_get_title_set_length(vts_ifo, tt_srpt, title_nr); + av_log(h, AV_LOG_INFO, "title %03d : (%d:%02d:%02d) and %d chapter(s)\n", + title_nr, + (title_length_ms / 3600000), + (title_length_ms % 3600000) / 60000, + (title_length_ms % 60000) / 1000, + tt_srpt->title[title_nr].nr_of_ptts); + + if (longest_title_length_ms <= title_length_ms) { + longest_title_nr = title_nr; + longest_title_length_ms = title_length_ms; + } + + ifoClose(vts_ifo); + } + } + + if (ctx->title_nr < 0) { + ctx->title_nr = longest_title_nr; + } + + /* check if title selection is valid */ + if (tt_srpt->title[ctx->title_nr].vts_ttn < 1 || + tt_srpt->title[ctx->title_nr].vts_ttn > num_titles) { + av_log(h, AV_LOG_INFO, "selected title %d is not valid, vts_ttn is out of bounds\n", + ctx->title_nr); + ifoClose(vmg_ifo); + return AVERROR(EIO); + } + + av_log(h, AV_LOG_INFO, "selected title %d\n", ctx->title_nr); + + if (ctx->angle_nr > 0 && ctx->angle_nr >= tt_srpt->title[ctx->title_nr].nr_of_angles) { + av_log(h, AV_LOG_ERROR, "incorrect angle selected %d out of %d angle(s)\n", + ctx->angle_nr, + tt_srpt->title[ctx->title_nr].nr_of_angles); + } + + /* open request title_set info */ + title_set_nr = tt_srpt->title[ctx->title_nr].title_set_nr; + if ((vts_ifo = ifoOpen(ctx->dvd, title_set_nr)) == NULL) { + av_log(h, AV_LOG_INFO, "ifoOpen(%d) failed\n", title_set_nr); + return AVERROR(ENOMEM); + } + + /* open file */ + if ((ctx->dvd_title_file = DVDOpenFile(ctx->dvd, title_set_nr, DVD_READ_TITLE_VOBS)) == NULL) { + av_log(h, AV_LOG_ERROR, "DVDOpenFile(%d) failed\n", title_set_nr); + return AVERROR(EIO); + } + + ctx->vmg_ifo = vmg_ifo; + ctx->vts_ifo = vts_ifo; + + ff_dvd_set_program_chain_info(ctx, ctx->title_nr, 0); + + return 0; +} + +static int dvd_url_read(URLContext *h, unsigned char *buf, int size) +{ + DvdProtocolContext *ctx = h->priv_data; + ssize_t blocks_got; + int num_blocks = size / DVD_SECTOR_SIZE; + + if (ctx->current_sector >= ctx->end_sector) { + int next_cell = ff_dvd_get_next_cell(ctx); + if (next_cell < 0) { + return AVERROR_EOF; + } + + ctx->current_cell = next_cell; + ctx->start_sector = ctx->current_pgc->cell_playback[ctx->current_cell].first_sector; + ctx->end_sector = ctx->current_pgc->cell_playback[ctx->current_cell].last_sector; + ctx->current_sector = ctx->start_sector; + } + + /* read all sectors that fit into buf */ + blocks_got = DVDReadBlocks(ctx->dvd_title_file, ctx->current_sector, num_blocks, buf); + if (blocks_got <= 0) { + av_log(h, AV_LOG_ERROR, "failed to DVDReadBlocks() %d blocks at offset %d\n", + num_blocks, ctx->current_sector); + return AVERROR(EIO); + } + ctx->current_sector += blocks_got; + return blocks_got <= 0 ? AVERROR_EOF : blocks_got * DVD_SECTOR_SIZE; +} + +#define OFFSET(x) offsetof(DvdProtocolContext, x) +static const AVOption options[] = { + {"title", "", OFFSET(title_nr), AV_OPT_TYPE_INT, { .i64=-1 }, -1, 9999, AV_OPT_FLAG_DECODING_PARAM }, + {"angle", "", OFFSET(angle_nr), AV_OPT_TYPE_INT, { .i64=0 }, 0, 256, 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, +}; + +const URLProtocol ff_dvd_protocol = { + .name = "dvd", + .url_close = dvd_url_close, + .url_open = dvd_url_open, + .url_read = dvd_url_read, + .priv_data_size = sizeof(DvdProtocolContext), + .priv_data_class = &dvd_context_class, +}; diff --git a/libavformat/protocols.c b/libavformat/protocols.c index 9491dc7d00..ab0c988caf 100644 --- a/libavformat/protocols.c +++ b/libavformat/protocols.c @@ -30,6 +30,7 @@ extern const URLProtocol ff_concatf_protocol; extern const URLProtocol ff_crypto_protocol; extern const URLProtocol ff_data_protocol; extern const URLProtocol ff_fd_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;