avdevice/decklink_dec: add support for extracting and outputing klv from vanc
Signed-off-by: Milos Zivkovic <zivkovic@teralogics.com> Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
parent
4979afdb85
commit
c4e0868243
2
configure
vendored
2
configure
vendored
@ -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 cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; }
|
||||||
enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint
|
enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint
|
||||||
enabled decklink && { require_headers DeckLinkAPI.h &&
|
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 frei0r && require_headers "frei0r.h dlfcn.h"
|
||||||
enabled gmp && require gmp gmp.h mpz_export -lgmp
|
enabled gmp && require gmp gmp.h mpz_export -lgmp
|
||||||
enabled gnutls && require_pkg_config gnutls gnutls gnutls/gnutls.h gnutls_global_init
|
enabled gnutls && require_pkg_config gnutls gnutls gnutls/gnutls.h gnutls_global_init
|
||||||
|
@ -398,6 +398,12 @@ are dropped till a frame with timecode is received.
|
|||||||
Option @var{timecode_format} must be specified.
|
Option @var{timecode_format} must be specified.
|
||||||
Defaults to @option{false}.
|
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
|
@end table
|
||||||
|
|
||||||
@subsection Examples
|
@subsection Examples
|
||||||
|
@ -120,12 +120,14 @@ struct decklink_ctx {
|
|||||||
unsigned int dropped;
|
unsigned int dropped;
|
||||||
AVStream *audio_st;
|
AVStream *audio_st;
|
||||||
AVStream *video_st;
|
AVStream *video_st;
|
||||||
|
AVStream *klv_st;
|
||||||
AVStream *teletext_st;
|
AVStream *teletext_st;
|
||||||
uint16_t cdp_sequence_num;
|
uint16_t cdp_sequence_num;
|
||||||
|
|
||||||
/* Options */
|
/* Options */
|
||||||
int list_devices;
|
int list_devices;
|
||||||
int list_formats;
|
int list_formats;
|
||||||
|
int enable_klv;
|
||||||
int64_t teletext_lines;
|
int64_t teletext_lines;
|
||||||
double preroll;
|
double preroll;
|
||||||
int duplex_mode;
|
int duplex_mode;
|
||||||
|
@ -40,6 +40,7 @@ struct decklink_cctx {
|
|||||||
/* Options */
|
/* Options */
|
||||||
int list_devices;
|
int list_devices;
|
||||||
int list_formats;
|
int list_formats;
|
||||||
|
int enable_klv;
|
||||||
int64_t teletext_lines;
|
int64_t teletext_lines;
|
||||||
double preroll;
|
double preroll;
|
||||||
int audio_channels;
|
int audio_channels;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
using std::atomic;
|
using std::atomic;
|
||||||
|
|
||||||
/* Include internal.h first to avoid conflict between winsock.h (used by
|
/* 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;
|
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<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 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<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;
|
||||||
|
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
|
class decklink_input_callback : public IDeckLinkInputCallback
|
||||||
{
|
{
|
||||||
public:
|
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_buf0[3531]; // 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + 1920 bytes OP47 decode buffer
|
||||||
uint8_t *txt_buf = txt_buf0;
|
uint8_t *txt_buf = txt_buf0;
|
||||||
|
|
||||||
|
if (ctx->enable_klv) {
|
||||||
|
handle_klv(avctx, ctx, videoFrame, pkt.pts);
|
||||||
|
}
|
||||||
|
|
||||||
if (videoFrame->GetAncillaryData(&vanc) == S_OK) {
|
if (videoFrame->GetAncillaryData(&vanc) == S_OK) {
|
||||||
int i;
|
int i;
|
||||||
int64_t line_mask = 1;
|
int64_t line_mask = 1;
|
||||||
@ -1012,6 +1120,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
|
|||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
ctx->list_devices = cctx->list_devices;
|
ctx->list_devices = cctx->list_devices;
|
||||||
ctx->list_formats = cctx->list_formats;
|
ctx->list_formats = cctx->list_formats;
|
||||||
|
ctx->enable_klv = cctx->enable_klv;
|
||||||
ctx->teletext_lines = cctx->teletext_lines;
|
ctx->teletext_lines = cctx->teletext_lines;
|
||||||
ctx->preroll = cctx->preroll;
|
ctx->preroll = cctx->preroll;
|
||||||
ctx->duplex_mode = cctx->duplex_mode;
|
ctx->duplex_mode = cctx->duplex_mode;
|
||||||
@ -1202,6 +1311,20 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
|
|||||||
|
|
||||||
ctx->video_st=st;
|
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) {
|
if (ctx->teletext_lines) {
|
||||||
st = avformat_new_stream(avctx, NULL);
|
st = avformat_new_stream(avctx, NULL);
|
||||||
if (!st) {
|
if (!st) {
|
||||||
|
@ -39,6 +39,7 @@ static const AVOption options[] = {
|
|||||||
{ "argb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, DEC, "raw_format"},
|
{ "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"},
|
{ "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"},
|
{ "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"},
|
{ "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"},
|
{ "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"},
|
{ "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"},
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
#define LIBAVDEVICE_VERSION_MAJOR 58
|
#define LIBAVDEVICE_VERSION_MAJOR 58
|
||||||
#define LIBAVDEVICE_VERSION_MINOR 11
|
#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, \
|
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
|
||||||
LIBAVDEVICE_VERSION_MINOR, \
|
LIBAVDEVICE_VERSION_MINOR, \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user