#include #include #include #include struct async_sync_test { obs_source_t *source; os_event_t *stop_signal; pthread_t thread; bool initialized; }; /* middle C */ static const double rate = 261.63 / 48000.0; #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif #define M_PI_X2 M_PI * 2 static const char *ast_getname(void *unused) { UNUSED_PARAMETER(unused); return "Sync Test (Async Video/Audio Source)"; } static void ast_destroy(void *data) { struct async_sync_test *ast = data; if (ast->initialized) { os_event_signal(ast->stop_signal); pthread_join(ast->thread, NULL); } os_event_destroy(ast->stop_signal); bfree(ast); } static inline void fill_texture(uint32_t *pixels, uint32_t color) { size_t x, y; for (y = 0; y < 20; y++) { for (x = 0; x < 20; x++) { pixels[y * 20 + x] = color; } } } static void *video_thread(void *data) { struct async_sync_test *ast = data; uint32_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); uint32_t *pixels = bmalloc(20 * 20 * sizeof(uint32_t)); float *samples = bmalloc(sample_rate * sizeof(float)); uint64_t cur_time = os_gettime_ns(); bool whitelist = false; double cos_val = 0.0; uint64_t start_time = cur_time; struct obs_source_frame frame = { .data = {[0] = (uint8_t *)pixels}, .linesize = {[0] = 20 * 4}, .width = 20, .height = 20, .format = VIDEO_FORMAT_BGRX, }; struct obs_source_audio audio = { .speakers = SPEAKERS_MONO, .data = {[0] = (uint8_t *)samples}, .samples_per_sec = sample_rate, .frames = sample_rate, .format = AUDIO_FORMAT_FLOAT, }; while (os_event_try(ast->stop_signal) == EAGAIN) { fill_texture(pixels, whitelist ? 0xFFFFFFFF : 0xFF000000); frame.timestamp = cur_time - start_time; audio.timestamp = cur_time - start_time; if (whitelist) { for (size_t i = 0; i < sample_rate; i++) { cos_val += rate * M_PI_X2; if (cos_val > M_PI_X2) cos_val -= M_PI_X2; samples[i] = (float)(cos(cos_val) * 0.5); } } else { for (size_t i = 0; i < sample_rate; i++) samples[i] = 0.0f; } obs_source_output_video(ast->source, &frame); obs_source_output_audio(ast->source, &audio); os_sleepto_ns(cur_time += 1000000000); whitelist = !whitelist; } bfree(pixels); bfree(samples); return NULL; } static void *ast_create(obs_data_t *settings, obs_source_t *source) { struct async_sync_test *ast = bzalloc(sizeof(struct async_sync_test)); ast->source = source; if (os_event_init(&ast->stop_signal, OS_EVENT_TYPE_MANUAL) != 0) { ast_destroy(ast); return NULL; } if (pthread_create(&ast->thread, NULL, video_thread, ast) != 0) { ast_destroy(ast); return NULL; } ast->initialized = true; UNUSED_PARAMETER(settings); UNUSED_PARAMETER(source); return ast; } struct obs_source_info async_sync_test = { .id = "async_sync_test", .type = OBS_SOURCE_TYPE_INPUT, .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO, .get_name = ast_getname, .create = ast_create, .destroy = ast_destroy, };