]> git.sesse.net Git - nageru/blob - decklink_output.h
Support DeckLink cards that don't have an HDMI connector.
[nageru] / decklink_output.h
1 #ifndef _DECKLINK_OUTPUT_H
2 #define _DECKLINK_OUTPUT_H 1
3
4 #include <epoxy/gl.h>
5 #include <stdint.h>
6 #include <atomic>
7 #include <condition_variable>
8 #include <memory>
9 #include <mutex>
10 #include <queue>
11 #include <thread>
12 #include <vector>
13
14 #include "DeckLinkAPI.h"
15 #include "DeckLinkAPITypes.h"
16 #include "LinuxCOM.h"
17
18 #include "context.h"
19 #include "print_latency.h"
20 #include "ref_counted_frame.h"
21 #include "ref_counted_gl_sync.h"
22
23 namespace movit {
24
25 class ResourcePool;
26
27 }  // namespace movit
28
29 class ChromaSubsampler;
30 class IDeckLink;
31 class IDeckLinkOutput;
32 class QSurface;
33
34 class DeckLinkOutput : public IDeckLinkVideoOutputCallback {
35 public:
36         DeckLinkOutput(movit::ResourcePool *resource_pool, QSurface *surface, unsigned width, unsigned height, unsigned card_index);
37
38         void set_device(IDeckLink *output);
39         void start_output(uint32_t mode, int64_t base_pts);  // Mode comes from get_available_video_modes().
40         void end_output();
41
42         void send_frame(GLuint y_tex, GLuint cbcr_tex, const std::vector<RefCountedFrame> &input_frames, int64_t pts, int64_t duration);
43         void send_audio(int64_t pts, const std::vector<float> &samples);
44         void wait_for_frame(int64_t pts, int *dropped_frames, int64_t *frame_duration);
45
46         // Analogous to CaptureInterface. Will only return modes that have the right width/height.
47         std::map<uint32_t, bmusb::VideoMode> get_available_video_modes() const { return video_modes; }
48
49         // IUnknown.
50         HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) override;
51         ULONG STDMETHODCALLTYPE AddRef() override;
52         ULONG STDMETHODCALLTYPE Release() override;
53
54         // IDeckLinkVideoOutputCallback.
55         HRESULT ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) override;
56         HRESULT ScheduledPlaybackHasStopped() override;
57
58 private:
59         struct Frame : public IDeckLinkVideoFrame {
60         public:
61                 ~Frame();
62
63                 // IUnknown.
64                 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) override;
65                 ULONG STDMETHODCALLTYPE AddRef() override;
66                 ULONG STDMETHODCALLTYPE Release() override;
67
68                 // IDeckLinkVideoFrame.
69                 long GetWidth() override;
70                 long GetHeight() override;
71                 long GetRowBytes() override;
72                 BMDPixelFormat GetPixelFormat() override;
73                 BMDFrameFlags GetFlags() override;
74                 HRESULT GetBytes(/* out */ void **buffer) override;
75
76                 HRESULT GetTimecode(/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) override;
77                 HRESULT GetAncillaryData(/* out */ IDeckLinkVideoFrameAncillary **ancillary) override;
78
79         private:
80                 std::atomic<int> refcount{1};
81                 RefCountedGLsync fence;  // Needs to be waited on before uyvy_ptr can be read from.
82                 std::vector<RefCountedFrame> input_frames;  // Cannot be released before we are done rendering (ie., <fence> is asserted).
83                 ReceivedTimestamps received_ts;
84                 int64_t pts, duration;
85                 movit::ResourcePool *resource_pool;
86
87                 // These members are persistently allocated, and reused when the frame object is.
88                 GLuint uyvy_tex;  // Owned by <resource_pool>.
89                 GLuint pbo;
90                 uint8_t *uyvy_ptr;  // Persistent mapping into the PBO.
91
92                 // Current Blackmagic drivers (January 2017) have a bug where sending a PBO
93                 // pointer to the driver causes a kernel oops. Thus, we do an extra copy into
94                 // this pointer before giving the data to the driver. (We don't do a get
95                 // directly into this pointer, because e.g. Intel/Mesa hits a slow path when
96                 // you do readback into something that's not a PBO.) When Blackmagic fixes
97                 // the bug, we should drop this.
98                 std::unique_ptr<uint8_t[]> uyvy_ptr_local;
99
100                 friend class DeckLinkOutput;
101         };
102         std::unique_ptr<Frame> get_frame();
103         void create_uyvy(GLuint y_tex, GLuint cbcr_tex, GLuint dst_tex);
104
105         void present_thread_func();
106
107         std::atomic<int> refcount{1};
108
109         std::unique_ptr<ChromaSubsampler> chroma_subsampler;
110         std::map<uint32_t, bmusb::VideoMode> video_modes;
111
112         std::thread present_thread;
113         std::atomic<bool> should_quit{false};
114
115         std::mutex frame_queue_mutex;
116         std::queue<std::unique_ptr<Frame>> pending_video_frames;  // Under <frame_queue_mutex>.
117         std::queue<std::unique_ptr<Frame>> frame_freelist;  // Under <frame_queue_mutex>.
118         int num_frames_in_flight = 0;  // Number of frames allocated but not on the freelist. Under <frame_queue_mutex>.
119         std::condition_variable frame_queues_changed;
120         bool playback_started = false;
121         int64_t base_pts, frame_duration;
122
123         movit::ResourcePool *resource_pool;
124         IDeckLinkOutput *output = nullptr;
125         BMDVideoConnection video_connection;
126         QSurface *surface;
127         unsigned width, height;
128         unsigned card_index;
129
130         GLuint uyvy_vbo;  // Holds position and texcoord data.
131         GLuint uyvy_program_num;  // Owned by <resource_pool>.
132         GLuint uyvy_position_attribute_index, uyvy_texcoord_attribute_index;
133 };
134
135 #endif  // !defined(_DECKLINK_OUTPUT_H)