@ -7,6 +7,7 @@
# include "../lib/u_async.h"
# include "../src/stream/stream_source.h"
# include "../src/stream/stream_media.h"
# include "../src/stream/stream_media_buffer.h"
# define CHECK(x) do { if (!(x)) { debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_ERROR); DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "stream media test line=%d: %s", __LINE__, #x); exit(1); } } while (0)
@ -224,13 +225,76 @@ static void test_codecs(void) {
memset ( key , 0 , 4 ) ; CHECK ( stream_media_key_parse ( key , sizeof ( key ) , & config , & config_size , & frame , & frame_size ) = = STREAM_OK ) ;
}
int main ( void ) {
struct media_fixture {
struct stream_encoder * encoder ;
struct stream_buffer * buffer ;
struct stream_media_store * store ;
struct stream_media_cursor * cursor ;
uint64_t seq ;
} ;
static void media_fixture_open ( struct media_fixture * f ) {
struct stream_descriptor d = { . group_id = 1 , . source_node_id = 2 , . source_epoch = 3 ,
. n = 8 , . r = 2 , . block_size = STREAM_RS_DEFAULT_SIZE } ;
uint8_t key [ 32 ] = { 1 } ; memset ( f , 0 , sizeof ( * f ) ) ;
CHECK ( stream_encoder_create ( & f - > encoder , & d , key ) = = STREAM_OK ) ;
CHECK ( stream_buffer_create ( & f - > buffer , & d , stream_encoder_public_key ( f - > encoder ) , 128 * 1300 ) = = STREAM_OK ) ;
CHECK ( stream_media_store_create ( & f - > store , f - > buffer , & d ) = = STREAM_OK ) ;
CHECK ( stream_media_cursor_create ( & f - > cursor , f - > store , STREAM_MEDIA_TRACK_VIDEO | STREAM_MEDIA_TRACK_AUDIO ) = = STREAM_OK ) ;
}
static void media_fixture_close ( struct media_fixture * f ) {
stream_media_cursor_destroy ( f - > cursor ) ; stream_media_store_destroy ( f - > store ) ;
stream_buffer_destroy ( f - > buffer ) ; stream_encoder_destroy ( f - > encoder ) ;
}
/* Проверяем настоящий путь: сериализация, RS-кольцо, проверка подписи, индекс и независимый cursor. */
static int media_fixture_feed ( struct media_fixture * f , const struct stream_media_block * b ) {
uint8_t wire [ 7080 ] , decoded [ 7080 ] ; size_t length = 0 , part_size = 0 , decoded_size = 0 ;
const uint8_t * parts [ RS_MAX_BLOCKS ] ;
CHECK ( stream_media_encode ( b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_encoder_encode ( f - > encoder , f - > seq , 1 , 0 , wire , length , parts , & part_size ) = = STREAM_OK ) ;
for ( unsigned i = 0 ; i < 6 ; i + + ) CHECK ( stream_buffer_put ( f - > buffer , parts [ i ] , part_size ) = = STREAM_OK ) ;
CHECK ( stream_buffer_decode ( f - > buffer , f - > seq , decoded , sizeof ( decoded ) , & decoded_size ) = = STREAM_OK ) ;
CHECK ( length = = decoded_size & & ! memcmp ( wire , decoded , length ) ) ;
return stream_media_store_feed ( f - > store , f - > seq + + , decoded , decoded_size ) ;
}
/* Диагностика расхождения store/cursor; отдельный запуск --revision-transition до согласования контракта. */
static void test_revision_transition ( void ) {
struct media_fixture f ; media_fixture_open ( & f ) ;
struct stream_media_block b = { . format = { 1280 , 720 , 90 , STREAM_MEDIA_H264 , 0 , 1 } , . video_count = 1 } ;
b . video [ 0 ] = ( struct stream_media_video ) { 1 , 1000000 , 12 , 0 , 12 , STREAM_MEDIA_KEY , expected_video } ;
CHECK ( media_fixture_feed ( & f , & b ) = = STREAM_OK ) ;
struct stream_media_packet packet ;
CHECK ( stream_media_cursor_read ( f . cursor , - 1 , 1 , - 1 , & packet ) = = STREAM_OK & & packet . format . revision = = 1 ) ;
b . video [ 0 ] = ( struct stream_media_video ) { 2 , 1040000 , 12 , 0 , 6 , 0 , expected_video } ;
CHECK ( media_fixture_feed ( & f , & b ) = = STREAM_OK ) ;
b . format = ( struct stream_media_format ) { 1920 , 1080 , 270 , STREAM_MEDIA_H264 , 0 , 2 } ;
b . video [ 0 ] = ( struct stream_media_video ) { 3 , 1080000 , 12 , 0 , 12 , 0 , expected_video } ;
CHECK ( media_fixture_feed ( & f , & b ) = = STREAM_OK ) ;
struct stream_media_status status ;
CHECK ( stream_media_cursor_status ( f . cursor , & status ) = = STREAM_OK & & status . video_us = = 1000000 ) ;
int result = stream_media_cursor_read ( f . cursor , 1000000 , 1 , - 1 , & packet ) ;
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " revision transition before KEY: indexed_video_us=%lld result=%d kind=%d revision=%u frame=%llu flags=%u " ,
( long long ) status . video_us , result , packet . kind , packet . format . revision , ( unsigned long long ) packet . video . frame_id , packet . video . flags ) ;
media_fixture_close ( & f ) ;
CHECK ( result = = STREAM_WAIT ) ;
}
int main ( int argc , char * * argv ) {
debug_config_init ( ) ; debug_set_level ( DEBUG_LEVEL_INFO ) ;
/* Усечения проверяются массово; ожидаемые отказы не загромождают диагностический лог. */
debug_set_category_level ( DEBUG_CATEGORY_STREAM , DEBUG_LEVEL_DISABLED ) ;
size_t before = u_get_allocated_count ( ) ;
for ( unsigned i = 0 ; i < sizeof ( expected_video ) ; i + + ) expected_video [ i ] = ( uint8_t ) ( i * 31 + 7 ) ;
test_media ( ) ; test_batches ( ) ; test_revisions ( ) ; test_stereo ( ) ; test_codecs ( ) ;
if ( argc = = 2 & & ! strcmp ( argv [ 1 ] , " --revision-transition " ) ) {
debug_set_level ( DEBUG_LEVEL_ERROR ) ; debug_set_category_level ( DEBUG_CATEGORY_STREAM , DEBUG_LEVEL_DEBUG ) ;
test_revision_transition ( ) ;
} else {
CHECK ( argc = = 1 ) ;
test_media ( ) ; test_batches ( ) ; test_revisions ( ) ; test_stereo ( ) ; test_codecs ( ) ;
}
CHECK ( u_get_allocated_count ( ) = = before ) ;
debug_set_category_level ( DEBUG_CATEGORY_STREAM , DEBUG_LEVEL_INFO ) ;
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " PASS stream_media: protocol, FEC, frame recovery, boundaries, no leaks " ) ;