123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- #include "decklink-device-instance.hpp"
- #include "audio-repack.hpp"
- #include "DecklinkInput.hpp"
- #include "DecklinkOutput.hpp"
- #include <util/platform.h>
- #include <util/threading.h>
- #include <sstream>
- #include <algorithm>
- static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
- {
- switch (format) {
- case bmdFormat8BitBGRA: return VIDEO_FORMAT_BGRX;
- 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)
- {
- switch (format) {
- case SPEAKERS_2POINT1:
- return repack_mode_8to3ch;
- case SPEAKERS_4POINT0:
- return repack_mode_8to4ch;
- case SPEAKERS_4POINT1:
- return repack_mode_8to5ch;
- case SPEAKERS_5POINT1:
- return repack_mode_8to6ch;
- case SPEAKERS_7POINT1:
- default:
- assert(false && "No repack requested");
- return (audio_repack_mode_t)-1;
- }
- }
- DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_,
- DeckLinkDevice *device_) :
- currentFrame(), currentPacket(), decklink(decklink_), device(device_)
- {
- currentPacket.samples_per_sec = 48000;
- currentPacket.speakers = SPEAKERS_STEREO;
- currentPacket.format = AUDIO_FORMAT_16BIT;
- }
- DeckLinkDeviceInstance::~DeckLinkDeviceInstance()
- {
- }
- 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 -=
- (uint64_t)frameCount * 1000000000ULL /
- (uint64_t)currentPacket.samples_per_sec;
- }
- int maxdevicechannel = device->GetMaxChannel();
- if (channelFormat != SPEAKERS_UNKNOWN &&
- channelFormat != SPEAKERS_MONO &&
- channelFormat != SPEAKERS_STEREO &&
- channelFormat != SPEAKERS_7POINT1 &&
- 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 +
- ((uint64_t)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;
- void *bytes;
- if (videoFrame->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)videoFrame->GetRowBytes();
- currentFrame.width = (uint32_t)videoFrame->GetWidth();
- currentFrame.height = (uint32_t)videoFrame->GetHeight();
- currentFrame.timestamp = timestamp;
- obs_source_output_video(static_cast<DeckLinkInput*>(decklink)->GetSource(), ¤tFrame);
- }
- 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 1
- void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
- {
- if (mode_ == nullptr)
- return;
- currentFrame.format = ConvertPixelFormat(pixelFormat);
- colorSpace = static_cast<DeckLinkInput*>(decklink)->GetColorSpace();
- if (colorSpace == VIDEO_CS_DEFAULT) {
- const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();
- 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.full_range = colorRange == VIDEO_RANGE_FULL;
- video_format_get_parameters(activeColorSpace, colorRange,
- currentFrame.color_matrix, currentFrame.color_range_min,
- currentFrame.color_range_max);
- #ifdef LOG_SETUP_VIDEO_FORMAT
- LOG(LOG_INFO, "Setup video format: %s, %s, %s",
- pixelFormat == bmdFormat8BitYUV ? "YUV" : "RGB",
- activeColorSpace == VIDEO_CS_709 ? "BT.709" : "BT.601",
- colorRange == VIDEO_RANGE_FULL ? "full" : "limited");
- #endif
- }
- bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
- {
- if (mode != nullptr)
- return false;
- if (mode_ == nullptr)
- return false;
- LOG(LOG_INFO, "Starting capture...");
- if (!device->GetInput(&input))
- return false;
- BMDVideoInputFlags flags;
- bool isauto = mode_->GetName() == "Auto";
- if (isauto) {
- displayMode = bmdModeNTSC;
- pixelFormat = bmdFormat8BitYUV;
- flags = bmdVideoInputEnableFormatDetection;
- } else {
- displayMode = mode_->GetDisplayMode();
- pixelFormat = static_cast<DeckLinkInput*>(decklink)->GetPixelFormat();
- flags = bmdVideoInputFlagDefault;
- }
- 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;
- 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 &&
- maxdevicechannel >= 8) {
- const audio_repack_mode_t repack_mode = ConvertRepackFormat
- (channelFormat);
- 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;
- LOG(LOG_INFO, "Starting 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;
- }
- mode = mode_;
- int keyerMode = device->GetKeyerMode();
- IDeckLinkKeyer *deckLinkKeyer = nullptr;
- if (device->GetKeyer(&deckLinkKeyer)) {
- if (keyerMode) {
- deckLinkKeyer->Enable(keyerMode == 1);
- deckLinkKeyer->SetLevel(255);
- } else {
- deckLinkKeyer->Disable();
- }
- }
- auto decklinkOutput = dynamic_cast<DeckLinkOutput*>(decklink);
- if (decklinkOutput == nullptr)
- return false;
- int rowBytes = decklinkOutput->GetWidth() * 2;
- if (decklinkOutput->keyerMode != 0) {
- rowBytes = decklinkOutput->GetWidth() * 4;
- }
- BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
- if (keyerMode != 0) {
- pixelFormat = bmdFormat8BitBGRA;
- }
- HRESULT result;
- result = output->CreateVideoFrame(decklinkOutput->GetWidth(),
- decklinkOutput->GetHeight(),
- rowBytes,
- pixelFormat,
- bmdFrameFlagDefault,
- &decklinkOutputFrame);
- if (result != S_OK) {
- blog(LOG_ERROR ,"failed to make frame 0x%X", result);
- return false;
- }
- return true;
- }
- bool DeckLinkDeviceInstance::StopOutput()
- {
- if (mode == nullptr || output == nullptr)
- return false;
- LOG(LOG_INFO, "Stopping output of '%s'...",
- GetDevice()->GetDisplayName().c_str());
- output->DisableVideoOutput();
- output->DisableAudioOutput();
- if (decklinkOutputFrame != nullptr) {
- decklinkOutputFrame->Release();
- decklinkOutputFrame = nullptr;
- }
- return true;
- }
- void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame)
- {
- auto decklinkOutput = dynamic_cast<DeckLinkOutput*>(decklink);
- if (decklinkOutput == nullptr)
- return;
- uint8_t *destData;
- decklinkOutputFrame->GetBytes((void**)&destData);
- uint8_t *outData = frame->data[0];
- int rowBytes = decklinkOutput->GetWidth() * 2;
- if (device->GetKeyerMode()) {
- rowBytes = decklinkOutput->GetWidth() * 4;
- }
- std::copy(outData, outData + (decklinkOutput->GetHeight() *
- rowBytes), destData);
- output->DisplayVideoFrameSync(decklinkOutputFrame);
- }
- void DeckLinkDeviceInstance::WriteAudio(audio_data *frames)
- {
- uint32_t sampleFramesWritten;
- output->WriteAudioSamplesSync(frames->data[0],
- frames->frames,
- &sampleFramesWritten);
- }
- #define TIME_BASE 1000000000
- HRESULT 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 STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged(
- BMDVideoInputFormatChangedEvents events,
- IDeckLinkDisplayMode *newMode,
- BMDDetectedVideoInputFormatFlags detectedSignalFlags)
- {
- input->PauseStreams();
- mode->SetMode(newMode);
- if (events & bmdVideoInputDisplayModeChanged) {
- displayMode = mode->GetDisplayMode();
- }
- if (events & bmdVideoInputColorspaceChanged) {
- switch (detectedSignalFlags) {
- case bmdDetectedVideoInputRGB444:
- pixelFormat = bmdFormat8BitBGRA;
- break;
- default:
- case bmdDetectedVideoInputYCbCr422:
- pixelFormat = bmdFormat8BitYUV;
- break;
- }
- }
- 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;
- }
|