X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Fdecklink_output.cpp;h=20de9229fdc35e42502626c40b571f5741150f4c;hb=ccc2b89c9cf879ccbd948e169a029917cc16f0ee;hp=bd59b32a5a3abb0eb762c89ed49fcc8991bc75fc;hpb=131a051c4cd3719a9be415386fdf0f4e15da7c66;p=nageru diff --git a/nageru/decklink_output.cpp b/nageru/decklink_output.cpp index bd59b32..20de922 100644 --- a/nageru/decklink_output.cpp +++ b/nageru/decklink_output.cpp @@ -82,8 +82,9 @@ DeckLinkOutput::DeckLinkOutput(ResourcePool *resource_pool, QSurface *surface, u }); } -bool DeckLinkOutput::set_device(IDeckLink *decklink) +bool DeckLinkOutput::set_device(IDeckLink *decklink, IDeckLinkInput *input_arg) { + input_arg = input; if (decklink->QueryInterface(IID_IDeckLinkOutput, (void**)&output) != S_OK) { fprintf(stderr, "Warning: Card %u has no outputs\n", card_index); return false; @@ -130,7 +131,7 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) if (video_modes.empty()) { fprintf(stderr, "ERROR: No matching output modes for %dx%d found\n", width, height); - exit(1); + abort(); } should_quit.unquit(); @@ -141,19 +142,19 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) IDeckLinkConfiguration *config = nullptr; if (output->QueryInterface(IID_IDeckLinkConfiguration, (void**)&config) != S_OK) { fprintf(stderr, "Failed to get configuration interface for output card\n"); - exit(1); + abort(); } if (config->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true) != S_OK) { fprintf(stderr, "Failed to set low latency output\n"); - exit(1); + abort(); } if (config->SetInt(bmdDeckLinkConfigVideoOutputConnection, video_connection) != S_OK) { fprintf(stderr, "Failed to set video output connection for card %u\n", card_index); - exit(1); + abort(); } - if (config->SetFlag(bmdDeckLinkConfigUse1080pNotPsF, true) != S_OK) { + if (config->SetFlag(bmdDeckLinkConfigOutput1080pAsPsF, true) != S_OK) { fprintf(stderr, "Failed to set PsF flag for card\n"); - exit(1); + abort(); } if (config->SetFlag(bmdDeckLinkConfigSMPTELevelAOutput, true) != S_OK) { // This affects at least some no-name SDI->HDMI converters. @@ -167,12 +168,12 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) if (output->DoesSupportVideoMode(mode, pixel_format, bmdVideoOutputFlagDefault, &support, &display_mode) != S_OK) { fprintf(stderr, "Couldn't ask for format support\n"); - exit(1); + abort(); } if (support == bmdDisplayModeNotSupported) { fprintf(stderr, "Requested display mode not supported\n"); - exit(1); + abort(); } current_mode_flags = display_mode->GetFlags(); @@ -181,7 +182,7 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) BMDTimeScale time_scale; if (display_mode->GetFrameRate(&time_value, &time_scale) != S_OK) { fprintf(stderr, "Couldn't get frame rate\n"); - exit(1); + abort(); } metric_decklink_output_width_pixels = width; @@ -193,23 +194,32 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) display_mode->Release(); + if (input != nullptr) { + if (input->DisableVideoInput() != S_OK) { + fprintf(stderr, "Warning: Failed to disable video input for card %d\n", card_index); + } + if (input->DisableAudioInput() != S_OK) { + fprintf(stderr, "Warning: Failed to disable audio input for card %d\n", card_index); + } + } + HRESULT result = output->EnableVideoOutput(mode, bmdVideoOutputFlagDefault); if (result != S_OK) { fprintf(stderr, "Couldn't enable output with error 0x%x\n", result); - exit(1); + abort(); } if (output->SetScheduledFrameCompletionCallback(this) != S_OK) { fprintf(stderr, "Couldn't set callback\n"); - exit(1); + abort(); } assert(OUTPUT_FREQUENCY == 48000); if (output->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, 2, bmdAudioOutputStreamTimestamped) != S_OK) { fprintf(stderr, "Couldn't enable audio output\n"); - exit(1); + abort(); } if (output->BeginAudioPreroll() != S_OK) { fprintf(stderr, "Couldn't begin audio preroll\n"); - exit(1); + abort(); } present_thread = thread([this]{ @@ -218,7 +228,7 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts) if (!make_current(context, this->surface)) { printf("display=%p surface=%p context=%p curr=%p err=%d\n", eglGetCurrentDisplay(), this->surface, context, eglGetCurrentContext(), eglGetError()); - exit(1); + abort(); } present_thread_func(); delete_context(context); @@ -243,11 +253,20 @@ void DeckLinkOutput::end_output() // Wait until all frames are accounted for, and free them. { unique_lock lock(frame_queue_mutex); - while (!(frame_freelist.empty() && num_frames_in_flight == 0)) { + while (!(frame_freelist.empty() && scheduled_frames.empty())) { frame_queues_changed.wait(lock, [this]{ return !frame_freelist.empty(); }); 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) @@ -317,7 +336,7 @@ void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoeffici frame->duration = duration; { - unique_lock lock(frame_queue_mutex); + lock_guard lock(frame_queue_mutex); pending_video_frames.push(move(frame)); } frame_queues_changed.notify_all(); @@ -334,10 +353,10 @@ void DeckLinkOutput::send_audio(int64_t pts, const std::vector &samples) HRESULT result = output->ScheduleAudioSamples(int_samples.get(), samples.size() / 2, pts, TIMEBASE, &frames_written); if (result != S_OK) { - fprintf(stderr, "ScheduleAudioSamples(pts=%ld) failed (result=0x%08x)\n", pts, result); + fprintf(stderr, "ScheduleAudioSamples(pts=%" PRId64 ") failed (result=0x%08x)\n", pts, result); } else { if (frames_written != samples.size() / 2) { - fprintf(stderr, "ScheduleAudioSamples() returned short write (%u/%ld)\n", frames_written, samples.size() / 2); + fprintf(stderr, "ScheduleAudioSamples() returned short write (%u/%zu)\n", frames_written, samples.size() / 2); } } metric_decklink_output_scheduled_samples += samples.size() / 2; @@ -366,11 +385,11 @@ void DeckLinkOutput::wait_for_frame(int64_t pts, int *dropped_frames, int64_t *f if (!playback_started) { if (output->EndAudioPreroll() != S_OK) { fprintf(stderr, "Could not end audio preroll\n"); - exit(1); // TODO + abort(); // TODO } if (output->StartScheduledPlayback(base_pts, TIMEBASE, 1.0) != S_OK) { fprintf(stderr, "Could not start playback\n"); - exit(1); // TODO + abort(); // TODO } playback_started = true; } @@ -418,7 +437,7 @@ uint32_t DeckLinkOutput::pick_video_mode(uint32_t mode) const } // Prioritize 59.94 > 60 > 29.97. If none of those are found, then pick the highest one. - for (const pair &desired : vector>{ { 60000, 1001 }, { 60, 0 }, { 30000, 1001 } }) { + for (const pair &desired : vector>{ { 60000, 1001 }, { 60, 1 }, { 30000, 1001 } }) { for (const auto &it : video_modes) { if (it.second.frame_rate_num * desired.second == desired.first * it.second.frame_rate_den) { return it.first; @@ -457,17 +476,17 @@ HRESULT DeckLinkOutput::ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *co ++metric_decklink_output_completed_frames_completed; break; case bmdOutputFrameDisplayedLate: - fprintf(stderr, "Output frame displayed late (pts=%ld)\n", frame->pts); + fprintf(stderr, "Output frame displayed late (pts=%" PRId64 ")\n", frame->pts); fprintf(stderr, "Consider increasing --output-buffer-frames if this persists.\n"); ++metric_decklink_output_completed_frames_late; break; case bmdOutputFrameDropped: - fprintf(stderr, "Output frame was dropped (pts=%ld)\n", frame->pts); + fprintf(stderr, "Output frame was dropped (pts=%" PRId64 ")\n", frame->pts); fprintf(stderr, "Consider increasing --output-buffer-frames if this persists.\n"); ++metric_decklink_output_completed_frames_dropped; break; case bmdOutputFrameFlushed: - fprintf(stderr, "Output frame was flushed (pts=%ld)\n", frame->pts); + fprintf(stderr, "Output frame was flushed (pts=%" PRId64 ")\n", frame->pts); ++metric_decklink_output_completed_frames_flushed; break; default: @@ -482,7 +501,8 @@ HRESULT DeckLinkOutput::ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *co { lock_guard lock(frame_queue_mutex); frame_freelist.push(unique_ptr(frame)); - --num_frames_in_flight; + assert(scheduled_frames.front() == frame); + scheduled_frames.pop_front(); --metric_decklink_output_inflight_frames; } @@ -543,17 +563,15 @@ void DeckLinkOutput::present_thread_func() for ( ;; ) { unique_ptr frame; { - unique_lock lock(frame_queue_mutex); - frame_queues_changed.wait(lock, [this]{ - return should_quit.should_quit() || !pending_video_frames.empty(); - }); - if (should_quit.should_quit()) { + unique_lock lock(frame_queue_mutex); + frame_queues_changed.wait(lock, [this]{ + return should_quit.should_quit() || !pending_video_frames.empty(); + }); + if (should_quit.should_quit()) { return; } frame = move(pending_video_frames.front()); pending_video_frames.pop(); - ++num_frames_in_flight; - ++metric_decklink_output_inflight_frames; } for ( ;; ) { @@ -580,15 +598,14 @@ void DeckLinkOutput::present_thread_func() 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) { - frame.release(); // Owned by the driver now. + 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); - lock_guard lock(frame_queue_mutex); frame_freelist.push(move(frame)); - --num_frames_in_flight; - --metric_decklink_output_inflight_frames; } } }