This file is indexed.

/usr/include/bmusb/bmusb.h is in libbmusb-dev 0.5.4-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#ifndef _BMUSB_H
#define _BMUSB_H

#include <libusb.h>
#include <stdint.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <deque>
#include <functional>
#include <map>
#include <mutex>
#include <stack>
#include <string>
#include <thread>
#include <vector>

namespace bmusb {

class BMUSBCapture;

// An interface for frame allocators; if you do not specify one
// (using set_video_frame_allocator), a default one that pre-allocates
// a freelist of eight frames using new[] will be used. Specifying
// your own can be useful if you have special demands for where you want the
// frame to end up and don't want to spend the extra copy to get it there, for
// instance GPU memory.
class FrameAllocator {
 public:
	struct Frame {
		uint8_t *data = nullptr;
		uint8_t *data2 = nullptr;  // Only if interleaved == true.
		size_t len = 0;  // Number of bytes we actually have.
		size_t size = 0;  // Number of bytes we have room for.
		size_t overflow = 0;
		void *userdata = nullptr;
		FrameAllocator *owner = nullptr;

		// If set to true, every other byte will go to data and to data2.
		// If so, <len> and <size> are still about the number of total bytes
		// so if size == 1024, there's 512 bytes in data and 512 in data2.
		bool interleaved = false;

		// At what point this frame was received. Note that this marks the
		// _end_ of the frame being received, not the beginning.
		// Thus, if you want to measure latency, you'll also need to include
		// the time the frame actually took to transfer (usually 1/fps,
		// ie., the frames are typically transferred in real time).
		std::chrono::steady_clock::time_point received_timestamp =
			std::chrono::steady_clock::time_point::min();
	};

	virtual ~FrameAllocator();

	// Request a video frame. Note that this is called from the
	// USB thread, which runs with realtime priority and is
	// very sensitive to delays. Thus, you should not do anything
	// here that might sleep, including calling malloc().
	// (Taking a mutex is borderline.)
	//
	// The Frame object will be given to the frame callback,
	// which is responsible for releasing the video frame back
	// once it is usable for new frames (ie., it will no longer
	// be read from). You can use the "userdata" pointer for
	// whatever you want to identify this frame if you need to.
	//
	// Returning a Frame with data==nullptr is allowed;
	// if so, the frame in progress will be dropped.
	virtual Frame alloc_frame() = 0;

	virtual void release_frame(Frame frame) = 0;
};

// Audio is more important than video, and also much cheaper.
// By having many more audio frames available, hopefully if something
// starts to drop, we'll have CPU load go down (from not having to
// process as much video) before we have to drop audio.
#define NUM_QUEUED_VIDEO_FRAMES 16
#define NUM_QUEUED_AUDIO_FRAMES 64

class MallocFrameAllocator : public FrameAllocator {
public:
	MallocFrameAllocator(size_t frame_size, size_t num_queued_frames);
	Frame alloc_frame() override;
	void release_frame(Frame frame) override;

private:
	size_t frame_size;

	std::mutex freelist_mutex;
	std::stack<std::unique_ptr<uint8_t[]>> freelist;  // All of size <frame_size>.
};

// Represents an input mode you can tune a card to.
struct VideoMode {
	std::string name;
	bool autodetect = false;  // If true, all the remaining fields are irrelevant.
	unsigned width = 0, height = 0;
	unsigned frame_rate_num = 0, frame_rate_den = 0;
	bool interlaced = false;
};

// Represents the format of an actual frame coming in.
// Note: Frame rate is _frame_ rate, not field rate. So 1080i60 gets 30/1, _not_ 60/1.
// "second_field_start" is only valid for interlaced modes. If it is 1,
// the two fields are actually stored interlaced (ie., every other line).
// If not, each field is stored consecutively, and it signifies how many lines
// from the very top of the frame there are before the second field
// starts (so it will always be >= height/2 + extra_lines_top).
struct VideoFormat {
	uint16_t id = 0;  // For debugging/logging only.
	unsigned width = 0, height = 0, second_field_start = 0;
	unsigned extra_lines_top = 0, extra_lines_bottom = 0;
	unsigned frame_rate_nom = 0, frame_rate_den = 0;
	bool interlaced = false;
	bool has_signal = false;
	bool is_connected = true;  // If false, then has_signal makes no sense.
};

struct AudioFormat {
	uint16_t id = 0;  // For debugging/logging only.
	unsigned bits_per_sample = 0;
	unsigned num_channels = 0;
};

typedef std::function<void(uint16_t timecode,
                           FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format,
                           FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format)>
	frame_callback_t;

typedef std::function<void(libusb_device *dev)> card_connected_callback_t;
typedef std::function<void()> card_disconnected_callback_t;

class CaptureInterface {
 public:
	virtual ~CaptureInterface() {}

	virtual std::map<uint32_t, VideoMode> get_available_video_modes() const = 0;
	virtual uint32_t get_current_video_mode() const = 0;
	virtual void set_video_mode(uint32_t video_mode_id) = 0;

	virtual std::map<uint32_t, std::string> get_available_video_inputs() const = 0;
	virtual void set_video_input(uint32_t video_input_id) = 0;
	virtual uint32_t get_current_video_input() const = 0;

	virtual std::map<uint32_t, std::string> get_available_audio_inputs() const = 0;
	virtual void set_audio_input(uint32_t audio_input_id) = 0;
	virtual uint32_t get_current_audio_input() const = 0;

	// Does not take ownership.
	virtual void set_video_frame_allocator(FrameAllocator *allocator) = 0;

	virtual FrameAllocator *get_video_frame_allocator() = 0;

	// Does not take ownership.
	virtual void set_audio_frame_allocator(FrameAllocator *allocator) = 0;

	virtual FrameAllocator *get_audio_frame_allocator() = 0;

	virtual void set_frame_callback(frame_callback_t callback) = 0;

	// Needs to be run before configure_card().
	virtual void set_dequeue_thread_callbacks(std::function<void()> init, std::function<void()> cleanup) = 0;

	// Only valid after configure_card().
	virtual std::string get_description() const = 0;

	virtual void configure_card() = 0;

	virtual void start_bm_capture() = 0;

	virtual void stop_dequeue_thread() = 0;

	// If a card is disconnected, it cannot come back; you should call stop_dequeue_thread()
	// and delete it.
	virtual bool get_disconnected() const = 0;
};

// The actual capturing class, representing capture from a single card.
class BMUSBCapture : public CaptureInterface {
 public:
	BMUSBCapture(int card_index, libusb_device *dev = nullptr)
		: card_index(card_index), dev(dev)
	{
	}

	~BMUSBCapture() {}

	// Note: Cards could be unplugged and replugged between this call and
	// actually opening the card (in configure_card()).
	static unsigned num_cards();

	std::map<uint32_t, VideoMode> get_available_video_modes() const override;
	uint32_t get_current_video_mode() const override;
	void set_video_mode(uint32_t video_mode_id) override;

	virtual std::map<uint32_t, std::string> get_available_video_inputs() const override;
	virtual void set_video_input(uint32_t video_input_id) override;
	virtual uint32_t get_current_video_input() const override { return current_video_input; }

	virtual std::map<uint32_t, std::string> get_available_audio_inputs() const override;
	virtual void set_audio_input(uint32_t audio_input_id) override;
	virtual uint32_t get_current_audio_input() const override { return current_audio_input; }

	// Does not take ownership.
	void set_video_frame_allocator(FrameAllocator *allocator) override
	{
		video_frame_allocator = allocator;
		if (owned_video_frame_allocator.get() != allocator) {
			owned_video_frame_allocator.reset();
		}
	}

	FrameAllocator *get_video_frame_allocator() override
	{
		return video_frame_allocator;
	}

	// Does not take ownership.
	void set_audio_frame_allocator(FrameAllocator *allocator) override
	{
		audio_frame_allocator = allocator;
		if (owned_audio_frame_allocator.get() != allocator) {
			owned_audio_frame_allocator.reset();
		}
	}

	FrameAllocator *get_audio_frame_allocator() override
	{
		return audio_frame_allocator;
	}

	void set_frame_callback(frame_callback_t callback) override
	{
		frame_callback = callback;
	}

	// Needs to be run before configure_card().
	void set_dequeue_thread_callbacks(std::function<void()> init, std::function<void()> cleanup) override
	{
		dequeue_init_callback = init;
		dequeue_cleanup_callback = cleanup;
		has_dequeue_callbacks = true;
	}

	// Only valid after configure_card().
	std::string get_description() const override {
		return description;
	}

	void configure_card() override;
	void start_bm_capture() override;
	void stop_dequeue_thread() override;
	bool get_disconnected() const override { return disconnected; }

	// TODO: It's rather messy to have these outside the interface.
	static void start_bm_thread();
	static void stop_bm_thread();

	// Hotplug event (for devices being inserted between start_bm_thread()
	// and stop_bm_thread()); entirely optional, but must be set before
	// start_bm_capture(). Note that your callback should do as little work
	// as possible, since the callback comes from the main USB handling
	// thread, which is very time-sensitive.
	//
	// The callback function transfers ownership. If you don't want to hold
	// on to the device given to you in the callback, you need to call
	// libusb_unref_device().
	static void set_card_connected_callback(card_connected_callback_t callback,
	                                        bool hotplug_existing_devices_arg = false)
	{
		card_connected_callback = callback;
		hotplug_existing_devices = hotplug_existing_devices_arg;
	}

	// Similar to set_card_connected_callback(), with the same caveats.
	// (Note that this is set per-card and not global, as it is logically
	// connected to an existing BMUSBCapture object.)
	void set_card_disconnected_callback(card_disconnected_callback_t callback)
	{
		card_disconnected_callback = callback;
	}

 private:
	struct QueuedFrame {
		uint16_t timecode;
		uint16_t format;
		FrameAllocator::Frame frame;
	};

	void start_new_audio_block(const uint8_t *start);
	void start_new_frame(const uint8_t *start);

	void queue_frame(uint16_t format, uint16_t timecode, FrameAllocator::Frame frame, std::deque<QueuedFrame> *q);
	void dequeue_thread_func();

	static void usb_thread_func();
	static void cb_xfr(struct libusb_transfer *xfr);
	static int cb_hotplug(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data);

	void update_capture_mode();

	std::string description;

	FrameAllocator::Frame current_video_frame;
	FrameAllocator::Frame current_audio_frame;

	std::mutex queue_lock;
	std::condition_variable queues_not_empty;
	std::deque<QueuedFrame> pending_video_frames;
	std::deque<QueuedFrame> pending_audio_frames;

	FrameAllocator *video_frame_allocator = nullptr;
	FrameAllocator *audio_frame_allocator = nullptr;
	std::unique_ptr<FrameAllocator> owned_video_frame_allocator;
	std::unique_ptr<FrameAllocator> owned_audio_frame_allocator;
	frame_callback_t frame_callback = nullptr;
	static card_connected_callback_t card_connected_callback;
	static bool hotplug_existing_devices;
	card_disconnected_callback_t card_disconnected_callback = nullptr;

	std::thread dequeue_thread;
	std::atomic<bool> dequeue_thread_should_quit;
	bool has_dequeue_callbacks = false;
	std::function<void()> dequeue_init_callback = nullptr;
	std::function<void()> dequeue_cleanup_callback = nullptr;

	int current_register = 0;

	static constexpr int NUM_BMUSB_REGISTERS = 60;
	uint8_t register_file[NUM_BMUSB_REGISTERS];

	// If <dev> is nullptr, will choose device number <card_index> from the list
	// of available devices on the system. <dev> is not used after configure_card()
	// (it will be unref-ed).
	int card_index = -1;
	libusb_device *dev = nullptr;

	std::vector<libusb_transfer *> iso_xfrs;
	int assumed_frame_width = 1280;

	libusb_device_handle *devh = nullptr;
	uint32_t current_video_input = 0x00000000;  // HDMI/SDI.
	uint32_t current_audio_input = 0x00000000;  // Embedded.

	bool disconnected = false;
};

}  // namespace bmusb

#endif