X-Git-Url: https://git.sesse.net/?p=nageru;a=blobdiff_plain;f=nageru%2Fdecklink_output.cpp;h=4c478fdf8e667353ef5de338a6cbd82a20df878b;hp=20de9229fdc35e42502626c40b571f5741150f4c;hb=f34a3e1bbc207541842e0b54d5418d95bafc8e5b;hpb=ccc2b89c9cf879ccbd948e169a029917cc16f0ee diff --git a/nageru/decklink_output.cpp b/nageru/decklink_output.cpp index 20de922..4c478fd 100644 --- a/nageru/decklink_output.cpp +++ b/nageru/decklink_output.cpp @@ -82,9 +82,16 @@ DeckLinkOutput::DeckLinkOutput(ResourcePool *resource_pool, QSurface *surface, u }); } +DeckLinkOutput::~DeckLinkOutput() +{ + if (output != nullptr) { + output->Release(); + } +} + bool DeckLinkOutput::set_device(IDeckLink *decklink, IDeckLinkInput *input_arg) { - input_arg = input; + 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; @@ -124,10 +131,11 @@ bool DeckLinkOutput::set_device(IDeckLink *decklink, IDeckLinkInput *input_arg) 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); @@ -164,7 +172,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"); @@ -217,9 +225,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]{ @@ -246,7 +258,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(); @@ -258,15 +272,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) @@ -292,7 +297,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); @@ -305,7 +310,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)); @@ -350,13 +355,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; @@ -501,8 +513,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; } @@ -528,7 +540,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); @@ -586,7 +598,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); @@ -595,16 +607,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)); } } @@ -672,7 +692,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; @@ -681,7 +701,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;