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 = { 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; IDeckLinkVideoInputFrame* last_video_frame;}; 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; last_video_frame = nullptr;} decklink_input_callback::~decklink_input_callback(){ if (last_video_frame) last_video_frame->Release();} 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); else pts = 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 >= 0x0b000000 int hfr = (tc_format == bmdTimecodeRP188HighFrameRate);#else int hfr = 0;#endif if (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 >= 0x0b000000 ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame); if (ret == AVERROR(ENOENT))#endif ret = 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; int64_t video_pkt_pts, audio_pkt_pts; 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(); video_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); audio_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts); // Handle Video Frame if (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->signal_loss_action == SIGNAL_LOSS_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]; } } else if (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT && last_video_frame) { videoFrame = last_video_frame; videoFrame->GetBytes(&frameBytes); } 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 (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT) { if (last_video_frame) last_video_frame->Release(); last_video_frame = videoFrame; last_video_frame->AddRef(); } 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 = video_pkt_pts; pkt.dts = pkt.pts; pkt.duration = frameDuration; //To be made sure it still applies pkt.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 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; BMDPixelFormat vanc_format = vanc->GetPixelFormat(); txt_buf[0] = 0x10; // data_identifier - EBU_data txt_buf++;#if CONFIG_LIBZVBI if (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); else txt_buf = teletext_data_unit_from_vbi_data_10bit(i, buf, txt_buf); } if (i == 22) i = 317; } }#endif if (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_length txt_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 Frame if (audioFrame) { AVPacket pkt = { 0 }; BMDTimeValue audio_pts; //hack among hacks pkt.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 = audio_pkt_pts; 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 callback if (!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; Lines 501–1000 of 1370. Previous lines Load more
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 = { 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; IDeckLinkVideoInputFrame* last_video_frame; }; 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; last_video_frame = nullptr; } decklink_input_callback::~decklink_input_callback() { if (last_video_frame) last_video_frame->Release(); } 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); else pts = 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 >= 0x0b000000 int hfr = (tc_format == bmdTimecodeRP188HighFrameRate); #else int hfr = 0; #endif if (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 >= 0x0b000000 ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame); if (ret == AVERROR(ENOENT)) #endif ret = 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; int64_t video_pkt_pts, audio_pkt_pts; 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(); video_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); audio_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts); // Handle Video Frame if (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->signal_loss_action == SIGNAL_LOSS_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]; } } else if (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT && last_video_frame) { videoFrame = last_video_frame; videoFrame->GetBytes(&frameBytes); } 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 (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT) { if (last_video_frame) last_video_frame->Release(); last_video_frame = videoFrame; last_video_frame->AddRef(); } 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 = video_pkt_pts; pkt.dts = pkt.pts; pkt.duration = frameDuration; //To be made sure it still applies pkt.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 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; BMDPixelFormat vanc_format = vanc->GetPixelFormat(); txt_buf[0] = 0x10; // data_identifier - EBU_data txt_buf++; #if CONFIG_LIBZVBI if (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); else txt_buf = teletext_data_unit_from_vbi_data_10bit(i, buf, txt_buf); } if (i == 22) i = 317; } } #endif if (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_length txt_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 Frame if (audioFrame) { AVPacket pkt = { 0 }; BMDTimeValue audio_pts; //hack among hacks pkt.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 = audio_pkt_pts; 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 callback if (!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;