From 41813f9ee2872cff3f8f56ead131665114a6c72f Mon Sep 17 00:00:00 2001 From: Frank Du Date: Wed, 27 Dec 2023 12:59:29 +0800 Subject: [PATCH] python/example: add tx from a encoder file python3 python/example/st20p_tx.py decode YUV video from an encoded file and transmit it as a ST2110 ST_FRAME_FMT_YUV422RFC4175PG2BE10 stream across the network Signed-off-by: Frank Du --- python/README.md | 16 +++- python/example/decode_st20p_tx.py | 130 ++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 python/example/decode_st20p_tx.py diff --git a/python/README.md b/python/README.md index d0002f25a..c315fdaa8 100644 --- a/python/README.md +++ b/python/README.md @@ -75,21 +75,29 @@ Extracting pymtl-0.1-py3.10-linux-x86_64.egg to /usr/local/lib/python3.10/dist-p Install `opencv-python` dependency: ```bash +# for yuv display sudo pip3 install opencv-python +# PyAv for video decode/encode +sudo pip3 install av ``` -Run example: st20p_rx.py +Execute the `st20p_rx.py` to receive a ST2110 ST_FRAME_FMT_YUV422RFC4175PG2BE10 stream and display it. ```bash -cd $imtl_source_code/ # Customize the port, IP and display option in the code before using python3 python/example/st20p_rx.py ``` -Run example: st20p_tx.py +Run the `st20p_tx.py`, which reads YUV video data from a file and transmits it over the network as a ST2110 ST_FRAME_FMT_YUV422RFC4175PG2BE10 stream. ```bash -cd $imtl_source_code/ # Customize the port, IP and display option in the code before using python3 python/example/st20p_tx.py ``` + +Use `decode_st20p_tx.py` to decode YUV video from an encoded file `jellyfish-3-mbps-hd-hevc-10bit.mkv` and transmit it as a ST2110 ST_FRAME_FMT_YUV422RFC4175PG2BE10 stream across the network. + +```bash +# Customize the port, IP and display option in the code before using +python3 python/example/decode_st20p_tx.py +``` diff --git a/python/example/decode_st20p_tx.py b/python/example/decode_st20p_tx.py new file mode 100644 index 000000000..4d07c3339 --- /dev/null +++ b/python/example/decode_st20p_tx.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2023 Intel Corporation + +import ctypes +import sys + +import av +import cv2_util +import numpy as np +import pymtl as mtl + + +def process_frame(mtl_handle, st20p_tx, frame): + # print(f"{frame.format.name}"); + # convert to yuv422p10le from yuv420p10le + yuv_frame = frame.reformat(format="yuv422p10le") + tx_frame = mtl.st20p_tx_get_frame(st20p_tx) + if not tx_frame: + print("st20p_tx_get_frame fail, skip this video frame") + pass + + width = frame.width + height = frame.height + y_size = width * height + u_size = y_size // 2 + v_size = u_size + yuv_array = np.empty(y_size + u_size + v_size, dtype=np.uint16) + + # pyav yuv422p10le not support Conversion to numpy array, use copy mode + yuv_array[:y_size] = np.frombuffer(yuv_frame.planes[0], np.uint16) + yuv_array[y_size : y_size + u_size] = np.frombuffer(yuv_frame.planes[1], np.uint16) + yuv_array[y_size + u_size :] = np.frombuffer(yuv_frame.planes[2], np.uint16) + src_p = ctypes.c_char_p(yuv_array.ctypes.data) + src_address = ctypes.cast(src_p, ctypes.c_void_p).value + src_address_uint64 = ctypes.c_uint64(src_address).value + + memcpy_ops = mtl.mtl_memcpy_ops() + memcpy_ops.dst = mtl.st_frame_addr_cpuva(tx_frame, 0) + memcpy_ops.src = src_address_uint64 + memcpy_ops.sz = tx_frame.data_size + mtl.mtl_memcpy_action(memcpy_ops) + + mtl.st20p_tx_put_frame(st20p_tx, tx_frame) + + +def main(): + input_file_path = "jellyfish-3-mbps-hd-hevc-10bit.mkv" + input_fmt = mtl.ST_FRAME_FMT_YUV422PLANAR10LE + + # Init para + init_para = mtl.mtl_init_params() + mtl.mtl_para_port_set(init_para, mtl.MTL_PORT_P, "0000:af:01.1") + init_para.num_ports = 1 + mtl.mtl_para_sip_set(init_para, mtl.MTL_PORT_P, "192.168.108.101") + init_para.flags = ( + mtl.MTL_FLAG_BIND_NUMA + | mtl.MTL_FLAG_DEV_AUTO_START_STOP + | mtl.MTL_FLAG_PTP_ENABLE + ) + mtl.mtl_para_tx_queues_cnt_set(init_para, mtl.MTL_PORT_P, 1) + mtl.mtl_para_rx_queues_cnt_set(init_para, mtl.MTL_PORT_P, 0) + + # Create MTL instance + mtl_handle = mtl.mtl_init(init_para) + if not mtl_handle: + print("mtl_init fail") + sys.exit(1) + + # Create st20p tx session + tx_para = mtl.st20p_tx_ops() + tx_para.name = "st20p_tx_python" + tx_para.width = 1920 + tx_para.height = 1080 + tx_para.fps = mtl.ST_FPS_P59_94 + tx_para.framebuff_cnt = 3 + tx_para.transport_fmt = mtl.ST20_FMT_YUV_422_10BIT + tx_para.input_fmt = input_fmt + # tx port + tx_port = mtl.st_tx_port() + mtl.st_txp_para_port_set( + tx_port, + mtl.MTL_SESSION_PORT_P, + mtl.mtl_para_port_get(init_para, mtl.MTL_SESSION_PORT_P), + ) + tx_port.num_port = 1 + mtl.st_txp_para_dip_set(tx_port, mtl.MTL_SESSION_PORT_P, "239.168.85.20") + mtl.st_txp_para_udp_port_set(tx_port, mtl.MTL_SESSION_PORT_P, 20000) + tx_port.payload_type = 112 + tx_para.port = tx_port + # enable block get mode + tx_para.flags = mtl.ST20P_TX_FLAG_BLOCK_GET + # create st20p_tx session + st20p_tx = mtl.st20p_tx_create(mtl_handle, tx_para) + if not st20p_tx: + print("st20p_tx_create fail") + sys.exit(1) + frame_sz = mtl.st20p_tx_frame_size(st20p_tx) + print(f"frame_sz: {hex(frame_sz)}") + + # loop until ctrl-c + container = av.open(input_file_path) + try: + stream = next(s for s in container.streams if s.type == "video") + while True: + for frame in container.decode(stream): + process_frame(mtl_handle, st20p_tx, frame) + + # seek to the first frame + print("Finish play, seek to first frame") + container.seek(0, stream=stream) + + except KeyboardInterrupt: + print("KeyboardInterrupt") + + finally: + container.close() + + print("Everything fine, bye") + + # Free st20p_tx session + mtl.st20p_tx_free(st20p_tx) + + # Free MTL instance + mtl.mtl_uninit(mtl_handle) + + print("Everything fine, bye") + + +if __name__ == "__main__": + main()