You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
6.3 KiB
115 lines
6.3 KiB
/* Android actor: локальный preview, очередь, stop/FIN, отменённый open и detach concurrent producer. */ |
|
#include <pthread.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/sqlite3.h" |
|
#include "../src/chat/chat_core.h" |
|
#include "../src/chat/chat_event.h" |
|
#include "../src/stream/stream.h" |
|
#include "../src/stream/stream_member.h" |
|
#include "../src/utun_instance.h" |
|
#include "../tools/chatgui-android/libutun_lite/stream_bridge.h" |
|
#include "../src/stream/stream_proto.h" |
|
#include "test_clock.h" |
|
|
|
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "Android stream test line=%d: %s", __LINE__, #x); abort(); } } while (0) |
|
static uint8_t video[9000]; |
|
static struct UASYNC *ua; |
|
static int producer_started, producer_stop; |
|
static unsigned source_events; |
|
static uint64_t source_group; |
|
static char *source_snapshot; |
|
|
|
static void chat_event(struct UTUN_INSTANCE *inst, int type, const uint8_t *data, int length) { |
|
(void)inst; |
|
if (type != CHAT_EVT_STREAM_SOURCES) return; |
|
CHECK(length >= 11 && data[8] == 1); |
|
source_group = stream_read_be(data, 8); source_events++; |
|
u_free(source_snapshot); source_snapshot = u_malloc((uint32_t)(length - 8)); CHECK(source_snapshot); |
|
memcpy(source_snapshot, data + 9, (size_t)(length - 9)); source_snapshot[length - 9] = '\0'; |
|
} |
|
|
|
static uint8_t *wait_event(uint64_t id, int kind, size_t *size) { |
|
for (unsigned i = 0; i < 1000; i++) { |
|
uint8_t *data = android_stream_pull(id, size); |
|
if (data && data[0] == kind) return data; |
|
u_free(data); test_clock_poll(ua, 100); |
|
} |
|
CHECK(0); return NULL; |
|
} |
|
static void *producer(void *arg) { |
|
(void)arg; |
|
__atomic_store_n(&producer_started, 1, __ATOMIC_RELEASE); |
|
uint64_t pts = 500000; |
|
while (!__atomic_load_n(&producer_stop, __ATOMIC_ACQUIRE)) { |
|
int result = android_stream_feed(3, 1, pts++, 1, video, sizeof(video)); |
|
if (result < 0) break; |
|
} |
|
return NULL; |
|
} |
|
|
|
int main(void) { |
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_INFO); |
|
size_t before = u_get_allocated_count(); test_clock_init(100); ua = uasync_create(); CHECK(ua); |
|
struct UTUN_INSTANCE *inst = utun_instance_create_from_str(ua, |
|
"[global]\nauto_sockets=no\n[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\nmember_files_enabled=0\n"); |
|
CHECK(inst && !utun_core_start(inst) && !chat_service_start(inst)); |
|
chat_core_create_channel_auto(inst, "Stream \"test\" 😀"); |
|
sqlite3_stmt *statement = NULL; |
|
CHECK(sqlite3_prepare_v2(chat_core_get_db(inst), "SELECT channel_id FROM channels LIMIT 1", -1, &statement, NULL) == SQLITE_OK); |
|
CHECK(sqlite3_step(statement) == SQLITE_ROW); |
|
uint64_t group = strtoull((const char *)sqlite3_column_text(statement, 0), NULL, 10); sqlite3_finalize(statement); CHECK(group); |
|
uasync_mark_running(ua); android_stream_attach(inst); |
|
chat_event_set_handler(inst, chat_event); |
|
CHECK(android_stream_watch(group) == 0); test_clock_poll(ua, 100); |
|
CHECK(source_events == 1 && source_group == group && !strcmp(source_snapshot, "[]")); |
|
CHECK(!stream_service_needs_activity(inst)); |
|
CHECK(android_stream_open(1, group, 0, 1280, 720, 90, 1, 0) == 0); |
|
size_t size = 0; u_free(wait_event(1, 4, &size)); |
|
char *sources = android_stream_sources(group); u_free(sources); test_clock_poll(ua, 100); |
|
sources = android_stream_sources(group); CHECK(strstr(sources, "epoch") && strstr(sources, "name")); u_free(sources); |
|
CHECK(source_events == 2 && strstr(source_snapshot, "epoch")); |
|
struct stream_member_state member; |
|
CHECK(stream_member_load(inst, group, inst->node_id, &member, NULL) == STREAM_OK && member.published); |
|
CHECK(android_stream_open(5, group, inst->node_id, 0, 0, 0, 1, member.source.source_epoch ^ 1) == 0); |
|
uint8_t *error = wait_event(5, 6, &size); CHECK(error[1] == STREAM_CLOSE_SOURCE_CHANGED); u_free(error); |
|
CHECK(android_stream_open(2, group, inst->node_id, 0, 0, 0, 1, member.source.source_epoch) == 0); |
|
u_free(wait_event(2, 4, &size)); |
|
CHECK(source_events == 2); /* Подписка не меняет каталог публикаций. */ |
|
for (unsigned i = 0; i < sizeof(video); i++) video[i] = (uint8_t)i; |
|
CHECK(android_stream_feed(1, 1, 100000, 0, video, sizeof(video)) == 0); |
|
CHECK(android_stream_feed(1, 1, 100000, 1, video, sizeof(video)) == 1); |
|
CHECK(android_stream_feed(1, 0, 100000, 0, (const uint8_t *)"ops", 3) == 1); |
|
uint8_t *output = NULL; int video_seen = 0, audio_seen = 0; |
|
for (unsigned i = 0; i < 1000 && (!video_seen || !audio_seen); i++) { |
|
output = android_stream_pull(2, &size); |
|
if (output && output[0] == 1) { |
|
CHECK(size == sizeof(video) + 20 && stream_read_be(output + 8, 8) == 100000 && !memcmp(output + 20, video, sizeof(video))); |
|
video_seen++; |
|
} else if (output && output[0] == 2) { CHECK(size == 23 && !memcmp(output + 20, "ops", 3)); audio_seen++; } |
|
u_free(output); test_clock_poll(ua, 100); |
|
} |
|
CHECK(video_seen == 1 && audio_seen == 1); |
|
android_stream_stop(1); u_free(wait_event(2, 5, &size)); android_stream_stop(2); u_free(wait_event(1, 5, &size)); |
|
while ((output = android_stream_pull(2, &size))) u_free(output); |
|
CHECK(!strcmp(source_snapshot, "[]")); |
|
CHECK(android_stream_open(3, group, 0, 1280, 720, 0, 0, 0) == 0); u_free(wait_event(3, 4, &size)); |
|
pthread_t thread; CHECK(pthread_create(&thread, NULL, producer, NULL) == 0); |
|
while (!__atomic_load_n(&producer_started, __ATOMIC_ACQUIRE)) {} |
|
android_stream_detach(inst); |
|
__atomic_store_n(&producer_stop, 1, __ATOMIC_RELEASE); CHECK(pthread_join(thread, NULL) == 0); |
|
CHECK(android_stream_feed(3, 1, 900000, 1, video, sizeof(video)) == -1); |
|
while ((output = android_stream_pull(3, &size))) u_free(output); |
|
android_stream_attach(inst); |
|
CHECK(android_stream_open(4, group, 0, 1280, 720, 0, 0, 0) == 0); android_stream_stop(4); |
|
u_free(wait_event(4, 5, &size)); |
|
android_stream_detach(inst); uasync_mark_stopped(ua); utun_instance_destroy(inst); uasync_destroy(ua, 0); |
|
u_free(source_snapshot); |
|
CHECK(u_get_allocated_count() == before); |
|
test_clock_finish(); |
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "PASS Android stream: preview, source snapshot, FIN, cancellation, concurrent detach and no leaks"); |
|
return 0; |
|
}
|
|
|