1 // A fake capture device that sends single-color frames at a given rate.
2 // Mostly useful for testing themes without actually hooking up capture devices.
4 #include "bmusb/fake_capture.h"
15 #include <immintrin.h>
20 #include "bmusb/bmusb.h"
22 #define FRAME_SIZE (8 << 20) // 8 MB.
24 // Pure-color inputs: Red, green, blue, white, two shades of gray.
26 constexpr uint8_t ys[NUM_COLORS] = { 63, 173, 32, 235, 180, 128 };
27 constexpr uint8_t cbs[NUM_COLORS] = { 102, 42, 240, 128, 128, 128 };
28 constexpr uint8_t crs[NUM_COLORS] = { 240, 26, 118, 128, 128, 128 };
31 using namespace std::chrono;
36 // We don't bother with multiversioning for this, because SSE2
37 // is on by default for all 64-bit compiles, which is really
38 // the target user segment here.
40 void memset2(uint8_t *s, const uint8_t c[2], size_t n)
44 const uint8_t c_expanded[16] = {
45 c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1],
46 c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1]
48 __m128i cc = *(__m128i *)c_expanded;
49 __m128i *out = (__m128i *)s;
51 for ( ; i < (n & ~15); i += 16) {
52 _mm_storeu_si128(out++, cc);
53 _mm_storeu_si128(out++, cc);
64 void memset4(uint8_t *s, const uint8_t c[4], size_t n)
68 const uint8_t c_expanded[16] = {
69 c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3],
70 c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3]
72 __m128i cc = *(__m128i *)c_expanded;
73 __m128i *out = (__m128i *)s;
75 for ( ; i < (n & ~7); i += 8) {
76 _mm_storeu_si128(out++, cc);
77 _mm_storeu_si128(out++, cc);
90 void memset16(uint8_t *s, const uint32_t c[4], size_t n)
94 __m128i cc = *(__m128i *)c;
95 __m128i *out = (__m128i *)s;
97 for ( ; i < (n & ~1); i += 2) {
98 _mm_storeu_si128(out++, cc);
99 _mm_storeu_si128(out++, cc);
104 for ( ; i < n; ++i) {
112 FakeCapture::FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_sample_frequency, int card_index, bool has_audio)
113 : width(width), height(height), fps(fps), audio_sample_frequency(audio_sample_frequency), card_index(card_index)
116 snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
119 y = ys[card_index % NUM_COLORS];
120 cb = cbs[card_index % NUM_COLORS];
121 cr = crs[card_index % NUM_COLORS];
124 audio_ref_level = pow(10.0f, -23.0f / 20.0f) * (1u << 31); // -23 dBFS (EBU R128 level).
126 float freq = 440.0 * pow(2.0, card_index / 12.0);
127 sincosf(2 * M_PI * freq / audio_sample_frequency, &audio_sin, &audio_cos);
128 audio_real = audio_ref_level;
133 FakeCapture::~FakeCapture()
135 if (has_dequeue_callbacks) {
136 dequeue_cleanup_callback();
140 void FakeCapture::configure_card()
142 if (video_frame_allocator == nullptr) {
143 owned_video_frame_allocator.reset(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES));
144 set_video_frame_allocator(owned_video_frame_allocator.get());
146 if (audio_frame_allocator == nullptr) {
147 owned_audio_frame_allocator.reset(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES));
148 set_audio_frame_allocator(owned_audio_frame_allocator.get());
152 void FakeCapture::start_bm_capture()
154 producer_thread_should_quit = false;
155 producer_thread = thread(&FakeCapture::producer_thread_func, this);
158 void FakeCapture::stop_dequeue_thread()
160 producer_thread_should_quit = true;
161 producer_thread.join();
164 std::map<uint32_t, VideoMode> FakeCapture::get_available_video_modes() const
169 snprintf(buf, sizeof(buf), "%ux%u", width, height);
172 mode.autodetect = false;
174 mode.height = height;
175 mode.frame_rate_num = fps;
176 mode.frame_rate_den = 1;
177 mode.interlaced = false;
179 return {{ 0, mode }};
182 std::map<uint32_t, std::string> FakeCapture::get_available_video_inputs() const
184 return {{ 0, "Fake video input (single color)" }};
187 std::map<uint32_t, std::string> FakeCapture::get_available_audio_inputs() const
189 return {{ 0, "Fake audio input (silence)" }};
192 void FakeCapture::set_video_mode(uint32_t video_mode_id)
194 assert(video_mode_id == 0);
197 void FakeCapture::set_video_input(uint32_t video_input_id)
199 assert(video_input_id == 0);
202 void FakeCapture::set_audio_input(uint32_t audio_input_id)
204 assert(audio_input_id == 0);
209 void add_time(double t, timespec *ts)
211 ts->tv_nsec += lrint(t * 1e9);
212 ts->tv_sec += ts->tv_nsec / 1000000000;
213 ts->tv_nsec %= 1000000000;
216 bool timespec_less_than(const timespec &a, const timespec &b)
218 return make_pair(a.tv_sec, a.tv_nsec) < make_pair(b.tv_sec, b.tv_nsec);
223 void FakeCapture::producer_thread_func()
225 char thread_name[16];
226 snprintf(thread_name, sizeof(thread_name), "FakeCapture_%d", card_index);
227 pthread_setname_np(pthread_self(), thread_name);
229 uint16_t timecode = 0;
231 if (has_dequeue_callbacks) {
232 dequeue_init_callback();
236 clock_gettime(CLOCK_MONOTONIC, &next_frame);
237 add_time(1.0 / fps, &next_frame);
239 while (!producer_thread_should_quit) {
241 clock_gettime(CLOCK_MONOTONIC, &now);
243 if (timespec_less_than(now, next_frame)) {
244 // Wait until the next frame.
245 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
246 &next_frame, nullptr) == -1) {
247 if (errno == EINTR) continue; // Re-check the flag and then sleep again.
248 perror("clock_nanosleep");
252 // We've seemingly missed a frame. If we're more than one second behind,
253 // reset the timer; otherwise, just keep going.
254 timespec limit = next_frame;
256 if (!timespec_less_than(now, limit)) {
257 fprintf(stderr, "More than one second of missed fake frames; resetting clock.\n");
261 steady_clock::time_point timestamp = steady_clock::now();
263 // Figure out when the next frame is to be, then compute the current one.
264 add_time(1.0 / fps, &next_frame);
266 VideoFormat video_format;
267 video_format.width = width;
268 video_format.height = height;
269 if (current_pixel_format == PixelFormat_10BitYCbCr) {
270 video_format.stride = (width + 5) / 6 * 4 * sizeof(uint32_t);
272 video_format.stride = width * 2;
274 video_format.frame_rate_nom = fps;
275 video_format.frame_rate_den = 1;
276 video_format.has_signal = true;
277 video_format.is_connected = false;
279 FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame();
280 if (video_frame.data != nullptr) {
281 assert(video_frame.size >= width * height * 2);
282 if (video_frame.interleaved) {
283 assert(current_pixel_format == PixelFormat_8BitYCbCr);
284 uint8_t cbcr[] = { cb, cr };
285 memset2(video_frame.data, cbcr, width * height / 2);
286 memset(video_frame.data2, y, width * height);
288 if (current_pixel_format == PixelFormat_10BitYCbCr) {
289 // Just use the 8-bit-values shifted left by 2.
290 // It's not 100% correct, but it's close enough.
292 pix[0] = (cb << 2) | (y << 12) | (cr << 22);
293 pix[1] = (y << 2) | (cb << 12) | ( y << 22);
294 pix[2] = (cr << 2) | (y << 12) | (cb << 22);
295 pix[3] = (y << 2) | (cr << 12) | ( y << 22);
296 memset16(video_frame.data, pix, video_format.stride * height / sizeof(pix));
298 uint8_t ycbcr[] = { y, cb, y, cr };
299 memset4(video_frame.data, ycbcr, width * height / 2);
302 video_frame.len = video_format.stride * height;
303 video_frame.received_timestamp = timestamp;
306 AudioFormat audio_format;
307 audio_format.bits_per_sample = 32;
308 audio_format.num_channels = 8;
310 FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
311 if (audio_frame.data != nullptr) {
312 const unsigned num_stereo_samples = audio_sample_frequency / fps;
313 assert(audio_frame.size >= audio_format.num_channels * sizeof(int32_t) * num_stereo_samples);
314 audio_frame.len = audio_format.num_channels * sizeof(int32_t) * num_stereo_samples;
315 audio_frame.received_timestamp = timestamp;
317 if (audio_sin == 0.0f) {
319 memset(audio_frame.data, 0, audio_frame.len);
321 make_tone((int32_t *)audio_frame.data, num_stereo_samples, audio_format.num_channels);
325 frame_callback(timecode++,
326 video_frame, 0, video_format,
327 audio_frame, 0, audio_format);
329 if (has_dequeue_callbacks) {
330 dequeue_cleanup_callback();
334 void FakeCapture::make_tone(int32_t *out, unsigned num_stereo_samples, unsigned num_channels)
337 float r = audio_real, i = audio_imag;
338 for (unsigned sample_num = 0; sample_num < num_stereo_samples; ++sample_num) {
339 int32_t s = lrintf(r);
340 for (unsigned i = 0; i < num_channels; ++i) {
344 // Rotate the phaser by one sample.
345 float new_r = r * audio_cos - i * audio_sin;
346 float new_i = r * audio_sin + i * audio_cos;
351 // Periodically renormalize to counteract precision issues.
352 double corr = audio_ref_level / hypot(r, i);
353 audio_real = r * corr;
354 audio_imag = i * corr;