diff --git a/configure b/configure index f0eaf2ded1..bdfd731602 100755 --- a/configure +++ b/configure @@ -6281,7 +6281,7 @@ enabled avisynth && require_headers "avisynth/avisynth_c.h" enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; } enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint enabled decklink && { require_headers DeckLinkAPI.h && - { test_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a090500" || die "ERROR: Decklink API version must be >= 10.9.5."; } } + { test_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a0a0000" || die "ERROR: Decklink API version must be >= 10.10"; } } enabled frei0r && require_headers "frei0r.h dlfcn.h" enabled gmp && require gmp gmp.h mpz_export -lgmp enabled gnutls && require_pkg_config gnutls gnutls gnutls/gnutls.h gnutls_global_init diff --git a/doc/indevs.texi b/doc/indevs.texi index 6f5afaf344..0f33fc66d8 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -398,6 +398,12 @@ are dropped till a frame with timecode is received. Option @var{timecode_format} must be specified. Defaults to @option{false}. +@item enable_klv(@emph{bool}) +If set to @option{true}, extracts KLV data from VANC and outputs KLV packets. +KLV VANC packets are joined based on MID and PSC fields and aggregated into +one KLV packet. +Defaults to @option{false}. + @end table @subsection Examples diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h index 27ce6a8a40..bd68c7ba77 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -120,12 +120,14 @@ struct decklink_ctx { unsigned int dropped; AVStream *audio_st; AVStream *video_st; + AVStream *klv_st; AVStream *teletext_st; uint16_t cdp_sequence_num; /* Options */ int list_devices; int list_formats; + int enable_klv; int64_t teletext_lines; double preroll; int duplex_mode; diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 88b1eae18d..a78262aaac 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -40,6 +40,7 @@ struct decklink_cctx { /* Options */ int list_devices; int list_formats; + int enable_klv; int64_t teletext_lines; double preroll; int audio_channels; diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 82106aa69e..a499972df8 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -22,6 +22,7 @@ */ #include +#include using std::atomic; /* Include internal.h first to avoid conflict between winsock.h (used by @@ -583,6 +584,109 @@ static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block) return ret; } +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 data; + }; + + size_t total_size = 0; + std::vector> 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 PSC + if (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 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; + av_init_packet(&klv_packet); + 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 (avpacket_queue_put(&ctx->queue, &klv_packet) < 0) { + ++ctx->dropped; + } + } +} + class decklink_input_callback : public IDeckLinkInputCallback { public: @@ -821,6 +925,10 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( uint8_t txt_buf0[3531]; // 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + 1920 bytes OP47 decode buffer uint8_t *txt_buf = txt_buf0; + if (ctx->enable_klv) { + handle_klv(avctx, ctx, videoFrame, pkt.pts); + } + if (videoFrame->GetAncillaryData(&vanc) == S_OK) { int i; int64_t line_mask = 1; @@ -1012,6 +1120,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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; @@ -1202,6 +1311,20 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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) { diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index b59876994a..9f4b32088c 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -39,6 +39,7 @@ static const AVOption options[] = { { "argb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, DEC, "raw_format"}, { "bgra", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('B','G','R','A') }, 0, 0, DEC, "raw_format"}, { "rgb10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('r','2','1','0') }, 0, 0, DEC, "raw_format"}, + { "enable_klv", "output klv if present in vanc", OFFSET(enable_klv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, { "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, "teletext_lines"}, { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"}, { "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"}, diff --git a/libavdevice/version.h b/libavdevice/version.h index 48b981ec3d..8ca715b8b4 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -29,7 +29,7 @@ #define LIBAVDEVICE_VERSION_MAJOR 58 #define LIBAVDEVICE_VERSION_MINOR 11 -#define LIBAVDEVICE_VERSION_MICRO 100 +#define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ LIBAVDEVICE_VERSION_MINOR, \