+++ /dev/null
-// A fake capture device that sends single-color frames at a given rate.
-// Mostly useful for testing themes without actually hooking up capture devices.
-
-#include "fake_capture.h"
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <unistd.h>
-#if __SSE2__
-#include <immintrin.h>
-#endif
-#include <cstddef>
-
-#include "bmusb/bmusb.h"
-#include "defs.h"
-
-#define FRAME_SIZE (8 << 20) // 8 MB.
-#define FAKE_FPS 25 // Must be an integer.
-
-// Pure-color inputs: Red, green, blue, white.
-#define NUM_COLORS 4
-constexpr uint8_t ys[NUM_COLORS] = { 81, 145, 41, 235 };
-constexpr uint8_t cbs[NUM_COLORS] = { 90, 54, 240, 128 };
-constexpr uint8_t crs[NUM_COLORS] = { 240, 34, 110, 128 };
-
-using namespace std;
-
-namespace bmusb {
-namespace {
-
-void memset2(uint8_t *s, const uint8_t c[2], size_t n)
-{
- size_t i = 0;
-#if __SSE2__
- const uint8_t c_expanded[16] = {
- c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1],
- c[0], c[1], c[0], c[1], c[0], c[1], c[0], c[1]
- };
- __m128i cc = *(__m128i *)c_expanded;
- __m128i *out = (__m128i *)s;
-
- for ( ; i < (n & ~15); i += 16) {
- _mm_storeu_si128(out++, cc);
- _mm_storeu_si128(out++, cc);
- }
-
- s = (uint8_t *)out;
-#endif
- for ( ; i < n; ++i) {
- *s++ = c[0];
- *s++ = c[1];
- }
-}
-
-void memset4(uint8_t *s, const uint8_t c[4], size_t n)
-{
- size_t i = 0;
-#if __SSE2__
- const uint8_t c_expanded[16] = {
- c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3],
- c[0], c[1], c[2], c[3], c[0], c[1], c[2], c[3]
- };
- __m128i cc = *(__m128i *)c_expanded;
- __m128i *out = (__m128i *)s;
-
- for ( ; i < (n & ~7); i += 8) {
- _mm_storeu_si128(out++, cc);
- _mm_storeu_si128(out++, cc);
- }
-
- s = (uint8_t *)out;
-#endif
- for ( ; i < n; ++i) {
- *s++ = c[0];
- *s++ = c[1];
- *s++ = c[2];
- *s++ = c[3];
- }
-}
-
-} // namespace
-
-FakeCapture::FakeCapture(int card_index)
-{
- char buf[256];
- snprintf(buf, sizeof(buf), "Fake card %d", card_index + 1);
- description = buf;
-
- y = ys[card_index % NUM_COLORS];
- cb = cbs[card_index % NUM_COLORS];
- cr = crs[card_index % NUM_COLORS];
-}
-
-FakeCapture::~FakeCapture()
-{
- if (has_dequeue_callbacks) {
- dequeue_cleanup_callback();
- }
-}
-
-void FakeCapture::configure_card()
-{
- if (video_frame_allocator == nullptr) {
- owned_video_frame_allocator.reset(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES));
- set_video_frame_allocator(owned_video_frame_allocator.get());
- }
- if (audio_frame_allocator == nullptr) {
- owned_audio_frame_allocator.reset(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES));
- set_audio_frame_allocator(owned_audio_frame_allocator.get());
- }
-}
-
-void FakeCapture::start_bm_capture()
-{
- producer_thread_should_quit = false;
- producer_thread = thread(&FakeCapture::producer_thread_func, this);
-}
-
-void FakeCapture::stop_dequeue_thread()
-{
- producer_thread_should_quit = true;
- producer_thread.join();
-}
-
-std::map<uint32_t, VideoMode> FakeCapture::get_available_video_modes() const
-{
- VideoMode mode;
-
- char buf[256];
- snprintf(buf, sizeof(buf), "%dx%d", WIDTH, HEIGHT);
- mode.name = buf;
-
- mode.autodetect = false;
- mode.width = WIDTH;
- mode.height = HEIGHT;
- mode.frame_rate_num = FAKE_FPS;
- mode.frame_rate_den = 1;
- mode.interlaced = false;
-
- return {{ 0, mode }};
-}
-
-std::map<uint32_t, std::string> FakeCapture::get_available_video_inputs() const
-{
- return {{ 0, "Fake video input (single color)" }};
-}
-
-std::map<uint32_t, std::string> FakeCapture::get_available_audio_inputs() const
-{
- return {{ 0, "Fake audio input (silence)" }};
-}
-
-void FakeCapture::set_video_mode(uint32_t video_mode_id)
-{
- assert(video_mode_id == 0);
-}
-
-void FakeCapture::set_video_input(uint32_t video_input_id)
-{
- assert(video_input_id == 0);
-}
-
-void FakeCapture::set_audio_input(uint32_t audio_input_id)
-{
- assert(audio_input_id == 0);
-}
-
-namespace {
-
-void add_time(double t, timespec *ts)
-{
- ts->tv_nsec += lrint(t * 1e9);
- ts->tv_sec += ts->tv_nsec / 1000000000;
- ts->tv_nsec %= 1000000000;
-}
-
-bool timespec_less_than(const timespec &a, const timespec &b)
-{
- return make_pair(a.tv_sec, a.tv_nsec) < make_pair(b.tv_sec, b.tv_nsec);
-}
-
-} // namespace
-
-void FakeCapture::producer_thread_func()
-{
- uint16_t timecode = 0;
-
- if (has_dequeue_callbacks) {
- dequeue_init_callback();
- }
-
- timespec next_frame;
- clock_gettime(CLOCK_MONOTONIC, &next_frame);
- add_time(1.0 / FAKE_FPS, &next_frame);
-
- while (!producer_thread_should_quit) {
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- if (timespec_less_than(now, next_frame)) {
- // Wait until the next frame.
- if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
- &next_frame, nullptr) == -1) {
- if (errno == EINTR) continue; // Re-check the flag and then sleep again.
- perror("clock_nanosleep");
- exit(1);
- }
- } else {
- // We've seemingly missed a frame. If we're more than one second behind,
- // reset the timer; otherwise, just keep going.
- timespec limit = next_frame;
- ++limit.tv_sec;
- if (!timespec_less_than(now, limit)) {
- fprintf(stderr, "More than one second of missed fake frames; resetting clock.\n");
- next_frame = now;
- }
- }
-
- // Figure out when the next frame is to be, then compute the current one.
- add_time(1.0 / FAKE_FPS, &next_frame);
-
- VideoFormat video_format;
- video_format.width = WIDTH;
- video_format.height = HEIGHT;
- video_format.frame_rate_nom = FAKE_FPS;
- video_format.frame_rate_den = 1;
- video_format.has_signal = true;
- video_format.is_connected = false;
-
- FrameAllocator::Frame video_frame = video_frame_allocator->alloc_frame();
- if (video_frame.data != nullptr) {
- assert(video_frame.size >= WIDTH * HEIGHT * 2);
- if (video_frame.interleaved) {
- uint8_t cbcr[] = { cb, cr };
- memset2(video_frame.data, cbcr, WIDTH * HEIGHT / 2);
- memset(video_frame.data2, y, WIDTH * HEIGHT);
- } else {
- uint8_t ycbcr[] = { y, cb, y, cr };
- memset4(video_frame.data, ycbcr, WIDTH * HEIGHT / 2);
- }
- video_frame.len = WIDTH * HEIGHT * 2;
- }
-
- AudioFormat audio_format;
- audio_format.bits_per_sample = 32;
- audio_format.num_channels = 2;
-
- FrameAllocator::Frame audio_frame = audio_frame_allocator->alloc_frame();
- if (audio_frame.data != nullptr) {
- assert(audio_frame.size >= 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS);
- audio_frame.len = 2 * sizeof(uint32_t) * OUTPUT_FREQUENCY / FAKE_FPS;
- memset(audio_frame.data, 0, audio_frame.len);
- }
-
- frame_callback(timecode++,
- video_frame, 0, video_format,
- audio_frame, 0, audio_format);
- }
- if (has_dequeue_callbacks) {
- dequeue_cleanup_callback();
- }
-}
-
-} // namespace bmusb
+++ /dev/null
-#ifndef _FAKE_CAPTURE_H
-#define _FAKE_CAPTURE_H 1
-
-#include <stdint.h>
-#include <functional>
-#include <string>
-
-#include "bmusb/bmusb.h"
-
-namespace bmusb {
-
-class FakeCapture : public CaptureInterface
-{
-public:
- FakeCapture(int card_index);
- ~FakeCapture();
-
- // CaptureInterface.
- 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;
- }
-
- 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;
- }
-
- 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 false; }
-
- std::map<uint32_t, VideoMode> get_available_video_modes() const override;
- void set_video_mode(uint32_t video_mode_id) override;
- uint32_t get_current_video_mode() const override { return 0; }
-
- std::map<uint32_t, std::string> get_available_video_inputs() const override;
- void set_video_input(uint32_t video_input_id) override;
- uint32_t get_current_video_input() const override { return 0; }
-
- std::map<uint32_t, std::string> get_available_audio_inputs() const override;
- void set_audio_input(uint32_t audio_input_id) override;
- uint32_t get_current_audio_input() const override { return 0; }
-
-private:
- void producer_thread_func();
-
- uint8_t y, cb, cr;
-
- bool has_dequeue_callbacks = false;
- std::function<void()> dequeue_init_callback = nullptr;
- std::function<void()> dequeue_cleanup_callback = nullptr;
-
- 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;
-
- std::string description;
-
- std::atomic<bool> producer_thread_should_quit{false};
- std::thread producer_thread;
-};
-
-} // namespace bmusb
-
-#endif // !defined(_FAKE_CAPTURE_H)