Reproduce Android audio startup deadlock with real Opus playback

This commit is contained in:
evgeny committed 2026-10-16 17:25:19 +02:00
1 parent c03fe3da71
commit fb2a615165
1 file changed
+53 -1
+53 -1
View File
@@ -9,6 +9,7 @@ extern "C" {
#include "../../../lib/mem.h"
#include "../../../lib/opus_codec.h"
#include "../../../src/stream/stream_playback_audio.h"
#include "../../../src/stream/stream_media_buffer.h"
}
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "stream audio test line=%d: %s", __LINE__, #x); exit(1); } } while (0)
@@ -109,9 +110,60 @@ static void test_pcm_buffer() {
opus_codec_encoder_destroy(encoder);
}
/* PTS из Windows→Android лога: звук начинается на 428ms позже KEY, audio clock ещё неподвижен. */
static void test_late_audio_start() {
constexpr int64_t videoPts = 20669261, audioPts = 21097059;
stream_descriptor descriptor{};
descriptor.group_id = 1; descriptor.source_node_id = 2; descriptor.source_epoch = 3;
descriptor.n = 8; descriptor.r = 2; descriptor.block_size = STREAM_RS_DEFAULT_SIZE;
uint8_t secret[32] = {7}; stream_encoder* encoder = nullptr; stream_buffer* buffer = nullptr;
stream_media_store* store = nullptr; stream_media_cursor* cursor = nullptr;
CHECK(stream_encoder_create(&encoder, &descriptor, secret) == STREAM_OK);
CHECK(stream_buffer_create(&buffer, &descriptor, stream_encoder_public_key(encoder), 256 * 1300) == STREAM_OK);
CHECK(stream_media_store_create(&store, buffer, &descriptor) == STREAM_OK);
CHECK(stream_media_cursor_create(&cursor, store, STREAM_MEDIA_TRACK_VIDEO | STREAM_MEDIA_TRACK_AUDIO) == STREAM_OK);
auto* opus = opus_codec_encoder_create(48000, 2); CHECK(opus);
int16_t pcm[1920] = {}, output[1920]; uint8_t packet[1275], video[8] = {};
int length = opus_codec_encode(opus, pcm, 960, packet, sizeof(packet)); CHECK(length > 0);
stream_media_block block{};
block.format = {64, 64, 0, STREAM_MEDIA_H265, STREAM_MEDIA_OPUS_STEREO, 1}; block.video_count = 2; block.audio_count = 10;
block.video[0] = {1, videoPts, sizeof(video), 0, sizeof(video), STREAM_MEDIA_KEY, video};
block.video[1] = {2, audioPts + 200000, sizeof(video), 0, sizeof(video), 0, video};
for (unsigned i = 0; i < block.audio_count; i++) block.audio[i] = {(uint64_t)(audioPts + i * 20000), (uint16_t)length, 960, packet};
uint8_t wire[7345]; size_t wireLength = 0, partSize = 0; const uint8_t* parts[RS_MAX_BLOCKS];
CHECK(stream_media_encode(&block, wire, sizeof(wire), &wireLength) == STREAM_OK);
CHECK(stream_encoder_encode(encoder, 0, 1, 0, wire, wireLength, parts, &partSize) == STREAM_OK);
for (unsigned i = 0; i < 8; i++) CHECK(stream_buffer_put(buffer, parts[i], partSize) == STREAM_OK);
CHECK(stream_media_store_feed(store, 0, wire, wireLength) == STREAM_OK);
for (unsigned pass = 0; pass < 2; pass++) {
auto* audio = stream_playback_audio_create(videoPts, 40); CHECK(audio);
stream_media_packet record{}; stream_media_status status{};
CHECK(stream_media_cursor_status(cursor, &status) == STREAM_OK && status.start_us == videoPts);
CHECK(stream_media_cursor_read(cursor, videoPts, 0, -1, &record) == STREAM_WAIT);
CHECK(stream_media_cursor_read(cursor, -1, 0, 80000, &record) == STREAM_WAIT); // Preroll не включает будущий звук.
CHECK(stream_media_cursor_read(cursor, videoPts, 1, 80000, &record) == STREAM_OK);
CHECK(record.kind == STREAM_MEDIA_READ_VIDEO && record.video.pts_us == videoPts);
for (unsigned i = 0; i < 5; i++) {
CHECK(stream_media_cursor_read(cursor, videoPts, 0, 80000, &record) == STREAM_OK);
CHECK(record.kind == STREAM_MEDIA_READ_AUDIO && record.audio.pts_us == (uint64_t)(audioPts + i * 20000));
CHECK(stream_playback_audio_feed(audio, record.audio.pts_us, record.audio.data, record.audio.length) == 0);
}
CHECK(stream_media_cursor_read(cursor, videoPts, 0, 80000, &record) == STREAM_WAIT); // Окно не ползёт вперёд без часов.
CHECK(!stream_playback_audio_needs_input(audio));
int count = stream_playback_audio_pull(audio, output, 960, status.ready_us); CHECK(count == 960);
int64_t heard = stream_playback_audio_position(audio, count); CHECK(heard == audioPts + 20000);
CHECK(stream_media_cursor_read(cursor, heard, 0, 80000, &record) == STREAM_OK);
CHECK(record.kind == STREAM_MEDIA_READ_AUDIO && record.audio.pts_us == audioPts + 100000);
stream_playback_audio_destroy(audio); stream_media_cursor_reset(cursor);
}
opus_codec_encoder_destroy(opus); stream_media_cursor_destroy(cursor); stream_media_store_destroy(store);
stream_buffer_destroy(buffer); stream_encoder_destroy(encoder);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] late audio: 428ms startup gap, bounded 80ms priming, real Opus/SoundTouch clock advances and reset repeats");
}
int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_INFO);
size_t allocations = u_get_allocated_count();
test_stereo_tempo(); test_silence_clock(); test_pcm_buffer(); CHECK(u_get_allocated_count() == allocations);
test_stereo_tempo(); test_silence_clock(); test_pcm_buffer(); test_late_audio_start(); CHECK(u_get_allocated_count() == allocations);
return 0;
}