*/
#include <atomic>
+#include <vector>
using std::atomic;
/* Include internal.h first to avoid conflict between winsock.h (used by
#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"
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
{
public:
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();
AVDictionary* metadata_dict = NULL;
int metadata_len;
uint8_t* packed_metadata;
+ AVTimecode tcr;
+
+ if (av_timecode_init_from_string(&tcr, ctx->video_st->r_frame_rate, tc, ctx) >= 0) {
+ 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, AV_DICT_DONT_STRDUP_VAL) >= 0) {
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;
}
}
}
}
}
+ 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;
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;
HRESULT decklink_input_callback::VideoInputFormatChanged(
BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
- BMDDetectedVideoInputFormatFlags)
+ 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 callback
+ if (!cctx->raw_format)
+ ctx->raw_format = (formatFlags & bmdDetectedVideoInputRGB444) ? bmdFormat8BitARGB : bmdFormat8BitYUV;
return S_OK;
}
return -1;
}
- // 1 second timeout
- for (i = 0; i < 10; i++) {
+ // 3 second timeout
+ for (i = 0; i < 30; i++) {
av_usleep(100000);
/* Sometimes VideoInputFrameArrived is called without the
* bmdFrameHasNoInputSource flag before VideoInputFormatChanged.
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
- if (ctx->capture_started) {
+ if (ctx->dli) {
ctx->dli->StopStreams();
ctx->dli->DisableVideoInput();
ctx->dli->DisableAudioInput();
class decklink_input_callback *input_callback;
AVStream *st;
HRESULT result;
- char fname[1024];
- char *tmp;
- int mode_num = 0;
int ret;
ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_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;
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 */
/* List available devices. */
if (ctx->list_devices) {
+ av_log(avctx, AV_LOG_WARNING, "The -list_devices option is deprecated and will be removed. Please use ffmpeg -sources decklink instead.\n");
ff_decklink_list_devices_legacy(avctx, 1, 0);
return AVERROR_EXIT;
}
- if (cctx->v210) {
- av_log(avctx, AV_LOG_WARNING, "The bm_v210 option is deprecated and will be removed. Please use the -raw_format yuv422p10.\n");
- cctx->raw_format = MKBETAG('v','2','1','0');
- }
-
- av_strlcpy(fname, avctx->url, sizeof(fname));
- tmp=strchr (fname, '@');
- if (tmp != NULL) {
- av_log(avctx, AV_LOG_WARNING, "The @mode syntax is deprecated and will be removed. Please use the -format_code option.\n");
- mode_num = atoi (tmp+1);
- *tmp = 0;
- }
-
- ret = ff_decklink_init_device(avctx, fname);
+ ret = ff_decklink_init_device(avctx, avctx->url);
if (ret < 0)
return ret;
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);
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;
- }
-
input_callback = new decklink_input_callback(avctx);
ret = (ctx->dli->SetCallback(input_callback) == S_OK ? 0 : AVERROR_EXTERNAL);
input_callback->Release();
goto error;
}
- if (mode_num == 0 && !cctx->format_code) {
+ 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);
}
av_log(avctx, AV_LOG_INFO, "Autodetected the input mode\n");
}
- if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
- av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n",
- mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname);
+ 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;
}
st->time_base.num = ctx->bmd_tb_num;
st->r_frame_rate = av_make_q(st->time_base.den, st->time_base.num);
- switch((BMDPixelFormat)cctx->raw_format) {
+ switch(ctx->raw_format) {
case bmdFormat8BitYUV:
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y');
break;
case bmdFormat8BitARGB:
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
- st->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);
st->codecpar->format = AV_PIX_FMT_0RGB;
+ st->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);
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->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);
st->codecpar->format = AV_PIX_FMT_BGR0;
+ st->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);
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->bits_per_coded_sample = 10;
break;
default:
- av_log(avctx, AV_LOG_ERROR, "Raw Format %.4s not supported\n", (char*) &cctx->raw_format);
+ 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;
}
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) {
}
result = ctx->dli->EnableVideoInput(ctx->bmd_mode,
- (BMDPixelFormat) cctx->raw_format,
+ ctx->raw_format,
bmdVideoInputFlagDefault);
if (result != S_OK) {