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