diff --git a/app/meson.build b/app/meson.build index e4413b567..b242d83dc 100644 --- a/app/meson.build +++ b/app/meson.build @@ -19,6 +19,7 @@ libm = cc.find_library('m', required : true) libpthread = cc.find_library('pthread', required : true) libjson_c = dependency('json-c', required : true) libpcap = dependency('pcap', required: true) + libsdl2 = dependency('sdl2', required: false) if libsdl2.found() add_global_arguments('-DAPP_HAS_SDL2', language : 'c') @@ -33,8 +34,13 @@ if libsdl2_ttf.found() else message('SDL2_ttf not found') endif -libopenssl = dependency('openssl', required : true) -dpdk_dep = dependency('libdpdk', required : true) + +libopenssl = dependency('openssl', required : false) +if libopenssl.found() + add_global_arguments('-DAPP_HAS_SSL', language : 'c') +else + message('openssl not found') +endif # add source file subdir('src') @@ -94,7 +100,7 @@ executable('RxTxApp', sources, c_args : app_c_args, link_args: app_ld_args, # asan should be always the first dep - dependencies: [asan_dep, mtl, libjson_c, libpcap, libsdl2, libsdl2_ttf, libm, libpthread, ws2_32_dep, mman_dep] + dependencies: [asan_dep, mtl, libjson_c, libpcap, libsdl2, libsdl2_ttf, libm, libpthread, ws2_32_dep, mman_dep, libopenssl] ) # Color convert tool for raw yuv file diff --git a/app/sample/dma/dma_sample.c b/app/sample/dma/dma_sample.c index d094ba8d6..77e9b00fa 100644 --- a/app/sample/dma/dma_sample.c +++ b/app/sample/dma/dma_sample.c @@ -49,7 +49,7 @@ static int dma_copy_sample(mtl_handle st) { } fb_src_iova = mtl_hp_virt2iova(st, fb_src); rand_data((uint8_t*)fb_src, fb_size, 0); - SHA256((unsigned char*)fb_src, fb_size, fb_src_shas); + st_sha256((unsigned char*)fb_src, fb_size, fb_src_shas); uint64_t start_ns = mtl_ptp_read_time(st); while (fb_dst_iova_off < fb_size) { @@ -72,7 +72,7 @@ static int dma_copy_sample(mtl_handle st) { uint64_t end_ns = mtl_ptp_read_time(st); /* all copy completed, check sha */ - SHA256((unsigned char*)fb_dst, fb_size, fb_dst_shas); + st_sha256((unsigned char*)fb_dst, fb_size, fb_dst_shas); ret = memcmp(fb_dst_shas, fb_src_shas, SHA256_DIGEST_LENGTH); if (ret != 0) { err("sha check fail\n"); @@ -142,7 +142,7 @@ static int dma_map_copy_sample(mtl_handle st) { } rand_data((uint8_t*)fb_src, fb_size, 0); - SHA256((unsigned char*)fb_src, fb_size, fb_src_shas); + st_sha256((unsigned char*)fb_src, fb_size, fb_src_shas); uint64_t start_ns = mtl_ptp_read_time(st); while (fb_dst_iova_off < fb_size) { @@ -165,7 +165,7 @@ static int dma_map_copy_sample(mtl_handle st) { uint64_t end_ns = mtl_ptp_read_time(st); /* all copy completed, check sha */ - SHA256((unsigned char*)fb_dst, fb_size, fb_dst_shas); + st_sha256((unsigned char*)fb_dst, fb_size, fb_dst_shas); ret = memcmp(fb_dst_shas, fb_src_shas, SHA256_DIGEST_LENGTH); if (ret != 0) { err("%s: sha check fail\n", __func__); diff --git a/app/src/app_base.h b/app/src/app_base.h index 862e87a49..ac2b15333 100644 --- a/app/src/app_base.h +++ b/app/src/app_base.h @@ -108,6 +108,7 @@ struct st_app_tx_video_session { uint16_t framebuff_producer_idx; uint16_t framebuff_consumer_idx; struct st_tx_frame* framebuffs; + bool sha_check; pcap_t* st20_pcap; bool st20_pcap_input; @@ -231,6 +232,7 @@ struct st_app_rx_video_session { bool slice; uint8_t num_port; uint64_t last_stat_time_ns; + bool sha_check; char st20_dst_url[ST_APP_URL_MAX_LEN]; int st20_dst_fb_cnt; /* the count of received fbs will be saved to file */ @@ -432,6 +434,7 @@ struct st_app_tx_st20p_session { int height; uint8_t num_port; uint64_t last_stat_time_ns; + bool sha_check; char st20p_source_url[ST_APP_URL_MAX_LEN]; uint8_t* st20p_source_begin; @@ -458,6 +461,7 @@ struct st_app_rx_st20p_session { int height; uint8_t num_port; uint64_t last_stat_time_ns; + bool sha_check; /* stat */ int stat_frame_received; @@ -521,6 +525,7 @@ struct st_app_context { int32_t tx_ts_delta_us; enum st21_pacing tx_pacing_type; bool tx_no_bulk; + bool video_sha_check; struct st_app_tx_audio_session* tx_audio_sessions; char tx_audio_url[ST_APP_URL_MAX_LEN]; @@ -614,4 +619,6 @@ uint8_t* st_json_ip(struct st_app_context* ctx, st_json_session_base_t* base, int st_set_mtl_log_file(struct st_app_context* ctx, const char* file); +void st_sha_dump(const char* tag, const unsigned char* sha); + #endif diff --git a/app/src/app_platform.h b/app/src/app_platform.h index 1a7bdb77e..21c2461a3 100644 --- a/app/src/app_platform.h +++ b/app/src/app_platform.h @@ -31,6 +31,10 @@ #include #include +#ifdef APP_HAS_SSL +#include +#endif + #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif @@ -61,17 +65,23 @@ enum st_tx_frame_status { ST_TX_FRAME_STATUS_MAX, }; +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + struct st_tx_frame { enum st_tx_frame_status stat; size_t size; bool second_field; /* for interlaced mode */ bool slice_trigger; /* for slice */ uint16_t lines_ready; /* for slice */ + uint8_t shas[SHA256_DIGEST_LENGTH]; }; struct st_rx_frame { void* frame; size_t size; + uint8_t shas[SHA256_DIGEST_LENGTH]; }; static inline int st_pthread_mutex_init(pthread_mutex_t* mutex, @@ -195,4 +205,19 @@ static inline int st_set_real_time(struct timespec* ts) { #endif } +#ifdef APP_HAS_SSL +static inline unsigned char* st_sha256(const unsigned char* d, size_t n, + unsigned char* md) { + return SHA256(d, n, md); +} +#else +static inline unsigned char* st_sha256(const unsigned char* d, size_t n, + unsigned char* md) { + MTL_MAY_UNUSED(d); + MTL_MAY_UNUSED(n); + md[0] = rand(); + return NULL; +} +#endif + #endif diff --git a/app/src/args.c b/app/src/args.c index 12c0a2ca2..0ca4f405e 100644 --- a/app/src/args.c +++ b/app/src/args.c @@ -113,6 +113,7 @@ enum st_args_cmd { ST_ARG_SHARED_RX_QUEUES, ST_ARG_RX_USE_CNI, ST_ARG_VIRTIO_USER, + ST_ARG_VIDEO_SHA_CHECK, ST_ARG_MAX, }; @@ -227,6 +228,7 @@ static struct option st_app_args_options[] = { {"shared_rx_queues", no_argument, 0, ST_ARG_SHARED_RX_QUEUES}, {"rx_use_cni", no_argument, 0, ST_ARG_RX_USE_CNI}, {"virtio_user", no_argument, 0, ST_ARG_VIRTIO_USER}, + {"video_sha_check", no_argument, 0, ST_ARG_VIDEO_SHA_CHECK}, {0, 0, 0, 0}}; @@ -691,6 +693,9 @@ int st_app_parse_args(struct st_app_context* ctx, struct mtl_init_params* p, int case ST_ARG_VIRTIO_USER: p->flags |= MTL_FLAG_VIRTIO_USER; break; + case ST_ARG_VIDEO_SHA_CHECK: + ctx->video_sha_check = true; + break; case '?': break; default: diff --git a/app/src/rx_st20p_app.c b/app/src/rx_st20p_app.c index fbc947a90..bf8d5d705 100644 --- a/app/src/rx_st20p_app.c +++ b/app/src/rx_st20p_app.c @@ -40,6 +40,8 @@ static void app_rx_st20p_consume_frame(struct st_app_rx_st20p_session* s, static void* app_rx_st20p_frame_thread(void* arg) { struct st_app_rx_st20p_session* s = arg; struct st_frame* frame; + uint8_t shas[SHA256_DIGEST_LENGTH]; + int idx = s->idx; info("%s(%d), start\n", __func__, s->idx); while (!s->st20p_app_thread_stop) { @@ -70,6 +72,19 @@ static void* app_rx_st20p_frame_thread(void* arg) { } app_rx_st20p_consume_frame(s, frame); + if (s->sha_check) { + if (frame->user_meta_size != sizeof(shas)) { + err("%s(%d), invalid user meta size %" PRId64 "\n", __func__, idx, + frame->user_meta_size); + } else { + st_sha256((unsigned char*)frame->addr[0], st_frame_plane_size(frame, 0), shas); + if (memcmp(shas, frame->user_meta, sizeof(shas))) { + err("%s(%d), sha check fail for frame %p\n", __func__, idx, frame->addr); + st_sha_dump("user meta sha:", frame->user_meta); + st_sha_dump("frame sha:", shas); + } + } + } s->stat_frame_total_received++; if (!s->stat_frame_first_rx_time) s->stat_frame_first_rx_time = st_app_get_monotonic_time(); @@ -157,6 +172,7 @@ static int app_rx_st20p_init(struct st_app_context* ctx, memset(&ops, 0, sizeof(ops)); s->last_stat_time_ns = st_app_get_monotonic_time(); + s->sha_check = ctx->video_sha_check; snprintf(name, 32, "app_rx_st20p_%d", idx); ops.name = name; diff --git a/app/src/rx_video_app.c b/app/src/rx_video_app.c index 1f16d41d1..fd6b5a445 100644 --- a/app/src/rx_video_app.c +++ b/app/src/rx_video_app.c @@ -24,7 +24,7 @@ static inline bool app_rx_video_is_frame_type(enum st20_type type) { } static int app_rx_video_enqueue_frame(struct st_app_rx_video_session* s, void* frame, - size_t size) { + struct st20_rx_frame_meta* meta) { uint16_t producer_idx = s->framebuff_producer_idx; struct st_rx_frame* framebuff = &s->framebuffs[producer_idx]; @@ -32,9 +32,18 @@ static int app_rx_video_enqueue_frame(struct st_app_rx_video_session* s, void* f return -EBUSY; } + if (s->sha_check) { + if (meta->user_meta_size != sizeof(framebuff->shas)) { + err("%s(%d), invalid user meta size %" PRId64 "\n", __func__, s->idx, + meta->user_meta_size); + return -EIO; + } + memcpy(framebuff->shas, meta->user_meta, sizeof(framebuff->shas)); + } + dbg("%s(%d), frame idx %d\n", __func__, s->idx, producer_idx); framebuff->frame = frame; - framebuff->size = size; + framebuff->size = meta->frame_total_size; /* point to next */ producer_idx++; if (producer_idx >= s->framebuff_cnt) producer_idx = 0; @@ -62,7 +71,7 @@ static void app_rx_video_consume_frame(struct st_app_rx_video_session* s, void* st_pthread_cond_signal(&d->display_wake_cond); st_pthread_mutex_unlock(&d->display_wake_mutex); } - } else { + } else if (s->st20_dst_fd > 0) { if (s->st20_dst_cursor + frame_size > s->st20_dst_end) s->st20_dst_cursor = s->st20_dst_begin; dbg("%s(%d), dst %p src %p size %" PRIu64 "\n", __func__, s->idx, s->st20_dst_cursor, @@ -94,6 +103,15 @@ static void* app_rx_video_frame_thread(void* arg) { dbg("%s(%d), frame idx %d\n", __func__, idx, consumer_idx); app_rx_video_consume_frame(s, framebuff->frame, framebuff->size); + if (s->sha_check) { + uint8_t shas[SHA256_DIGEST_LENGTH]; + st_sha256((unsigned char*)framebuff->frame, framebuff->size, shas); + if (memcmp(shas, framebuff->shas, sizeof(shas))) { + err("%s(%d), sha check fail for frame idx %d\n", __func__, idx, consumer_idx); + st_sha_dump("user meta sha:", framebuff->shas); + st_sha_dump("frame sha:", shas); + } + } st20_rx_put_framebuff(s->handle, framebuff->frame); /* point to next */ st_pthread_mutex_lock(&s->st20_wake_mutex); @@ -267,7 +285,7 @@ static int app_rx_video_init_frame_thread(struct st_app_rx_video_session* s) { int ret, idx = s->idx; /* user do not require fb save to file or display */ - if (s->st20_dst_fb_cnt < 1 && s->display == NULL) return 0; + if (s->st20_dst_fb_cnt < 1 && !s->display && !s->sha_check) return 0; ret = pthread_create(&s->st20_app_thread, NULL, app_rx_video_frame_thread, s); if (ret < 0) { @@ -323,19 +341,19 @@ static int app_rx_video_frame_ready(void* priv, void* frame, if (!s->stat_frame_first_rx_time) s->stat_frame_first_rx_time = st_app_get_monotonic_time(); - if (s->st20_dst_fd < 0 && s->display == NULL) { + if (!s->st20_app_thread) { /* free the queue directly as no read thread is running */ st20_rx_put_framebuff(s->handle, frame); return 0; } st_pthread_mutex_lock(&s->st20_wake_mutex); - ret = app_rx_video_enqueue_frame(s, frame, meta->frame_total_size); + ret = app_rx_video_enqueue_frame(s, frame, meta); if (ret < 0) { /* free the queue */ st20_rx_put_framebuff(s->handle, frame); st_pthread_mutex_unlock(&s->st20_wake_mutex); - return ret; + return 0; } st_pthread_cond_signal(&s->st20_wake_cond); st_pthread_mutex_unlock(&s->st20_wake_mutex); @@ -458,6 +476,7 @@ static int app_rx_video_init(struct st_app_context* ctx, st_json_video_session_t memset(&ops, 0, sizeof(ops)); s->last_stat_time_ns = st_app_get_monotonic_time(); + s->sha_check = ctx->video_sha_check; snprintf(name, 32, "app_rx_video_%d", idx); ops.name = name; diff --git a/app/src/rxtx_app.c b/app/src/rxtx_app.c index 6dcdbe2c5..9b57a6296 100644 --- a/app/src/rxtx_app.c +++ b/app/src/rxtx_app.c @@ -586,3 +586,11 @@ int st_set_mtl_log_file(struct st_app_context* ctx, const char* file) { info("%s, succ to %s\n", __func__, file); return 0; } + +void st_sha_dump(const char* tag, const unsigned char* sha) { + if (tag) info("%s, ", tag); + for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { + info("0x%02x ", sha[i]); + } + info("\n"); +} diff --git a/app/src/tx_st20p_app.c b/app/src/tx_st20p_app.c index 0a8e8ee9f..9270c6513 100644 --- a/app/src/tx_st20p_app.c +++ b/app/src/tx_st20p_app.c @@ -70,6 +70,7 @@ static void* app_tx_st20p_frame_thread(void* arg) { st20p_tx_handle handle = s->handle; int idx = s->idx; struct st_frame* frame; + uint8_t shas[SHA256_DIGEST_LENGTH]; info("%s(%d), start\n", __func__, idx); while (!s->st20p_app_thread_stop) { @@ -82,6 +83,11 @@ static void* app_tx_st20p_frame_thread(void* arg) { continue; } app_tx_st20p_build_frame(s, frame); + if (s->sha_check) { + st_sha256((unsigned char*)frame->addr[0], st_frame_plane_size(frame, 0), shas); + frame->user_meta = shas; + frame->user_meta_size = sizeof(shas); + } st20p_tx_put_frame(handle, frame); } info("%s(%d), stop\n", __func__, idx); @@ -236,6 +242,7 @@ static int app_tx_st20p_init(struct st_app_context* ctx, st_json_st20p_session_t memset(&ops, 0, sizeof(ops)); s->last_stat_time_ns = st_app_get_monotonic_time(); + s->sha_check = ctx->video_sha_check; snprintf(name, 32, "app_tx_st20p_%d", idx); ops.name = name; diff --git a/app/src/tx_video_app.c b/app/src/tx_video_app.c index 3356b73ee..e07027a05 100644 --- a/app/src/tx_video_app.c +++ b/app/src/tx_video_app.c @@ -56,6 +56,10 @@ static int app_tx_video_next_frame(void* priv, uint16_t* next_frame_idx, framebuff->stat = ST_TX_FRAME_IN_TRANSMITTING; *next_frame_idx = consumer_idx; meta->second_field = framebuff->second_field; + if (s->sha_check) { + meta->user_meta = framebuff->shas; + meta->user_meta_size = sizeof(framebuff->shas); + } /* point to next */ consumer_idx++; if (consumer_idx >= s->framebuff_cnt) consumer_idx = 0; @@ -234,6 +238,10 @@ static void* app_tx_video_frame_thread(void* arg) { /* interlaced use different layout? */ app_tx_video_build_frame(s, frame_addr, s->st20_frame_size); } + if (s->sha_check) { + st_sha256((unsigned char*)frame_addr, s->st20_frame_size, framebuff->shas); + // st_sha_dump("frame sha:", framebuff->shas); + } st_pthread_mutex_lock(&s->st20_wake_mutex); framebuff->size = s->st20_frame_size; @@ -710,6 +718,7 @@ static int app_tx_video_init(struct st_app_context* ctx, st_json_video_session_t s->ctx = ctx; s->enable_vsync = false; s->last_stat_time_ns = st_app_get_monotonic_time(); + s->sha_check = ctx->video_sha_check; snprintf(name, 32, "app_tx_video_%d", idx); ops.name = name;