]> git.sesse.net Git - nageru/blobdiff - nageru/decklink_output.cpp
Fix DeckLink capture using the 11.7 or newer drivers.
[nageru] / nageru / decklink_output.cpp
index f6826b51366e4a73afcfdcf0edac39179601a5ad..20de9229fdc35e42502626c40b571f5741150f4c 100644 (file)
@@ -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;
@@ -151,7 +152,7 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts)
                fprintf(stderr, "Failed to set video output connection for card %u\n", card_index);
                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");
                abort();
        }
@@ -193,6 +194,15 @@ 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);
@@ -243,11 +253,20 @@ void DeckLinkOutput::end_output()
        // Wait until all frames are accounted for, and free them.
        {
                unique_lock<mutex> 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<RefCountedFrame> &input_frames, int64_t pts, int64_t duration)
@@ -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<int, int> &desired : vector<pair<int, int>>{ { 60000, 1001 }, { 60, 0 }, { 30000, 1001 } }) {
+       for (const pair<int, int> &desired : vector<pair<int, int>>{ { 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;
@@ -462,12 +481,12 @@ HRESULT DeckLinkOutput::ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *co
                ++metric_decklink_output_completed_frames_late;
                break;
        case bmdOutputFrameDropped:
-               fprintf(stderr, "Output frame was dropped (pts=%" PRId64 "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=%" PRId64 "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<mutex> lock(frame_queue_mutex);
                frame_freelist.push(unique_ptr<Frame>(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> frame;
                {
-                        unique_lock<mutex> 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<mutex> 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<mutex> 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<mutex> lock(frame_queue_mutex);
                        frame_freelist.push(move(frame));
-                       --num_frames_in_flight;
-                       --metric_decklink_output_inflight_frames;
                }
        }
 }