| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 | #include "decklink-device-instance.hpp"#include "audio-repack.hpp"#include "DecklinkInput.hpp"#include "DecklinkOutput.hpp"#include <util/platform.h>#include <util/threading.h>#include <util/util_uint64.h>#include <sstream>#include <iomanip>#include <algorithm>#include "OBSVideoFrame.h"#include <caption/caption.h>#include <util/bitstream.h>template<typename T> RenderDelegate<T>::RenderDelegate(T *pOwner){	m_pOwner = pOwner;}template<typename T> RenderDelegate<T>::~RenderDelegate() {}template<typename T> HRESULT RenderDelegate<T>::QueryInterface(REFIID, LPVOID *ppv){	*ppv = NULL;	return E_NOINTERFACE;}template<typename T> ULONG RenderDelegate<T>::AddRef(){	return ++m_refCount;}template<typename T> ULONG RenderDelegate<T>::Release(){	const ULONG newRefValue = --m_refCount;	if (newRefValue == 0) {		delete this;		return 0;	}	return newRefValue;}template<typename T>HRESULT RenderDelegate<T>::ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame, BMDOutputFrameCompletionResult){	m_pOwner->ScheduleVideoFrame(completedFrame);	return S_OK;}template<typename T> HRESULT RenderDelegate<T>::ScheduledPlaybackHasStopped(){	return S_OK;}static inline enum video_format ConvertPixelFormat(BMDPixelFormat format){	switch (format) {	case bmdFormat8BitBGRA:		return VIDEO_FORMAT_BGRX;	case bmdFormat10BitRGBXLE:		return VIDEO_FORMAT_R10L;	case bmdFormat10BitYUV:		return VIDEO_FORMAT_V210;	default:	case bmdFormat8BitYUV:		return VIDEO_FORMAT_UYVY;	}}static inline int ConvertChannelFormat(speaker_layout format){	switch (format) {	case SPEAKERS_2POINT1:	case SPEAKERS_4POINT0:	case SPEAKERS_4POINT1:	case SPEAKERS_5POINT1:	case SPEAKERS_7POINT1:		return 8;	default:	case SPEAKERS_STEREO:		return 2;	}}static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format, bool swap){	switch (format) {	case SPEAKERS_2POINT1:		return repack_mode_8to3ch;	case SPEAKERS_4POINT0:		return repack_mode_8to4ch;	case SPEAKERS_4POINT1:		return swap ? repack_mode_8to5ch_swap : repack_mode_8to5ch;	case SPEAKERS_5POINT1:		return swap ? repack_mode_8to6ch_swap : repack_mode_8to6ch;	case SPEAKERS_7POINT1:		return swap ? repack_mode_8ch_swap : repack_mode_8ch;	default:		assert(false && "No repack requested");		return (audio_repack_mode_t)-1;	}}DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_, DeckLinkDevice *device_)	: currentFrame(),	  currentPacket(),	  currentCaptions(),	  decklink(decklink_),	  device(device_){	currentPacket.samples_per_sec = 48000;	currentPacket.speakers = SPEAKERS_STEREO;	currentPacket.format = AUDIO_FORMAT_16BIT;}DeckLinkDeviceInstance::~DeckLinkDeviceInstance(){	if (convertFrame) {		delete convertFrame;	}}void DeckLinkDeviceInstance::HandleAudioPacket(IDeckLinkAudioInputPacket *audioPacket, const uint64_t timestamp){	if (audioPacket == nullptr)		return;	void *bytes;	if (audioPacket->GetBytes(&bytes) != S_OK) {		LOG(LOG_WARNING, "Failed to get audio packet data");		return;	}	const uint32_t frameCount = (uint32_t)audioPacket->GetSampleFrameCount();	currentPacket.frames = frameCount;	currentPacket.timestamp = timestamp;	if (decklink && !static_cast<DeckLinkInput *>(decklink)->buffering) {		currentPacket.timestamp = os_gettime_ns();		currentPacket.timestamp -= util_mul_div64(frameCount, 1000000000ULL, currentPacket.samples_per_sec);	}	int maxdevicechannel = device->GetMaxChannel();	if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO && channelFormat != SPEAKERS_STEREO &&	    (channelFormat != SPEAKERS_7POINT1 || static_cast<DeckLinkInput *>(decklink)->swap) &&	    maxdevicechannel >= 8) {		if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) {			LOG(LOG_ERROR, "Failed to convert audio packet data");			return;		}		currentPacket.data[0] = (*audioRepacker)->packet_buffer;	} else {		currentPacket.data[0] = (uint8_t *)bytes;	}	nextAudioTS = timestamp + util_mul_div64(frameCount, 1000000000ULL, 48000ULL) + 1;	obs_source_output_audio(static_cast<DeckLinkInput *>(decklink)->GetSource(), ¤tPacket);}void DeckLinkDeviceInstance::HandleVideoFrame(IDeckLinkVideoInputFrame *videoFrame, const uint64_t timestamp){	if (videoFrame == nullptr)		return;	ComPtr<IDeckLinkVideoFrameAncillaryPackets> packets;	if (videoFrame->QueryInterface(IID_IDeckLinkVideoFrameAncillaryPackets, (void **)&packets) == S_OK) {		ComPtr<IDeckLinkAncillaryPacketIterator> iterator;		packets->GetPacketIterator(&iterator);		ComPtr<IDeckLinkAncillaryPacket> packet;		iterator->Next(&packet);		if (packet) {			auto did = packet->GetDID();			auto sdid = packet->GetSDID();			// Caption data			if (did == 0x61 && sdid == 0x01) {				this->HandleCaptionPacket(packet, timestamp);			}		}	}	ComPtr<IDeckLinkVideoFrame> frame;	if (videoFrame->GetPixelFormat() != convertFrame->GetPixelFormat()) {		ComPtr<IDeckLinkVideoConversion> frameConverter;		frameConverter.Set(CreateVideoConversionInstance());		frameConverter->ConvertFrame(videoFrame, convertFrame);		frame = convertFrame;	} else {		frame = videoFrame;	}	void *bytes;	if (frame->GetBytes(&bytes) != S_OK) {		LOG(LOG_WARNING, "Failed to get video frame data");		return;	}	currentFrame.data[0] = (uint8_t *)bytes;	currentFrame.linesize[0] = (uint32_t)frame->GetRowBytes();	currentFrame.width = (uint32_t)frame->GetWidth();	currentFrame.height = (uint32_t)frame->GetHeight();	currentFrame.timestamp = timestamp;	if (currentFrame.width == 0 || currentFrame.height == 0)		return;	enum video_trc trc = VIDEO_TRC_DEFAULT;	if (frame->GetFlags() & bmdFrameContainsHDRMetadata) {		ComPtr<IDeckLinkVideoFrameMetadataExtensions> metadata;		if (SUCCEEDED(videoFrame->QueryInterface(IID_IDeckLinkVideoFrameMetadataExtensions,							 (void **)&metadata))) {			int64_t range;			if (SUCCEEDED(				    metadata->GetInt(bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc, &range))) {				switch (range) {				case 2:					trc = VIDEO_TRC_PQ;					break;				case 3:					trc = VIDEO_TRC_HLG;					break;				default:					trc = VIDEO_TRC_DEFAULT;					break;				}			}		}	}	currentFrame.trc = trc;	obs_source_output_video2(static_cast<DeckLinkInput *>(decklink)->GetSource(), ¤tFrame);}void DeckLinkDeviceInstance::HandleCaptionPacket(IDeckLinkAncillaryPacket *packet, const uint64_t timestamp){	const void *data;	uint32_t size;	packet->GetBytes(bmdAncillaryPacketFormatUInt8, &data, &size);	auto anc = (uint8_t *)data;	struct bitstream_reader reader;	bitstream_reader_init(&reader, anc, size);	// header1	bitstream_reader_r8(&reader);	// header2	bitstream_reader_r8(&reader);	// length	bitstream_reader_r8(&reader);	// frameRate	bitstream_reader_read_bits(&reader, 4);	//reserved	bitstream_reader_read_bits(&reader, 4);	auto cdp_timecode_added = bitstream_reader_read_bits(&reader, 1);	// cdp_data_block_added	bitstream_reader_read_bits(&reader, 1);	// cdp_service_info_added	bitstream_reader_read_bits(&reader, 1);	// cdp_service_info_start	bitstream_reader_read_bits(&reader, 1);	// cdp_service_info_changed	bitstream_reader_read_bits(&reader, 1);	// cdp_service_info_end	bitstream_reader_read_bits(&reader, 1);	auto cdp_contains_captions = bitstream_reader_read_bits(&reader, 1);	//reserved	bitstream_reader_read_bits(&reader, 1);	// cdp_counter	bitstream_reader_r8(&reader);	// cdp_counter2	bitstream_reader_r8(&reader);	if (cdp_timecode_added) {		// timecodeSectionID		bitstream_reader_r8(&reader);		//reserved		bitstream_reader_read_bits(&reader, 2);		bitstream_reader_read_bits(&reader, 2);		bitstream_reader_read_bits(&reader, 4);		// reserved		bitstream_reader_read_bits(&reader, 1);		bitstream_reader_read_bits(&reader, 3);		bitstream_reader_read_bits(&reader, 4);		bitstream_reader_read_bits(&reader, 1);		bitstream_reader_read_bits(&reader, 3);		bitstream_reader_read_bits(&reader, 4);		bitstream_reader_read_bits(&reader, 1);		bitstream_reader_read_bits(&reader, 1);		bitstream_reader_read_bits(&reader, 3);		bitstream_reader_read_bits(&reader, 4);	}	if (cdp_contains_captions) {		// cdp_data_section		bitstream_reader_r8(&reader);		//process_em_data_flag		bitstream_reader_read_bits(&reader, 1);		// process_cc_data_flag		bitstream_reader_read_bits(&reader, 1);		//additional_data_flag		bitstream_reader_read_bits(&reader, 1);		auto cc_count = bitstream_reader_read_bits(&reader, 5);		auto *outData = (uint8_t *)bzalloc(sizeof(uint8_t) * cc_count * 3);		memcpy(outData, anc + reader.pos, cc_count * 3);		currentCaptions.data = outData;		currentCaptions.timestamp = timestamp;		currentCaptions.packets = cc_count;		obs_source_output_cea708(static_cast<DeckLinkInput *>(decklink)->GetSource(), ¤tCaptions);		bfree(outData);	}}void DeckLinkDeviceInstance::FinalizeStream(){	input->SetCallback(nullptr);	input->DisableVideoInput();	if (channelFormat != SPEAKERS_UNKNOWN)		input->DisableAudioInput();	if (audioRepacker != nullptr) {		delete audioRepacker;		audioRepacker = nullptr;	}	mode = nullptr;}//#define LOG_SETUP_VIDEO_FORMAT 1void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_){	if (mode_ == nullptr)		return;	const enum video_format format = ConvertPixelFormat(pixelFormat);	currentFrame.format = format;	colorSpace = static_cast<DeckLinkInput *>(decklink)->GetColorSpace();	if (colorSpace == VIDEO_CS_DEFAULT) {		const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();		/* 2020 wasn't set when testing but maybe it will be someday */		if (flags & bmdDisplayModeColorspaceRec2020)			activeColorSpace = VIDEO_CS_2100_PQ;		else if (flags & bmdDisplayModeColorspaceRec709)			activeColorSpace = VIDEO_CS_709;		else if (flags & bmdDisplayModeColorspaceRec601)			activeColorSpace = VIDEO_CS_601;		else			activeColorSpace = VIDEO_CS_DEFAULT;	} else {		activeColorSpace = colorSpace;	}	colorRange = static_cast<DeckLinkInput *>(decklink)->GetColorRange();	currentFrame.range = colorRange;	video_format_get_parameters_for_format(activeColorSpace, colorRange, format, currentFrame.color_matrix,					       currentFrame.color_range_min, currentFrame.color_range_max);	delete convertFrame;	BMDPixelFormat convertFormat;	switch (pixelFormat) {	case bmdFormat10BitYUV:	case bmdFormat8BitBGRA:	case bmdFormat10BitRGBXLE:		convertFormat = pixelFormat;		break;	default:		convertFormat = bmdFormat8BitYUV;		break;	}	convertFrame = new OBSVideoFrame(mode_->GetWidth(), mode_->GetHeight(), convertFormat);#ifdef LOG_SETUP_VIDEO_FORMAT	LOG(LOG_INFO, "Setup video format: %s, %s, %s", pixelFormat == bmdFormat8BitYUV ? "YUV" : "RGB",	    activeColorSpace == VIDEO_CS_601 ? "BT.601" : "BT.709",	    colorRange == VIDEO_RANGE_FULL ? "full" : "limited");#endif}bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_, bool allow10Bit_,					  BMDVideoConnection bmdVideoConnection, BMDAudioConnection bmdAudioConnection){	if (mode != nullptr)		return false;	if (mode_ == nullptr)		return false;	LOG(LOG_INFO, "Starting capture...");	if (!device->GetInput(&input))		return false;	HRESULT result = input->QueryInterface(IID_IDeckLinkConfiguration, (void **)&deckLinkConfiguration);	if (result != S_OK) {		LOG(LOG_ERROR, "Could not obtain the IDeckLinkConfiguration interface: %08x\n", result);	} else {		if (bmdVideoConnection > 0) {			result = deckLinkConfiguration->SetInt(bmdDeckLinkConfigVideoInputConnection,							       bmdVideoConnection);			if (result != S_OK) {				LOG(LOG_ERROR, "Couldn't set input video port to %d\n\n", bmdVideoConnection);			}		}		if (bmdAudioConnection > 0) {			result = deckLinkConfiguration->SetInt(bmdDeckLinkConfigAudioInputConnection,							       bmdAudioConnection);			if (result != S_OK) {				LOG(LOG_ERROR, "Couldn't set input audio port to %d\n\n", bmdVideoConnection);			}		}	}	videoConnection = bmdVideoConnection;	audioConnection = bmdAudioConnection;	BMDVideoInputFlags flags;	bool isauto = mode_->GetName() == "Auto";	if (isauto) {		displayMode = bmdModeNTSC;		if (allow10Bit_) {			pixelFormat = bmdFormat10BitYUV;		} else {			pixelFormat = bmdFormat8BitYUV;		}		flags = bmdVideoInputEnableFormatDetection;	} else {		displayMode = mode_->GetDisplayMode();		pixelFormat = static_cast<DeckLinkInput *>(decklink)->GetPixelFormat();		flags = bmdVideoInputFlagDefault;	}	allow10Bit = allow10Bit_;	const HRESULT videoResult = input->EnableVideoInput(displayMode, pixelFormat, flags);	if (videoResult != S_OK) {		LOG(LOG_ERROR, "Failed to enable video input");		return false;	}	SetupVideoFormat(mode_);	channelFormat = static_cast<DeckLinkInput *>(decklink)->GetChannelFormat();	currentPacket.speakers = channelFormat;	swap = static_cast<DeckLinkInput *>(decklink)->swap;	int maxdevicechannel = device->GetMaxChannel();	if (channelFormat != SPEAKERS_UNKNOWN) {		const int channel = ConvertChannelFormat(channelFormat);		const HRESULT audioResult =			input->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, channel);		if (audioResult != S_OK)			LOG(LOG_WARNING, "Failed to enable audio input; continuing...");		if (channelFormat != SPEAKERS_UNKNOWN && channelFormat != SPEAKERS_MONO &&		    channelFormat != SPEAKERS_STEREO && (channelFormat != SPEAKERS_7POINT1 || swap) &&		    maxdevicechannel >= 8) {			const audio_repack_mode_t repack_mode = ConvertRepackFormat(channelFormat, swap);			audioRepacker = new AudioRepacker(repack_mode);		}	}	if (input->SetCallback(this) != S_OK) {		LOG(LOG_ERROR, "Failed to set callback");		FinalizeStream();		return false;	}	if (input->StartStreams() != S_OK) {		LOG(LOG_ERROR, "Failed to start streams");		FinalizeStream();		return false;	}	mode = mode_;	return true;}bool DeckLinkDeviceInstance::StopCapture(void){	if (mode == nullptr || input == nullptr)		return false;	LOG(LOG_INFO, "Stopping capture of '%s'...", GetDevice()->GetDisplayName().c_str());	input->StopStreams();	FinalizeStream();	return true;}bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_){	if (mode != nullptr)		return false;	if (mode_ == nullptr)		return false;	auto decklinkOutput = dynamic_cast<DeckLinkOutput *>(decklink);	if (decklinkOutput == nullptr)		return false;	LOG(LOG_INFO, "Starting output...");	ComPtr<IDeckLinkOutput> output_;	if (!device->GetOutput(&output_))		return false;	const HRESULT videoResult = output_->EnableVideoOutput(mode_->GetDisplayMode(), bmdVideoOutputFlagDefault);	if (videoResult != S_OK) {		LOG(LOG_ERROR, "Failed to enable video output");		return false;	}	const HRESULT audioResult = output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,							       2, bmdAudioOutputStreamTimestamped);	if (audioResult != S_OK) {		LOG(LOG_ERROR, "Failed to enable audio output");		return false;	}	if (!mode_->GetFrameRate(&frameDuration, &frameTimescale)) {		LOG(LOG_ERROR, "Failed to get frame rate");		return false;	}	ComPtr<IDeckLinkKeyer> deckLinkKeyer;	if (device->GetKeyer(&deckLinkKeyer)) {		const int keyerMode = device->GetKeyerMode();		if (keyerMode) {			deckLinkKeyer->Enable(keyerMode == 1);			deckLinkKeyer->SetLevel(255);		} else {			deckLinkKeyer->Disable();		}	}	frameQueueDecklinkToObs.reset();	frameQueueObsToDecklink.reset();	const int rowSize = decklinkOutput->GetWidth() * 4;	const int frameSize = rowSize * decklinkOutput->GetHeight();	for (std::vector<uint8_t> &blob : frameBlobs) {		blob.assign(frameSize, 0);		frameQueueDecklinkToObs.push(blob.data());	}	activeBlob = nullptr;	struct obs_video_info ovi;	const enum video_colorspace colorspace = obs_get_video_info(&ovi) ? ovi.colorspace : VIDEO_CS_DEFAULT;	const bool source_hdr = (colorspace == VIDEO_CS_2100_PQ) || (colorspace == VIDEO_CS_2100_HLG);	const bool enable_hdr =		source_hdr &&		(obs_output_get_video_conversion(decklinkOutput->GetOutput())->colorspace == VIDEO_CS_2100_PQ);	BMDPixelFormat pixelFormat = enable_hdr ? bmdFormat10BitRGBXLE : bmdFormat8BitBGRA;	const int64_t minimumPrerollFrames = std::max(device->GetMinimumPrerollFrames(), INT64_C(3));	for (int64_t i = 0; i < minimumPrerollFrames; ++i) {		ComPtr<IDeckLinkMutableVideoFrame> decklinkOutputFrame;		HRESULT result = output_->CreateVideoFrame(decklinkOutput->GetWidth(), decklinkOutput->GetHeight(),							   rowSize, pixelFormat, bmdFrameFlagDefault,							   &decklinkOutputFrame);		if (result != S_OK) {			blog(LOG_ERROR, "failed to create video frame 0x%X", result);			return false;		}		IDeckLinkVideoFrame *theFrame = decklinkOutputFrame.Get();		ComPtr<HDRVideoFrame> decklinkOutputHDRFrame;		if (enable_hdr) {			*decklinkOutputHDRFrame.Assign() = new HDRVideoFrame(decklinkOutputFrame);			theFrame = decklinkOutputHDRFrame.Get();		}		result = output_->ScheduleVideoFrame(theFrame, i * frameDuration, frameDuration, frameTimescale);		if (result != S_OK) {			blog(LOG_ERROR, "failed to schedule video frame for preroll 0x%X", result);			return false;		}	}	totalFramesScheduled = minimumPrerollFrames;	*renderDelegate.Assign() = new RenderDelegate<DeckLinkDeviceInstance>(this);	output_->SetScheduledFrameCompletionCallback(renderDelegate);	output_->StartScheduledPlayback(0, 100, 1.0);	mode = mode_;	output = std::move(output_);	return true;}bool DeckLinkDeviceInstance::StopOutput(){	if (mode == nullptr || output == nullptr)		return false;	LOG(LOG_INFO, "Stopping output of '%s'...", GetDevice()->GetDisplayName().c_str());	output->SetScheduledFrameCompletionCallback(NULL);	output->DisableVideoOutput();	output->DisableAudioOutput();	output.Clear();	renderDelegate.Clear();	frameQueueDecklinkToObs.reset();	frameQueueObsToDecklink.reset();	return true;}void DeckLinkDeviceInstance::UpdateVideoFrame(video_data *frame){	auto decklinkOutput = dynamic_cast<DeckLinkOutput *>(decklink);	if (decklinkOutput == nullptr)		return;	uint8_t *const blob = frameQueueDecklinkToObs.pop();	if (blob) {		memcpy(blob, frame->data[0], frame->linesize[0] * decklinkOutput->GetHeight());		frameQueueObsToDecklink.push(blob);	}}void DeckLinkDeviceInstance::ScheduleVideoFrame(IDeckLinkVideoFrame *frame){	void *bytes;	if (SUCCEEDED(frame->GetBytes(&bytes))) {		uint8_t *blob = frameQueueObsToDecklink.pop();		if (blob) {			if (activeBlob)				frameQueueDecklinkToObs.push(activeBlob);			activeBlob = blob;		} else {			blob = activeBlob;		}		const int frameSize = frame->GetRowBytes() * frame->GetHeight();		if (blob)			memcpy(bytes, blob, frameSize);		else			memset(bytes, 0, frameSize);		output->ScheduleVideoFrame(frame, totalFramesScheduled * frameDuration, frameDuration, frameTimescale);		++totalFramesScheduled;	}}void DeckLinkDeviceInstance::WriteAudio(audio_data *frames){	uint32_t sampleFramesWritten;	output->WriteAudioSamplesSync(frames->data[0], frames->frames, &sampleFramesWritten);}#define TIME_BASE 1000000000HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(IDeckLinkVideoInputFrame *videoFrame,									 IDeckLinkAudioInputPacket *audioPacket){	BMDTimeValue videoTS = 0;	BMDTimeValue videoDur = 0;	BMDTimeValue audioTS = 0;	if (videoFrame) {		videoFrame->GetStreamTime(&videoTS, &videoDur, TIME_BASE);		lastVideoTS = (uint64_t)videoTS;	}	if (audioPacket) {		BMDTimeValue newAudioTS = 0;		int64_t diff;		audioPacket->GetPacketTime(&newAudioTS, TIME_BASE);		audioTS = newAudioTS + audioOffset;		diff = (int64_t)audioTS - (int64_t)nextAudioTS;		if (diff > 10000000LL) {			audioOffset -= diff;			audioTS = newAudioTS + audioOffset;		} else if (diff < -1000000) {			audioOffset = 0;			audioTS = newAudioTS;		}	}	if (videoFrame && videoTS >= 0)		HandleVideoFrame(videoFrame, (uint64_t)videoTS);	if (audioPacket && audioTS >= 0)		HandleAudioPacket(audioPacket, (uint64_t)audioTS);	return S_OK;}HRESULT STDMETHODCALLTYPEDeckLinkDeviceInstance::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *newMode,						BMDDetectedVideoInputFormatFlags detectedSignalFlags){	bool formatChanged = false;	if (events & bmdVideoInputColorspaceChanged) {		constexpr BMDDetectedVideoInputFormatFlags highBitFlags =			(bmdDetectedVideoInput12BitDepth | bmdDetectedVideoInput10BitDepth);		if (detectedSignalFlags & bmdDetectedVideoInputRGB444) {			const BMDPixelFormat nextFormat = ((detectedSignalFlags & highBitFlags) && allow10Bit)								  ? bmdFormat10BitRGBXLE								  : bmdFormat8BitBGRA;			formatChanged = pixelFormat != nextFormat;			pixelFormat = nextFormat;		}		if (detectedSignalFlags & bmdDetectedVideoInputYCbCr422) {			const BMDPixelFormat nextFormat = ((detectedSignalFlags & highBitFlags) && allow10Bit)								  ? bmdFormat10BitYUV								  : bmdFormat8BitYUV;			formatChanged = pixelFormat != nextFormat;			pixelFormat = nextFormat;		}	}	if (formatChanged || (events & bmdVideoInputDisplayModeChanged)) {		input->PauseStreams();		mode->SetMode(newMode);		displayMode = mode->GetDisplayMode();		const HRESULT videoResult =			input->EnableVideoInput(displayMode, pixelFormat, bmdVideoInputEnableFormatDetection);		if (videoResult != S_OK) {			LOG(LOG_ERROR, "Failed to enable video input");			input->StopStreams();			FinalizeStream();			return E_FAIL;		}		SetupVideoFormat(mode);		input->FlushStreams();		input->StartStreams();	}	return S_OK;}ULONG STDMETHODCALLTYPE DeckLinkDeviceInstance::AddRef(void){	return os_atomic_inc_long(&refCount);}HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::QueryInterface(REFIID iid, LPVOID *ppv){	HRESULT result = E_NOINTERFACE;	*ppv = nullptr;	CFUUIDBytes unknown = CFUUIDGetUUIDBytes(IUnknownUUID);	if (memcmp(&iid, &unknown, sizeof(REFIID)) == 0) {		*ppv = this;		AddRef();		result = S_OK;	} else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) == 0) {		*ppv = (IDeckLinkNotificationCallback *)this;		AddRef();		result = S_OK;	}	return result;}ULONG STDMETHODCALLTYPE DeckLinkDeviceInstance::Release(void){	const long newRefCount = os_atomic_dec_long(&refCount);	if (newRefCount == 0) {		delete this;		return 0;	}	return newRefCount;}
 |