diff --git a/include/st_convert_api.h b/include/st_convert_api.h index c384c63c2..0b8a05543 100644 --- a/include/st_convert_api.h +++ b/include/st_convert_api.h @@ -249,6 +249,31 @@ static inline int st20_rfc4175_422be10_to_yuv422p8(struct st20_rfc4175_422_10_pg return st20_rfc4175_422be10_to_yuv422p8_simd(pg, y, b, r, w, h, MTL_SIMD_LEVEL_MAX); } +/** + * Convert rfc4175_422be10 to yuv420p8 with the max optimized SIMD level. + * + * @param pg + * Point to pg(rfc4175_422be10) data. + * @param y + * Point to Y(yuv420p8) vector. + * @param b + * Point to b(yuv420p8) vector. + * @param r + * Point to r(yuv420p8) vector. + * @param w + * The st2110-20(video) width. + * @param h + * The st2110-20(video) height. + * @return + * - 0 if successful. + * - <0: Error code if convert fail. + */ +static inline int st20_rfc4175_422be10_to_yuv420p8(struct st20_rfc4175_422_10_pg2_be* pg, + uint8_t* y, uint8_t* b, uint8_t* r, + uint32_t w, uint32_t h) { + return st20_rfc4175_422be10_to_yuv420p8_simd(pg, y, b, r, w, h, MTL_SIMD_LEVEL_MAX); +} + /** * Convert rfc4175_422be12 to yuv422p12le with the max optimized SIMD level. * diff --git a/include/st_convert_internal.h b/include/st_convert_internal.h index e991189eb..93edaa7e2 100644 --- a/include/st_convert_internal.h +++ b/include/st_convert_internal.h @@ -266,6 +266,32 @@ int st20_rfc4175_422be10_to_yuv422p8_simd(struct st20_rfc4175_422_10_pg2_be* pg, uint8_t* y, uint8_t* b, uint8_t* r, uint32_t w, uint32_t h, enum mtl_simd_level level); +/** + * Convert rfc4175_422be10 to yuv420p8 with required SIMD level. + * Note the level may downgrade to the SIMD which system really support. + * + * @param pg + * Point to pg(rfc4175_422be10) data. + * @param y + * Point to Y(yuv420p8) vector. + * @param b + * Point to b(yuv420p8) vector. + * @param r + * Point to r(yuv420p8) vector. + * @param w + * The st2110-20(video) width. + * @param h + * The st2110-20(video) height. + * @param level + * simd level. + * @return + * - 0 if successful. + * - <0: Error code if convert fail. + */ +int st20_rfc4175_422be10_to_yuv420p8_simd(struct st20_rfc4175_422_10_pg2_be* pg, + uint8_t* y, uint8_t* b, uint8_t* r, uint32_t w, + uint32_t h, enum mtl_simd_level level); + /** * Convert rfc4175_422be12 to yuv422p12le with required SIMD level. * Note the level may downgrade to the SIMD which system really support. diff --git a/include/st_pipeline_api.h b/include/st_pipeline_api.h index cb66fd401..433040731 100644 --- a/include/st_pipeline_api.h +++ b/include/st_pipeline_api.h @@ -146,6 +146,8 @@ enum st_frame_fmt { * transport frame without conversion. The frame should not have lines padding. */ ST_FRAME_FMT_YUV422CUSTOM8 = 13, + /** YUV 420 planar 8bit */ + ST_FRAME_FMT_YUV420PLANAR8 = 14, /** End of yuv format list, new yuv should be inserted before this */ ST_FRAME_FMT_YUV_END, diff --git a/lib/src/st2110/pipeline/st20_pipeline_rx.c b/lib/src/st2110/pipeline/st20_pipeline_rx.c index ca360de29..5c74c9201 100644 --- a/lib/src/st2110/pipeline/st20_pipeline_rx.c +++ b/lib/src/st2110/pipeline/st20_pipeline_rx.c @@ -127,6 +127,14 @@ static int rx_st20p_packet_convert(void* priv, void* frame, uint8_t* r = (uint8_t*)framebuff->dst.addr[2] + framebuff->dst.linesize[2] * meta->row_number + meta->row_offset; ret = st20_rfc4175_422be10_to_yuv422p8(src, y, b, r, meta->pg_cnt, 2); + } else if (ctx->ops.output_fmt == ST_FRAME_FMT_YUV420PLANAR8) { + uint8_t* y = (uint8_t*)framebuff->dst.addr[0] + + framebuff->dst.linesize[0] * meta->row_number + meta->row_offset * 2; + uint8_t* b = (uint8_t*)framebuff->dst.addr[1] + + framebuff->dst.linesize[1] * meta->row_number + meta->row_offset; + uint8_t* r = (uint8_t*)framebuff->dst.addr[2] + + framebuff->dst.linesize[2] * meta->row_number + meta->row_offset; + ret = st20_rfc4175_422be10_to_yuv420p8(src, y, b, r, meta->pg_cnt, 2); } return ret; diff --git a/lib/src/st2110/st_convert.c b/lib/src/st2110/st_convert.c index 8341d1a0e..784d86723 100644 --- a/lib/src/st2110/st_convert.c +++ b/lib/src/st2110/st_convert.c @@ -111,6 +111,33 @@ static int convert_rfc4175_422be10_to_yuv422p8(struct st_frame* src, return ret; } +static int convert_rfc4175_422be10_to_yuv420p8(struct st_frame* src, + struct st_frame* dst) { + int ret = 0; + struct st20_rfc4175_422_10_pg2_be* be10 = NULL; + uint8_t* y = NULL; + uint8_t* b = NULL; + uint8_t* r = NULL; + uint32_t h = st_frame_data_height(dst); + + if (!has_lines_padding(src, dst)) { + be10 = src->addr[0]; + y = dst->addr[0]; + b = dst->addr[1]; + r = dst->addr[2]; + ret = st20_rfc4175_422be10_to_yuv420p8(be10, y, b, r, dst->width, h); + } else { + for (uint32_t line = 0; line < h; line++) { + be10 = src->addr[0] + src->linesize[0] * line; + y = dst->addr[0] + dst->linesize[0] * line; + b = dst->addr[1] + dst->linesize[1] * line; + r = dst->addr[2] + dst->linesize[2] * line; + ret = st20_rfc4175_422be10_to_yuv420p8(be10, y, b, r, dst->width, 1); + } + } + return ret; +} + static int convert_rfc4175_422be10_to_v210(struct st_frame* src, struct st_frame* dst) { int ret = 0; struct st20_rfc4175_422_10_pg2_be* be10 = NULL; @@ -504,6 +531,11 @@ static const struct st_frame_converter converters[] = { .dst_fmt = ST_FRAME_FMT_YUV422PLANAR8, .convert_func = convert_rfc4175_422be10_to_yuv422p8, }, + { + .src_fmt = ST_FRAME_FMT_YUV422RFC4175PG2BE10, + .dst_fmt = ST_FRAME_FMT_YUV420PLANAR8, + .convert_func = convert_rfc4175_422be10_to_yuv420p8, + }, { .src_fmt = ST_FRAME_FMT_YUV422RFC4175PG2BE10, .dst_fmt = ST_FRAME_FMT_V210, @@ -1329,6 +1361,45 @@ int st20_rfc4175_422be10_to_yuv422p8_simd(struct st20_rfc4175_422_10_pg2_be* pg, return st20_rfc4175_422be10_to_yuv422p8_scalar(pg, y, b, r, w, h); } +static int st20_rfc4175_422be10_to_yuv420p8_scalar(struct st20_rfc4175_422_10_pg2_be* pg, + uint8_t* y, uint8_t* b, uint8_t* r, + uint32_t w, uint32_t h) { + uint32_t line_pg_cnt = w / 2; /* two pgs in one convert */ + + for (uint32_t i = 0; i < (h / 2); i++) { /* 2 lines each loop */ + /* first line */ + for (uint32_t j = 0; j < line_pg_cnt; j++) { + *b++ = pg->Cb00; + *y++ = (pg->Y00 << 2) | (pg->Y00_ >> 2); + *r++ = (pg->Cr00 << 4) | (pg->Cr00_ >> 2); + *y++ = (pg->Y01 << 6) | (pg->Y01_ >> 2); + pg++; + } + /* second line, no u and v */ + for (uint32_t j = 0; j < line_pg_cnt; j++) { + *y++ = (pg->Y00 << 2) | (pg->Y00_ >> 2); + *y++ = (pg->Y01 << 6) | (pg->Y01_ >> 2); + pg++; + } + } + + return 0; +} + +int st20_rfc4175_422be10_to_yuv420p8_simd(struct st20_rfc4175_422_10_pg2_be* pg, + uint8_t* y, uint8_t* b, uint8_t* r, uint32_t w, + uint32_t h, enum mtl_simd_level level) { + enum mtl_simd_level cpu_level = mtl_get_simd_level(); + int ret; + + MTL_MAY_UNUSED(cpu_level); + MTL_MAY_UNUSED(level); + MTL_MAY_UNUSED(ret); + + /* the last option */ + return st20_rfc4175_422be10_to_yuv420p8_scalar(pg, y, b, r, w, h); +} + int st20_rfc4175_422le10_to_v210_scalar(uint8_t* pg_le, uint8_t* pg_v210, uint32_t w, uint32_t h) { uint32_t pg_count = w * h / 2; diff --git a/lib/src/st2110/st_fmt.c b/lib/src/st2110/st_fmt.c index 98621711f..47d5e497b 100644 --- a/lib/src/st2110/st_fmt.c +++ b/lib/src/st2110/st_fmt.c @@ -352,6 +352,13 @@ static const struct st_frame_fmt_desc st_frame_fmt_descs[] = { .planes = 1, .sampling = ST_FRAME_SAMPLING_422, }, + { + /* ST_FRAME_FMT_YUV420PLANAR8 */ + .fmt = ST_FRAME_FMT_YUV420PLANAR8, + .name = "YUV420PLANAR8", + .planes = 3, + .sampling = ST_FRAME_SAMPLING_420, + }, { /* ST_FRAME_FMT_RGBRFC4175PG4BE10 */ .fmt = ST_FRAME_FMT_RGBRFC4175PG4BE10, @@ -522,6 +529,7 @@ size_t st_frame_size(enum st_frame_fmt fmt, uint32_t width, uint32_t height, size = pixels * 3; /* 8 bits RGB pixel in a 24 bits */ break; case ST_FRAME_FMT_YUV420CUSTOM8: + case ST_FRAME_FMT_YUV420PLANAR8: size = st20_frame_size(ST20_FMT_YUV_420_8BIT, width, height); break; default: diff --git a/python/example/misc_util.py b/python/example/misc_util.py index 34a0d21df..880564d44 100644 --- a/python/example/misc_util.py +++ b/python/example/misc_util.py @@ -131,6 +131,19 @@ def ptr_to_yuv422p8(ptr, width, height): return y, u, v +def ptr_to_yuv420p8(ptr, width, height): + y_size = width * height + u_size = width * height // 4 + + frame = np.frombuffer(ptr, dtype=np.uint8) + + y = frame[:y_size].reshape((height, width)) + u = frame[y_size : y_size + u_size].reshape((height // 2, width // 2)) + v = frame[y_size + u_size :].reshape((height // 2, width // 2)) + + return y, u, v + + def yuv422p10le_to_yuv422(ptr, width, height): y_size = width * height u_size = width * height // 2 @@ -170,6 +183,28 @@ def downscale_yuv422(y, u, v, scale_factor): return y_downscaled, u_downscaled, v_downscaled +def downscale_yuv420(y, u, v, scale_factor): + height, width = y.shape + + y_downscaled = cv2.resize( + y, + (width // scale_factor, height // scale_factor), + interpolation=cv2.INTER_LINEAR, + ) + u_downscaled = cv2.resize( + u, + (width // (2 * scale_factor), height // (2 * scale_factor)), + interpolation=cv2.INTER_LINEAR, + ) + v_downscaled = cv2.resize( + v, + (width // (2 * scale_factor), height // (2 * scale_factor)), + interpolation=cv2.INTER_LINEAR, + ) + + return y_downscaled, u_downscaled, v_downscaled + + def display_yuv422(y, u, v): u = cv2.resize(u, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_LINEAR) v = cv2.resize(v, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_LINEAR) @@ -180,6 +215,18 @@ def display_yuv422(y, u, v): cv2.waitKey(1) +def display_yuv420(y, u, v): + u = cv2.resize(u, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_LINEAR) + v = cv2.resize(v, (y.shape[1], y.shape[0]), interpolation=cv2.INTER_LINEAR) + + yuv = cv2.merge([y, u, v]) + + # display_img = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420) + display_img = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR) + cv2.imshow(sys.argv[0], display_img) + cv2.waitKey(1) + + def destroy(): cv2.destroyAllWindows() @@ -200,6 +247,22 @@ def frame_display_yuv422p8(frame, display_scale_factor): display_yuv422(y_scale, u_scale, v_scale) +def frame_display_yuv420p8(frame, display_scale_factor): + # Pack frame pointer from frame addr + ptr = (ctypes.c_ubyte * (frame.data_size)).from_address( + mtl.st_frame_addr_cpuva(frame, 0) + ) + width = frame.width + height = frame.height + + # Convert to yuv420 planar 8 + y, u, v = ptr_to_yuv420p8(ptr, width, height) + # Downscale + y_scale, u_scale, v_scale = downscale_yuv420(y, u, v, display_scale_factor) + # Convert to RGB and display + display_yuv420(y_scale, u_scale, v_scale) + + def frame_display_yuv422p10le(frame, display_scale_factor): # Pack frame pointer from frame addr ptr = (ctypes.c_ubyte * (frame.data_size)).from_address( @@ -259,6 +322,8 @@ def frame_display(mtl_handle, frame, display_scale_factor): frame_display_yuv422p8(frame, display_scale_factor) elif frame.fmt == mtl.ST_FRAME_FMT_YUV422RFC4175PG2BE10: frame_display_rfc4175be10(mtl_handle, frame, display_scale_factor) + elif frame.fmt == mtl.ST_FRAME_FMT_YUV420PLANAR8: + frame_display_yuv420p8(frame, display_scale_factor) else: print(f"Unknown fmt: {mtl.st_frame_fmt_name(frame.fmt)}")