decklink-device-instance.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #include "decklink-device-instance.hpp"
  2. #include <util/platform.h>
  3. #include <util/threading.h>
  4. #include <sstream>
  5. #define LOG(level, message, ...) blog(level, "%s: " message, \
  6. obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__)
  7. DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_,
  8. DeckLinkDevice *device_) :
  9. currentFrame(), currentPacket(), decklink(decklink_), device(device_)
  10. {
  11. currentFrame.format = VIDEO_FORMAT_UYVY;
  12. currentPacket.samples_per_sec = 48000;
  13. currentPacket.speakers = SPEAKERS_STEREO;
  14. currentPacket.format = AUDIO_FORMAT_16BIT;
  15. }
  16. DeckLinkDeviceInstance::~DeckLinkDeviceInstance()
  17. {
  18. }
  19. void DeckLinkDeviceInstance::HandleAudioPacket(
  20. IDeckLinkAudioInputPacket *audioPacket,
  21. const uint64_t timestamp)
  22. {
  23. if (audioPacket == nullptr)
  24. return;
  25. void *bytes;
  26. if (audioPacket->GetBytes(&bytes) != S_OK) {
  27. LOG(LOG_WARNING, "Failed to get audio packet data");
  28. return;
  29. }
  30. currentPacket.data[0] = (uint8_t *)bytes;
  31. currentPacket.frames = (uint32_t)audioPacket->GetSampleFrameCount();
  32. currentPacket.timestamp = timestamp;
  33. obs_source_output_audio(decklink->GetSource(), &currentPacket);
  34. }
  35. void DeckLinkDeviceInstance::HandleVideoFrame(
  36. IDeckLinkVideoInputFrame *videoFrame, const uint64_t timestamp)
  37. {
  38. if (videoFrame == nullptr)
  39. return;
  40. void *bytes;
  41. if (videoFrame->GetBytes(&bytes) != S_OK) {
  42. LOG(LOG_WARNING, "Failed to get video frame data");
  43. return;
  44. }
  45. currentFrame.data[0] = (uint8_t *)bytes;
  46. currentFrame.linesize[0] = (uint32_t)videoFrame->GetRowBytes();
  47. currentFrame.width = (uint32_t)videoFrame->GetWidth();
  48. currentFrame.height = (uint32_t)videoFrame->GetHeight();
  49. currentFrame.timestamp = timestamp;
  50. video_format_get_parameters(VIDEO_CS_601, VIDEO_RANGE_PARTIAL,
  51. currentFrame.color_matrix, currentFrame.color_range_min,
  52. currentFrame.color_range_max);
  53. obs_source_output_video(decklink->GetSource(), &currentFrame);
  54. }
  55. bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
  56. {
  57. if (mode != nullptr)
  58. return false;
  59. if (mode_ == nullptr)
  60. return false;
  61. LOG(LOG_INFO, "Starting capture...");
  62. if (!device->GetInput(&input))
  63. return false;
  64. input->SetCallback(this);
  65. const BMDDisplayMode displayMode = mode_->GetDisplayMode();
  66. const HRESULT videoResult = input->EnableVideoInput(displayMode,
  67. bmdFormat8BitYUV, bmdVideoInputFlagDefault);
  68. if (videoResult != S_OK) {
  69. LOG(LOG_ERROR, "Failed to enable video input");
  70. input->SetCallback(nullptr);
  71. return false;
  72. }
  73. const HRESULT audioResult = input->EnableAudioInput(
  74. bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
  75. 2);
  76. if (audioResult != S_OK)
  77. LOG(LOG_WARNING, "Failed to enable audio input; continuing...");
  78. if (input->StartStreams() != S_OK) {
  79. LOG(LOG_ERROR, "Failed to start streams");
  80. input->SetCallback(nullptr);
  81. input->DisableVideoInput();
  82. input->DisableAudioInput();
  83. return false;
  84. }
  85. mode = mode_;
  86. return true;
  87. }
  88. bool DeckLinkDeviceInstance::StopCapture(void)
  89. {
  90. if (mode == nullptr || input == nullptr)
  91. return false;
  92. LOG(LOG_INFO, "Stopping capture of '%s'...",
  93. GetDevice()->GetDisplayName().c_str());
  94. input->StopStreams();
  95. input->SetCallback(nullptr);
  96. input->DisableVideoInput();
  97. input->DisableAudioInput();
  98. mode = nullptr;
  99. return true;
  100. }
  101. #define TIME_BASE 1000000000
  102. HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(
  103. IDeckLinkVideoInputFrame *videoFrame,
  104. IDeckLinkAudioInputPacket *audioPacket)
  105. {
  106. BMDTimeValue videoTS = 0;
  107. BMDTimeValue videoDur = 0;
  108. BMDTimeValue audioTS = 0;
  109. videoFrame->GetStreamTime(&videoTS, &videoDur, TIME_BASE);
  110. audioPacket->GetPacketTime(&audioTS, TIME_BASE);
  111. if (videoTS >= 0)
  112. HandleVideoFrame(videoFrame, (uint64_t)videoTS);
  113. if (audioTS >= 0)
  114. HandleAudioPacket(audioPacket, (uint64_t)audioTS);
  115. return S_OK;
  116. }
  117. HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged(
  118. BMDVideoInputFormatChangedEvents events,
  119. IDeckLinkDisplayMode *newMode,
  120. BMDDetectedVideoInputFormatFlags detectedSignalFlags)
  121. {
  122. UNUSED_PARAMETER(events);
  123. UNUSED_PARAMETER(newMode);
  124. UNUSED_PARAMETER(detectedSignalFlags);
  125. // There is no implementation for automatic format detection, so this
  126. // method goes unused.
  127. return S_OK;
  128. }
  129. ULONG STDMETHODCALLTYPE DeckLinkDeviceInstance::AddRef(void)
  130. {
  131. return os_atomic_inc_long(&refCount);
  132. }
  133. HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::QueryInterface(REFIID iid,
  134. LPVOID *ppv)
  135. {
  136. HRESULT result = E_NOINTERFACE;
  137. *ppv = nullptr;
  138. CFUUIDBytes unknown = CFUUIDGetUUIDBytes(IUnknownUUID);
  139. if (memcmp(&iid, &unknown, sizeof(REFIID)) == 0) {
  140. *ppv = this;
  141. AddRef();
  142. result = S_OK;
  143. } else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback,
  144. sizeof(REFIID)) == 0) {
  145. *ppv = (IDeckLinkNotificationCallback *)this;
  146. AddRef();
  147. result = S_OK;
  148. }
  149. return result;
  150. }
  151. ULONG STDMETHODCALLTYPE DeckLinkDeviceInstance::Release(void)
  152. {
  153. const long newRefCount = os_atomic_dec_long(&refCount);
  154. if (newRefCount == 0) {
  155. delete this;
  156. return 0;
  157. }
  158. return newRefCount;
  159. }