From patchwork Tue Mar 19 17:39:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marth64 X-Patchwork-Id: 47221 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:958e:b0:1a3:31a3:7958 with SMTP id iu14csp1823059pzb; Tue, 19 Mar 2024 10:39:28 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXKbc7rUZNGU2rUy1OrMYYtrnrYXJBrvdgagU8eNoV6MbOUL0Yl3ItE23U/qNxbIhDBHUtX2GJEik+SVKrPzREWCKv2+mSPWNGXyA== X-Google-Smtp-Source: AGHT+IELjDpuy3WivijhkGXm/BzknohyIqIPVCBW93yVRqCqNG7ozxsnej/R/D9MqJb+TLZxelfF X-Received: by 2002:ac2:5511:0:b0:513:cb0a:9632 with SMTP id j17-20020ac25511000000b00513cb0a9632mr10823573lfk.50.1710869968396; Tue, 19 Mar 2024 10:39:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1710869968; cv=none; d=google.com; s=arc-20160816; b=fcnyH05Q6+Lk1i7snpC/tIzO6bb9CDzp3oNAcpX5j5zN8e4+uP6mgExuOCXXFRhIlY 834ZAhbMOCGLDvz14zkHDvqSyFjTpmNUOGJW6cha+bxPH9ZReL0rZNh9DTW5HKX4mVHQ wOMf73HPzBontQmeyUr8SMjuVoUf9iEZEDW/9GrJCgo9/Uan7VOIBONFvZEgIbAOJ7du aX/EIgJJsttYKXpsffBpfUCTQ1jlad8ULiT+0c94QQIiKDoX5IkaT9zXIL8SazgaVHsY SUwOU1qg0nsQE/F5Su7LILEtbEZkAGieFpGP++ItyfunTfARABqOQZ2IWERDMesFQAWm sydQ== 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=du2EI33J9OTHPcq4dfZO8na0IG87/9ingMXZbtkeYHU=; fh=PlWMzmI9LD2qGS7ipLrQl8z0iaQTLQLHzoGuXcBzpCg=; b=ae0iLDiTjhUB1dxu9vbZrtun36P8BcwKv12EyCD1TJJiEK3o8M/gKw/JWSdxOEXqyT m7888bYClkArrPIUZ51Zn9QN9r6VHW/NyIrvv9rDFCFsNC9M7XvSJEzUtl/E2RjEzX4P VI1aJ9a8GnZ7k0BWJVbyMFMlGG4eOl8g18cFV1SPEsvxI9HQTQumdN2cOYmgjh7DXQKY cSQvvlH7kcN3P5aHlmTgRxBTg6keOZNB6z+ZSCNJNv+GHHXo4a3IYBhSc2C5DWRMXg/G G0ARDHUx989bydFanur7GPrTuCFtxPtOt0FT5YFMfZbdW9hkuiFv452Xz8V51KnUHmDU igUw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@proxyid.net header.s=google header.b=bVlJHN88; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y15-20020a056402440f00b005682e8d001bsi5839280eda.112.2024.03.19.10.39.27; Tue, 19 Mar 2024 10:39:28 -0700 (PDT) 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=@proxyid.net header.s=google header.b=bVlJHN88; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 022D068D400; Tue, 19 Mar 2024 19:39:24 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-yb1-f228.google.com (mail-yb1-f228.google.com [209.85.219.228]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AFDDE68CC4D for ; Tue, 19 Mar 2024 19:39:17 +0200 (EET) Received: by mail-yb1-f228.google.com with SMTP id 3f1490d57ef6-dcbef31a9dbso4087765276.1 for ; Tue, 19 Mar 2024 10:39:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=proxyid.net; s=google; t=1710869956; x=1711474756; darn=ffmpeg.org; 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=HzIiaQPcyXuOeEdqYDWisRgFJBRaCwo4q29/V30Z9gY=; b=bVlJHN883WwSYJmsij0w9tFPA92mZRXKxCs4TkZvxHNztA0d67ndPnQ8d1PmFsTR/k kV5tNHtp+rhVepq0KDg655NFnUNDAKXnNqOLdTfC8ExessbOdzgxTIQ1lhViv8tABSp8 Y7IPMdiPsGQ31QP20Be/iVSAhatTFwgmvjmAxjIs8X2NL40aFQ+XghP+vrYvapnjh0CX /MZ3ivpZwbkQ+RfTS32pWGHMn3d6TX/UemghbIvxEDuswb+SpW0DI/AbU2c/bIm/HBe1 wHixIkr3AooL/xJ6EPywo9L6/ocWsq0eBQo4a87bX/g9VMzttgz2slGnJS3Fnbd6SrSc abNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710869956; x=1711474756; 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=HzIiaQPcyXuOeEdqYDWisRgFJBRaCwo4q29/V30Z9gY=; b=PIcoYETzjvI2ih1VujkJFIJSVrcgdsh6T8KvQP1FtHF0T7Vkq3XsEgY1ayPAujuSip Gr4/G2Yyb51VTvOCo7EUZxJSl9Bsn6KLCHg/Zzec9UVeFHcdNxIdsIm8AMhQonY+3gAl BMs7Y64qAOUwvSHOxKGnpElz5QB451EC8F2s3luzOr06NMMsl665FCs/OG+OFV8laQTY pySaLybFyy2IP6HM63aFB0EUSWxsrigvtM1eJ6W7BMjiEbgIvrKwE5manCo8AANX8+KR IKVKIvGhWvmi26w4ovJdjTw5Df+37OSmexp5m7qftDpIN0dpIyS30vFsgxtewpX+kPdC WwIg== X-Gm-Message-State: AOJu0YyZn+PtmcVvSJB6QgJ2p7XJAiZUcllo4Hn1PG5uvUi4gL9mDXvd 3Dv65XEpbLcyfJdLYoHiVi8j8/Ry+oi+nqPxLWx5oEQUxdh6J0ogTJZJNnAOB4ob3PrsZpAlNIk rmggxo9tCDe9+jVs5GunbAQwWsjezYYfmROpzzYHn X-Received: by 2002:a05:6902:248e:b0:dc7:42b8:2561 with SMTP id ds14-20020a056902248e00b00dc742b82561mr2837361ybb.34.1710869956082; Tue, 19 Mar 2024 10:39:16 -0700 (PDT) Received: from wsx-cc1-001.. (c-76-141-249-38.hsd1.il.comcast.net. [76.141.249.38]) by smtp-relay.gmail.com with ESMTPS id g14-20020a25db0e000000b00dcde1710318sm466988ybf.11.2024.03.19.10.39.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 19 Mar 2024 10:39:16 -0700 (PDT) X-Relaying-Domain: proxyid.net From: Marth64 To: ffmpeg-devel@ffmpeg.org Date: Tue, 19 Mar 2024 12:39:10 -0500 Message-Id: <20240319173913.2754690-1-marth64@proxyid.net> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v5 1/4] avformat/rcwtdec: add RCWT Closed Captions demuxer 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: Marth64 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 2g8qBDRS8IJ/ Signed-off-by: Marth64 --- Changelog | 2 +- doc/demuxers.texi | 29 ++++++++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/rcwtdec.c | 148 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 libavformat/rcwtdec.c diff --git a/Changelog b/Changelog index e3ca52430c..be871f75cd 100644 --- a/Changelog +++ b/Changelog @@ -19,7 +19,7 @@ version : - lavu/eval: introduce randomi() function in expressions - VVC decoder - fsync filter -- Raw Captions with Time (RCWT) closed caption muxer +- Raw Captions with Time (RCWT) closed caption muxer and demuxer - ffmpeg CLI -bsf option may now be used for input as well as output - ffmpeg CLI options may now be used as -/opt , which is equivalent to -opt > diff --git a/doc/demuxers.texi b/doc/demuxers.texi index b70f3a38d7..26d4ba18a1 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -1038,6 +1038,35 @@ the command: ffplay -f rawvideo -pixel_format rgb24 -video_size 320x240 -framerate 10 input.raw @end example +@anchor{rcwtdec} +@section rcwt + +RCWT (Raw Captions With Time) is a format native to ccextractor, a commonly +used open source tool for processing 608/708 Closed Captions (CC) sources. +It can be used to archive the original, raw CC bitstream and to produce +a source file for later CC processing or conversion. This demuxer can process +RCWT sources created by ccextractor or FFmpeg. For more information on the format, +see @ref{rcwtenc,,,ffmpeg-formats}. + +This demuxer implements the specification as of March 2024, which has +been stable and unchanged since April 2014. + +@subsection Examples + +@itemize +@item +Render Closed Captions to ASSA (using FFmpeg's CC decoder): +@example +ffmpeg -i CC.rcwt.bin CC.ass +@end example + +@item +Convert an RCWT backup to SCC: +@example +ffmpeg -i CC.rcwt.bin -c:s copy CC.scc +@end example +@end itemize + @section sbg SBaGen script demuxer. diff --git a/libavformat/Makefile b/libavformat/Makefile index 94a949f555..a6de720d8c 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -493,6 +493,7 @@ OBJS-$(CONFIG_QOA_DEMUXER) += qoadec.o OBJS-$(CONFIG_R3D_DEMUXER) += r3d.o OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_RCWT_DEMUXER) += rcwtdec.o subtitles.o OBJS-$(CONFIG_RCWT_MUXER) += rcwtenc.o subtitles.o OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index e15d0fa6d7..3140018f8d 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -391,6 +391,7 @@ extern const FFInputFormat ff_qoa_demuxer; extern const FFInputFormat ff_r3d_demuxer; extern const FFInputFormat ff_rawvideo_demuxer; extern const FFOutputFormat ff_rawvideo_muxer; +extern const FFInputFormat ff_rcwt_demuxer; extern const FFOutputFormat ff_rcwt_muxer; extern const FFInputFormat ff_realtext_demuxer; extern const FFInputFormat ff_redspark_demuxer; diff --git a/libavformat/rcwtdec.c b/libavformat/rcwtdec.c new file mode 100644 index 0000000000..1baa571231 --- /dev/null +++ b/libavformat/rcwtdec.c @@ -0,0 +1,148 @@ +/* + * RCWT (Raw Captions With Time) demuxer + * + * 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 + */ + +/* + * RCWT (Raw Captions With Time) is a format native to ccextractor, a commonly + * used open source tool for processing 608/708 Closed Captions (CC) sources. + * + * This demuxer implements the specification as of March 2024, which has + * been stable and unchanged since April 2014. + * + * A free specification of RCWT can be found here: + * @url{https://github.com/CCExtractor/ccextractor/blob/master/docs/BINARY_FILE_FORMAT.TXT} + */ + +#include "avformat.h" +#include "demux.h" +#include "internal.h" +#include "subtitles.h" +#include "libavutil/intreadwrite.h" + +#define RCWT_CLUSTER_MAX_BLOCKS 65535 +#define RCWT_BLOCK_SIZE 3 +#define RCWT_HEADER_SIZE 11 + +typedef struct RCWTContext { + FFDemuxSubtitlesQueue q; +} RCWTContext; + +static int rcwt_read_header(AVFormatContext *avf) +{ + RCWTContext *rcwt = avf->priv_data; + + AVPacket *sub = NULL; + AVStream *st; + uint8_t header[RCWT_HEADER_SIZE] = {0}; + int nb_bytes = 0; + + /* validate the header */ + nb_bytes = avio_read(avf->pb, header, RCWT_HEADER_SIZE); + if (nb_bytes != RCWT_HEADER_SIZE) { + av_log(avf, AV_LOG_ERROR, "Header does not have the expected size " + "(expected=%d actual=%d)\n", + RCWT_HEADER_SIZE, nb_bytes); + return AVERROR_INVALIDDATA; + } + + if (AV_RB16(header + 6) != 0x0001) { + av_log(avf, AV_LOG_ERROR, "RCWT format version is not compatible " + "(only version 0.001 is known)\n"); + return AVERROR_INVALIDDATA; + } + + av_log(avf, AV_LOG_DEBUG, "RCWT writer application: %02X version: %02x\n", + header[3], header[5]); + + /* setup AVStream */ + st = avformat_new_stream(avf, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codecpar->codec_id = AV_CODEC_ID_EIA_608; + + avpriv_set_pts_info(st, 64, 1, 1000); + + /* demux */ + while (!avio_feof(avf->pb)) { + int64_t cluster_pts = AV_NOPTS_VALUE; + int cluster_nb_blocks = 0; + int cluster_size = 0; + uint8_t *cluster_buf; + + cluster_pts = avio_rl64(avf->pb); + cluster_nb_blocks = avio_rl16(avf->pb); + if (cluster_nb_blocks == 0) + continue; + + cluster_size = cluster_nb_blocks * RCWT_BLOCK_SIZE; + cluster_buf = av_malloc(cluster_size); + if (!cluster_buf) + return AVERROR(ENOMEM); + + nb_bytes = avio_read(avf->pb, cluster_buf, cluster_size); + if (nb_bytes < 0) + return nb_bytes; + + if (nb_bytes != cluster_size) { + av_freep(&cluster_buf); + av_log(avf, AV_LOG_ERROR, "Cluster does not have the expected size " + "(expected=%d actual=%d pos=%ld)\n", + cluster_size, nb_bytes, avio_tell(avf->pb)); + return AVERROR_INVALIDDATA; + } + + sub = ff_subtitles_queue_insert(&rcwt->q, cluster_buf, cluster_size, 0); + if (!sub) { + av_freep(&cluster_buf); + return AVERROR(ENOMEM); + } + + sub->pos = avio_tell(avf->pb); + sub->pts = cluster_pts; + + av_freep(&cluster_buf); + cluster_buf = NULL; + } + + ff_subtitles_queue_finalize(avf, &rcwt->q); + + return 0; +} + +static int rcwt_probe(const AVProbeData *p) +{ + return p->buf_size > RCWT_HEADER_SIZE && + AV_RB16(p->buf) == 0xCCCC && AV_RB8(p->buf + 2) == 0xED ? 50 : 0; +} + +const FFInputFormat ff_rcwt_demuxer = { + .p.name = "rcwt", + .p.long_name = NULL_IF_CONFIG_SMALL("RCWT (Raw Captions With Time)"), + .p.extensions = "bin", + .p.flags = AVFMT_TS_DISCONT, + .priv_data_size = sizeof(RCWTContext), + .flags_internal = FF_FMT_INIT_CLEANUP, + .read_probe = rcwt_probe, + .read_header = rcwt_read_header, + .read_packet = ff_subtitles_read_packet, + .read_seek2 = ff_subtitles_read_seek, + .read_close = ff_subtitles_read_close +};