decklink: Pass frames between threads using queues

Eliminates the stalls that have been seen when waiting for frame copies.

We shouldn't be copying at all ideally, but that would break the API.
This commit is contained in:
jpark37
2023-03-29 21:35:07 -07:00
committed by Jim
parent 7475261542
commit b2a83a3145
2 changed files with 139 additions and 21 deletions

View File

@@ -613,24 +613,31 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
}
}
frameData.clear();
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;
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(),
decklinkOutput->GetWidth() * 4, bmdFormat8BitBGRA,
bmdFrameFlagDefault, &decklinkOutputFrame);
rowSize, bmdFormat8BitBGRA, bmdFrameFlagDefault,
&decklinkOutputFrame);
if (result != S_OK) {
blog(LOG_ERROR, "failed to create video frame 0x%X",
result);
return false;
}
const long size = decklinkOutputFrame->GetRowBytes() *
decklinkOutputFrame->GetHeight();
frameData.resize(size);
result = output_->ScheduleVideoFrame(decklinkOutputFrame,
i * frameDuration,
frameDuration,
@@ -669,6 +676,8 @@ bool DeckLinkDeviceInstance::StopOutput()
output->DisableAudioOutput();
output.Clear();
renderDelegate.Clear();
frameQueueDecklinkToObs.reset();
frameQueueObsToDecklink.reset();
return true;
}
@@ -679,26 +688,36 @@ void DeckLinkDeviceInstance::UpdateVideoFrame(video_data *frame)
if (decklinkOutput == nullptr)
return;
std::lock_guard lock(frameDataMutex);
const uint8_t *const outData = frame->data[0];
frameData.assign(outData,
outData + decklinkOutput->GetWidth() *
decklinkOutput->GetHeight() * 4);
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))) {
{
std::lock_guard lock(frameDataMutex);
memcpy(bytes, frameData.data(),
frame->GetRowBytes() * frame->GetHeight());
uint8_t *blob = frameQueueObsToDecklink.pop();
if (blob) {
if (activeBlob)
frameQueueDecklinkToObs.push(activeBlob);
activeBlob = blob;
} else {
blob = activeBlob;
}
output->ScheduleVideoFrame(
frame, (totalFramesScheduled * frameDuration),
frameDuration, frameTimescale);
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;
}
}