/** 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 <atomic>#include <vector>using std::atomic;/* 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 "libavcodec/packet_internal.h"#include "libavformat/avformat.h"#include "libavutil/avassert.h"#include "libavutil/avutil.h"#include "libavutil/common.h"#include "libavutil/internal.h"#include "libavutil/imgutils.h"#include "libavutil/intreadwrite.h"#include "libavutil/time.h"#include "libavutil/timecode.h"#include "libavutil/mathematics.h"#include "libavutil/reverse.h"#include "avdevice.h"#if CONFIG_LIBZVBI#include <libzvbi.h>#endif}#include "decklink_common.h"#include "decklink_dec.h"#define MAX_WIDTH_VANC 1920const BMDDisplayMode AUTODETECT_DEFAULT_MODE = bmdModeNTSC;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}};class decklink_allocator : public IDeckLinkMemoryAllocator{public:decklink_allocator(): _refs(1) { }virtual ~decklink_allocator() { }// IDeckLinkMemoryAllocator methodsvirtual HRESULT STDMETHODCALLTYPE AllocateBuffer(unsigned int bufferSize, void* *allocatedBuffer){void *buf = av_malloc(bufferSize + AV_INPUT_BUFFER_PADDING_SIZE);if (!buf)return E_OUTOFMEMORY;*allocatedBuffer = buf;return S_OK;}virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(void* buffer){av_free(buffer);return S_OK;}virtual HRESULT STDMETHODCALLTYPE Commit() { return S_OK; }virtual HRESULT STDMETHODCALLTYPE Decommit() { return S_OK; }// IUnknown methodsvirtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }virtual ULONG STDMETHODCALLTYPE AddRef(void) { return ++_refs; }virtual ULONG STDMETHODCALLTYPE Release(void){int ret = --_refs;if (!ret)delete this;return ret;}private:std::atomic<int> _refs;};extern "C" {static void decklink_object_free(void *opaque, uint8_t *data){IUnknown *obj = (class IUnknown *)opaque;obj->Release();}}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 failedreturn -1;}vanc_sum += v;}vanc_sum &= 0x1ff;vanc_sum |= ((~vanc_sum & 0x100) << 1);if (checksum != vanc_sum) {// Checksum verification failedreturn -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++) {*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 void unpack_v210(uint16_t *dst, const uint8_t *src, int width){int i;for (i = 0; i < width * 2 / 3; i++) {*dst++ = src[0] + ((src[1] & 3) << 8);*dst++ = (src[1] >> 2) + ((src[2] & 15) << 6);*dst++ = (src[2] >> 4) + ((src[3] & 63) << 4);src += 4;}}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_idtgt[1] = 0x2c; // data_unit_lengthtgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offsettgt[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);}#endifstatic 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 teletextuint16_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 iduint16_t sdid = py[1]; // secondary data iduint16_t dc = py[2] & 255; // data countpy += 3;pend = FFMIN(pend, py + dc);if (did == 0x143 && sdid == 0x102) { // subtitle distribution packettgt = teletext_data_unit_from_op47_data(py, pend, tgt, wanted_lines);} else if (allow_multipacket && did == 0x143 && sdid == 0x203) { // VANC multipacketpy += 2; // priority, line/fieldwhile (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;}static 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 followsif (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 checksumif (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;}static 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 iduint16_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, 1);} 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 handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideoInputFrame *videoFrame, int64_t pts){const uint8_t KLV_DID = 0x44;const uint8_t KLV_IN_VANC_SDID = 0x04;struct KLVPacket{uint16_t sequence_counter;std::vector<uint8_t> data;};size_t total_size = 0;std::vector<std::vector<KLVPacket>> klv_packets(256);IDeckLinkVideoFrameAncillaryPackets *packets = nullptr;if (videoFrame->QueryInterface(IID_IDeckLinkVideoFrameAncillaryPackets, (void**)&packets) != S_OK)return;IDeckLinkAncillaryPacketIterator *it = nullptr;if (packets->GetPacketIterator(&it) != S_OK) {packets->Release();return;}IDeckLinkAncillaryPacket *packet = nullptr;while (it->Next(&packet) == S_OK) {uint8_t *data = nullptr;uint32_t size = 0;if (packet->GetDID() == KLV_DID && packet->GetSDID() == KLV_IN_VANC_SDID) {av_log(avctx, AV_LOG_DEBUG, "Found KLV VANC packet on line: %d\n", packet->GetLineNumber());if (packet->GetBytes(bmdAncillaryPacketFormatUInt8, (const void**) &data, &size) == S_OK) {// MID and PSCif (size > 3) {uint8_t mid = data[0];uint16_t psc = data[1] << 8 | data[2];av_log(avctx, AV_LOG_DEBUG, "KLV with MID: %d and PSC: %d\n", mid, psc);auto& list = klv_packets[mid];uint16_t expected_psc = list.size() + 1;if (psc == expected_psc) {uint32_t data_len = size - 3;total_size += data_len;KLVPacket packet{ psc };packet.data.resize(data_len);memcpy(packet.data.data(), data + 3, data_len);list.push_back(std::move(packet));} else {av_log(avctx, AV_LOG_WARNING, "Out of order PSC: %d for MID: %d\n", psc, mid);if (!list.empty()) {for (auto& klv : list)total_size -= klv.data.size();list.clear();}}}}}packet->Release();}it->Release();packets->Release();if (total_size > 0) {std::vector<uint8_t> klv;klv.reserve(total_size);for (size_t i = 0; i < klv_packets.size(); ++i) {auto& list = klv_packets[i];if (list.empty())continue;av_log(avctx, AV_LOG_DEBUG, "Joining MID: %d\n", (int)i);for (auto& packet : list)klv.insert(klv.end(), packet.data.begin(), packet.data.end());}AVPacket klv_packet = { 0 };klv_packet.pts = pts;klv_packet.dts = pts;klv_packet.flags |= AV_PKT_FLAG_KEY;klv_packet.stream_index = ctx->klv_st->index;klv_packet.data = klv.data();klv_packet.size = klv.size();if (ff_decklink_packet_queue_put(&ctx->queue, &klv_packet) < 0) {++ctx->dropped;}}}class decklink_input_callback : public IDeckLinkInputCallback{public:explicit decklink_input_callback(AVFormatContext *_avctx);~decklink_input_callback();virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }virtual ULONG STDMETHODCALLTYPE AddRef(void);virtual ULONG STDMETHODCALLTYPE Release(void);virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags);virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);private:std::atomic<int> _refs;AVFormatContext *avctx;decklink_ctx *ctx;int no_video;int64_t initial_video_pts;int64_t initial_audio_pts;};decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : _refs(1){avctx = _avctx;decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;ctx = (struct decklink_ctx *)cctx->ctx;no_video = 0;initial_audio_pts = initial_video_pts = AV_NOPTS_VALUE;}decklink_input_callback::~decklink_input_callback(){}ULONG decklink_input_callback::AddRef(void){return ++_refs;}ULONG decklink_input_callback::Release(void){int ret = --_refs;if (!ret)delete this;return ret;}static int64_t get_pkt_pts(IDeckLinkVideoInputFrame *videoFrame,IDeckLinkAudioInputPacket *audioFrame,int64_t wallclock,int64_t abs_wallclock,DecklinkPtsSource pts_src,AVRational time_base, int64_t *initial_pts,int copyts){int64_t pts = AV_NOPTS_VALUE;BMDTimeValue bmd_pts;BMDTimeValue bmd_duration;HRESULT res = E_INVALIDARG;switch (pts_src) {case PTS_SRC_AUDIO:if (audioFrame)res = audioFrame->GetPacketTime(&bmd_pts, time_base.den);break;case PTS_SRC_VIDEO:if (videoFrame)res = videoFrame->GetStreamTime(&bmd_pts, &bmd_duration, time_base.den);break;case PTS_SRC_REFERENCE:if (videoFrame)res = videoFrame->GetHardwareReferenceTimestamp(time_base.den, &bmd_pts, &bmd_duration);break;case PTS_SRC_WALLCLOCK:/* fall through */case PTS_SRC_ABS_WALLCLOCK:{/* MSVC does not support compound literals like AV_TIME_BASE_Q* in C++ code (compiler error C4576) */AVRational timebase;timebase.num = 1;timebase.den = AV_TIME_BASE;if (pts_src == PTS_SRC_WALLCLOCK)pts = av_rescale_q(wallclock, timebase, time_base);elsepts = av_rescale_q(abs_wallclock, timebase, time_base);break;}}if (res == S_OK)pts = bmd_pts / time_base.num;if (!copyts) {if (pts != AV_NOPTS_VALUE && *initial_pts == AV_NOPTS_VALUE)*initial_pts = pts;if (*initial_pts != AV_NOPTS_VALUE)pts -= *initial_pts;}return pts;}static int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, AVRational frame_rate, BMDTimecodeFormat tc_format, IDeckLinkVideoInputFrame *videoFrame){IDeckLinkTimecode *timecode;int ret = AVERROR(ENOENT);#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000int hfr = (tc_format == bmdTimecodeRP188HighFrameRate);#elseint hfr = 0;#endifif (videoFrame->GetTimecode(tc_format, &timecode) == S_OK) {uint8_t hh, mm, ss, ff;if (timecode->GetComponents(&hh, &mm, &ss, &ff) == S_OK) {int flags = (timecode->GetFlags() & bmdTimecodeIsDropFrame) ? AV_TIMECODE_FLAG_DROPFRAME : 0;if (!hfr && av_cmp_q(frame_rate, av_make_q(30, 1)) == 1)ff = ff << 1 | !!(timecode->GetFlags() & bmdTimecodeFieldMark);ret = av_timecode_init_from_components(tc, frame_rate, flags, hh, mm, ss, ff, avctx);}timecode->Release();}return ret;}static int get_frame_timecode(AVFormatContext *avctx, decklink_ctx *ctx, AVTimecode *tc, IDeckLinkVideoInputFrame *videoFrame){AVRational frame_rate = ctx->video_st->r_frame_rate;int ret;/* 50/60 fps content has alternating VITC1 and VITC2 timecode (see SMPTE ST* 12-2, section 7), so the native ordering of RP188Any (HFR, VITC1, LTC,* VITC2) would not work because LTC might not contain the field flag.* Therefore we query the types manually. */if (ctx->tc_format == bmdTimecodeRP188Any && av_cmp_q(frame_rate, av_make_q(30, 1)) == 1) {#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame);if (ret == AVERROR(ENOENT))#endifret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC1, videoFrame);if (ret == AVERROR(ENOENT))ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC2, videoFrame);if (ret == AVERROR(ENOENT))ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188LTC, videoFrame);} else {ret = get_bmd_timecode(avctx, tc, frame_rate, ctx->tc_format, videoFrame);}return ret;}HRESULT decklink_input_callback::VideoInputFrameArrived(IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame){void *frameBytes;void *audioFrameBytes;BMDTimeValue frameTime;BMDTimeValue frameDuration;int64_t wallclock = 0, abs_wallclock = 0;struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;if (ctx->autodetect) {if (videoFrame && !(videoFrame->GetFlags() & bmdFrameHasNoInputSource) &&ctx->bmd_mode == bmdModeUnknown){ctx->bmd_mode = AUTODETECT_DEFAULT_MODE;}return S_OK;}// Drop the frames till system's timestamp aligns with the configured value.if (0 == ctx->frameCount && cctx->timestamp_align) {AVRational remainder = av_make_q(av_gettime() % cctx->timestamp_align, 1000000);AVRational frame_duration = av_inv_q(ctx->video_st->r_frame_rate);if (av_cmp_q(remainder, frame_duration) > 0) {++ctx->dropped;return S_OK;}}ctx->frameCount++;if (ctx->audio_pts_source == PTS_SRC_WALLCLOCK || ctx->video_pts_source == PTS_SRC_WALLCLOCK)wallclock = av_gettime_relative();if (ctx->audio_pts_source == PTS_SRC_ABS_WALLCLOCK || ctx->video_pts_source == PTS_SRC_ABS_WALLCLOCK)abs_wallclock = av_gettime();// Handle Video Frameif (videoFrame) {AVPacket pkt = { 0 };if (ctx->frameCount % 25 == 0) {unsigned long long qsize = ff_decklink_packet_queue_size(&ctx->queue);av_log(avctx, AV_LOG_DEBUG,"Frame received (#%lu) - Valid (%liB) - QSize %fMB\n",ctx->frameCount,videoFrame->GetRowBytes() * videoFrame->GetHeight(),(double)qsize / 1024 / 1024);}videoFrame->GetBytes(&frameBytes);videoFrame->GetStreamTime(&frameTime, &frameDuration,ctx->video_st->time_base.den);if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {if (ctx->draw_bars && videoFrame->GetPixelFormat() == bmdFormat8BitYUV) {unsigned bars[8] = {0xEA80EA80, 0xD292D210, 0xA910A9A5, 0x90229035,0x6ADD6ACA, 0x51EF515A, 0x286D28EF, 0x10801080 };int width = videoFrame->GetWidth();int height = videoFrame->GetHeight();unsigned *p = (unsigned *)frameBytes;for (int y = 0; y < height; y++) {for (int x = 0; x < width; x += 2)*p++ = bars[(x * 8) / width];}}if (!no_video) {av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - No input signal detected ""- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);}no_video = 1;} else {if (no_video) {av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - Input returned ""- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);}no_video = 0;// Handle Timecode (if requested)if (ctx->tc_format) {AVTimecode tcr;if (get_frame_timecode(avctx, ctx, &tcr, videoFrame) >= 0) {char tcstr[AV_TIMECODE_STR_SIZE];const char *tc = av_timecode_make_string(&tcr, tcstr, 0);if (tc) {AVDictionary* metadata_dict = NULL;uint8_t* packed_metadata;if (av_cmp_q(ctx->video_st->r_frame_rate, av_make_q(60, 1)) < 1) {uint32_t tc_data = av_timecode_get_smpte_from_framenum(&tcr, 0);int size = sizeof(uint32_t) * 4;uint32_t *sd = (uint32_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size);if (sd) {*sd = 1; // one TC*(sd + 1) = tc_data; // TC}}if (av_dict_set(&metadata_dict, "timecode", tc, 0) >= 0) {size_t metadata_len;packed_metadata = av_packet_pack_dictionary(metadata_dict, &metadata_len);av_dict_free(&metadata_dict);if (packed_metadata) {if (av_packet_add_side_data(&pkt, AV_PKT_DATA_STRINGS_METADATA, packed_metadata, metadata_len) < 0)av_freep(&packed_metadata);else if (!ctx->tc_seen)ctx->tc_seen = ctx->frameCount;}}}} else {av_log(avctx, AV_LOG_DEBUG, "Unable to find timecode.\n");}}}if (ctx->tc_format && cctx->wait_for_tc && !ctx->tc_seen) {av_log(avctx, AV_LOG_WARNING, "No TC detected yet. wait_for_tc set. Dropping. \n");av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - ""- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);return S_OK;}pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts);pkt.dts = pkt.pts;pkt.duration = frameDuration;//To be made sure it still appliespkt.flags |= AV_PKT_FLAG_KEY;pkt.stream_index = ctx->video_st->index;pkt.data = (uint8_t *)frameBytes;pkt.size = videoFrame->GetRowBytes() *videoFrame->GetHeight();//fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts);if (!no_video) {IDeckLinkVideoFrameAncillary *vanc;AVPacket txt_pkt = { 0 };uint8_t txt_buf0[3531]; // 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + 1920 bytes OP47 decode bufferuint8_t *txt_buf = txt_buf0;if (ctx->enable_klv) {handle_klv(avctx, ctx, videoFrame, pkt.pts);}if (videoFrame->GetAncillaryData(&vanc) == S_OK) {int i;BMDPixelFormat vanc_format = vanc->GetPixelFormat();txt_buf[0] = 0x10; // data_identifier - EBU_datatxt_buf++;#if CONFIG_LIBZVBIif (ctx->bmd_mode == bmdModePAL && ctx->teletext_lines &&(vanc_format == bmdFormat8BitYUV || vanc_format == bmdFormat10BitYUV)) {int64_t line_mask = 1;av_assert0(videoFrame->GetWidth() == 720);for (i = 6; i < 336; i++, line_mask <<= 1) {uint8_t *buf;if ((ctx->teletext_lines & line_mask) && vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) {if (vanc_format == bmdFormat8BitYUV)txt_buf = teletext_data_unit_from_vbi_data(i, buf, txt_buf, VBI_PIXFMT_UYVY);elsetxt_buf = teletext_data_unit_from_vbi_data_10bit(i, buf, txt_buf);}if (i == 22)i = 317;}}#endifif (vanc_format == bmdFormat10BitYUV && videoFrame->GetWidth() <= MAX_WIDTH_VANC) {int idx = get_vanc_line_idx(ctx->bmd_mode);for (i = vanc_line_numbers[idx].vanc_start; i <= vanc_line_numbers[idx].vanc_end; i++) {uint8_t *buf;if (vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) {uint16_t vanc[MAX_WIDTH_VANC];size_t vanc_size = videoFrame->GetWidth();if (ctx->bmd_mode == bmdModeNTSC && videoFrame->GetWidth() * 2 <= MAX_WIDTH_VANC) {vanc_size = vanc_size * 2;unpack_v210(vanc, buf, videoFrame->GetWidth());} else {extract_luma_from_v210(vanc, buf, videoFrame->GetWidth());}txt_buf = get_metadata(avctx, vanc, vanc_size,txt_buf, sizeof(txt_buf0) - (txt_buf - txt_buf0), &pkt);}if (i == vanc_line_numbers[idx].field0_vanc_end)i = vanc_line_numbers[idx].field1_vanc_start - 1;}}vanc->Release();if (txt_buf - txt_buf0 > 1) {int stuffing_units = (4 - ((45 + txt_buf - txt_buf0) / 46) % 4) % 4;while (stuffing_units--) {memset(txt_buf, 0xff, 46);txt_buf[1] = 0x2c; // data_unit_lengthtxt_buf += 46;}txt_pkt.pts = pkt.pts;txt_pkt.dts = pkt.dts;txt_pkt.stream_index = ctx->teletext_st->index;txt_pkt.data = txt_buf0;txt_pkt.size = txt_buf - txt_buf0;if (ff_decklink_packet_queue_put(&ctx->queue, &txt_pkt) < 0) {++ctx->dropped;}}}}pkt.buf = av_buffer_create(pkt.data, pkt.size, decklink_object_free, videoFrame, 0);if (pkt.buf)videoFrame->AddRef();if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) {++ctx->dropped;}}// Handle Audio Frameif (audioFrame) {AVPacket pkt = { 0 };BMDTimeValue audio_pts;//hack among hackspkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codecpar->ch_layout.nb_channels * (ctx->audio_depth / 8);audioFrame->GetBytes(&audioFrameBytes);audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den);pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts);pkt.dts = pkt.pts;//fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts);pkt.flags |= AV_PKT_FLAG_KEY;pkt.stream_index = ctx->audio_st->index;pkt.data = (uint8_t *)audioFrameBytes;if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) {++ctx->dropped;}}return S_OK;}HRESULT decklink_input_callback::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,BMDDetectedVideoInputFormatFlags formatFlags){struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;ctx->bmd_mode = mode->GetDisplayMode();// check the C context member to make sure we set both raw_format and bmd_mode with data from the same format change callbackif (!cctx->raw_format)ctx->raw_format = (formatFlags & bmdDetectedVideoInputRGB444) ? bmdFormat8BitARGB : bmdFormat8BitYUV;return S_OK;}static int decklink_autodetect(struct decklink_cctx *cctx) {struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;DECKLINK_BOOL autodetect_supported = false;int i;if (ctx->attr->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &autodetect_supported) != S_OK)return -1;if (autodetect_supported == false)return -1;ctx->autodetect = 1;ctx->bmd_mode = bmdModeUnknown;if (ctx->dli->EnableVideoInput(AUTODETECT_DEFAULT_MODE,bmdFormat8BitYUV,bmdVideoInputEnableFormatDetection) != S_OK) {return -1;}if (ctx->dli->StartStreams() != S_OK) {return -1;}// 3 second timeoutfor (i = 0; i < 30; i++) {av_usleep(100000);/* Sometimes VideoInputFrameArrived is called without the* bmdFrameHasNoInputSource flag before VideoInputFormatChanged.* So don't break for bmd_mode == AUTODETECT_DEFAULT_MODE. */if (ctx->bmd_mode != bmdModeUnknown &&ctx->bmd_mode != AUTODETECT_DEFAULT_MODE)break;}ctx->dli->PauseStreams();ctx->dli->FlushStreams();ctx->autodetect = 0;if (ctx->bmd_mode != bmdModeUnknown) {cctx->format_code = (char *)av_mallocz(5);if (!cctx->format_code)return -1;AV_WB32(cctx->format_code, ctx->bmd_mode);return 0;} else {return -1;}}extern "C" {av_cold int ff_decklink_read_close(AVFormatContext *avctx){struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;if (ctx->dli) {ctx->dli->StopStreams();ctx->dli->DisableVideoInput();ctx->dli->DisableAudioInput();}ff_decklink_cleanup(avctx);ff_decklink_packet_queue_end(&ctx->queue);av_freep(&cctx->ctx);return 0;}av_cold int ff_decklink_read_header(AVFormatContext *avctx){struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;struct decklink_ctx *ctx;class decklink_allocator *allocator;class decklink_input_callback *input_callback;AVStream *st;HRESULT result;int ret;ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx));if (!ctx)return AVERROR(ENOMEM);ctx->list_devices = cctx->list_devices;ctx->list_formats = cctx->list_formats;ctx->enable_klv = cctx->enable_klv;ctx->teletext_lines = cctx->teletext_lines;ctx->preroll = cctx->preroll;ctx->duplex_mode = cctx->duplex_mode;if (cctx->tc_format > 0 && (unsigned int)cctx->tc_format < FF_ARRAY_ELEMS(decklink_timecode_format_map))ctx->tc_format = decklink_timecode_format_map[cctx->tc_format];if (cctx->video_input > 0 && (unsigned int)cctx->video_input < FF_ARRAY_ELEMS(decklink_video_connection_map))ctx->video_input = decklink_video_connection_map[cctx->video_input];if (cctx->audio_input > 0 && (unsigned int)cctx->audio_input < FF_ARRAY_ELEMS(decklink_audio_connection_map))ctx->audio_input = decklink_audio_connection_map[cctx->audio_input];ctx->audio_pts_source = cctx->audio_pts_source;ctx->video_pts_source = cctx->video_pts_source;ctx->draw_bars = cctx->draw_bars;ctx->audio_depth = cctx->audio_depth;if (cctx->raw_format > 0 && (unsigned int)cctx->raw_format < FF_ARRAY_ELEMS(decklink_raw_format_map))ctx->raw_format = decklink_raw_format_map[cctx->raw_format];cctx->ctx = ctx;/* Check audio channel option for valid values: 2, 8 or 16 */switch (cctx->audio_channels) {case 2:case 8:case 16:break;default:av_log(avctx, AV_LOG_ERROR, "Value of channels option must be one of 2, 8 or 16\n");return AVERROR(EINVAL);}/* Check audio bit depth option for valid values: 16 or 32 */switch (cctx->audio_depth) {case 16:case 32:break;default:av_log(avctx, AV_LOG_ERROR, "Value for audio bit depth option must be either 16 or 32\n");return AVERROR(EINVAL);}/* List available devices. */if (ctx->list_devices) {ff_decklink_list_devices_legacy(avctx, 1, 0);return AVERROR_EXIT;}ret = ff_decklink_init_device(avctx, avctx->url);if (ret < 0)return ret;/* Get input device. */if (ctx->dl->QueryInterface(IID_IDeckLinkInput, (void **) &ctx->dli) != S_OK) {av_log(avctx, AV_LOG_ERROR, "Could not open input device from '%s'\n",avctx->url);ret = AVERROR(EIO);goto error;}if (ff_decklink_set_configs(avctx, DIRECTION_IN) < 0) {av_log(avctx, AV_LOG_ERROR, "Could not set input configuration\n");ret = AVERROR(EIO);goto error;}/* List supported formats. */if (ctx->list_formats) {ff_decklink_list_formats(avctx, DIRECTION_IN);ret = AVERROR_EXIT;goto error;}input_callback = new decklink_input_callback(avctx);ret = (ctx->dli->SetCallback(input_callback) == S_OK ? 0 : AVERROR_EXTERNAL);input_callback->Release();if (ret < 0) {av_log(avctx, AV_LOG_ERROR, "Cannot set input callback\n");goto error;}allocator = new decklink_allocator();ret = (ctx->dli->SetVideoInputFrameMemoryAllocator(allocator) == S_OK ? 0 : AVERROR_EXTERNAL);allocator->Release();if (ret < 0) {av_log(avctx, AV_LOG_ERROR, "Cannot set custom memory allocator\n");goto error;}if (!cctx->format_code) {if (decklink_autodetect(cctx) < 0) {av_log(avctx, AV_LOG_ERROR, "Cannot Autodetect input stream or No signal\n");ret = AVERROR(EIO);goto error;}av_log(avctx, AV_LOG_INFO, "Autodetected the input mode\n");}if (ctx->raw_format == (BMDPixelFormat)0)ctx->raw_format = bmdFormat8BitYUV;if (ff_decklink_set_format(avctx, DIRECTION_IN) < 0) {av_log(avctx, AV_LOG_ERROR, "Could not set format code %s for %s\n",cctx->format_code ? cctx->format_code : "(unset)", avctx->url);ret = AVERROR(EIO);goto error;}#if !CONFIG_LIBZVBIif (ctx->teletext_lines && ctx->bmd_mode == bmdModePAL) {av_log(avctx, AV_LOG_ERROR, "Libzvbi support is needed for capturing SD PAL teletext, please recompile FFmpeg.\n");ret = AVERROR(ENOSYS);goto error;}#endif/* Setup streams. */st = avformat_new_stream(avctx, NULL);if (!st) {av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");ret = AVERROR(ENOMEM);goto error;}st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;st->codecpar->codec_id = cctx->audio_depth == 32 ? AV_CODEC_ID_PCM_S32LE : AV_CODEC_ID_PCM_S16LE;st->codecpar->sample_rate = bmdAudioSampleRate48kHz;st->codecpar->ch_layout.nb_channels = cctx->audio_channels;avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ctx->audio_st=st;st = avformat_new_stream(avctx, NULL);if (!st) {av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");ret = AVERROR(ENOMEM);goto error;}st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;st->codecpar->width = ctx->bmd_width;st->codecpar->height = ctx->bmd_height;st->time_base.den = ctx->bmd_tb_den;st->time_base.num = ctx->bmd_tb_num;st->r_frame_rate = av_make_q(st->time_base.den, st->time_base.num);switch(ctx->raw_format) {case bmdFormat8BitYUV:st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;st->codecpar->format = AV_PIX_FMT_UYVY422;st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 16, st->time_base.den, st->time_base.num);break;case bmdFormat10BitYUV:st->codecpar->codec_id = AV_CODEC_ID_V210;st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 64, st->time_base.den, st->time_base.num * 3);break;case bmdFormat8BitARGB:st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;st->codecpar->format = AV_PIX_FMT_0RGB;st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 32, st->time_base.den, st->time_base.num);break;case bmdFormat8BitBGRA:st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;st->codecpar->format = AV_PIX_FMT_BGR0;st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 32, st->time_base.den, st->time_base.num);break;case bmdFormat10BitRGB:st->codecpar->codec_id = AV_CODEC_ID_R210;st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 30, st->time_base.den, st->time_base.num);break;default:char fourcc_str[AV_FOURCC_MAX_STRING_SIZE] = {0};av_fourcc_make_string(fourcc_str, ctx->raw_format);av_log(avctx, AV_LOG_ERROR, "Raw Format %s not supported\n", fourcc_str);ret = AVERROR(EINVAL);goto error;}switch (ctx->bmd_field_dominance) {case bmdUpperFieldFirst:st->codecpar->field_order = AV_FIELD_TT;break;case bmdLowerFieldFirst:st->codecpar->field_order = AV_FIELD_BB;break;case bmdProgressiveFrame:case bmdProgressiveSegmentedFrame:st->codecpar->field_order = AV_FIELD_PROGRESSIVE;break;}avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ctx->video_st=st;if (ctx->enable_klv) {st = avformat_new_stream(avctx, NULL);if (!st) {ret = AVERROR(ENOMEM);goto error;}st->codecpar->codec_type = AVMEDIA_TYPE_DATA;st->time_base.den = ctx->bmd_tb_den;st->time_base.num = ctx->bmd_tb_num;st->codecpar->codec_id = AV_CODEC_ID_SMPTE_KLV;avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ctx->klv_st = st;}if (ctx->teletext_lines) {st = avformat_new_stream(avctx, NULL);if (!st) {av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");ret = AVERROR(ENOMEM);goto error;}st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;st->time_base.den = ctx->bmd_tb_den;st->time_base.num = ctx->bmd_tb_num;st->codecpar->codec_id = AV_CODEC_ID_DVB_TELETEXT;avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ctx->teletext_st = st;}av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n", ctx->audio_st->codecpar->ch_layout.nb_channels);result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, cctx->audio_depth == 32 ? bmdAudioSampleType32bitInteger : bmdAudioSampleType16bitInteger, ctx->audio_st->codecpar->ch_layout.nb_channels);if (result != S_OK) {av_log(avctx, AV_LOG_ERROR, "Cannot enable audio input\n");ret = AVERROR(EIO);goto error;}result = ctx->dli->EnableVideoInput(ctx->bmd_mode,ctx->raw_format,bmdVideoInputFlagDefault);if (result != S_OK) {av_log(avctx, AV_LOG_ERROR, "Cannot enable video input\n");ret = AVERROR(EIO);goto error;}ff_decklink_packet_queue_init(avctx, &ctx->queue, cctx->queue_size);if (ctx->dli->StartStreams() != S_OK) {av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n");ret = AVERROR(EIO);goto error;}return 0;error:ff_decklink_cleanup(avctx);return ret;}int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt){struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;ff_decklink_packet_queue_get(&ctx->queue, pkt, 1);if (ctx->tc_format && !(av_dict_get(ctx->video_st->metadata, "timecode", NULL, 0))) {size_t size;const uint8_t *side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size);if (side_metadata) {if (av_packet_unpack_dictionary(side_metadata, size, &ctx->video_st->metadata) < 0)av_log(avctx, AV_LOG_ERROR, "Unable to set timecode\n");}}return 0;}int ff_decklink_list_input_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list){return ff_decklink_list_devices(avctx, device_list, 1, 0);}} /* extern "C" */
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。