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 "fake_capture.h"
15 #include "bmusb/bmusb.h"
18 #define FRAME_SIZE (8 << 20) // 8 MB.
19 #define FAKE_FPS 25 // Must be an integer.
21 // Pure-color inputs: Red, green, blue, white.
23 constexpr uint8_t ys[NUM_COLORS] = { 81, 145, 41, 235 };
24 constexpr uint8_t cbs[NUM_COLORS] = { 90, 54, 240, 128 };
25 constexpr uint8_t crs[NUM_COLORS] = { 240, 34, 110, 128 };
31 // TODO: SSE2-optimize (or at least write full int64s) if speed becomes a problem.
33 void memset2(uint8_t *s, const uint8_t c[2], size_t n)
35 for (size_t i = 0; i < n; ++i) {
41 void memset4(uint8_t *s, const uint8_t c[4], size_t n)
43 for (size_t i = 0; i < n; ++i) {
53 FakeCapture::FakeCapture(int card_index)
56 snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
59 y = ys[card_index % NUM_COLORS];
60 cb = cbs[card_index % NUM_COLORS];
61 cr = crs[card_index % NUM_COLORS];
64 FakeCapture::~FakeCapture()
66 if (has_dequeue_callbacks) {
67 dequeue_cleanup_callback();
71 void FakeCapture::configure_card()
73 if (video_frame_allocator == nullptr) {
74 owned_video_frame_allocator.reset(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES));
75 set_video_frame_allocator(owned_video_frame_allocator.get());
77 if (audio_frame_allocator == nullptr) {
78 owned_audio_frame_allocator.reset(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES));
79 set_audio_frame_allocator(owned_audio_frame_allocator.get());
83 void FakeCapture::start_bm_capture()
85 producer_thread_should_quit = false;
86 producer_thread = thread(&FakeCapture::producer_thread_func, this);
89 void FakeCapture::stop_dequeue_thread()
91 producer_thread_should_quit = true;
92 producer_thread.join();
95 std::map<uint32_t, VideoMode> FakeCapture::get_available_video_modes() const
100 snprintf(buf, sizeof(buf), "%dx%d", WIDTH, HEIGHT);
103 mode.autodetect = false;
105 mode.height = HEIGHT;
106 mode.frame_rate_num = FAKE_FPS;
107 mode.frame_rate_den = 1;
108 mode.interlaced = false;
110 return {{ 0, mode }};
113 std::map<uint32_t, std::string> FakeCapture::get_available_video_inputs() const
115 return {{ 0, "Fake video input (single color)" }};
118 std::map<uint32_t, std::string> FakeCapture::get_available_audio_inputs() const
120 return {{ 0, "Fake audio input (silence)" }};
123 void FakeCapture::set_video_mode(uint32_t video_mode_id)
125 assert(video_mode_id == 0);
128 void FakeCapture::set_video_input(uint32_t video_input_id)
130 assert(video_input_id == 0);
133 void FakeCapture::set_audio_input(uint32_t audio_input_id)
135 assert(audio_input_id == 0);
140 void add_time(double t, timespec *ts)
142 ts->tv_nsec += lrint(t * 1e9);
143 ts->tv_sec += ts->tv_nsec / 1000000000;
144 ts->tv_nsec %= 1000000000;
147 bool timespec_less_than(const timespec &a, const timespec &b)
149 return make_pair(a.tv_sec, a.tv_nsec) < make_pair(b.tv_sec, b.tv_nsec);
154 void FakeCapture::producer_thread_func()
156 uint16_t timecode = 0;
158 if (has_dequeue_callbacks) {
159 dequeue_init_callback();
163 clock_gettime(CLOCK_MONOTONIC, &next_frame);
164 add_time(1.0 / FAKE_FPS, &next_frame);
166 while (!producer_thread_should_quit) {
168 clock_gettime(CLOCK_MONOTONIC, &now);
170 if (timespec_less_than(now, next_frame)) {
171 // Wait until the next frame.
172 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
173 &next_frame, nullptr) == -1) {
174 if (errno == EINTR) continue; // Re-check the flag and then sleep again.
175 perror("clock_nanosleep");
179 // We've seemingly missed a frame. If we're more than one second behind,
180 // reset the timer; otherwise, just keep going.
181 timespec limit = next_frame;
183 if (!timespec_less_than(now, limit)) {
184 fprintf(stderr, "More than one second of missed fake frames; resetting clock.\n");
189 // Figure out when the next frame is to be, then compute the current one.
190 add_time(1.0 / FAKE_FPS, &next_frame);
192 VideoFormat video_format;
193 video_format.width = WIDTH;
194 video_format.height = HEIGHT;
195 video_format.frame_rate_nom = FAKE_FPS;
196 video_format.frame_rate_den = 1;
197 video_format.has_signal = true;
198 video_format.is_connected = false;
200 FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame();
201 if (video_frame.data != nullptr) {
202 assert(video_frame.size >= WIDTH * HEIGHT * 2);
203 if (video_frame.interleaved) {
204 uint8_t cbcr[] = { cb, cr };
205 memset2(video_frame.data, cbcr, WIDTH * HEIGHT / 2);
206 memset(video_frame.data2, y, WIDTH * HEIGHT);
208 uint8_t ycbcr[] = { y, cb, y, cr };
209 memset4(video_frame.data, ycbcr, WIDTH * HEIGHT / 2);
211 video_frame.len = WIDTH * HEIGHT * 2;
214 AudioFormat audio_format;
215 audio_format.bits_per_sample = 32;
216 audio_format.num_channels = 2;
218 FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
219 if (audio_frame.data != nullptr) {
220 assert(audio_frame.size >= 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS);
221 audio_frame.len = 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS;
222 memset(audio_frame.data, 0, audio_frame.len);
225 frame_callback(timecode++,
226 video_frame, 0, video_format,
227 audio_frame, 0, audio_format);
229 if (has_dequeue_callbacks) {
230 dequeue_cleanup_callback();