]> git.sesse.net Git - nageru/blob - nageru/decklink_output.h
IWYU-fix nageru/*.h.
[nageru] / nageru / decklink_output.h
1 #ifndef _DECKLINK_OUTPUT_H
2 #define _DECKLINK_OUTPUT_H 1
3
4 #include <epoxy/gl.h>
5 #include <movit/image_format.h>
6 #include <stdint.h>
7 #include <atomic>
8 #include <chrono>
9 #include <condition_variable>
10 #include <map>
11 #include <memory>
12 #include <mutex>
13 #include <queue>
14 #include <thread>
15 #include <unordered_set>
16 #include <vector>
17
18 #include "DeckLinkAPI.h"
19 #include "DeckLinkAPIModes.h"
20 #include "DeckLinkAPITypes.h"
21 #include "LinuxCOM.h"
22
23 #include <bmusb/bmusb.h>
24
25 #include "shared/context.h"
26 #include "print_latency.h"
27 #include "quittable_sleeper.h"
28 #include "ref_counted_frame.h"
29 #include "shared/ref_counted_gl_sync.h"
30
31 namespace movit {
32
33 class ResourcePool;
34
35 }  // namespace movit
36
37 class ChromaSubsampler;
38 class IDeckLink;
39 class IDeckLinkOutput;
40 class QSurface;
41
42 class DeckLinkOutput : public IDeckLinkVideoOutputCallback {
43 public:
44         DeckLinkOutput(movit::ResourcePool *resource_pool, QSurface *surface, unsigned width, unsigned height, unsigned card_index);
45         ~DeckLinkOutput();
46
47         // The IDecklinkInput argument is to work around a bug
48         // in the 11.7 and newer drivers against older SDKs,
49         // where you get a freeze if querying an IDeckLinkInput interface
50         // on an already-started card.
51         bool set_device(IDeckLink *decklink, IDeckLinkInput *input_arg);
52         void start_output(uint32_t mode, int64_t base_pts, bool is_master_card);  // Mode comes from get_available_video_modes().
53         void end_output();
54
55         void send_frame(GLuint y_tex, GLuint cbcr_tex, movit::YCbCrLumaCoefficients ycbcr_coefficients, const std::vector<RefCountedFrame> &input_frames, int64_t pts, int64_t duration);
56         void send_audio(int64_t pts, const std::vector<float> &samples);
57
58         // NOTE: The returned timestamp is undefined for preroll.
59         // Otherwise, it is the timestamp of the output frame as it should have been,
60         // even if we're overshooting. E.g. at 50 fps (0.02 spf), assuming the
61         // last frame was at t=0.980:
62         //
63         //   If we're at t=0.999, we wait until t=1.000 and return that.
64         //   If we're at t=1.001, we return t=1.000 immediately (small overshoot).
65         //   If we're at t=1.055, we drop two frames and return t=1.040 immediately.
66         void wait_for_frame(int64_t pts, int *dropped_frames, int64_t *frame_duration, bool *is_preroll, std::chrono::steady_clock::time_point *frame_timestamp);
67
68         // Analogous to CaptureInterface. Will only return modes that have the right width/height.
69         std::map<uint32_t, bmusb::VideoMode> get_available_video_modes() const { return video_modes; }
70
71         // If the given mode is supported, return it. If not, pick some “best” valid mode.
72         uint32_t pick_video_mode(uint32_t mode) const;
73
74         // Desired Y'CbCr coefficients for the current mode. Undefined before start_output().
75         movit::YCbCrLumaCoefficients preferred_ycbcr_coefficients() const;
76
77         // IUnknown.
78         HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) override;
79         ULONG STDMETHODCALLTYPE AddRef() override;
80         ULONG STDMETHODCALLTYPE Release() override;
81
82         // IDeckLinkVideoOutputCallback.
83         HRESULT ScheduledFrameCompleted(/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) override;
84         HRESULT ScheduledPlaybackHasStopped() override;
85
86 private:
87         struct Frame : public IDeckLinkVideoFrame {
88         public:
89                 ~Frame();
90
91                 // IUnknown.
92                 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) override;
93                 ULONG STDMETHODCALLTYPE AddRef() override;
94                 ULONG STDMETHODCALLTYPE Release() override;
95
96                 // IDeckLinkVideoFrame.
97                 long GetWidth() override;
98                 long GetHeight() override;
99                 long GetRowBytes() override;
100                 BMDPixelFormat GetPixelFormat() override;
101                 BMDFrameFlags GetFlags() override;
102                 HRESULT GetBytes(/* out */ void **buffer) override;
103
104                 HRESULT GetTimecode(/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) override;
105                 HRESULT GetAncillaryData(/* out */ IDeckLinkVideoFrameAncillary **ancillary) override;
106
107         private:
108                 std::atomic<int> refcount{1};
109                 RefCountedGLsync fence;  // Needs to be waited on before uyvy_ptr can be read from.
110                 std::vector<RefCountedFrame> input_frames;  // Cannot be released before we are done rendering (ie., <fence> is asserted).
111                 ReceivedTimestamps received_ts;
112                 int64_t pts, duration;
113                 movit::ResourcePool *resource_pool;
114
115                 // These members are persistently allocated, and reused when the frame object is.
116                 GLuint uyvy_tex;  // Owned by <resource_pool>. Can also hold v210 data.
117                 GLuint pbo;
118                 uint8_t *uyvy_ptr;  // Persistent mapping into the PBO.
119
120                 // Current Blackmagic drivers (January 2017) have a bug where sending a PBO
121                 // pointer to the driver causes a kernel oops. Thus, we do an extra copy into
122                 // this pointer before giving the data to the driver. (We don't do a get
123                 // directly into this pointer, because e.g. Intel/Mesa hits a slow path when
124                 // you do readback into something that's not a PBO.) When Blackmagic fixes
125                 // the bug, we should drop this.
126                 std::unique_ptr<uint8_t[]> uyvy_ptr_local;
127
128                 friend class DeckLinkOutput;
129         };
130         std::unique_ptr<Frame> get_frame();
131         void create_uyvy(GLuint y_tex, GLuint cbcr_tex, GLuint dst_tex);
132
133         void present_thread_func();
134
135         std::atomic<int> refcount{1};
136
137         std::unique_ptr<ChromaSubsampler> chroma_subsampler;
138         std::map<uint32_t, bmusb::VideoMode> video_modes;
139
140         std::thread present_thread;
141         QuittableSleeper should_quit;
142
143         std::mutex frame_queue_mutex;
144         std::queue<std::unique_ptr<Frame>> pending_video_frames;  // Under <frame_queue_mutex>.
145         std::queue<std::unique_ptr<Frame>> frame_freelist;  // Under <frame_queue_mutex>.
146         std::unordered_set<Frame *> scheduled_frames;  // Owned by the driver, so no unique_ptr. Under <frame_queue_mutex>.
147
148         std::condition_variable frame_queues_changed;
149         bool playback_initiated = false, playback_started = false, is_master_card = false;
150         int64_t base_pts, frame_duration;
151         BMDDisplayModeFlags current_mode_flags = 0;
152         bool last_frame_had_mode_mismatch = false;
153
154         movit::ResourcePool *resource_pool;
155         IDeckLinkInput *input = nullptr;
156         IDeckLinkOutput *output = nullptr;
157         BMDVideoConnection video_connection;
158         QSurface *surface;
159         unsigned width, height;
160         unsigned card_index;
161
162         GLuint uyvy_vbo;  // Holds position and texcoord data.
163         GLuint uyvy_program_num;  // Owned by <resource_pool>.
164         GLuint uyvy_position_attribute_index, uyvy_texcoord_attribute_index;
165 };
166
167 #endif  // !defined(_DECKLINK_OUTPUT_H)