X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Fdecklink_output.cpp;h=464aba9ebae107589bf1496d64d490541d433d35;hb=f81ae3be1aae619fe4ad022f55d95a4a83ace076;hp=da172af686acbc3ff7acf489a7befd01a2efb2f7;hpb=cd1b6b6b8dd3d4d6f52d917cec3e5a152f87e822;p=nageru diff --git a/nageru/decklink_output.cpp b/nageru/decklink_output.cpp index da172af..464aba9 100644 --- a/nageru/decklink_output.cpp +++ b/nageru/decklink_output.cpp @@ -1,19 +1,39 @@ -#include +#include "DeckLinkAPI.h" +#include "DeckLinkAPIConfiguration.h" +#include "DeckLinkAPIModes.h" +#include "DeckLinkAPITypes.h" +#include "LinuxCOM.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include // Must be above the Xlib includes. #include -#include +#include #include #include +#include +#include #include "chroma_subsampler.h" #include "decklink_output.h" #include "decklink_util.h" #include "flags.h" +#include "ref_counted_frame.h" #include "shared/metrics.h" #include "print_latency.h" +#include "shared/ref_counted_gl_sync.h" +#include "shared/shared_defs.h" #include "shared/timebase.h" #include "v210_converter.h" @@ -82,11 +102,16 @@ DeckLinkOutput::DeckLinkOutput(ResourcePool *resource_pool, QSurface *surface, u }); } -bool DeckLinkOutput::set_device(IDeckLink *decklink) +DeckLinkOutput::~DeckLinkOutput() { - if (decklink->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) { - input = nullptr; + if (output != nullptr) { + output->Release(); } +} + +bool DeckLinkOutput::set_device(IDeckLink *decklink, IDeckLinkInput *input_arg) +{ + input = input_arg; if (decklink->QueryInterface(IID_IDeckLinkOutput, (void**)&output) != S_OK) { fprintf(stderr, "Warning: Card %u has no outputs\n", card_index); return false; @@ -126,10 +151,11 @@ bool DeckLinkOutput::set_device(IDeckLink *decklink) return true; } -void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) +void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts, bool is_master_card_arg) { assert(output); assert(!playback_initiated); + this->is_master_card = is_master_card_arg; if (video_modes.empty()) { fprintf(stderr, "ERROR: No matching output modes for %dx%d found\n", width, height); @@ -166,7 +192,7 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) BMDDisplayModeSupport support; IDeckLinkDisplayMode *display_mode; - BMDPixelFormat pixel_format = global_flags.ten_bit_output ? bmdFormat10BitYUV : bmdFormat8BitYUV; + BMDPixelFormat pixel_format = global_flags.bit_depth > 8 ? bmdFormat10BitYUV : bmdFormat8BitYUV; if (output->DoesSupportVideoMode(mode, pixel_format, bmdVideoOutputFlagDefault, &support, &display_mode) != S_OK) { fprintf(stderr, "Couldn't ask for format support\n"); @@ -219,9 +245,13 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) fprintf(stderr, "Couldn't enable audio output\n"); abort(); } - if (output->BeginAudioPreroll() != S_OK) { - fprintf(stderr, "Couldn't begin audio preroll\n"); - abort(); + if (is_master_card) { + if (output->BeginAudioPreroll() != S_OK) { + fprintf(stderr, "Couldn't begin audio preroll\n"); + abort(); + } + } else { + playback_started = true; } present_thread = thread([this]{ @@ -248,7 +278,9 @@ void DeckLinkOutput::end_output() present_thread.join(); playback_initiated = false; - output->StopScheduledPlayback(0, nullptr, 0); + if (is_master_card) { + output->StopScheduledPlayback(0, nullptr, 0); + } output->DisableVideoOutput(); output->DisableAudioOutput(); @@ -260,15 +292,6 @@ void DeckLinkOutput::end_output() frame_freelist.pop(); } } - - if (input != nullptr) { - input->Release(); - input = nullptr; - } - if (output != nullptr) { - output->Release(); - output = nullptr; - } } void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoefficients output_ycbcr_coefficients, const vector &input_frames, int64_t pts, int64_t duration) @@ -294,7 +317,7 @@ void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoeffici } unique_ptr frame = get_frame(); - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { chroma_subsampler->create_v210(y_tex, cbcr_tex, width, height, frame->uyvy_tex); } else { chroma_subsampler->create_uyvy(y_tex, cbcr_tex, width, height, frame->uyvy_tex); @@ -307,7 +330,7 @@ void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoeffici glBindBuffer(GL_PIXEL_PACK_BUFFER, frame->pbo); check_error(); - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { glBindTexture(GL_TEXTURE_2D, frame->uyvy_tex); check_error(); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, BUFFER_OFFSET(0)); @@ -352,13 +375,20 @@ void DeckLinkOutput::send_audio(int64_t pts, const std::vector &samples) } uint32_t frames_written; - HRESULT result = output->ScheduleAudioSamples(int_samples.get(), samples.size() / 2, - pts, TIMEBASE, &frames_written); + HRESULT result; + if (is_master_card) { + result = output->ScheduleAudioSamples(int_samples.get(), samples.size() / 2, + pts, TIMEBASE, &frames_written); + } else { + result = output->WriteAudioSamplesSync(int_samples.get(), samples.size() / 2, + &frames_written); + } if (result != S_OK) { - fprintf(stderr, "ScheduleAudioSamples(pts=%" PRId64 ") failed (result=0x%08x)\n", pts, result); + fprintf(stderr, "write audio to DeckLink (pts=%" PRId64 ") failed (result=0x%08x)\n", pts, result); } else { - if (frames_written != samples.size() / 2) { - fprintf(stderr, "ScheduleAudioSamples() returned short write (%u/%zu)\n", frames_written, samples.size() / 2); + // Non-master card is not really synchronized on audio at all, so we don't warn on it. + if (frames_written != samples.size() / 2 && is_master_card) { + fprintf(stderr, "write audio to DeckLink returned short write (%u/%zu)\n", frames_written, samples.size() / 2); } } metric_decklink_output_scheduled_samples += samples.size() / 2; @@ -503,8 +533,8 @@ HRESULT DeckLinkOutput::ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *co { lock_guard lock(frame_queue_mutex); frame_freelist.push(unique_ptr(frame)); - assert(scheduled_frames.front() == frame); - scheduled_frames.pop_front(); + assert(scheduled_frames.count(frame)); + scheduled_frames.erase(frame); --metric_decklink_output_inflight_frames; } @@ -530,7 +560,7 @@ unique_ptr DeckLinkOutput::get_frame() unique_ptr frame(new Frame); size_t stride; - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { stride = v210Converter::get_v210_stride(width); GLint v210_width = stride / sizeof(uint32_t); frame->uyvy_tex = resource_pool->create_2d_texture(GL_RGB10_A2, v210_width, height); @@ -588,7 +618,7 @@ void DeckLinkOutput::present_thread_func() check_error(); frame->fence.reset(); - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { memcpy(frame->uyvy_ptr_local.get(), frame->uyvy_ptr, v210Converter::get_v210_stride(width) * height); } else { memcpy(frame->uyvy_ptr_local.get(), frame->uyvy_ptr, width * height * 2); @@ -597,16 +627,24 @@ void DeckLinkOutput::present_thread_func() // Release any input frames we needed to render this frame. frame->input_frames.clear(); - BMDTimeValue pts = frame->pts; - BMDTimeValue duration = frame->duration; - HRESULT res = output->ScheduleVideoFrame(frame.get(), pts, duration, TIMEBASE); - lock_guard lock(frame_queue_mutex); - if (res == S_OK) { - scheduled_frames.push_back(frame.release()); // Owned by the driver now. - ++metric_decklink_output_inflight_frames; - } else { - fprintf(stderr, "Could not schedule video frame! (error=0x%08x)\n", res); + if (is_master_card) { + BMDTimeValue pts = frame->pts; + BMDTimeValue duration = frame->duration; + HRESULT res = output->ScheduleVideoFrame(frame.get(), pts, duration, TIMEBASE); + lock_guard lock(frame_queue_mutex); + if (res == S_OK) { + scheduled_frames.insert(frame.release()); // Owned by the driver now. + ++metric_decklink_output_inflight_frames; + } else { + fprintf(stderr, "Could not schedule video frame! (error=0x%08x)\n", res); + frame_freelist.push(move(frame)); + } + } else { + HRESULT res = output->DisplayVideoFrameSync(frame.get()); + if (res != S_OK) { + fprintf(stderr, "Could not schedule video frame! (error=0x%08x)\n", res); + } frame_freelist.push(move(frame)); } } @@ -674,7 +712,7 @@ long DeckLinkOutput::Frame::GetHeight() long DeckLinkOutput::Frame::GetRowBytes() { - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { return v210Converter::get_v210_stride(global_flags.width); } else { return global_flags.width * 2; @@ -683,7 +721,7 @@ long DeckLinkOutput::Frame::GetRowBytes() BMDPixelFormat DeckLinkOutput::Frame::GetPixelFormat() { - if (global_flags.ten_bit_output) { + if (global_flags.bit_depth > 8) { return bmdFormat10BitYUV; } else { return bmdFormat8BitYUV;