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"
14 #include <immintrin.h>
18 #include "bmusb/bmusb.h"
20 #define FRAME_SIZE (8 << 20) // 8 MB.
22 // Pure-color inputs: Red, green, blue, white.
24 constexpr uint8_t ys[NUM_COLORS] = { 81, 145, 41, 235 };
25 constexpr uint8_t cbs[NUM_COLORS] = { 90, 54, 240, 128 };
26 constexpr uint8_t crs[NUM_COLORS] = { 240, 34, 110, 128 };
33 // We don't bother with multiversioning for this, because SSE2
34 // is on by default for all 64-bit compiles, which is really
35 // the target user segment here.
37 void memset2(uint8_t *s, const uint8_t c[2], size_t n)
41 const uint8_t c_expanded[16] = {
42 c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1],
43 c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1]
45 __m128i cc = *(__m128i *)c_expanded;
46 __m128i *out = (__m128i *)s;
48 for ( ; i < (n & ~15); i += 16) {
49 _mm_storeu_si128(out++, cc);
50 _mm_storeu_si128(out++, cc);
61 void memset4(uint8_t *s, const uint8_t c[4], size_t n)
65 const uint8_t c_expanded[16] = {
66 c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3],
67 c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3]
69 __m128i cc = *(__m128i *)c_expanded;
70 __m128i *out = (__m128i *)s;
72 for ( ; i < (n & ~7); i += 8) {
73 _mm_storeu_si128(out++, cc);
74 _mm_storeu_si128(out++, cc);
89 FakeCapture::FakeCapture(unsigned width, unsigned height, unsigned fps, unsigned audio_frequency, int card_index)
90 : width(width), height(height), fps(fps), audio_frequency(audio_frequency)
93 snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
96 y = ys[card_index % NUM_COLORS];
97 cb = cbs[card_index % NUM_COLORS];
98 cr = crs[card_index % NUM_COLORS];
101 FakeCapture::~FakeCapture()
103 if (has_dequeue_callbacks) {
104 dequeue_cleanup_callback();
108 void FakeCapture::configure_card()
110 if (video_frame_allocator == nullptr) {
111 owned_video_frame_allocator.reset(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES));
112 set_video_frame_allocator(owned_video_frame_allocator.get());
114 if (audio_frame_allocator == nullptr) {
115 owned_audio_frame_allocator.reset(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES));
116 set_audio_frame_allocator(owned_audio_frame_allocator.get());
120 void FakeCapture::start_bm_capture()
122 producer_thread_should_quit = false;
123 producer_thread = thread(&FakeCapture::producer_thread_func, this);
126 void FakeCapture::stop_dequeue_thread()
128 producer_thread_should_quit = true;
129 producer_thread.join();
132 std::map<uint32_t, VideoMode> FakeCapture::get_available_video_modes() const
137 snprintf(buf, sizeof(buf), "%ux%u", width, height);
140 mode.autodetect = false;
142 mode.height = height;
143 mode.frame_rate_num = fps;
144 mode.frame_rate_den = 1;
145 mode.interlaced = false;
147 return {{ 0, mode }};
150 std::map<uint32_t, std::string> FakeCapture::get_available_video_inputs() const
152 return {{ 0, "Fake video input (single color)" }};
155 std::map<uint32_t, std::string> FakeCapture::get_available_audio_inputs() const
157 return {{ 0, "Fake audio input (silence)" }};
160 void FakeCapture::set_video_mode(uint32_t video_mode_id)
162 assert(video_mode_id == 0);
165 void FakeCapture::set_video_input(uint32_t video_input_id)
167 assert(video_input_id == 0);
170 void FakeCapture::set_audio_input(uint32_t audio_input_id)
172 assert(audio_input_id == 0);
177 void add_time(double t, timespec *ts)
179 ts->tv_nsec += lrint(t * 1e9);
180 ts->tv_sec += ts->tv_nsec / 1000000000;
181 ts->tv_nsec %= 1000000000;
184 bool timespec_less_than(const timespec &a, const timespec &b)
186 return make_pair(a.tv_sec, a.tv_nsec) < make_pair(b.tv_sec, b.tv_nsec);
191 void FakeCapture::producer_thread_func()
193 uint16_t timecode = 0;
195 if (has_dequeue_callbacks) {
196 dequeue_init_callback();
200 clock_gettime(CLOCK_MONOTONIC, &next_frame);
201 add_time(1.0 / fps, &next_frame);
203 while (!producer_thread_should_quit) {
205 clock_gettime(CLOCK_MONOTONIC, &now);
207 if (timespec_less_than(now, next_frame)) {
208 // Wait until the next frame.
209 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
210 &next_frame, nullptr) == -1) {
211 if (errno == EINTR) continue; // Re-check the flag and then sleep again.
212 perror("clock_nanosleep");
216 // We've seemingly missed a frame. If we're more than one second behind,
217 // reset the timer; otherwise, just keep going.
218 timespec limit = next_frame;
220 if (!timespec_less_than(now, limit)) {
221 fprintf(stderr, "More than one second of missed fake frames; resetting clock.\n");
226 // Figure out when the next frame is to be, then compute the current one.
227 add_time(1.0 / fps, &next_frame);
229 VideoFormat video_format;
230 video_format.width = width;
231 video_format.height = height;
232 video_format.frame_rate_nom = fps;
233 video_format.frame_rate_den = 1;
234 video_format.has_signal = true;
235 video_format.is_connected = false;
237 FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame();
238 if (video_frame.data != nullptr) {
239 assert(video_frame.size >= width * height * 2);
240 if (video_frame.interleaved) {
241 uint8_t cbcr[] = { cb, cr };
242 memset2(video_frame.data, cbcr, width * height / 2);
243 memset(video_frame.data2, y, width * height);
245 uint8_t ycbcr[] = { y, cb, y, cr };
246 memset4(video_frame.data, ycbcr, width * height / 2);
248 video_frame.len = width * height * 2;
251 AudioFormat audio_format;
252 audio_format.bits_per_sample = 32;
253 audio_format.num_channels = 2;
255 FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
256 if (audio_frame.data != nullptr) {
257 assert(audio_frame.size >= 2 * sizeof(uint32_t) * audio_frequency / fps);
258 audio_frame.len = 2 * sizeof(uint32_t) * audio_frequency / fps;
259 memset(audio_frame.data, 0, audio_frame.len);
262 frame_callback(timecode++,
263 video_frame, 0, video_format,
264 audio_frame, 0, audio_format);
266 if (has_dequeue_callbacks) {
267 dequeue_cleanup_callback();