// Copyright Epic Games, Inc. All Rights Reserved. #include "AutoDetectChannel.h" #include "Helpers.h" #include "DeviceScanner.h" namespace AJA { namespace Private { /* AutoDetectChannel implementation *****************************************************************************/ bool AutoDetectChannel::Initialize(IAJAAutoDetectCallbackInterface* InCallbackInterface) { Uninitialize(); AutoDetectImplementation.reset(new AutoDetectChannelImpl(InCallbackInterface)); bool bResult = AutoDetectImplementation->CanInitialize(); if (bResult) { // Get the first device. It doesn't matter which one since we want only want the device to detect the input for other device. AJADeviceOptions DeviceOption = AJADeviceOptions(0); Device = DeviceCache::GetDevice(DeviceOption); std::shared_ptr SharedCommand(new AutoDetectChannelInitialize_DeviceCommand(Device, AutoDetectImplementation)); InitializeCommand = SharedCommand; Device->AddCommand(std::static_pointer_cast(SharedCommand)); } return bResult; } void AutoDetectChannel::Uninitialize() { if (AutoDetectImplementation) { std::shared_ptr SharedCommand = InitializeCommand.lock(); if (SharedCommand && Device) { Device->RemoveCommand(std::static_pointer_cast(SharedCommand)); } AutoDetectImplementation->Uninitialize(); // delete is done in AutoDetectChannelUninitialize_DeviceCommand completed if (Device) { std::shared_ptr UninitializeCommand; UninitializeCommand.reset(new AutoDetectChannelUninitialize_DeviceCommand(Device, AutoDetectImplementation)); Device->AddCommand(std::static_pointer_cast(UninitializeCommand)); } } InitializeCommand.reset(); AutoDetectImplementation.reset(); Device.reset(); } std::vector AutoDetectChannel::GetFoundChannels() const { if (AutoDetectImplementation) { return AutoDetectImplementation->GetFoundChannels(); } return std::vector(); } /* AutoDetectChannelInitialize_DeviceCommand implementation *****************************************************************************/ AutoDetectChannelInitialize_DeviceCommand::AutoDetectChannelInitialize_DeviceCommand(const std::shared_ptr& InConnection, const std::weak_ptr& InChannel) : DeviceCommand(InConnection) , AutoDetectImplementation(InChannel) {} void AutoDetectChannelInitialize_DeviceCommand::Execute(DeviceConnection::CommandList& InCommandList) { std::shared_ptr Shared = AutoDetectImplementation.lock(); if (Shared) // could have been Uninitialize before we have time to execute it { if (!Shared->Thread_Initialize(InCommandList)) { Shared->Thread_Destroy(InCommandList); } } AutoDetectImplementation.reset(); } /* AutoDetectChannelUninitialize_DeviceCommand implementation *****************************************************************************/ AutoDetectChannelUninitialize_DeviceCommand::AutoDetectChannelUninitialize_DeviceCommand(const std::shared_ptr& InConnection, const std::weak_ptr& InChannel) : DeviceCommand(InConnection) , AutoDetectImplementation(InChannel) {} void AutoDetectChannelUninitialize_DeviceCommand::Execute(DeviceConnection::CommandList& InCommandList) { // keep a shared pointer to prevent the destruction of the SyncChannel until its turn comes up AutoDetectImplementation->Thread_Destroy(InCommandList); AutoDetectImplementation.reset(); } /* AutoDetectChannelImpl implementation *****************************************************************************/ AutoDetectChannelImpl::AutoDetectChannelImpl(IAJAAutoDetectCallbackInterface* InCallbackInterface) : CallbackInterface(InCallbackInterface) //, Card(nullptr) , bStopRequested(false) , bProcessCompleted(false) { } AutoDetectChannelImpl::~AutoDetectChannelImpl() { //assert(Card == nullptr); } bool AutoDetectChannelImpl::CanInitialize() const { return true; } bool AutoDetectChannelImpl::Thread_Initialize(DeviceConnection::CommandList& InCommandList) { bool bResult = Thread_Configure(InCommandList); bProcessCompleted = true; Thread_OnInitializationCompleted(bResult); return bResult; } void AutoDetectChannelImpl::Uninitialize() { AJAAutoLock AutoLock(&Lock); CallbackInterface = nullptr; bStopRequested = true; } void AutoDetectChannelImpl::Thread_Destroy(DeviceConnection::CommandList& InCommandList) { } std::vector AutoDetectChannelImpl::GetFoundChannels() const { if (bProcessCompleted) { return FoundChannels; } return std::vector(); } void AutoDetectChannelImpl::Thread_OnInitializationCompleted(bool bSucceed) { AJAAutoLock AutoLock(&Lock); if (CallbackInterface && !bStopRequested) { CallbackInterface->OnCompletion(bSucceed); } } bool AutoDetectChannelImpl::Thread_Configure(DeviceConnection::CommandList& InCommandList) { if (bStopRequested) { return false; } if (!CanInitialize()) { return false; } if (bStopRequested) { return false; } std::unique_ptr Scanner = std::make_unique(); const int32_t NumberOfDevices = Scanner->GetNumDevices(); for (int32_t DeviceIndex = 0; DeviceIndex < NumberOfDevices; ++DeviceIndex) { if (bStopRequested) { return false; } AJADeviceScanner::DeviceInfo DeviceInfo; bool bValidDeviceInfo = Scanner->GetDeviceInfo(DeviceIndex, DeviceInfo); bValidDeviceInfo = bValidDeviceInfo && DeviceInfo.bIsSupported; if (!bValidDeviceInfo) { UE_LOG(LogTemp, Display, TEXT("AutoDetectChannel: Device %d is not valid.\n"), DeviceIndex); break; } if (!Thread_Detect(DeviceIndex, DeviceInfo)) { break; } } return !bStopRequested; } bool AutoDetectChannelImpl::Thread_Detect(int32_t InDeviceIndex, const AJADeviceScanner::DeviceInfo& InDeviceInfo) { std::unique_ptr NewCard = std::make_unique(InDeviceIndex); uint32_t Counter = 0; while (!NewCard->IsDeviceReady()) { const DWORD SleepMilli = 10; const DWORD TimeoutMilli = 2000; if (Counter * SleepMilli > TimeoutMilli) { UE_LOG(LogTemp, Display, TEXT("AutoDetectChannel: Can't get the %d device initialized.\n"), InDeviceIndex); return false; } ++Counter; ::Sleep(SleepMilli); } // save the service level. NTV2EveryFrameTaskMode TaskMode; NewCard->GetEveryFrameServices(TaskMode); // Use the OEM service level. NewCard->SetEveryFrameServices(NTV2_OEM_TASKS); ::Sleep(50 * 1); // Wait 1 frame const bool bIsSDI = true; const bool bHasBiDirectionalSDI = ::NTV2DeviceHasBiDirectionalSDI(NewCard->GetDeviceID()); std::vector ChannelsToEnable; for (int32_t InputSourceIndex = 0; InputSourceIndex < InDeviceInfo.NumSdiInput; ++InputSourceIndex) { const NTV2Channel Channel = NTV2Channel(InputSourceIndex); bool bChannelEnabled = false; AJA_CHECK(NewCard->IsChannelEnabled(Channel, bChannelEnabled)); if (!bChannelEnabled) { ChannelsToEnable.push_back(Channel); AJA_CHECK(NewCard->EnableChannel(Channel)); } if (bHasBiDirectionalSDI) { AJA_CHECK(NewCard->SetSDITransmitEnable(Channel, false)); } } ::Sleep(50 * 12); // Wait 12 frames for (int32_t InputSourceIndex = 0; InputSourceIndex < InDeviceInfo.NumSdiInput; ++InputSourceIndex) { const NTV2Channel Channel = NTV2Channel(InputSourceIndex); const NTV2InputSource InputSource = bIsSDI ? GetNTV2InputSourceForIndex(InputSourceIndex) : GetNTV2HDMIInputSourceForIndex(InputSourceIndex); bool bIsProgressive = true; NTV2VideoFormat FoundVideoFormat = NewCard->GetInputVideoFormat(InputSource, bIsProgressive); if (FoundVideoFormat != NTV2_FORMAT_UNKNOWN) { AJAAutoDetectChannel::AutoDetectChannelData AutoData; AutoData.DeviceIndex = InDeviceIndex; AutoData.ChannelIndex = InputSourceIndex; AutoData.DetectedVideoFormat = FoundVideoFormat; AutoData.TimecodeFormat = Helpers::GetTimecodeFormat(NewCard.get(), Channel); FoundChannels.push_back(AutoData); } } for (NTV2Channel Channel : ChannelsToEnable) { AJA_CHECK(NewCard->DisableChannel(Channel)); } NewCard->SetEveryFrameServices(TaskMode); return true; } } }