]> git.sesse.net Git - nageru/blobdiff - nageru/decklink_output.cpp
Support unsynchronized HDMI/SDI output.
[nageru] / nageru / decklink_output.cpp
index 56a184e99b326cc6ab3107c985f59bf0345bd384..91fbd4095c23c94532823a4158e223845ae43b5c 100644 (file)
@@ -131,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);
@@ -224,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]{
@@ -253,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();
 
@@ -348,13 +355,20 @@ void DeckLinkOutput::send_audio(int64_t pts, const std::vector<float> &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;
@@ -593,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<mutex> 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);
+               if (is_master_card) {
+                       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) {
+                               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));
                }
        }