/* * Blackmagic DeckLink input * Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl * Copyright (c) 2014 Rafaël Carré * Copyright (c) 2017 Akamai Technologies, Inc. * * 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 internal.h first to avoid conflict between winsock.h (used by * DeckLink headers) and winsock2.h (used by libavformat) in MSVC++ builds */extern "C" {#include "libavformat/internal.h"} #include <DeckLinkAPI.h> extern "C" {#include "config.h"#include "libavformat/avformat.h"#include "libavutil/avassert.h"#include "libavutil/avutil.h"#include "libavutil/common.h"#include "libavutil/imgutils.h"#include "libavutil/time.h"#include "libavutil/mathematics.h"#include "libavutil/reverse.h"#if CONFIG_LIBZVBI#include <libzvbi.h>#endif} #include "decklink_common.h"#include "decklink_dec.h" #define MAX_WIDTH_VANC 1920 typedef struct VANCLineNumber { BMDDisplayMode mode; int vanc_start; int field0_vanc_end; int field1_vanc_start; int vanc_end;} VANCLineNumber; /* These VANC line numbers need not be very accurate. In any case * GetBufferForVerticalBlankingLine() will return an error when invalid * ancillary line number was requested. We just need to make sure that the * entire VANC region is covered, while making sure we don't decode VANC of * another source during switching*/static VANCLineNumber vanc_line_numbers[] = { /* SD Modes */ {bmdModeNTSC, 11, 19, 274, 282}, {bmdModeNTSC2398, 11, 19, 274, 282}, {bmdModePAL, 7, 22, 320, 335}, {bmdModeNTSCp, 11, -1, -1, 39}, {bmdModePALp, 7, -1, -1, 45}, /* HD 1080 Modes */ {bmdModeHD1080p2398, 8, -1, -1, 42}, {bmdModeHD1080p24, 8, -1, -1, 42}, {bmdModeHD1080p25, 8, -1, -1, 42}, {bmdModeHD1080p2997, 8, -1, -1, 42}, {bmdModeHD1080p30, 8, -1, -1, 42}, {bmdModeHD1080i50, 8, 20, 570, 585}, {bmdModeHD1080i5994, 8, 20, 570, 585}, {bmdModeHD1080i6000, 8, 20, 570, 585}, {bmdModeHD1080p50, 8, -1, -1, 42}, {bmdModeHD1080p5994, 8, -1, -1, 42}, {bmdModeHD1080p6000, 8, -1, -1, 42}, /* HD 720 Modes */ {bmdModeHD720p50, 8, -1, -1, 26}, {bmdModeHD720p5994, 8, -1, -1, 26}, {bmdModeHD720p60, 8, -1, -1, 26}, /* For all other modes, for which we don't support VANC */ {bmdModeUnknown, 0, -1, -1, -1}}; static int get_vanc_line_idx(BMDDisplayMode mode){ unsigned int i; for (i = 0; i < FF_ARRAY_ELEMS(vanc_line_numbers); i++) { if (mode == vanc_line_numbers[i].mode) return i; } /* Return the VANC idx for Unknown mode */ return i - 1;} static inline void clear_parity_bits(uint16_t *buf, int len) { int i; for (i = 0; i < len; i++) buf[i] &= 0xff;} static int check_vanc_parity_checksum(uint16_t *buf, int len, uint16_t checksum) { int i; uint16_t vanc_sum = 0; for (i = 3; i < len - 1; i++) { uint16_t v = buf[i]; int np = v >> 8; int p = av_parity(v & 0xff); if ((!!p ^ !!(v & 0x100)) || (np != 1 && np != 2)) { // Parity check failed return -1; } vanc_sum += v; } vanc_sum &= 0x1ff; vanc_sum |= ((~vanc_sum & 0x100) << 1); if (checksum != vanc_sum) { // Checksum verification failed return -1; } return 0;} /* 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){ int i; for (i = 0; i < width / 3; i += 3) { *dst++ = (src[1] >> 2) + ((src[2] & 15) << 6); *dst++ = src[4] + ((src[5] & 3) << 8); *dst++ = (src[6] >> 4) + ((src[7] & 63) << 4); src += 8; }} static uint8_t calc_parity_and_line_offset(int line){ uint8_t ret = (line < 313) << 5; if (line >= 7 && line <= 22) ret += line; if (line >= 320 && line <= 335) ret += (line - 313); return ret;} static void fill_data_unit_head(int line, uint8_t *tgt){ tgt[0] = 0x02; // data_unit_id tgt[1] = 0x2c; // data_unit_length tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset tgt[3] = 0xe4; // framing code} #if CONFIG_LIBZVBIstatic uint8_t* teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt, vbi_pixfmt fmt){ vbi_bit_slicer slicer; vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, fmt); if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE) return tgt; fill_data_unit_head(line, tgt); return tgt + 46;} static uint8_t* teletext_data_unit_from_vbi_data_10bit(int line, uint8_t *src, uint8_t *tgt){ uint8_t y[720]; uint8_t *py = y; uint8_t *pend = y + 720; /* The 10-bit VBI data is packed in V210, but libzvbi only supports 8-bit, * so we extract the 8 MSBs of the luma component, that is enough for * teletext bit slicing. */ while (py < pend) { *py++ = (src[1] >> 4) + ((src[2] & 15) << 4); *py++ = (src[4] >> 2) + ((src[5] & 3 ) << 6); *py++ = (src[6] >> 6) + ((src[7] & 63) << 2); src += 8; } return teletext_data_unit_from_vbi_data(line, y, tgt, VBI_PIXFMT_YUV420);}#endif static uint8_t* teletext_data_unit_from_op47_vbi_packet(int line, uint16_t *py, uint8_t *tgt){ int i; if (py[0] != 0x255 || py[1] != 0x255 || py[2] != 0x227) return tgt; fill_data_unit_head(line, tgt); py += 3; tgt += 4; for (i = 0; i < 42; i++) *tgt++ = ff_reverse[py[i] & 255]; return tgt;} static int linemask_matches(int line, int64_t mask){ int shift = -1; if (line >= 6 && line <= 22) shift = line - 6; if (line >= 318 && line <= 335) shift = line - 318 + 17; return shift >= 0 && ((1ULL << shift) & mask);} static uint8_t* teletext_data_unit_from_op47_data(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines){ if (py < pend - 9) { if (py[0] == 0x151 && py[1] == 0x115 && py[3] == 0x102) { // identifier, identifier, format code for WST teletext uint16_t *descriptors = py + 4; int i; py += 9; for (i = 0; i < 5 && py < pend - 45; i++, py += 45) { int line = (descriptors[i] & 31) + (!(descriptors[i] & 128)) * 313; if (line && linemask_matches(line, wanted_lines)) tgt = teletext_data_unit_from_op47_vbi_packet(line, py, tgt); } } } return tgt;} static uint8_t* teletext_data_unit_from_ancillary_packet(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines, int allow_multipacket){ uint16_t did = py[0]; // data id uint16_t sdid = py[1]; // secondary data id uint16_t dc = py[2] & 255; // data count py += 3; pend = FFMIN(pend, py + dc); if (did == 0x143 && sdid == 0x102) { // subtitle distribution packet tgt = teletext_data_unit_from_op47_data(py, pend, tgt, wanted_lines); } else if (allow_multipacket && did == 0x143 && sdid == 0x203) { // VANC multipacket py += 2; // priority, line/field while (py < pend - 3) { tgt = teletext_data_unit_from_ancillary_packet(py, pend, tgt, wanted_lines, 0); py += 4 + (py[2] & 255); // ndid, nsdid, ndc, line/field } } return tgt;} uint8_t *vanc_to_cc(AVFormatContext *avctx, uint16_t *buf, size_t words, unsigned &cc_count){ size_t i, len = (buf[5] & 0xff) + 6 + 1; uint8_t cdp_sum, rate; uint16_t hdr, ftr; uint8_t *cc; uint16_t *cdp = &buf[6]; // CDP follows if (cdp[0] != 0x96 || cdp[1] != 0x69) { av_log(avctx, AV_LOG_WARNING, "Invalid CDP header 0x%.2x 0x%.2x\n", 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\n", cdp[2], len); return NULL; } cdp_sum = 0; for (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\n", cdp_sum, cdp[len-1]); return NULL; } rate = cdp[3]; if (!(rate & 0x0f)) { av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)\n", rate); return NULL; } rate >>= 4; if (rate > 8) { av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)\n", rate); return NULL; } if (!(cdp[4] & 0x43)) /* ccdata_present | caption_service_active | reserved */ { av_log(avctx, AV_LOG_WARNING, "CDP flags invalid (0x%.2x)\n", cdp[4]); return NULL; } hdr = (cdp[5] << 8) | cdp[6]; if (cdp[7] != 0x72) /* ccdata_id */ { av_log(avctx, AV_LOG_WARNING, "Invalid ccdata_id 0x%.2x\n", cdp[7]); return NULL; } cc_count = cdp[8]; if (!(cc_count & 0xe0)) { av_log(avctx, AV_LOG_WARNING, "Invalid cc_count 0x%.2x\n", cc_count); return NULL; } cc_count &= 0x1f; if ((len - 13) < cc_count * 3) { av_log(avctx, AV_LOG_WARNING, "Invalid cc_count %d (> %zu)\n", 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\n", cdp[len-4]); return NULL; } ftr = (cdp[len - 3] << 8) | cdp[len - 2]; if (ftr != hdr) { av_log(avctx, AV_LOG_WARNING, "Header 0x%.4x != Footer 0x%.4x\n", hdr, ftr); return NULL; } cc = (uint8_t *)av_malloc(cc_count * 3); if (cc == NULL) { av_log(avctx, AV_LOG_WARNING, "CC - av_malloc failed for cc_count = %d\n", cc_count); return NULL; } 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 *max_buf = buf + width; while (buf < max_buf - 6) { int len; uint16_t did = buf[3] & 0xFF; // data id uint16_t sdid = buf[4] & 0xFF; // secondary data id /* Check for VANC header */ if (buf[0] != 0 || buf[1] != 0x3ff || buf[2] != 0x3ff) { return tgt; } len = (buf[5] & 0xff) + 6 + 1; if (len > max_buf - buf) { av_log(avctx, AV_LOG_WARNING, "Data Count (%d) > data left (%zu)\n", len, max_buf - buf); return tgt; } if (did == 0x43 && (sdid == 0x02 || sdid == 0x03) && cctx->teletext_lines && width == 1920 && tgt_size >= 1920) { if (check_vanc_parity_checksum(buf, len, buf[len - 1]) < 0) { av_log(avctx, AV_LOG_WARNING, "VANC parity or checksum incorrect\n"); goto skip_packet; } tgt = teletext_data_unit_from_ancillary_packet(buf + 3, buf + len, tgt, cctx->teletext_lines, 0); } else if (did == 0x61 && sdid == 0x01) { unsigned int data_len; uint8_t *data; if (check_vanc_parity_checksum(buf, len, buf[len - 1]) < 0) { av_log(avctx, AV_LOG_WARNING, "VANC parity or checksum incorrect\n"); goto skip_packet; } clear_parity_bits(buf, len); data = vanc_to_cc(avctx, buf, width, data_len); if (data) { if (av_packet_add_side_data(pkt, AV_PKT_DATA_A53_CC, data, data_len) < 0) av_free(data); } } else { av_log(avctx, AV_LOG_DEBUG, "Unknown meta data DID = 0x%.2x SDID = 0x%.2x\n", did, sdid); }skip_packet: buf += len; } return tgt;} static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q){ struct decklink_cctx *ctx = (struct decklink_cctx *)avctx->priv_data; memset(q, 0, sizeof(AVPacketQueue)); pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cond, NULL); q->avctx = avctx; q->max_q_size = ctx->queue_size;} static void avpacket_queue_flush(AVPacketQueue *q){ AVPacketList *pkt, *pkt1; pthread_mutex_lock(&q->mutex); for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_packet_unref(&pkt->pkt); av_freep(&pkt); } q->last_pkt = NULL; q->first_pkt = NULL; q->nb_packets = 0; q->size = 0; pthread_mutex_unlock(&q->mutex);} static void avpacket_queue_end(AVPacketQueue *q){ avpacket_queue_flush(q); pthread_mutex_destroy(&q->mutex); pthread_cond_destroy(&q->cond);} static unsigned long long avpacket_queue_size(AVPacketQueue *q){ unsigned long long size; pthread_mutex_lock(&q->mutex); size = q->size; pthread_mutex_unlock(&q->mutex); return size;} static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt){ AVPacketList *pkt1; // Drop Packet if queue size is > maximum queue size if (avpacket_queue_size(q) > (uint64_t)q->max_q_size) { av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); return -1; } /* duplicate the packet */ if (av_dup_packet(pkt) < 0) { return -1; } pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList)); if (!pkt1) { return -1; } pkt1->pkt = *pkt; pkt1->next = NULL; pthread_mutex_lock(&q->mutex); if (!q->last_pkt) { q->first_pkt = pkt1; } else { q->last_pkt->next = pkt1; } q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size + sizeof(*pkt1); pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->mutex); return 0;} static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block){ AVPacketList *pkt1; int ret; pthread_mutex_lock(&q->mutex); for (;; ) { pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) {Lines 1–500 of 1064. Load more
/* * Blackmagic DeckLink input * Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl * Copyright (c) 2014 Rafaël Carré * Copyright (c) 2017 Akamai Technologies, Inc. * * 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 internal.h first to avoid conflict between winsock.h (used by * DeckLink headers) and winsock2.h (used by libavformat) in MSVC++ builds */ extern "C" { #include "libavformat/internal.h" } #include <DeckLinkAPI.h> extern "C" { #include "config.h" #include "libavformat/avformat.h" #include "libavutil/avassert.h" #include "libavutil/avutil.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/time.h" #include "libavutil/mathematics.h" #include "libavutil/reverse.h" #if CONFIG_LIBZVBI #include <libzvbi.h> #endif } #include "decklink_common.h" #include "decklink_dec.h" #define MAX_WIDTH_VANC 1920 typedef struct VANCLineNumber { BMDDisplayMode mode; int vanc_start; int field0_vanc_end; int field1_vanc_start; int vanc_end; } VANCLineNumber; /* These VANC line numbers need not be very accurate. In any case * GetBufferForVerticalBlankingLine() will return an error when invalid * ancillary line number was requested. We just need to make sure that the * entire VANC region is covered, while making sure we don't decode VANC of * another source during switching*/ static VANCLineNumber vanc_line_numbers[] = { /* SD Modes */ {bmdModeNTSC, 11, 19, 274, 282}, {bmdModeNTSC2398, 11, 19, 274, 282}, {bmdModePAL, 7, 22, 320, 335}, {bmdModeNTSCp, 11, -1, -1, 39}, {bmdModePALp, 7, -1, -1, 45}, /* HD 1080 Modes */ {bmdModeHD1080p2398, 8, -1, -1, 42}, {bmdModeHD1080p24, 8, -1, -1, 42}, {bmdModeHD1080p25, 8, -1, -1, 42}, {bmdModeHD1080p2997, 8, -1, -1, 42}, {bmdModeHD1080p30, 8, -1, -1, 42}, {bmdModeHD1080i50, 8, 20, 570, 585}, {bmdModeHD1080i5994, 8, 20, 570, 585}, {bmdModeHD1080i6000, 8, 20, 570, 585}, {bmdModeHD1080p50, 8, -1, -1, 42}, {bmdModeHD1080p5994, 8, -1, -1, 42}, {bmdModeHD1080p6000, 8, -1, -1, 42}, /* HD 720 Modes */ {bmdModeHD720p50, 8, -1, -1, 26}, {bmdModeHD720p5994, 8, -1, -1, 26}, {bmdModeHD720p60, 8, -1, -1, 26}, /* For all other modes, for which we don't support VANC */ {bmdModeUnknown, 0, -1, -1, -1} }; static int get_vanc_line_idx(BMDDisplayMode mode) { unsigned int i; for (i = 0; i < FF_ARRAY_ELEMS(vanc_line_numbers); i++) { if (mode == vanc_line_numbers[i].mode) return i; } /* Return the VANC idx for Unknown mode */ return i - 1; } static inline void clear_parity_bits(uint16_t *buf, int len) { int i; for (i = 0; i < len; i++) buf[i] &= 0xff; } static int check_vanc_parity_checksum(uint16_t *buf, int len, uint16_t checksum) { int i; uint16_t vanc_sum = 0; for (i = 3; i < len - 1; i++) { uint16_t v = buf[i]; int np = v >> 8; int p = av_parity(v & 0xff); if ((!!p ^ !!(v & 0x100)) || (np != 1 && np != 2)) { // Parity check failed return -1; } vanc_sum += v; } vanc_sum &= 0x1ff; vanc_sum |= ((~vanc_sum & 0x100) << 1); if (checksum != vanc_sum) { // Checksum verification failed return -1; } return 0; } /* 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) { int i; for (i = 0; i < width / 3; i += 3) { *dst++ = (src[1] >> 2) + ((src[2] & 15) << 6); *dst++ = src[4] + ((src[5] & 3) << 8); *dst++ = (src[6] >> 4) + ((src[7] & 63) << 4); src += 8; } } static uint8_t calc_parity_and_line_offset(int line) { uint8_t ret = (line < 313) << 5; if (line >= 7 && line <= 22) ret += line; if (line >= 320 && line <= 335) ret += (line - 313); return ret; } static void fill_data_unit_head(int line, uint8_t *tgt) { tgt[0] = 0x02; // data_unit_id tgt[1] = 0x2c; // data_unit_length tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset tgt[3] = 0xe4; // framing code } #if CONFIG_LIBZVBI static uint8_t* teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt, vbi_pixfmt fmt) { vbi_bit_slicer slicer; vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, fmt); if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE) return tgt; fill_data_unit_head(line, tgt); return tgt + 46; } static uint8_t* teletext_data_unit_from_vbi_data_10bit(int line, uint8_t *src, uint8_t *tgt) { uint8_t y[720]; uint8_t *py = y; uint8_t *pend = y + 720; /* The 10-bit VBI data is packed in V210, but libzvbi only supports 8-bit, * so we extract the 8 MSBs of the luma component, that is enough for * teletext bit slicing. */ while (py < pend) { *py++ = (src[1] >> 4) + ((src[2] & 15) << 4); *py++ = (src[4] >> 2) + ((src[5] & 3 ) << 6); *py++ = (src[6] >> 6) + ((src[7] & 63) << 2); src += 8; } return teletext_data_unit_from_vbi_data(line, y, tgt, VBI_PIXFMT_YUV420); } #endif static uint8_t* teletext_data_unit_from_op47_vbi_packet(int line, uint16_t *py, uint8_t *tgt) { int i; if (py[0] != 0x255 || py[1] != 0x255 || py[2] != 0x227) return tgt; fill_data_unit_head(line, tgt); py += 3; tgt += 4; for (i = 0; i < 42; i++) *tgt++ = ff_reverse[py[i] & 255]; return tgt; } static int linemask_matches(int line, int64_t mask) { int shift = -1; if (line >= 6 && line <= 22) shift = line - 6; if (line >= 318 && line <= 335) shift = line - 318 + 17; return shift >= 0 && ((1ULL << shift) & mask); } static uint8_t* teletext_data_unit_from_op47_data(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines) { if (py < pend - 9) { if (py[0] == 0x151 && py[1] == 0x115 && py[3] == 0x102) { // identifier, identifier, format code for WST teletext uint16_t *descriptors = py + 4; int i; py += 9; for (i = 0; i < 5 && py < pend - 45; i++, py += 45) { int line = (descriptors[i] & 31) + (!(descriptors[i] & 128)) * 313; if (line && linemask_matches(line, wanted_lines)) tgt = teletext_data_unit_from_op47_vbi_packet(line, py, tgt); } } } return tgt; } static uint8_t* teletext_data_unit_from_ancillary_packet(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines, int allow_multipacket) { uint16_t did = py[0]; // data id uint16_t sdid = py[1]; // secondary data id uint16_t dc = py[2] & 255; // data count py += 3; pend = FFMIN(pend, py + dc); if (did == 0x143 && sdid == 0x102) { // subtitle distribution packet tgt = teletext_data_unit_from_op47_data(py, pend, tgt, wanted_lines); } else if (allow_multipacket && did == 0x143 && sdid == 0x203) { // VANC multipacket py += 2; // priority, line/field while (py < pend - 3) { tgt = teletext_data_unit_from_ancillary_packet(py, pend, tgt, wanted_lines, 0); py += 4 + (py[2] & 255); // ndid, nsdid, ndc, line/field } } return tgt; } uint8_t *vanc_to_cc(AVFormatContext *avctx, uint16_t *buf, size_t words, unsigned &cc_count) { size_t i, len = (buf[5] & 0xff) + 6 + 1; uint8_t cdp_sum, rate; uint16_t hdr, ftr; uint8_t *cc; uint16_t *cdp = &buf[6]; // CDP follows if (cdp[0] != 0x96 || cdp[1] != 0x69) { av_log(avctx, AV_LOG_WARNING, "Invalid CDP header 0x%.2x 0x%.2x\n", 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\n", cdp[2], len); return NULL; } cdp_sum = 0; for (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\n", cdp_sum, cdp[len-1]); return NULL; } rate = cdp[3]; if (!(rate & 0x0f)) { av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)\n", rate); return NULL; } rate >>= 4; if (rate > 8) { av_log(avctx, AV_LOG_WARNING, "CDP frame rate invalid (0x%.2x)\n", rate); return NULL; } if (!(cdp[4] & 0x43)) /* ccdata_present | caption_service_active | reserved */ { av_log(avctx, AV_LOG_WARNING, "CDP flags invalid (0x%.2x)\n", cdp[4]); return NULL; } hdr = (cdp[5] << 8) | cdp[6]; if (cdp[7] != 0x72) /* ccdata_id */ { av_log(avctx, AV_LOG_WARNING, "Invalid ccdata_id 0x%.2x\n", cdp[7]); return NULL; } cc_count = cdp[8]; if (!(cc_count & 0xe0)) { av_log(avctx, AV_LOG_WARNING, "Invalid cc_count 0x%.2x\n", cc_count); return NULL; } cc_count &= 0x1f; if ((len - 13) < cc_count * 3) { av_log(avctx, AV_LOG_WARNING, "Invalid cc_count %d (> %zu)\n", 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\n", cdp[len-4]); return NULL; } ftr = (cdp[len - 3] << 8) | cdp[len - 2]; if (ftr != hdr) { av_log(avctx, AV_LOG_WARNING, "Header 0x%.4x != Footer 0x%.4x\n", hdr, ftr); return NULL; } cc = (uint8_t *)av_malloc(cc_count * 3); if (cc == NULL) { av_log(avctx, AV_LOG_WARNING, "CC - av_malloc failed for cc_count = %d\n", cc_count); return NULL; } 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 *max_buf = buf + width; while (buf < max_buf - 6) { int len; uint16_t did = buf[3] & 0xFF; // data id uint16_t sdid = buf[4] & 0xFF; // secondary data id /* Check for VANC header */ if (buf[0] != 0 || buf[1] != 0x3ff || buf[2] != 0x3ff) { return tgt; } len = (buf[5] & 0xff) + 6 + 1; if (len > max_buf - buf) { av_log(avctx, AV_LOG_WARNING, "Data Count (%d) > data left (%zu)\n", len, max_buf - buf); return tgt; } if (did == 0x43 && (sdid == 0x02 || sdid == 0x03) && cctx->teletext_lines && width == 1920 && tgt_size >= 1920) { if (check_vanc_parity_checksum(buf, len, buf[len - 1]) < 0) { av_log(avctx, AV_LOG_WARNING, "VANC parity or checksum incorrect\n"); goto skip_packet; } tgt = teletext_data_unit_from_ancillary_packet(buf + 3, buf + len, tgt, cctx->teletext_lines, 0); } else if (did == 0x61 && sdid == 0x01) { unsigned int data_len; uint8_t *data; if (check_vanc_parity_checksum(buf, len, buf[len - 1]) < 0) { av_log(avctx, AV_LOG_WARNING, "VANC parity or checksum incorrect\n"); goto skip_packet; } clear_parity_bits(buf, len); data = vanc_to_cc(avctx, buf, width, data_len); if (data) { if (av_packet_add_side_data(pkt, AV_PKT_DATA_A53_CC, data, data_len) < 0) av_free(data); } } else { av_log(avctx, AV_LOG_DEBUG, "Unknown meta data DID = 0x%.2x SDID = 0x%.2x\n", did, sdid); } skip_packet: buf += len; } return tgt; } static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) { struct decklink_cctx *ctx = (struct decklink_cctx *)avctx->priv_data; memset(q, 0, sizeof(AVPacketQueue)); pthread_mutex_init(&q->mutex, NULL); pthread_cond_init(&q->cond, NULL); q->avctx = avctx; q->max_q_size = ctx->queue_size; } static void avpacket_queue_flush(AVPacketQueue *q) { AVPacketList *pkt, *pkt1; pthread_mutex_lock(&q->mutex); for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_packet_unref(&pkt->pkt); av_freep(&pkt); } q->last_pkt = NULL; q->first_pkt = NULL; q->nb_packets = 0; q->size = 0; pthread_mutex_unlock(&q->mutex); } static void avpacket_queue_end(AVPacketQueue *q) { avpacket_queue_flush(q); pthread_mutex_destroy(&q->mutex); pthread_cond_destroy(&q->cond); } static unsigned long long avpacket_queue_size(AVPacketQueue *q) { unsigned long long size; pthread_mutex_lock(&q->mutex); size = q->size; pthread_mutex_unlock(&q->mutex); return size; } static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; // Drop Packet if queue size is > maximum queue size if (avpacket_queue_size(q) > (uint64_t)q->max_q_size) { av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); return -1; } /* duplicate the packet */ if (av_dup_packet(pkt) < 0) { return -1; } pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList)); if (!pkt1) { return -1; } pkt1->pkt = *pkt; pkt1->next = NULL; pthread_mutex_lock(&q->mutex); if (!q->last_pkt) { q->first_pkt = pkt1; } else { q->last_pkt->next = pkt1; } q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size + sizeof(*pkt1); pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->mutex); return 0; } static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret; pthread_mutex_lock(&q->mutex); for (;; ) { pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) {