@ -11,31 +11,23 @@
# 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)
static unsigned video_frames , audio_frames , batches ;
static unsigned batches ;
static uint8_t expected_video [ 9000 ] ;
static void frame ( void * arg , const struct stream_media_format * format ,
const struct stream_media_video * video , const struct stream_media_audio * audio ) {
( void ) arg ; CHECK ( format - > width = = 1280 & & format - > height = = 720 ) ;
if ( video ) { CHECK ( video - > length = = sizeof ( expected_video ) & & ! memcmp ( video - > data , expected_video , video - > length ) ) ; video_frames + + ; }
if ( audio ) { CHECK ( audio - > length = = 3 & & ! memcmp ( audio - > data , " ops " , 3 ) ) ; audio_frames + + ; }
}
static void ready ( struct stream_source * source , uint64_t seq , void * arg ) {
( void ) source ; ( void ) arg ; CHECK ( seq = = batches + + ) ;
}
/* Канонический формат и сохранение выходного view при ошибке всех классов полей. */
static void test_media ( void ) {
struct stream_media_receiver * receiver = NULL ;
CHECK ( stream_media_receiver_create ( & receiver , frame , NULL ) = = STREAM_OK ) ;
struct stream_media_block b = { . format = { 1280 , 720 , 90 , STREAM_MEDIA_H264 , STREAM_MEDIA_OPUS , 1 } , . video_count = 1 , . audio_count = 1 } ;
b . video [ 0 ] = ( struct stream_media_video ) { 1 , 1000000 , sizeof ( expected_video ) , 0 , 6000 , STREAM_MEDIA_KEY , expected_video } ;
b . audio [ 0 ] = ( struct stream_media_audio ) { 1000000 , 3 , 960 , ( const uint8_t * ) " ops " } ;
uint8_t wire [ 7080 ] , saved [ 7080 ] ; size_t length = 0 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 0 & & audio_frames = = 1 ) ;
memcpy ( saved , wire , length ) ;
struct stream_media_block parsed ;
CHECK ( stream_media_parse ( wire , length , & parsed ) = = STREAM_OK & & parsed . first_frame_offset = = 0 ) ;
CHECK ( parsed . audio_count = = 1 & & parsed . audio [ 0 ] . length = = 3 & & ! memcmp ( parsed . audio [ 0 ] . data , " ops " , 3 ) ) ;
const unsigned mutations [ ] = { 0 , 4 , 5 , 6 , 7 , 8 , 12 , 14 , 16 , 20 , 24 , 48 , 52 , 56 , 60 , 61 , 72 , 76 , 78 } ;
for ( unsigned i = 0 ; i < sizeof ( mutations ) / sizeof ( mutations [ 0 ] ) ; i + + ) {
memcpy ( wire , saved , length ) ; wire [ mutations [ i ] ] = 255 ;
@ -47,33 +39,11 @@ static void test_media(void) {
b . audio_count = 0 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_parse ( wire , length , & parsed ) = = STREAM_OK & & parsed . first_frame_offset = = UINT32_MAX ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 1 ) ;
stream_media_receiver_gap ( receiver ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 1 ) ;
b . video [ 0 ] = ( struct stream_media_video ) { 2 , 2000000 , sizeof ( expected_video ) , 0 , 6000 , 0 , expected_video } ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 1 ) ;
b . video [ 0 ] . flags = STREAM_MEDIA_KEY ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK ) ;
stream_media_receiver_gap ( receiver ) ;
b . video [ 0 ] . offset = 6000 ; b . video [ 0 ] . length = 3000 ; b . video [ 0 ] . data = expected_video + 6000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 1 ) ;
b . video_count = 2 ;
b . video [ 1 ] = ( struct stream_media_video ) { 3 , 300 0000, sizeof ( expected_video ) , 0 , 3000 , STREAM_MEDIA_KEY , expected_video } ;
b . video [ 1 ] = ( struct stream_media_video ) { 2 , 1040000 , sizeof ( expected_video ) , 0 , 3000 , STREAM_MEDIA_KEY , expected_video } ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_parse ( wire , length , & parsed ) = = STREAM_OK & & parsed . first_frame_offset = = 3000 ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 1 ) ;
b . video_count = 1 ; b . video [ 0 ] = b . video [ 1 ] ;
b . video [ 0 ] . offset = 3000 ; b . video [ 0 ] . length = 6000 ; b . video [ 0 ] . data = expected_video + 3000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & video_frames = = 2 ) ;
b . format . width + + ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_ERR_CONFLICT ) ;
stream_media_receiver_destroy ( receiver ) ;
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " PASS media: large frames, nonzero start, malformed fields, GAP and format changes " ) ;
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " PASS media: nonzero frame start, malformed fields and atomic parser output " ) ;
}
/* Две короткие записи остаются двумя пачками; каждый медиазаголовок переживает любую пару потерь 6+2. */
@ -116,76 +86,7 @@ static void test_batches(void) {
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " PASS batches: atomic boundaries, all FEC losses and mode isolation " ) ;
}
static unsigned revised_videos , revised_audios ;
static uint32_t received_revision ;
static void revised_frame ( void * arg , const struct stream_media_format * format ,
const struct stream_media_video * video , const struct stream_media_audio * audio ) {
( void ) arg ;
if ( video ) { revised_videos + + ; received_revision = format - > revision ; CHECK ( video - > flags & STREAM_MEDIA_KEY ) ; }
if ( audio ) revised_audios + + ;
}
/* Revision переживает потерю первого IDR; смена видео не сбрасывает PTS аудио и кадров. */
static void test_revisions ( void ) {
struct stream_media_receiver * r = NULL ;
CHECK ( stream_media_receiver_create ( & r , revised_frame , NULL ) = = STREAM_OK ) ;
struct stream_media_block b = { . format = { 1280 , 720 , 90 , STREAM_MEDIA_H264 , STREAM_MEDIA_OPUS , 1 } , . video_count = 1 } ;
b . video [ 0 ] = ( struct stream_media_video ) { 1 , 1000000 , 12 , 0 , 6 , STREAM_MEDIA_KEY , expected_video } ;
uint8_t wire [ 7080 ] ; size_t length ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 0 ) ;
b . format = ( struct stream_media_format ) { 1920 , 1080 , 270 , STREAM_MEDIA_H264 , STREAM_MEDIA_OPUS , 2 } ;
b . video_count = 0 ; b . audio_count = 1 ;
b . audio [ 0 ] = ( struct stream_media_audio ) { 1000000 , 3 , 960 , ( const uint8_t * ) " ops " } ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_audios = = 1 ) ;
b . video_count = 1 ; b . audio_count = 0 ;
b . video [ 0 ] = ( struct stream_media_video ) { 2 , 1100000 , 12 , 0 , 12 , 0 , expected_video } ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 0 ) ;
b . video [ 0 ] = ( struct stream_media_video ) { 3 , 1200000 , 12 , 0 , 6 , STREAM_MEDIA_KEY , expected_video } ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK ) ;
stream_media_receiver_gap ( r ) ;
b . video [ 0 ] . offset = 6 ; b . video [ 0 ] . data = expected_video + 6 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 0 ) ;
b . video [ 0 ] = ( struct stream_media_video ) { 4 , 1300000 , 12 , 0 , 12 , STREAM_MEDIA_KEY , expected_video } ;
b . audio_count = 1 ; b . audio [ 0 ] . pts_us = 1020000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 1 & & revised_audios = = 2 & & received_revision = = 2 ) ;
b . format . revision = 1 ; b . video [ 0 ] . frame_id = 5 ; b . video [ 0 ] . pts_us = 1400000 ;
b . audio [ 0 ] . pts_us = 1040000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 1 & & revised_audios = = 2 ) ;
b . format . revision = 4 ; b . video [ 0 ] . flags = 0 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 1 & & revised_audios = = 3 ) ;
b . video [ 0 ] . frame_id = 6 ; b . video [ 0 ] . pts_us = 1500000 ; b . video [ 0 ] . flags = STREAM_MEDIA_KEY ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 2 & & received_revision = = 4 & & revised_audios = = 3 ) ;
b . format . width + + ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_ERR_CONFLICT ) ;
b . format . width - - ; b . format . revision = 5 ; b . format . audio_codec = 0 ; b . audio_count = 0 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_ERR_CONFLICT ) ;
b . format . audio_codec = STREAM_MEDIA_OPUS ; b . format . revision = 6 ; b . video [ 0 ] . pts_us = 1400000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( r , wire , length ) = = STREAM_OK & & revised_videos = = 2 ) ;
memset ( wire + 28 , 0 , 4 ) ;
struct stream_media_block parsed ;
CHECK ( stream_media_parse ( wire , length , & parsed ) = = STREAM_ERR_FORMAT ) ;
stream_media_receiver_destroy ( r ) ;
}
static void stereo_frame ( void * arg , const struct stream_media_format * format ,
const struct stream_media_video * video , const struct stream_media_audio * audio ) {
CHECK ( ! video & & audio & & audio - > samples = = 960 & & format - > audio_codec = = STREAM_MEDIA_OPUS_STEREO ) ;
( * ( unsigned * ) arg ) + + ;
}
/* Стерео обозначается в wire явно; 960 — число кадров на канал, трек не меняется внутри эпохи. */
/* Стерео обозначается в wire явно; 960 — число кадров на канал. */
static void test_stereo ( void ) {
struct stream_media_block b = { . format = { 1280 , 720 , 0 , STREAM_MEDIA_H264 , STREAM_MEDIA_OPUS_STEREO , 1 } , . audio_count = 1 } ;
b . audio [ 0 ] = ( struct stream_media_audio ) { 1000000 , 3 , 960 , ( const uint8_t * ) " ops " } ;
@ -193,17 +94,11 @@ static void test_stereo(void) {
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
struct stream_media_block parsed ;
CHECK ( stream_media_parse ( wire , length , & parsed ) = = STREAM_OK & & parsed . format . audio_codec = = STREAM_MEDIA_OPUS_STEREO ) ;
unsigned received = 0 ; struct stream_media_receiver * receiver = NULL ;
CHECK ( stream_media_receiver_create ( & receiver , stereo_frame , & received ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_OK & & received = = 1 ) ;
b . format . audio_codec = STREAM_MEDIA_OPUS ; b . format . revision + + ; b . audio [ 0 ] . pts_us + = 20000 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_OK ) ;
CHECK ( stream_media_receiver_feed ( receiver , wire , length ) = = STREAM_ERR_CONFLICT & & received = = 1 ) ;
b . format . audio_codec = STREAM_MEDIA_OPUS_STEREO ; b . audio [ 0 ] . samples = 1920 ;
CHECK ( parsed . audio [ 0 ] . samples = = 960 & & parsed . audio [ 0 ] . length = = 3 & & ! memcmp ( parsed . audio [ 0 ] . data , " ops " , 3 ) ) ;
b . audio [ 0 ] . samples = 1920 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_ERR_FORMAT ) ;
b . audio [ 0 ] . samples = 960 ; b . format . audio_codec = 3 ;
CHECK ( stream_media_encode ( & b , wire , sizeof ( wire ) , & length ) = = STREAM_ERR_FORMAT ) ;
stream_media_receiver_destroy ( receiver ) ;
}
static void test_codecs ( void ) {
@ -260,6 +155,24 @@ static int media_fixture_feed(struct media_fixture *f, const struct stream_media
return stream_media_store_feed ( f - > store , f - > seq + + , decoded , decoded_size ) ;
}
/* Формат внутри revision и аудио внутри эпохи неизменны; регрессия live revision запрещена. */
static void test_format_conflicts ( void ) {
for ( unsigned conflict = 0 ; conflict < 4 ; conflict + + ) {
struct media_fixture f ; media_fixture_open ( & f ) ;
struct stream_media_block b = { . format = { 1280 , 720 , 0 , STREAM_MEDIA_H264 , STREAM_MEDIA_OPUS_STEREO , 2 } ,
. 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 ) ;
if ( conflict = = 0 ) b . format . revision = 1 ;
else if ( conflict = = 1 ) b . format . width + + ;
else { b . format . revision + + ; b . format . audio_codec = conflict = = 2 ? STREAM_MEDIA_OPUS : 0 ; }
b . video [ 0 ] . frame_id + + ; b . video [ 0 ] . pts_us + = 40000 ;
CHECK ( media_fixture_feed ( & f , & b ) = = STREAM_ERR_CONFLICT ) ;
media_fixture_close ( & f ) ;
}
DEBUG_INFO ( DEBUG_CATEGORY_STREAM , " PASS media store: revision regression, conflicting video and changed/removed audio rejected " ) ;
}
/* Новая revision начинается с целого KEY; аудио и отстающий reader сохраняют собственные позиции. */
static void test_revision_transition ( void ) {
struct media_fixture f ; media_fixture_open ( & f ) ;
@ -361,9 +274,9 @@ int main(int argc, char **argv) {
test_revision_transition ( ) ; test_cursor_key_recovery ( ) ;
} else {
CHECK ( argc = = 1 ) ;
test_media ( ) ; test_batches ( ) ; test_revisions ( ) ; test_ stereo ( ) ; test_codecs ( ) ;
test_media ( ) ; test_batches ( ) ; test_stereo ( ) ; test_codecs ( ) ;
debug_set_category_level ( DEBUG_CATEGORY_STREAM , DEBUG_LEVEL_INFO ) ;
test_revision_transition ( ) ; test_cursor_key_recovery ( ) ;
test_revision_transition ( ) ; test_cursor_key_recovery ( ) ; test_format_conflicts ( ) ;
}
CHECK ( u_get_allocated_count ( ) = = before ) ;
debug_set_category_level ( DEBUG_CATEGORY_STREAM , DEBUG_LEVEL_INFO ) ;