From patchwork Wed Jul 4 18:35:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Baptiste Coudurier X-Patchwork-Id: 9613 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:104:0:0:0:0:0 with SMTP id c4-v6csp1079503jad; Wed, 4 Jul 2018 11:36:52 -0700 (PDT) X-Google-Smtp-Source: AAOMgpeluPPe7cyFQ5PlUaneoU58ZpH+xgbTkDtwBPJOSS70xnZUSuW6RlRrXYxAL2pmi9Uxf1da X-Received: by 2002:adf:992d:: with SMTP id x42-v6mr2334272wrb.81.1530729411990; Wed, 04 Jul 2018 11:36:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530729411; cv=none; d=google.com; s=arc-20160816; b=AEKVmdvkG/srU4giOh/iyXrVQj6MwGMNTFXbJ3ZEgRi/lptZNGYf9rNE2zl2Ebt2BP IGsurotpaZxFAJTsX/89+jLsakZoRpRdfezi912yjuKSVpXrLBN4ZAzNilXJuRgmyiy8 L/91DBLsEPNqWGenTPS0lLzlA9Ys8iCMjViHsXtfw+9/yRO6lOYt5Zpr+h3mvCpcnZOM OpEu0sHA6NLfSxFjvskbgKCiefG1520OTz+J41HuN1lb+BkWfrcAFy7dQBc9obrBzBSo W2A1uR1AVqo08/xwKT6EmLDh1JooAV55Lds8eLe+9EW2jN9leC21B3ODgHNXMU3iCyP6 BqwQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from:dkim-signature :delivered-to:arc-authentication-results; bh=qPAYBNz8ca3VWCimYWNGLRc0gQIr0q9Du65NhRHuwz8=; b=0Q9ZqPO03QaHGKowA5xHfev1V8UMQU66xFDiSJApTNieQ/zhWMgJrofhTuXkxxIycV yntotUJUpGD4cklm95SNA5hhomeergaeCMlrwjtiaX+WkfiNWvtAHU8TW8H1EjiVtHkj ZBJyaN6YHeJc8bhdrML9Ksgiyoyk0m6m3/M/PFbfp1cB8kWhHJvdj8eIdeYjK4kmXnl0 5/kDB2VVs5jy/vEn3TS/eiqnuOtgujxb8XiRRzuUnHSGR39bBgJePCTu+ag5wnhhBR5+ lwMoUrJLbPb5uBltw+p52WSu/PFMsRc4LSuGBR0hV0ts1VGSzdyevuuw4IA0i0TRyVy7 OWKg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=gP5Liqk9; 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 q21-v6si3969222wra.110.2018.07.04.11.36.51; Wed, 04 Jul 2018 11:36:51 -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=@gmail.com header.s=20161025 header.b=gP5Liqk9; 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 2F37568AB1C; Wed, 4 Jul 2018 21:35:32 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf0-f179.google.com (mail-pf0-f179.google.com [209.85.192.179]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id EB78668AB4D for ; Wed, 4 Jul 2018 21:35:23 +0300 (EEST) Received: by mail-pf0-f179.google.com with SMTP id s21-v6so3256373pfm.6 for ; Wed, 04 Jul 2018 11:35:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=LsVAPnkWOuHoxfhp+S/meupA7UBRVQFxGccNJRqS13U=; b=gP5Liqk9R+w+e9BnrmStKGBUWUGUaL8ZPunvMiRrouGUKtbz6FITowSleYQM/98Mwj IVWSz6d51j8ADjnJrjvMPhUcjkFplTNRJDBcoclrBCFHTYn5E/FhTMGHB4VObjmnMiKT VnQei+IWajLct37WHig+vG3zohuaDmer6wQODEbrnTKPjWBXfEfiLgbf4mZVZBtN+Oec B4X2+rTw5V9S4eRfbRqswUFeZl3fcnimD4AshQZLMOutQ5nQeFBpWMW+4wNi7aOP4+1s o+qgh+dvXzHuwsW5i9pGZoQ3QezOK2bD6BIbUcS2ZxnpZh+qKgWYb2MJaYGguHT/rrD8 9gvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=LsVAPnkWOuHoxfhp+S/meupA7UBRVQFxGccNJRqS13U=; b=e3EnEFSnZxaoCQpffp3oNgJWbl7MD/wr2KUlLn44cuZ2umkbDuwBtBGiuo6qOvNcHt aW2dGZesE4AgKB8L3CLyQwchpZBBDYNUx1qGE26W6KD8Wtcth4ou2X8OZot6ZwCNQkxt jGvFbjcYsmDWtgj/BdlNQboUj2RLLV6RPvUFr7bBHtjXtQhfYp3iu/7te9qNCkv5ZCtF +n6foBvqnrZUnloycfnMfXXYNL9hQj21r56hqMevKWXZbkdgNQXnRr2zfb8sAFxViy2p El3QF5CMgjj6/P0gh3AK2FSmamx+z2MLULzzo8s7qSWZ7xTFs+WkOzeiEoUzC04VVSlZ q+Sg== X-Gm-Message-State: APt69E1PMFqqoE1IQiASflvYABD5T+GoxPFgwBGR/0fjhZ3rc9cLdxGh +dzA5HAw51rkLqkBfm0LdOw7Hg== X-Received: by 2002:a65:63cd:: with SMTP id n13-v6mr2821340pgv.185.1530729326815; Wed, 04 Jul 2018 11:35:26 -0700 (PDT) Received: from localhost.localdomain (cpe-104-173-251-254.socal.res.rr.com. [104.173.251.254]) by smtp.gmail.com with ESMTPSA id v126-v6sm8591239pgv.39.2018.07.04.11.35.25 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 04 Jul 2018 11:35:26 -0700 (PDT) From: Baptiste Coudurier To: ffmpeg-devel@ffmpeg.org Date: Wed, 4 Jul 2018 11:35:14 -0700 Message-Id: <20180704183514.71654-12-baptiste.coudurier@gmail.com> X-Mailer: git-send-email 2.17.0 (Apple Git-106) Subject: [FFmpeg-devel] [PATCH 12/12] lavf/sccenc: write proper timecode, use proper stream timebase and split lines properly X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" --- libavformat/sccenc.c | 90 ++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/libavformat/sccenc.c b/libavformat/sccenc.c index f3cf3d599c..dd6a4b36d8 100644 --- a/libavformat/sccenc.c +++ b/libavformat/sccenc.c @@ -23,16 +23,22 @@ #include "internal.h" #include "libavutil/log.h" #include "libavutil/intreadwrite.h" +#include "libavutil/timecode.h" +#include "libavutil/opt.h" typedef struct SCCContext { - int prev_h, prev_m, prev_s, prev_f; + AVClass *av_class; + int64_t expected_pts; int inside; int n; + AVTimecode tc; + char *timecode_start; } SCCContext; static int scc_write_header(AVFormatContext *avf) { SCCContext *scc = avf->priv_data; + AVStream *st = avf->streams[0]; if (avf->nb_streams != 1 || avf->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { @@ -46,11 +52,19 @@ static int scc_write_header(AVFormatContext *avf) avcodec_get_name(avf->streams[0]->codecpar->codec_id)); return AVERROR(EINVAL); } - avpriv_set_pts_info(avf->streams[0], 64, 1, 1000); - avio_printf(avf->pb, "Scenarist_SCC V1.0\n"); - scc->prev_h = scc->prev_m = scc->prev_s = scc->prev_f = -1; - scc->inside = 0; + if (!(st->time_base.den == 30000 && st->time_base.num == 1001)) { + av_log(avf, AV_LOG_ERROR, "Unsupported frame rate: %d/%d\n", + st->time_base.den, st->time_base.num); + return AVERROR(EINVAL); + } + + if (av_timecode_init_from_string(&scc->tc, (AVRational){30000, 1001}, scc->timecode_start, avf) < 0) + return -1; + + avio_printf(avf->pb, "Scenarist_SCC V1.0"); + + scc->expected_pts = -2; return 0; } @@ -59,58 +73,60 @@ static int scc_write_packet(AVFormatContext *avf, AVPacket *pkt) { SCCContext *scc = avf->priv_data; int64_t pts = pkt->pts; - int i, h, m, s, f; + char tcbuf[AV_TIMECODE_STR_SIZE]; + int i; if (pts == AV_NOPTS_VALUE) { - av_log(avf, AV_LOG_WARNING, - "Insufficient timestamps.\n"); - return 0; + av_log(avf, AV_LOG_WARNING, "Insufficient timestamps\n"); + return -1; } - h = (int)(pts / (3600000)); - m = (int)(pts / (60000)) % 60; - s = (int)(pts / 1000) % 60; - f = (int)(pts % 1000) / 33; + if (!av_timecode_make_string(&scc->tc, tcbuf, pts)) + return -1; - for (i = 0; i < pkt->size; i+=3) { - if (pkt->data[i] == 0xfc && ((pkt->data[i + 1] != 0x80 || pkt->data[i + 2] != 0x80))) - break; - } - if (i >= pkt->size) - return 0; - - if (!scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) { - avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f); - scc->inside = 1; - } for (i = 0; i < pkt->size; i+=3) { if (i + 3 > pkt->size) break; if (pkt->data[i] != 0xfc || (pkt->data[i + 1] == 0x80 && pkt->data[i + 2] == 0x80)) continue; - if (!scc->inside) { - avio_printf(avf->pb, "\n%02d:%02d:%02d:%02d\t", h, m, s, f); - scc->inside = 1; + + if (pts > scc->expected_pts+1 || AV_RB16(&pkt->data[i + 1]) == 0x942c) { + avio_printf(avf->pb, "\r\n\n%s\t", tcbuf); + scc->expected_pts = pts; + scc->n = 0; } + if (scc->n > 0) avio_printf(avf->pb, " "); + avio_printf(avf->pb, "%02x%02x", pkt->data[i + 1], pkt->data[i + 2]); + scc->expected_pts += 1; scc->n++; } - if (scc->inside && (scc->prev_h != h || scc->prev_m != m || scc->prev_s != s || scc->prev_f != f)) { - avio_printf(avf->pb, "\n"); - scc->n = 0; - scc->inside = 0; - } - scc->prev_h = h; - scc->prev_m = m; - scc->prev_s = s; - scc->prev_f = f; return 0; } +static int scc_write_trailer(AVFormatContext *avf) +{ + avio_printf(avf->pb, "\n\n"); + return 0; +} + +static const AVOption scc_options[] = { + { "timecode_start", "Set the SCC file initial timecode", + offsetof(SCCContext, timecode_start), AV_OPT_TYPE_STRING, {.str = "00:00:00;00"}, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { NULL }, +}; + +static const AVClass scc_muxer_class = { + .class_name = "scc muxer", + .item_name = av_default_item_name, + .option = scc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVOutputFormat ff_scc_muxer = { .name = "scc", .long_name = NULL_IF_CONFIG_SMALL("Scenarist Closed Captions"), @@ -118,6 +134,8 @@ AVOutputFormat ff_scc_muxer = { .priv_data_size = sizeof(SCCContext), .write_header = scc_write_header, .write_packet = scc_write_packet, + .write_trailer = scc_write_trailer, .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, .subtitle_codec = AV_CODEC_ID_EIA_608, + .priv_class = &scc_muxer_class, };