]> git.sesse.net Git - bmusb/blob - bmusb.h
Add a function to get video format details.
[bmusb] / bmusb.h
1 #ifndef _BMUSB_H
2 #define _BMUSB_H
3
4 #include <stdint.h>
5 #include <atomic>
6 #include <condition_variable>
7 #include <deque>
8 #include <functional>
9 #include <mutex>
10 #include <thread>
11 #include <vector>
12
13 struct libusb_transfer;
14
15 // An interface for frame allocators; if you do not specify one
16 // (using set_video_frame_allocator), a default one that pre-allocates
17 // a freelist of eight frames using new[] will be used. Specifying
18 // your own can be useful if you have special demands for where you want the
19 // frame to end up and don't want to spend the extra copy to get it there, for
20 // instance GPU memory.
21 class FrameAllocator {
22  public:
23         struct Frame {
24                 uint8_t *data = nullptr;
25                 uint8_t *data2 = nullptr;  // Only if interleaved == true.
26                 size_t len = 0;  // Number of bytes we actually have.
27                 size_t size = 0;  // Number of bytes we have room for.
28                 size_t overflow = 0;
29                 void *userdata = nullptr;
30                 FrameAllocator *owner = nullptr;
31
32                 // If set to true, every other byte will go to data and to data2.
33                 // If so, <len> and <size> are still about the number of total bytes
34                 // so if size == 1024, there's 512 bytes in data and 512 in data2.
35                 bool interleaved = false;
36         };
37
38         virtual ~FrameAllocator();
39
40         // Request a video frame. Note that this is called from the
41         // USB thread, which runs with realtime priority and is
42         // very sensitive to delays. Thus, you should not do anything
43         // here that might sleep, including calling malloc().
44         // (Taking a mutex is borderline.)
45         //
46         // The Frame object will be given to the frame callback,
47         // which is responsible for releasing the video frame back
48         // once it is usable for new frames (ie., it will no longer
49         // be read from). You can use the "userdata" pointer for
50         // whatever you want to identify this frame if you need to.
51         //
52         // Returning a Frame with data==nullptr is allowed;
53         // if so, the frame in progress will be dropped.
54         virtual Frame alloc_frame() = 0;
55
56         virtual void release_frame(Frame frame) = 0;
57 };
58
59 typedef std::function<void(uint16_t timecode,
60                            FrameAllocator::Frame video_frame, size_t video_offset, uint16_t video_format,
61                            FrameAllocator::Frame audio_frame, size_t audio_offset, uint16_t audio_format)>
62         frame_callback_t;
63
64 // The actual capturing class, representing capture from a single card.
65 class BMUSBCapture {
66  public:
67         BMUSBCapture(int card_index)
68                 : card_index(card_index)
69         {
70         }
71
72         // Does not take ownership.
73         void set_video_frame_allocator(FrameAllocator *allocator)
74         {
75                 video_frame_allocator = allocator;
76         }
77
78         FrameAllocator *get_video_frame_allocator()
79         {
80                 return video_frame_allocator;
81         }
82
83         // Does not take ownership.
84         void set_audio_frame_allocator(FrameAllocator *allocator)
85         {
86                 audio_frame_allocator = allocator;
87         }
88
89         FrameAllocator *get_audio_frame_allocator()
90         {
91                 return audio_frame_allocator;
92         }
93
94         void set_frame_callback(frame_callback_t callback)
95         {
96                 frame_callback = callback;
97         }
98
99         // Needs to be run before configure_card().
100         void set_dequeue_thread_callbacks(std::function<void()> init, std::function<void()> cleanup)
101         {
102                 dequeue_init_callback = init;
103                 dequeue_cleanup_callback = cleanup;
104                 has_dequeue_callbacks = true;
105         }
106
107         void configure_card();
108         void start_bm_capture();
109         void stop_dequeue_thread();
110
111         static void start_bm_thread();
112         static void stop_bm_thread();
113
114  private:
115         struct QueuedFrame {
116                 uint16_t timecode;
117                 uint16_t format;
118                 FrameAllocator::Frame frame;
119         };
120
121         void start_new_audio_block(const uint8_t *start);
122         void start_new_frame(const uint8_t *start);
123
124         void queue_frame(uint16_t format, uint16_t timecode, FrameAllocator::Frame frame, std::deque<QueuedFrame> *q);
125         void dequeue_thread_func();
126
127         static void usb_thread_func();
128         static void cb_xfr(struct libusb_transfer *xfr);
129
130         FrameAllocator::Frame current_video_frame;
131         FrameAllocator::Frame current_audio_frame;
132
133         std::mutex queue_lock;
134         std::condition_variable queues_not_empty;
135         std::deque<QueuedFrame> pending_video_frames;
136         std::deque<QueuedFrame> pending_audio_frames;
137
138         FrameAllocator *video_frame_allocator = nullptr;
139         FrameAllocator *audio_frame_allocator = nullptr;
140         frame_callback_t frame_callback = nullptr;
141
142         std::thread dequeue_thread;
143         std::atomic<bool> dequeue_thread_should_quit;
144         bool has_dequeue_callbacks = false;
145         std::function<void()> dequeue_init_callback = nullptr;
146         std::function<void()> dequeue_cleanup_callback = nullptr;
147
148         int current_register = 0;
149
150         static constexpr int NUM_BMUSB_REGISTERS = 60;
151         uint8_t register_file[NUM_BMUSB_REGISTERS];
152
153         int card_index;
154         std::vector<libusb_transfer *> iso_xfrs;
155 };
156
157 // Get details for the given video format; returns false if detection was incomplete.
158 // Note: Frame rate is _frame_ rate, not field rate. So 1080i60 gets 30/1, _not_ 60/1.
159 bool decode_video_format(uint16_t video_format, int *width, int *height, int *frame_rate_nom, int *frame_rate_den, bool *interlaced);
160
161 #endif