]> git.sesse.net Git - nageru/blobdiff - decklink_output.cpp
Fix an issue where video frames would not be properly shown, most commonly in previews.
[nageru] / decklink_output.cpp
index 3b00d28bbeee3afea7a13277e0f3b70db3c7b475..f2ac40b8a9600f417399dd64efeedf92ff2d84fc 100644 (file)
@@ -12,6 +12,7 @@
 #include "print_latency.h"
 #include "resource_pool.h"
 #include "timebase.h"
+#include "v210_converter.h"
 
 using namespace movit;
 using namespace std;
@@ -66,7 +67,12 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts)
        assert(output);
        assert(!playback_initiated);
 
-       should_quit = false;
+       if (video_modes.empty()) {
+               fprintf(stderr, "ERROR: No matching output modes for %dx%d found\n", width, height);
+               exit(1);
+       }
+
+       should_quit.unquit();
        playback_initiated = true;
        playback_started = false;
        this->base_pts = base_pts;
@@ -96,7 +102,8 @@ void DeckLinkOutput::start_output(uint32_t mode, int64_t base_pts)
 
        BMDDisplayModeSupport support;
        IDeckLinkDisplayMode *display_mode;
-       if (output->DoesSupportVideoMode(mode, bmdFormat8BitYUV, bmdVideoOutputFlagDefault,
+       BMDPixelFormat pixel_format = global_flags.ten_bit_output ? bmdFormat10BitYUV : bmdFormat8BitYUV;
+       if (output->DoesSupportVideoMode(mode, pixel_format, bmdVideoOutputFlagDefault,
                                         &support, &display_mode) != S_OK) {
                fprintf(stderr, "Couldn't ask for format support\n");
                exit(1);
@@ -158,7 +165,7 @@ void DeckLinkOutput::end_output()
                return;
        }
 
-       should_quit = true;
+       should_quit.quit();
        frame_queues_changed.notify_all();
        present_thread.join();
        playback_initiated = false;
@@ -179,7 +186,7 @@ void DeckLinkOutput::end_output()
 
 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)
 {
-       assert(!should_quit);
+       assert(!should_quit.should_quit());
 
        if ((current_mode_flags & bmdDisplayModeColorspaceRec601) && output_ycbcr_coefficients == YCBCR_REC_709) {
                if (!last_frame_had_mode_mismatch) {
@@ -198,7 +205,11 @@ void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoeffici
        }
 
        unique_ptr<Frame> frame = move(get_frame());
-       chroma_subsampler->create_uyvy(y_tex, cbcr_tex, width, height, frame->uyvy_tex);
+       if (global_flags.ten_bit_output) {
+               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);
+       }
 
        // Download the UYVY texture to the PBO.
        glPixelStorei(GL_PACK_ROW_LENGTH, 0);
@@ -207,10 +218,17 @@ void DeckLinkOutput::send_frame(GLuint y_tex, GLuint cbcr_tex, YCbCrLumaCoeffici
        glBindBuffer(GL_PIXEL_PACK_BUFFER, frame->pbo);
        check_error();
 
-       glBindTexture(GL_TEXTURE_2D, frame->uyvy_tex);
-       check_error();
-       glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
-       check_error();
+       if (global_flags.ten_bit_output) {
+               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));
+               check_error();
+       } else {
+               glBindTexture(GL_TEXTURE_2D, frame->uyvy_tex);
+               check_error();
+               glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
+               check_error();
+       }
 
        glBindTexture(GL_TEXTURE_2D, 0);
        check_error();
@@ -258,7 +276,7 @@ void DeckLinkOutput::send_audio(int64_t pts, const std::vector<float> &samples)
 
 void DeckLinkOutput::wait_for_frame(int64_t pts, int *dropped_frames, int64_t *frame_duration, bool *is_preroll, steady_clock::time_point *frame_timestamp)
 {
-       assert(!should_quit);
+       assert(!should_quit.should_quit());
 
        *dropped_frames = 0;
        *frame_duration = this->frame_duration;
@@ -296,7 +314,7 @@ void DeckLinkOutput::wait_for_frame(int64_t pts, int *dropped_frames, int64_t *f
 
        // If we're ahead of time, wait for the frame to (approximately) start.
        if (stream_frame_time < target_time) {
-               this_thread::sleep_until(*frame_timestamp);
+               should_quit.sleep_until(*frame_timestamp);
                return;
        }
 
@@ -406,17 +424,31 @@ unique_ptr<DeckLinkOutput::Frame> DeckLinkOutput::get_frame()
 
        unique_ptr<Frame> frame(new Frame);
 
-       frame->uyvy_tex = resource_pool->create_2d_texture(GL_RGBA8, width / 2, height);
+       size_t stride;
+       if (global_flags.ten_bit_output) {
+               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);
+
+               // We need valid texture state, or NVIDIA won't allow us to write to the texture.
+               glBindTexture(GL_TEXTURE_2D, frame->uyvy_tex);
+               check_error();
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               check_error();
+       } else {
+               stride = width * 2;
+               frame->uyvy_tex = resource_pool->create_2d_texture(GL_RGBA8, width / 2, height);
+       }
 
        glGenBuffers(1, &frame->pbo);
        check_error();
        glBindBuffer(GL_PIXEL_PACK_BUFFER, frame->pbo);
        check_error();
-       glBufferStorage(GL_PIXEL_PACK_BUFFER, width * height * 2, NULL, GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
+       glBufferStorage(GL_PIXEL_PACK_BUFFER, stride * height, NULL, GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
        check_error();
-       frame->uyvy_ptr = (uint8_t *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width * height * 2, GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
+       frame->uyvy_ptr = (uint8_t *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, stride * height, GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
        check_error();
-       frame->uyvy_ptr_local.reset(new uint8_t[width * height * 2]);
+       frame->uyvy_ptr_local.reset(new uint8_t[stride * height]);
        frame->resource_pool = resource_pool;
 
        return frame;
@@ -430,9 +462,9 @@ void DeckLinkOutput::present_thread_func()
                {
                         unique_lock<mutex> lock(frame_queue_mutex);
                         frame_queues_changed.wait(lock, [this]{
-                                return should_quit || !pending_video_frames.empty();
+                                return should_quit.should_quit() || !pending_video_frames.empty();
                         });
-                        if (should_quit) {
+                        if (should_quit.should_quit()) {
                                return;
                        }
                        frame = move(pending_video_frames.front());
@@ -444,7 +476,11 @@ void DeckLinkOutput::present_thread_func()
                check_error();
                frame->fence.reset();
 
-               memcpy(frame->uyvy_ptr_local.get(), frame->uyvy_ptr, width * height * 2);
+               if (global_flags.ten_bit_output) {
+                       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);
+               }
 
                // Release any input frames we needed to render this frame.
                frame->input_frames.clear();
@@ -526,12 +562,20 @@ long DeckLinkOutput::Frame::GetHeight()
 
 long DeckLinkOutput::Frame::GetRowBytes()
 {
-       return global_flags.width * 2;
+       if (global_flags.ten_bit_output) {
+               return v210Converter::get_v210_stride(global_flags.width);
+       } else {
+               return global_flags.width * 2;
+       }
 }
 
 BMDPixelFormat DeckLinkOutput::Frame::GetPixelFormat()
 {
-       return bmdFormat8BitYUV;
+       if (global_flags.ten_bit_output) {
+               return bmdFormat10BitYUV;
+       } else {
+               return bmdFormat8BitYUV;
+       }
 }
 
 BMDFrameFlags DeckLinkOutput::Frame::GetFlags()