1
0
Fork 0
alistair23-linux/drivers/mxc/vpu_malone/insert_startcode.c

660 lines
18 KiB
C

/*
* Copyright 2018-2020 NXP
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file insert_startcode.c
*
* copyright here may be changed later
*
*
*/
#include "insert_startcode.h"
static void set_payload_hdr(unsigned char *dst, unsigned int scd_type, unsigned int codec_id,
unsigned int buffer_size, unsigned int width, unsigned int height)
{
unsigned int payload_size;
/* payload_size = buffer_size + itself_size(16) - start_code(4) */
payload_size = buffer_size + 12;
dst[0] = 0x00;
dst[1] = 0x00;
dst[2] = 0x01;
dst[3] = scd_type;
/* length */
dst[4] = ((payload_size>>16)&0xff);
dst[5] = ((payload_size>>8)&0xff);
dst[6] = 0x4e;
dst[7] = ((payload_size>>0)&0xff);
/* Codec ID and Version */
dst[8] = codec_id;
dst[9] = IMX_CODEC_VERSION_ID;
/* width */
dst[10] = ((width>>8)&0xff);
dst[11] = ((width>>0)&0xff);
dst[12] = 0x58;
/* height */
dst[13] = ((height>>8)&0xff);
dst[14] = ((height>>0)&0xff);
dst[15] = 0x50;
}
static void set_vc1_rcv_seqhdr(unsigned char *dst, unsigned char *src,
unsigned int width, unsigned int height)
{
unsigned int frames = IMX_VC1_RCV_NUM_FRAMES;
unsigned int ext_data_size = IMX_VC1_RCV_SEQ_EXT_DATA_SIZE;
/* 0-2 Number of frames, used default value 0xFF */
dst[0] = (unsigned char)frames;
dst[1] = (unsigned char)(frames >> 8);
dst[2] = (unsigned char)(frames >> 16);
/* 3 RCV version, used V1 */
dst[3] = (unsigned char)(IMX_VC1_RCV_CODEC_V1_VERSION);
/* 4-7 extension data size */
dst[4] = (unsigned char)ext_data_size;
dst[5] = (unsigned char)(ext_data_size >> 8);
dst[6] = (unsigned char)(ext_data_size >> 16);
dst[7] = (unsigned char)(ext_data_size >> 24);
/* 8-11 extension data */
dst[8] = src[0];
dst[9] = src[1];
dst[10] = src[2];
dst[11] = src[3];
/* height */
dst[12] = (unsigned char)height;
dst[13] = (unsigned char)(((height >> 8) & 0xff));
dst[14] = (unsigned char)(((height >> 16) & 0xff));
dst[15] = (unsigned char)(((height >> 24) & 0xff));
/* width */
dst[16] = (unsigned char)width;
dst[17] = (unsigned char)(((width >> 8) & 0xff));
dst[18] = (unsigned char)(((width >> 16) & 0xff));
dst[19] = (unsigned char)(((width >> 24) & 0xff));
}
static void set_vc1_rcv_pichdr(unsigned char *dst, unsigned int buffer_size)
{
dst[0] = (unsigned char)buffer_size;
dst[1] = (unsigned char)(buffer_size >> 8);
dst[2] = (unsigned char)(buffer_size >> 16);
dst[3] = (unsigned char)(buffer_size >> 24);
}
static int create_vc1_nal_pichdr(unsigned char *dst, void *data)
{
int len = 0;
if (IMX_VC1_IS_NOT_NAL(*((unsigned int *)data))) {
/* need insert nal header: special ID */
dst[0] = 0x0;
dst[1] = 0x0;
dst[2] = 0x01;
dst[3] = 0x0D;
len = 4;
}
return len;
}
static unsigned int insert_scd_pic_vc1(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
if (q_data->fourcc == V4L2_PIX_FMT_VC1_ANNEX_G) {
unsigned int rcv_pichdr_size = IMX_VC1_RCV_PIC_HEADER_LEN;
unsigned char rcv_pichdr[IMX_VC1_RCV_PIC_HEADER_LEN] = { 0 };
unsigned char scd_pichdr[16] = { 0 };
set_payload_hdr(scd_pichdr, SCODE_NEW_PICTURE, IMX_CODEC_ID_VC1_SIMPLE,
rcv_pichdr_size + buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, scd_pichdr, 16);
set_vc1_rcv_pichdr(rcv_pichdr, buffer_size);
length += copy_buffer_to_stream(ctx, rcv_pichdr, rcv_pichdr_size);
} else {
unsigned char nal_hdr[IMX_VC1_NAL_HEADER_LEN] = { 0 };
unsigned int len;
len = create_vc1_nal_pichdr(nal_hdr, data);
length += copy_buffer_to_stream(ctx, nal_hdr, len);
}
return length;
}
static unsigned int insert_scd_seq_vc1(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned int rvc_seqhdr_size = IMX_VC1_RCV_SEQ_HEADER_LEN;
unsigned char rcv_seqhdr[IMX_VC1_RCV_SEQ_HEADER_LEN] = { 0 };
unsigned char scd_seqhdr[16] = { 0 };
if (q_data->fourcc == V4L2_PIX_FMT_VC1_ANNEX_G) {
set_payload_hdr(scd_seqhdr, SCODE_NEW_SEQUENCE, IMX_CODEC_ID_VC1_SIMPLE,
rvc_seqhdr_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, scd_seqhdr, 16);
set_vc1_rcv_seqhdr(rcv_seqhdr, data, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, rcv_seqhdr, rvc_seqhdr_size);
} else {
length += copy_buffer_to_stream(ctx, data, buffer_size);
}
return length;
}
static unsigned int insert_scd_pic_vp6(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char pic_header[16] = { 0 };
set_payload_hdr(pic_header, SCODE_NEW_PICTURE, IMX_CODEC_ID_VC1_SIMPLE,
buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, pic_header, 16);
return length;
}
static unsigned int insert_scd_seq_vp6(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char seq_header[16] = {0};
unsigned char *src = (unsigned char *)data;
set_payload_hdr(seq_header, SCODE_NEW_SEQUENCE, IMX_CODEC_ID_VP6,
0, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, seq_header, 16);
/* first data include frame data, need to handle them too */
length += insert_scd_pic_vp6(ctx, buffer_size, data);
length += copy_buffer_to_stream(ctx, src, buffer_size);
return length;
}
static void set_vp8_ivf_seqhdr(unsigned char *dst, int width, int height)
{
/* 0-3byte signature "DKIF" */
dst[0] = 0x44;
dst[1] = 0x4b;
dst[2] = 0x49;
dst[3] = 0x46;
/* 4-5byte version: should be 0*/
dst[4] = 0x00;
dst[5] = 0x00;
/* 6-7 length of Header */
dst[6] = IMX_VP8_IVF_SEQ_HEADER_LEN;
dst[7] = IMX_VP8_IVF_SEQ_HEADER_LEN >> 8;
/* 8-11 VP8 fourcc */
dst[8] = 0x56;
dst[9] = 0x50;
dst[10] = 0x38;
dst[11] = 0x30;
/* 12-13 width in pixels */
dst[12] = width;
dst[13] = width >> 8;
/* 14-15 height in pixels */
dst[14] = height;
dst[15] = height >> 8;
/* 16-19 frame rate */
dst[16] = 0xe8;
dst[17] = 0x03;
dst[18] = 0x00;
dst[19] = 0x00;
/* 20-23 time scale */
dst[20] = 0x01;
dst[21] = 0x00;
dst[22] = 0x00;
dst[23] = 0x00;
/* 24-27 number frames */
dst[24] = 0xdf;
dst[25] = 0xf9;
dst[26] = 0x09;
dst[27] = 0x00;
/* 28-31 reserved */
}
static void set_vp8_ivf_pichdr(unsigned char *dst, unsigned int frame_size)
{
/*
* firmware just parse 64-bit timestamp(8 bytes).
* As not transfer timestamp to firmware, use default value(ZERO).
* No need to do anything here
*/
}
static unsigned int insert_scd_pic_vp8(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned int ivf_pichdr_size = IMX_VP8_IVF_FRAME_HEADER_LEN;
unsigned char pic_header[16] = { 0 };
unsigned char ivf_frame_header[IMX_VP8_IVF_FRAME_HEADER_LEN] = { 0 };
set_payload_hdr(pic_header, SCODE_NEW_PICTURE, IMX_CODEC_ID_VP8,
ivf_pichdr_size + buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, pic_header, 16);
set_vp8_ivf_pichdr(ivf_frame_header, buffer_size);
length += copy_buffer_to_stream(ctx, ivf_frame_header, ivf_pichdr_size);
return length;
}
static unsigned int insert_scd_seq_vp8(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned int ivf_seqhdr_size = IMX_VP8_IVF_SEQ_HEADER_LEN;
unsigned char scd_seqhdr[16] = { 0 };
unsigned char ivf_seqhdr[IMX_VP8_IVF_SEQ_HEADER_LEN] = { 0 };
unsigned char *src = (unsigned char *)data;
set_payload_hdr(scd_seqhdr, SCODE_NEW_SEQUENCE, IMX_CODEC_ID_VP8,
ivf_seqhdr_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, scd_seqhdr, 16);
set_vp8_ivf_seqhdr(ivf_seqhdr, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, ivf_seqhdr, ivf_seqhdr_size);
/* first data include frame data, need to handle them too */
length += insert_scd_pic_vp8(ctx, buffer_size, data);
length += copy_buffer_to_stream(ctx, src, buffer_size);
return length;
}
static unsigned int insert_scd_pic_asp(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char pic_header[16] = { 0 };
if (q_data->fourcc == VPU_PIX_FMT_DIV3) {
set_payload_hdr(pic_header, SCODE_NEW_PICTURE, IMX_CODEC_ID_DIVX3,
buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, pic_header, 16);
}
return length;
}
static unsigned int insert_scd_seq_asp(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char seq_header[16] = {0};
unsigned char *src = (unsigned char *)data;
if (q_data->fourcc == VPU_PIX_FMT_DIV3) {
set_payload_hdr(seq_header, SCODE_NEW_SEQUENCE, IMX_CODEC_ID_DIVX3,
0, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, seq_header, 16);
/* first data include frame data, need to handle them too */
length += insert_scd_pic_asp(ctx, buffer_size, data);
length += copy_buffer_to_stream(ctx, src, buffer_size);
} else {
/*
* other format no sequence or picture header
* directly copy frame data to ring buffer
*/
length += copy_buffer_to_stream(ctx, src, buffer_size);
}
return length;
}
static unsigned int insert_scd_pic_spk(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char pic_header[16] = { 0 };
set_payload_hdr(pic_header, SCODE_NEW_PICTURE, IMX_CODEC_ID_SPK,
buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, pic_header, 16);
return length;
}
static unsigned int insert_scd_seq_spk(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char seq_header[16] = {0};
unsigned char *src = (unsigned char *)data;
set_payload_hdr(seq_header, SCODE_NEW_SEQUENCE, IMX_CODEC_ID_SPK,
0, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, seq_header, 16);
/* first data include frame data, need to handle them too */
length += insert_scd_pic_spk(ctx, buffer_size, data);
length += copy_buffer_to_stream(ctx, src, buffer_size);
return length;
}
static unsigned int insert_slice_arv(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char slice_header[16] = { 0 };
unsigned int codec_id;
if (ctx->arv_type == ARV_8)
codec_id = IMX_CODEC_ID_ARV8;
else
codec_id = IMX_CODEC_ID_ARV9;
set_payload_hdr(slice_header, SCODE_NEW_SLICE, codec_id, buffer_size,
q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, slice_header, 16);
return length;
}
static unsigned int insert_scd_pic_arv(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned int slice_num = 0;
unsigned int packlen = 0;
unsigned char pic_header[16] = { 0 };
unsigned char *src = (unsigned char *)data;
unsigned int codec_id;
slice_num = ((src[16] << 24) | (src[17] << 16) | (src[18] << 8) | (src[19]));
packlen = 20 + 8 * slice_num;
if (ctx->arv_type == ARV_8)
codec_id = IMX_CODEC_ID_ARV8;
else
codec_id = IMX_CODEC_ID_ARV9;
set_payload_hdr(pic_header, SCODE_NEW_PICTURE, codec_id, packlen,
q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, pic_header, 16);
return length;
}
static unsigned int insert_scd_seq_arv(struct vpu_ctx *ctx, unsigned int buffer_size, void *data)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
unsigned char seq_header[16] = {0};
unsigned int codec_id;
if (strncmp((const char *)(data + 8), "RV30", 4) == 0) {
ctx->arv_type = ARV_8;
codec_id = IMX_CODEC_ID_ARV8;
} else {
ctx->arv_type = ARV_9;
codec_id = IMX_CODEC_ID_ARV9;
}
set_payload_hdr(seq_header, SCODE_NEW_SEQUENCE, codec_id,
buffer_size, q_data->width, q_data->height);
length += copy_buffer_to_stream(ctx, seq_header, 16);
length += copy_buffer_to_stream(ctx, data, buffer_size);
return length;
}
u_int32 single_seq_info_format(struct queue_data *q_data)
{
u_int32 ret = 0;
switch (q_data->fourcc) {
case V4L2_PIX_FMT_VC1_ANNEX_G:
case V4L2_PIX_FMT_VC1_ANNEX_L:
case VPU_PIX_FMT_RV:
case V4L2_PIX_FMT_MPEG4:
case V4L2_PIX_FMT_MPEG2:
case V4L2_PIX_FMT_XVID:
ret = 1;
break;
default:
break;
}
return ret;
}
bool check_free_size_4_seq(struct vpu_ctx *ctx, u_int32 uPayloadSize)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
u_int32 nfreespace = 0;
u_int32 length = 0;
switch (q_data->vdec_std) {
case VPU_VIDEO_VC1:
length = IMX_VC1_RCV_SEQ_HEADER_LEN + 16;
break;
case VPU_VIDEO_VP6:
length = uPayloadSize + 32;
break;
case VPU_VIDEO_VP8:
length = uPayloadSize + 72;
break;
case VPU_VIDEO_ASP:
length = uPayloadSize + 16;
break;
case VPU_VIDEO_SPK:
length = uPayloadSize + 32;
break;
case VPU_VIDEO_RV:
length = uPayloadSize + 16;
break;
case VPU_VIDEO_AVC:
case VPU_VIDEO_MPEG2:
case VPU_VIDEO_AVS:
case VPU_VIDEO_JPEG:
case VPU_VIDEO_AVC_MVC:
case VPU_VIDEO_HEVC:
case VPU_VIDEO_UNDEFINED:
length = uPayloadSize;
break;
default:
break;
}
pStrBufDesc = get_str_buffer_desc(ctx);
nfreespace = got_free_space(pStrBufDesc->wptr, pStrBufDesc->rptr,
pStrBufDesc->start, pStrBufDesc->end);
if (nfreespace < (length + MIN_SPACE)) {
vpu_dbg(LVL_INFO, "buffer_full: the circular buffer freespace < buffer_size\n");
return false;
}
return true;
}
bool check_free_size_pic(struct vpu_ctx *ctx, unsigned int buffer_size)
{
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
pSTREAM_BUFFER_DESCRIPTOR_TYPE pStrBufDesc;
unsigned int nfreespace = 0;
unsigned int length = 0;
switch (q_data->vdec_std) {
case VPU_VIDEO_VC1:
length = buffer_size + IMX_VC1_RCV_PIC_HEADER_LEN
+ IMX_PAYLOAD_HEADER_SIZE;
break;
case VPU_VIDEO_VP8:
length = buffer_size + IMX_VP8_IVF_FRAME_HEADER_LEN + IMX_PAYLOAD_HEADER_SIZE;
break;
case VPU_VIDEO_VP6:
case VPU_VIDEO_ASP:
case VPU_VIDEO_SPK:
length = buffer_size + IMX_PAYLOAD_HEADER_SIZE;
break;
case VPU_VIDEO_RV:
/* speciall: buffer_size need to include all slice header size*/
length = buffer_size + IMX_PAYLOAD_HEADER_SIZE;
break;
case VPU_VIDEO_AVC:
case VPU_VIDEO_MPEG2:
case VPU_VIDEO_AVS:
case VPU_VIDEO_JPEG:
case VPU_VIDEO_AVC_MVC:
case VPU_VIDEO_HEVC:
case VPU_VIDEO_UNDEFINED:
length = buffer_size;
break;
default:
break;
}
pStrBufDesc = get_str_buffer_desc(ctx);
nfreespace = got_free_space(pStrBufDesc->wptr, pStrBufDesc->rptr,
pStrBufDesc->start, pStrBufDesc->end);
if (nfreespace < (length + MIN_SPACE)) {
vpu_dbg(LVL_INFO, "buffer_full: the circular buffer freespace < buffer_size\n");
return false;
}
return true;
}
struct VPU_FMT_INFO_ARV *get_arv_info(struct vpu_ctx *ctx, u_int8 *src, u_int32 size)
{
u_int32 i;
struct VPU_FMT_INFO_ARV *arv_frame;
if (!ctx || !src)
return NULL;
if (size < 28)
return NULL;
arv_frame = kzalloc(sizeof(struct VPU_FMT_INFO_ARV), GFP_KERNEL);
if (IS_ERR_OR_NULL(arv_frame)) {
vpu_err("%s() error: arv_frame alloc failed\n", __func__);
goto err;
}
arv_frame->type = ctx->arv_type;
arv_frame->data_len = ((src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]));
arv_frame->slice_num = ((src[16] << 24) | (src[17] << 16) | (src[18] << 8) | (src[19]));
if (arv_frame->data_len > size || arv_frame->slice_num > size) {
vpu_dbg(LVL_WARN, "arv frame info incorrect, data_len=%d slice_num=%d buffer_size=%d\n",
arv_frame->data_len, arv_frame->slice_num, size);
goto err;
}
arv_frame->slice_offset = kcalloc(arv_frame->slice_num, sizeof(u_int32), GFP_KERNEL);
if (IS_ERR_OR_NULL(arv_frame->slice_offset)) {
vpu_err("%s() error: slice_offset alloc failed\n", __func__);
goto err;
}
for (i = 0; i < arv_frame->slice_num; i++)
arv_frame->slice_offset[i] = ((src[20+8*i+4] << 24) | (src[20+8*i+5] << 16) | (src[20+8*i+6] << 8) | (src[20+8*i+7]));
return arv_frame;
err:
kfree(arv_frame->slice_offset);
kfree(arv_frame);
return NULL;
}
void put_arv_info(struct VPU_FMT_INFO_ARV *arv_frame)
{
kfree(arv_frame->slice_offset);
kfree(arv_frame);
}
static const struct imx_scd_handler handlers[] = {
{.vdec_std = VPU_VIDEO_VC1,
.insert_scd_seq = insert_scd_seq_vc1,
.insert_scd_pic = insert_scd_pic_vc1,
},
{.vdec_std = VPU_VIDEO_VP6,
.insert_scd_seq = insert_scd_seq_vp6,
.insert_scd_pic = insert_scd_pic_vp6,
},
{.vdec_std = VPU_VIDEO_VP8,
.insert_scd_seq = insert_scd_seq_vp8,
.insert_scd_pic = insert_scd_pic_vp8,
},
{.vdec_std = VPU_VIDEO_ASP,
.insert_scd_seq = insert_scd_seq_asp,
.insert_scd_pic = insert_scd_pic_asp,
},
{.vdec_std = VPU_VIDEO_SPK,
.insert_scd_seq = insert_scd_seq_spk,
.insert_scd_pic = insert_scd_pic_spk,
},
{.vdec_std = VPU_VIDEO_RV,
.insert_scd_seq = insert_scd_seq_arv,
.insert_scd_pic = insert_scd_pic_arv,
.insert_scd_slice = insert_slice_arv,
},
};
unsigned int insert_scode(struct vpu_ctx *ctx, unsigned int scd_type, unsigned int buffer_size, void *data)
{
const struct imx_scd_handler *handler;
int i = 0;
bool found = false;
struct queue_data *q_data = &ctx->q_data[V4L2_SRC];
unsigned int length = 0;
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
handler = &handlers[i];
if (handler->vdec_std != q_data->vdec_std)
continue;
found = true;
break;
}
if (scd_type == SCODE_NEW_SEQUENCE) {
if (!check_free_size_4_seq(ctx, buffer_size))
return length;
/* some format first data is frame data, and no need to add
* any header, directly copy it to ring buffer.
*/
if (found && handler->insert_scd_seq)
length = handler->insert_scd_seq(ctx, buffer_size, data);
else
length = copy_buffer_to_stream(ctx, data, buffer_size);
} else if (scd_type == SCODE_NEW_PICTURE) {
if (found && handler->insert_scd_pic)
length = handler->insert_scd_pic(ctx, buffer_size, data);
} else if (scd_type == SCODE_NEW_SLICE) {
if (found && handler->insert_scd_slice)
length = handler->insert_scd_slice(ctx, buffer_size, data);
} else {
vpu_dbg(LVL_WARN, "ctx[%d] scd_type invalid\n", ctx->str_index);
}
return length;
}