From patchwork Thu Aug 31 08:36:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jeyapal, Karthick" X-Patchwork-Id: 4919 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.15.201 with SMTP id 70csp1373914jao; Thu, 31 Aug 2017 01:37:31 -0700 (PDT) X-Google-Smtp-Source: ADKCNb7YDMDlgcR5HcsvQGZb1phErgp1KAOqVuHMQ9ouXlqjTy5iqUKVbWgYHyQoaejRsKV6SQdw X-Received: by 10.28.87.140 with SMTP id l134mr2859649wmb.3.1504168651547; Thu, 31 Aug 2017 01:37:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1504168651; cv=none; d=google.com; s=arc-20160816; b=gKwUkzswRkBGJ4Rd9ZpDgFqor3rJpj4Wbg3N3xDABxaxiuCLxAe09HGyPlJFpv6rOx c3UXJ8lVswjNXclhcPkuDvCdjfGGYUXLfzVhndpAY7bYZXdezi8JQFWrcqNcOOhb2BTh Oi/H+uNL0QyB1JL2lXw6qVR4xKl6bFSElRAAtPPzmO7kV6ZbiWQbXyAymxWis6lJ1MKh 3GpWICKbdVSuV0O1OMUmq2H9lInaTWoq0wXDO75iW3c3H7C9O+AHCMgJWUQ63swpprOa x56ahG1JUzwdeoLVc8Z1vQP1HYkndtiug+hkGKA5O+hndztTE1jQBocMTKOg3DERrmSb Rmsg== 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:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:feedback-id:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to :arc-authentication-results; bh=/PQ5EOLnKs9Np8MKAjKyqZyd0n8dnanufWfIuazsBEk=; b=e2U9nWGgTjF82eOmkrP/mj9aSd7r96wLhgcnUcBNHkLt2TSjNYbsJQRMzpoQAkqC+J hOYwLiRCUBrZTC+9ChCzEOJQdAHX/Xbv1fwW7o0RpMbv6PteUrtfEMCkxloty7Ext+ra UgOGqiUiQEhd8707xbQyG3U8kqKZQAjAL0Mh/kmewGhOIGXqtDA71Xt+IjrvDzcvh0Sa Jpm+axUMG7Dk4IkUR6p96dNNNknMz7SAa0ZJbaRVqTAPIJAX58k9VEM1QTtCgxdqJOyi Gy48s+jsfwDsbW73pHtq+jd8fQ6Jgurh3Ll2rITnF/MJb6gKxP4ilVOM6mHzpd1flf8L qwHw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@smtpservice.net header.s=m78bu0.a1-4.dyn header.b=c7qmKfsN; 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=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id e4si3197436wmc.102.2017.08.31.01.37.28; Thu, 31 Aug 2017 01:37:31 -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=@smtpservice.net header.s=m78bu0.a1-4.dyn header.b=c7qmKfsN; 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=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id BE8AB68A1DB; Thu, 31 Aug 2017 11:37:09 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from a1i844.smtp2go.com (a1i844.smtp2go.com [43.228.187.76]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id D1A6568A09E for ; Thu, 31 Aug 2017 11:37:08 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=smtpservice.net; s=m78bu0.a1-4.dyn; x=1504190230; h=Feedback-ID: X-Smtpcorp-Track:Message-Id:Date:Subject:To:From:Reply-To:Sender: List-Unsubscribe; bh=f0eQbOBsiQTrL8Zy34RM8Z6DkayTp+gAiTQTC55pins=; b=c7qmKfsN VGycQp28lzVabp8h/Q9cIy1QyXVy2h/jMadbmOPF3pj7iH85tmXhUSdhrCUyOZ9h+Rl3ouD54DBm7 47r3Ry7UXEJHN+SjcNuu53lsO+unviY+6Dtri8PfCtmGLmC8AiceUyVemnPtJrVG8GNfK3R/WWysp 68qWe5vXaJRMuDovijThCRsQL3TrQM2vvyndF1t4klUWFCEuOgyf7AerSsadPrNH/T6Y/piIaYNbn sjGRMMZgLZ4bMlNYnR1xyEvyRRV0Uv49QO9/rCcUW8ZSXWVHhdLJ3iD2l/OHZU7x47BeS+S381HRb r33wxYw23rWzJyjYWlRe9WbhiQ==; From: kjeyapal@akamai.com To: ffmpeg-devel@ffmpeg.org Date: Thu, 31 Aug 2017 14:06:49 +0530 Message-Id: <1504168610-2427-3-git-send-email-kjeyapal@akamai.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1504168610-2427-1-git-send-email-kjeyapal@akamai.com> References: <1504168610-2427-1-git-send-email-kjeyapal@akamai.com> X-Smtpcorp-Track: 1dnKym4ba6DOLU.p4qzrYGRU Feedback-ID: 337386m:337386asVRLGB:337386sF8G7-sOY0:SMTPCORP X-Report-Abuse: Please forward a copy of this message, including all headers, to Subject: [FFmpeg-devel] [PATCH 3/4] avdevice/decklink_dec: Added Closed caption decode from VANC 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 Cc: Karthick J MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Karthick J Signed-off-by: Karthick J --- libavcodec/avcodec.h | 7 ++ libavdevice/decklink_dec.cpp | 167 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 165 insertions(+), 9 deletions(-) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 513236a..d4a458f 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1600,6 +1600,13 @@ enum AVPacketSideDataType { AV_PKT_DATA_CONTENT_LIGHT_LEVEL, /** + * ATSC A53 Part 4 Closed Captions. This metadata should be associated with + * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data. + * The number of bytes of CC data is AVPacketSideData.size. + */ + AV_PKT_DATA_A53_CC, + + /** * The number of side data elements (in fact a bit more than it). * This is not part of the public API/ABI in the sense that it may * change when new side data types are added. diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 0b88fc8..23e1b35 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -106,6 +106,13 @@ static void get_vanc_lines(int bmd_field_dominance, int height, int *field0_vanc } } +static inline unsigned parity (unsigned x) +{ + for (unsigned i = 4 * sizeof (x); i > 0; i /= 2) + x ^= x >> i; + return x & 1; +} + /* The 10-bit VANC data is packed in V210, we only need the luma component. */ static void extract_luma_from_v210(uint16_t *dst, const uint8_t *src, int width) { @@ -249,6 +256,152 @@ static uint8_t* teletext_data_unit_from_vanc_data(uint16_t *py, uint8_t *tgt, in return tgt; } +uint8_t *vanc_to_cc(AVFormatContext *avctx, uint16_t *buf, size_t words, + unsigned &cc_count) +{ + size_t len = (buf[5] & 0xff) + 6 + 1; + + /* CDP follows */ + uint16_t *cdp = &buf[6]; + if (cdp[0] != 0x96 || cdp[1] != 0x69) { + av_log(avctx, AV_LOG_WARNING, "Invalid CDP header 0x%.2x 0x%.2x", cdp[0], cdp[1]); + return NULL; + } + + len -= 7; // remove VANC header and checksum + + if (cdp[2] != len) { + av_log(avctx, AV_LOG_WARNING, "CDP len %d != %zu", cdp[2], len); + return NULL; + } + + uint8_t cdp_sum = 0; + for (size_t i = 0; i < len - 1; i++) + cdp_sum += cdp[i]; + cdp_sum = cdp_sum ? 256 - cdp_sum : 0; + if (cdp[len - 1] != cdp_sum) { + av_log(avctx, AV_LOG_WARNING, "CDP checksum invalid 0x%.4x != 0x%.4x", cdp_sum, cdp[len-1]); + return NULL; + } + + uint8_t rate = cdp[3]; + if (!(rate & 0x0f)) { + av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)", rate); + return NULL; + } + rate >>= 4; + if (rate > 8) { + av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)", rate); + return NULL; + } + + if (!(cdp[4] & 0x43)) /* ccdata_present | caption_service_active | reserved */ { + av_log(avctx, AV_LOG_WARNING, "CDP flags invalid (0x%.2x)", cdp[4]); + return NULL; + } + + uint16_t hdr = (cdp[5] << 8) | cdp[6]; + if (cdp[7] != 0x72) /* ccdata_id */ { + av_log(avctx, AV_LOG_WARNING, "Invalid ccdata_id 0x%.2x", cdp[7]); + return NULL; + } + + cc_count = cdp[8]; + if (!(cc_count & 0xe0)) { + av_log(avctx, AV_LOG_WARNING, "Invalid cc_count 0x%.2x", cc_count); + return NULL; + } + + cc_count &= 0x1f; + if ((len - 13) < cc_count * 3) { + av_log(avctx, AV_LOG_WARNING, "Invalid cc_count %d (> %zu)", cc_count * 3, len - 13); + return NULL; + } + + if (cdp[len - 4] != 0x74) /* footer id */ { + av_log(avctx, AV_LOG_WARNING, "Invalid footer id 0x%.2x", cdp[len-4]); + return NULL; + } + + uint16_t ftr = (cdp[len - 3] << 8) | cdp[len - 2]; + if (ftr != hdr) { + av_log(avctx, AV_LOG_WARNING, "Header 0x%.4x != Footer 0x%.4x", hdr, ftr); + return NULL; + } + + uint8_t *cc = (uint8_t *)av_malloc(cc_count * 3); + + for (size_t i = 0; i < cc_count; i++) { + cc[3*i + 0] = cdp[9 + 3*i+0] /* & 3 */; + cc[3*i + 1] = cdp[9 + 3*i+1]; + cc[3*i + 2] = cdp[9 + 3*i+2]; + } + + cc_count *= 3; + return cc; +} + +uint8_t *get_metadata(AVFormatContext *avctx, uint16_t *buf, size_t width, + uint8_t *tgt, size_t tgt_size, AVPacket *pkt) +{ + decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + uint16_t did = buf[3] & 0xFF; // data id + uint16_t sdid = buf[4] & 0xFF; // secondary data id + + static const uint8_t vanc_header[6] = { 0x00, 0x00, 0xff, 0x03, 0xff, 0x03 }; + if (memcmp(vanc_header, buf, 3 * 2)) { + /* Does not start with the VANC header */ + return tgt; + } + + size_t len = (buf[5] & 0xff) + 6 + 1; + if (len > width) { + av_log(avctx, AV_LOG_WARNING, "Data Count (%zu) > line length (%zu)\n", + len, width); + return tgt; + } + + uint16_t vanc_sum = 0; + for (size_t i = 3; i < len - 1; i++) { + uint16_t v = buf[i]; + int np = v >> 8; + int p = parity(v & 0xff); + if ((!!p ^ !!(v & 0x100)) || (np != 1 && np != 2)) { + av_log(avctx, AV_LOG_WARNING, "Parity incorrect for word %zu\n", i); + return tgt; + } + vanc_sum += v; + vanc_sum &= 0x1ff; + buf[i] &= 0xff; + } + + vanc_sum |= ((~vanc_sum & 0x100) << 1); + if (buf[len - 1] != vanc_sum) { + av_log(avctx, AV_LOG_WARNING, + "VANC checksum incorrect: 0x%.4x != 0x%.4x\n", vanc_sum, + buf[len - 1]); + return tgt; + } + + if (did == 0x43 && (sdid == 0x02 || sdid == 0x03) && cctx->teletext_lines && + width == 1920 && tgt_size >= 1920) { + tgt = teletext_data_unit_from_vanc_data(buf, tgt, cctx->teletext_lines); + } else if (did == 0x61 && sdid == 0x01) { + unsigned int data_len; + uint8_t *data = vanc_to_cc(avctx, buf, width, data_len); + if (data) { + uint8_t *pkt_cc = av_packet_new_side_data(pkt, AV_PKT_DATA_A53_CC, data_len); + memcpy(pkt_cc, data, data_len); + av_free(data); + } + } else { + av_log(avctx, AV_LOG_WARNING, "Unknown meta data DID = 0x%.2x SDID = 0x%.2x\n", + did, sdid); + } + + return tgt; +} + static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) { struct decklink_cctx *ctx = (struct decklink_cctx *)avctx->priv_data; @@ -537,7 +690,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( videoFrame->GetHeight(); //fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts); - if (!no_video && ctx->teletext_lines) { + if (!no_video) { IDeckLinkVideoFrameAncillary *vanc; AVPacket txt_pkt; uint8_t txt_buf0[3531]; // 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + 1920 bytes OP47 decode buffer @@ -550,7 +703,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( txt_buf[0] = 0x10; // data_identifier - EBU_data txt_buf++; #if CONFIG_LIBZVBI - if (ctx->bmd_mode == bmdModePAL && (vanc_format == bmdFormat8BitYUV || vanc_format == bmdFormat10BitYUV)) { + if (ctx->bmd_mode == bmdModePAL && ctx->teletext_lines && + (vanc_format == bmdFormat8BitYUV || vanc_format == bmdFormat10BitYUV)) { av_assert0(videoFrame->GetWidth() == 720); for (i = 6; i < 336; i++, line_mask <<= 1) { uint8_t *buf; @@ -575,13 +729,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) { uint16_t luma_vanc[4096]; // Allocated for highest width (4K) extract_luma_from_v210(luma_vanc, buf, videoFrame->GetWidth()); - if (videoFrame->GetWidth() == 1920) { - txt_buf = teletext_data_unit_from_vanc_data(luma_vanc, txt_buf, ctx->teletext_lines); - if (txt_buf - txt_buf0 > 1611) { // ensure we still have at least 1920 bytes free in the buffer - av_log(avctx, AV_LOG_ERROR, "Too many OP47 teletext packets.\n"); - break; - } - } + txt_buf = get_metadata(avctx, luma_vanc, videoFrame->GetWidth(), + txt_buf, 3531 - (txt_buf - txt_buf0), &pkt); } if (i == field0_vanc_end) i = field1_vanc_start;