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"
14 #include "bmusb/bmusb.h"
17 #define FRAME_SIZE (8 << 20) // 8 MB.
18 #define FAKE_FPS 25 // Must be an integer.
20 // Pure-color inputs: Red, green, blue, white.
22 constexpr uint8_t ys[NUM_COLORS] = { 81, 145, 41, 235 };
23 constexpr uint8_t cbs[NUM_COLORS] = { 90, 54, 240, 128 };
24 constexpr uint8_t crs[NUM_COLORS] = { 240, 34, 110, 128 };
30 // TODO: SSE2-optimize (or at least write full int64s) if speed becomes a problem.
32 void memset2(uint8_t *s, const uint8_t c[2], size_t n)
34 for (size_t i = 0; i < n; ++i) {
40 void memset4(uint8_t *s, const uint8_t c[4], size_t n)
42 for (size_t i = 0; i < n; ++i) {
52 FakeCapture::FakeCapture(int card_index)
55 snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
58 y = ys[card_index % NUM_COLORS];
59 cb = cbs[card_index % NUM_COLORS];
60 cr = crs[card_index % NUM_COLORS];
63 FakeCapture::~FakeCapture()
65 if (has_dequeue_callbacks) {
66 dequeue_cleanup_callback();
70 void FakeCapture::configure_card()
72 if (video_frame_allocator == nullptr) {
73 owned_video_frame_allocator.reset(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES));
74 set_video_frame_allocator(owned_video_frame_allocator.get());
76 if (audio_frame_allocator == nullptr) {
77 owned_audio_frame_allocator.reset(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES));
78 set_audio_frame_allocator(owned_audio_frame_allocator.get());
82 void FakeCapture::start_bm_capture()
84 producer_thread_should_quit = false;
85 producer_thread = thread(&FakeCapture::producer_thread_func, this);
88 void FakeCapture::stop_dequeue_thread()
90 producer_thread_should_quit = true;
91 producer_thread.join();
94 std::map<uint32_t, VideoMode> FakeCapture::get_available_video_modes() const
99 snprintf(buf, sizeof(buf), "%dx%d", WIDTH, HEIGHT);
102 mode.autodetect = false;
104 mode.height = HEIGHT;
105 mode.frame_rate_num = FAKE_FPS;
106 mode.frame_rate_den = 1;
107 mode.interlaced = false;
109 return {{ 0, mode }};
112 std::map<uint32_t, std::string> FakeCapture::get_available_video_inputs() const
114 return {{ 0, "Fake video input (single color)" }};
117 std::map<uint32_t, std::string> FakeCapture::get_available_audio_inputs() const
119 return {{ 0, "Fake audio input (silence)" }};
122 void FakeCapture::set_video_mode(uint32_t video_mode_id)
124 assert(video_mode_id == 0);
127 void FakeCapture::set_video_input(uint32_t video_input_id)
129 assert(video_input_id == 0);
132 void FakeCapture::set_audio_input(uint32_t audio_input_id)
134 assert(audio_input_id == 0);
137 void FakeCapture::producer_thread_func()
139 uint16_t timecode = 0;
141 if (has_dequeue_callbacks) {
142 dequeue_init_callback();
144 while (!producer_thread_should_quit) {
145 usleep(1000000 / FAKE_FPS); // Rather approximate frame rate.
147 if (producer_thread_should_quit) break;
149 VideoFormat video_format;
150 video_format.width = WIDTH;
151 video_format.height = HEIGHT;
152 video_format.frame_rate_nom = FAKE_FPS;
153 video_format.frame_rate_den = 1;
154 video_format.has_signal = true;
156 FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame();
157 if (video_frame.data != nullptr) {
158 assert(video_frame.size >= WIDTH * HEIGHT * 2);
159 if (video_frame.interleaved) {
160 uint8_t cbcr[] = { cb, cr };
161 memset2(video_frame.data, cbcr, WIDTH * HEIGHT / 2);
162 memset(video_frame.data2, y, WIDTH * HEIGHT);
164 uint8_t ycbcr[] = { y, cb, y, cr };
165 memset4(video_frame.data, ycbcr, WIDTH * HEIGHT / 2);
167 video_frame.len = WIDTH * HEIGHT * 2;
170 AudioFormat audio_format;
171 audio_format.bits_per_sample = 32;
172 audio_format.num_channels = 2;
174 FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
175 if (audio_frame.data != nullptr) {
176 assert(audio_frame.size >= 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS);
177 audio_frame.len = 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS;
178 memset(audio_frame.data, 0, audio_frame.len);
181 frame_callback(timecode++,
182 video_frame, 0, video_format,
183 audio_frame, 0, audio_format);
185 if (has_dequeue_callbacks) {
186 dequeue_cleanup_callback();