In the general API, the video frames used are allocated by the library with rte_malloc (usually using hugepages). In some use cases, each frame data needs to be copied to/from the user. When working with UHD or high frame rate videos, copying can cause CPU/memory stalls and become a bottleneck in the pipeline.
The external frame API is introduced so that the library can use user-provided memory to receive/send ST2110-20 frames or as the color format conversion destination/source in the pipeline API.
/** The structure info for external frame */
struct st_ext_frame {
/** Each plane's virtual address of external frame */
void* addr[ST_MAX_PLANES];
/** Each plane's IOVA of external frame */
mtl_iova_t iova[ST_MAX_PLANES];
/** Each plane's linesize of external frame,
* if no padding, can be calculated from st_frame_least_linesize */
size_t linesize[ST_MAX_PLANES];
/** Buffer size of external frame */
size_t size;
/** Private data for user */
void* opaque;
};
in ops, set the flag
ops_tx.flags |= ST20P_TX_FLAG_EXT_FRAME;
when sending a frame, get the frame and put with ext_frame info
frame = st20p_tx_get_frame(tx_handle);
struct st_ext_frame ext_frame;
uint8_t planes = st_frame_fmt_planes(frame->fmt);
for(int i = 0; i < planes; i++) {
ext_frame.addr[i] = your_addr[i];
ext_frame.iova[i] = your_iova[i]; // must provide IOVA for no convert mode
ext_frame.linesize[i] = your_linesize[i];
}
ext_frame.size = your_frame_size;
ext_frame.opaque = your_frame_handle;
st20p_tx_put_ext_frame(tx_handle, frame, &ext_frame);
when the library finished handling the frame, it will notify by callback, you can return the frame buffer here
// set the callback in ops
ops_tx.notify_frame_done = tx_st20p_frame_done;
// ...
// implement the callback
static int tx_st20p_frame_done(void* priv, struct st_frame*frame) {
ctx* s = priv;
your_frame_handle = frame->opaque;
your_frame_free(your_frame_handle);
return 0;
}
Others follow the general API flow.
in ops, set the flag and set query_ext_frame callback
ops_rx.flags |= ST20P_RX_FLAG_EXT_FRAME;
ops_rx.query_ext_frame = st20p_rx_query_ext_frame;
//...
//implement the callback
static int st20p_rx_query_ext_frame(void* priv, st_ext_frame* ext_frame,
struct st20_rx_frame_meta* meta) {
ctx* s = (ctx*)priv;
uint8_t planes = st_frame_fmt_planes(fmt[i]);
/* fill the ext frame */
for (uint8_t plane = 0; plane < planes; plane++) {
ext_frame.addr[i] = your_addr[i];
ext_frame.iova[i] = your_iova[i]; // must provide IOVA for no convert mode
ext_frame.linesize[i] = your_linesize[i];
}
ext_frame.size = your_frame_size;
ext_frame.opaque = your_frame_handle;
return 0;
}
User should maintain the lifetime of frames after st22p_rx_get_frame.
set the ext_frames array in ops
struct st_ext_frame ext_frames[fb_cnt];
for (int i = 0; i < fb_cnt; ++i) {
uint8_t planes = st_frame_fmt_planes(frame->fmt);
for(int plane = 0; plane < planes; plane++) {
ext_frames[i].addr[plane] = your_addr[plane];
ext_frames[i].iova[plane] = your_iova[plane]; // must provide IOVA for no convert mode
ext_frames[i].linesize[plane] = your_linesize[plane];
}
ext_frames[i].size = your_frame_size;
ext_frames[i].opaque = your_frame_handle;
}
ops_rx.ext_frames = ext_frames;
rx_handle = st20p_rx_create(st, &ops_rx);
Others follow the general API flow.
in ops, set the flag
ops_tx.flags |= ST22P_TX_FLAG_EXT_FRAME;
when sending a frame, get the frame and put with ext_frame info
frame = st22p_tx_get_frame(tx_handle);
struct st_ext_frame ext_frame;
uint8_t planes = st_frame_fmt_planes(frame->fmt);
for(int i = 0; i < planes; i++) {
ext_frame.addr[i] = your_addr[i];
ext_frame.iova[i] = your_iova[i]; // must provide IOVA for no convert mode
ext_frame.linesize[i] = your_linesize[i];
}
ext_frame.size = your_frame_size;
ext_frame.opaque = your_frame_handle;
st22p_tx_put_ext_frame(tx_handle, frame, &ext_frame);
when the library finished handling the frame, it will notify by callback, you can return the frame buffer here
// set the callback in ops
ops_tx.notify_frame_done = tx_st22p_frame_done;
// ...
// implement the callback
static int tx_st22p_frame_done(void* priv, struct st_frame*frame) {
ctx* s = priv;
your_frame_handle = frame->opaque;
your_frame_free(your_frame_handle);
return 0;
}
Others follow the general API flow.
in ops, set the flag and set query_ext_frame callback
ops_rx.flags |= ST20P_RX_FLAG_EXT_FRAME;
ops_rx.query_ext_frame = st22p_rx_query_ext_frame;
//...
//implement the callback
static int st22p_rx_query_ext_frame(void* priv, st_ext_frame* ext_frame,
struct st22_rx_frame_meta* meta) {
ctx* s = (ctx*)priv;
uint8_t planes = st_frame_fmt_planes(fmt[i]);
/* fill the ext frame */
for (uint8_t plane = 0; plane < planes; plane++) {
ext_frame.addr[i] = your_addr[i];
ext_frame.iova[i] = your_iova[i]; // must provide IOVA for no convert mode
ext_frame.linesize[i] = your_linesize[i];
}
ext_frame.size = your_frame_size;
ext_frame.opaque = your_frame_handle;
return 0;
}
User should maintain the lifetime of frames after st22p_rx_get_frame.
/** External framebuffer */
struct st20_ext_frame {
/** Virtual address of external framebuffer */
void* buf_addr;
/** DMA mapped IOVA of external framebuffer */
mtl_iova_t buf_iova;
/** Length of external framebuffer */
size_t buf_len;
/** Private data for user, will be retrieved with st_frame or st20_rx_frame_meta */
void* opaque;
};
in ops, set the flag
ops_rx.flags |= ST20_TX_FLAG_EXT_FRAME;
explicitly set the ext frame, and in query_next_frame callback, provide the index
st20_tx_set_ext_frame(s->handle, idx, &ext_frame);
// in query_next_frame
*next_frame_idx = idx;
implement and set query_ext_frame callback and set incomplete frame flag
// set the callback in ops
// set the incomplete frame flag
ops_rx.query_ext_frame = rx_query_ext_frame;
ops_rx.flags |= ST20_RX_FLAG_RECEIVE_INCOMPLETE_FRAME;
//...
//implement the callback
static int rx_query_ext_frame(void* priv, st20_ext_frame*ext_frame, struct st20_rx_frame_meta* meta) {
ctx* s = (ctx*)priv;
ext_frame->buf_addr = your_addr;
ext_frame->buf_iova = your_iova;
ext_frame->buf_len = your_frame_size;
ext_frame->opaque = your_frame_handle;
return 0;
}
use as the general API, user should maintain the lifetime of frames
set the ext_frames array in ops
struct st20_ext_frame ext_frames[fb_cnt];
for (int i = 0; i < fb_cnt;++i) {
ext_frames[i].buf_addr = your_addr;
ext_frames[i].buf_iova = your_iova;
ext_frames[i].buf_len = your_frame_size;
ext_frames[i].opaque = your_frame_handle;
}
ops_rx.ext_frames = ext_frames;
rx_handle = st20_rx_create(st, &ops_rx);
Others follow the general API flow.