On 1/16/19 4:25 PM, Dafna Hirschfeld wrote: > If the the queues are not streaming then the first resolution > change is handled in the buf_queue callback. > The following resolution change events are handled in job_ready. > > Signed-off-by: Dafna Hirschfeld <dafna3@xxxxxxxxx> > --- > drivers/media/platform/vicodec/vicodec-core.c | 381 ++++++++++++++---- > 1 file changed, 304 insertions(+), 77 deletions(-) > > diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c > index c97300344fbd..44f2ed59f4a3 100644 > --- a/drivers/media/platform/vicodec/vicodec-core.c > +++ b/drivers/media/platform/vicodec/vicodec-core.c > @@ -129,6 +129,8 @@ struct vicodec_ctx { > u32 comp_frame_size; > bool comp_has_frame; > bool comp_has_next_frame; > + bool first_source_change_sent; > + bool source_changed; > }; > > static inline struct vicodec_ctx *file2ctx(struct file *file) > @@ -322,6 +324,107 @@ static void job_remove_src_buf(struct vicodec_ctx *ctx, u32 state) > spin_unlock(ctx->lock); > } > > +static const struct v4l2_fwht_pixfmt_info *info_from_header(struct fwht_cframe_hdr p_hdr) > +{ > + unsigned int flags = ntohl(p_hdr.flags); > + unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; > + unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; > + unsigned int components_num = 3; > + unsigned int pixenc = 0; > + unsigned int version = ntohl(p_hdr.version); > + > + if (version == FWHT_VERSION) { > + components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> > + FWHT_FL_COMPONENTS_NUM_OFFSET); > + pixenc = (flags & FWHT_FL_PIXENC_MSK); > + } > + return v4l2_fwht_default_fmt(width_div, height_div, version, > + components_num, pixenc, 0); > +} > + > +static bool is_header_valid(struct fwht_cframe_hdr p_hdr) > +{ > + const struct v4l2_fwht_pixfmt_info *info; > + unsigned int w = ntohl(p_hdr.width); > + unsigned int h = ntohl(p_hdr.height); > + unsigned int version = ntohl(p_hdr.version); > + unsigned int flags = ntohl(p_hdr.flags); > + > + if (p_hdr.magic1 != FWHT_MAGIC1 || p_hdr.magic2 != FWHT_MAGIC2) > + return false; I don't think this check is needed since you can never get here unless the magic sequence was found. > + > + if (!version || version > FWHT_VERSION) > + return false; > + > + if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT) > + return false; > + > + if (version == FWHT_VERSION) { > + unsigned int components_num = 1 + > + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> > + FWHT_FL_COMPONENTS_NUM_OFFSET); > + unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK; > + > + if (components_num == 0 || components_num > 4 || pixenc == BIT(19)) > + return false; > + } > + > + info = info_from_header(p_hdr); > + if (!info) > + return false; > + return true; > +} > + > +static void update_capture_data_from_header(struct vicodec_ctx *ctx) > +{ > + struct vicodec_q_data *q_dst = get_q_data(ctx, > + V4L2_BUF_TYPE_VIDEO_CAPTURE); > + struct fwht_cframe_hdr p_hdr = ctx->state.header; > + const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr); > + unsigned int flags = ntohl(p_hdr.flags); > + unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; > + unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; > + > + q_dst->info = info; > + q_dst->visible_width = ntohl(p_hdr.width); > + q_dst->visible_height = ntohl(p_hdr.height); > + q_dst->coded_width = vic_round_dim(q_dst->visible_width, hdr_width_div); > + q_dst->coded_height = vic_round_dim(q_dst->visible_height, > + hdr_height_div); > + > + q_dst->sizeimage = q_dst->coded_width * q_dst->coded_height * > + q_dst->info->sizeimage_mult / q_dst->info->sizeimage_div; > + ctx->state.colorspace = ntohl(p_hdr.colorspace); > + > + ctx->state.xfer_func = ntohl(p_hdr.xfer_func); > + ctx->state.ycbcr_enc = ntohl(p_hdr.ycbcr_enc); > + ctx->state.quantization = ntohl(p_hdr.quantization); > +} > + > +static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf, > + struct vb2_v4l2_buffer *src_buf, src_buf should be made const. > + struct vicodec_ctx *ctx) > +{ > + struct vicodec_q_data *q_dst = get_q_data(ctx, > + V4L2_BUF_TYPE_VIDEO_CAPTURE); > + > + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); > + dst_buf->sequence = q_dst->sequence++; > + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; > + > + if (src_buf->flags & V4L2_BUF_FLAG_TIMECODE) > + dst_buf->timecode = src_buf->timecode; > + dst_buf->field = src_buf->field; > + dst_buf->flags |= src_buf->flags & > + (V4L2_BUF_FLAG_TIMECODE | > + V4L2_BUF_FLAG_KEYFRAME | > + V4L2_BUF_FLAG_PFRAME | > + V4L2_BUF_FLAG_BFRAME | > + V4L2_BUF_FLAG_TSTAMP_SRC_MASK); Use v4l2_m2m_buf_copy_data(src_buf, dst_buf, true) > + dst_buf->flags |= V4L2_BUF_FLAG_LAST; > + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); > +} > + > static int job_ready(void *priv) > { > static const u8 magic[] = { > @@ -333,7 +436,15 @@ static int job_ready(void *priv) > u8 *p; > u32 sz; > u32 state; > - > + struct vicodec_q_data *q_dst = get_q_data(ctx, > + V4L2_BUF_TYPE_VIDEO_CAPTURE); > + unsigned int flags; > + unsigned int hdr_width_div; > + unsigned int hdr_height_div; > + unsigned int max_to_copy; > + > + if (ctx->source_changed) > + return 0; > if (ctx->is_enc || ctx->comp_has_frame) > return 1; > > @@ -358,11 +469,17 @@ static int job_ready(void *priv) > > ctx->comp_frame_size = ntohl(ctx->state.header.size); > > - if (ctx->comp_frame_size > ctx->comp_max_size) > - ctx->comp_frame_size = ctx->comp_max_size; > + /* > + * The current scanned frame might be the first frame of a new > + * resolution so its size might be larger than ctx->comp_max_size. > + * In that case it is copied up to the current buffer capacity and > + * the copy will continue after allocating new large enough buffer > + * when restreaming > + */ > + max_to_copy = min(ctx->comp_frame_size, ctx->comp_max_size); > > - if (ctx->comp_size < ctx->comp_frame_size) { > - u32 copy = ctx->comp_frame_size - ctx->comp_size; > + if (ctx->comp_size < max_to_copy) { > + u32 copy = max_to_copy - ctx->comp_size; > > if (copy > p_src + sz - p) > copy = p_src + sz - p; > @@ -371,15 +488,16 @@ static int job_ready(void *priv) > p, copy); > p += copy; > ctx->comp_size += copy; > - if (ctx->comp_size < ctx->comp_frame_size) { > + if (ctx->comp_size < max_to_copy) { > job_remove_src_buf(ctx, state); > goto restart; > } > } > ctx->cur_buf_offset = p - p_src; > - ctx->comp_has_frame = true; > + if (ctx->comp_size == ctx->comp_frame_size) > + ctx->comp_has_frame = true; > ctx->comp_has_next_frame = false; > - if (sz - ctx->cur_buf_offset >= sizeof(struct fwht_cframe_hdr)) { > + if (ctx->comp_has_frame && sz - ctx->cur_buf_offset >= sizeof(struct fwht_cframe_hdr)) { > struct fwht_cframe_hdr *p_hdr = (struct fwht_cframe_hdr *)p; > u32 frame_size = ntohl(p_hdr->size); > u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); > @@ -387,6 +505,36 @@ static int job_ready(void *priv) > if (!memcmp(p, magic, sizeof(magic))) > ctx->comp_has_next_frame = remaining >= frame_size; > } > + /* > + * if the header is invalid the device_run will just drop the frame > + * with an error > + */ > + if (!is_header_valid(ctx->state.header) && ctx->comp_has_frame) > + return 1; > + flags = ntohl(ctx->state.header.flags); > + hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; > + hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; > + > + if (ntohl(ctx->state.header.width) != q_dst->visible_width || > + ntohl(ctx->state.header.height) != q_dst->visible_height || > + !q_dst->info || > + hdr_width_div != q_dst->info->width_div || > + hdr_height_div != q_dst->info->height_div) { > + static const struct v4l2_event rs_event = { > + .type = V4L2_EVENT_SOURCE_CHANGE, > + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, > + }; > + > + struct vb2_v4l2_buffer *dst_buf = > + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); > + > + update_capture_data_from_header(ctx); > + ctx->first_source_change_sent = true; > + v4l2_event_queue_fh(&ctx->fh, &rs_event); > + set_last_buffer(dst_buf, src_buf, ctx); > + ctx->source_changed = true; > + return 0; > + } > return 1; > } > > @@ -426,7 +574,7 @@ static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, bool is_out > if (is_uncomp) { > const struct v4l2_fwht_pixfmt_info *info = get_q_data(ctx, f->type)->info; > > - if (ctx->is_enc) > + if (!info || ctx->is_enc) > info = v4l2_fwht_get_pixfmt(f->index); > else > info = v4l2_fwht_default_fmt(info->width_div, > @@ -477,6 +625,9 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) > q_data = get_q_data(ctx, f->type); > info = q_data->info; > > + if (!info) > + info = v4l2_fwht_get_pixfmt(0); > + > switch (f->type) { > case V4L2_BUF_TYPE_VIDEO_CAPTURE: > case V4L2_BUF_TYPE_VIDEO_OUTPUT: > @@ -950,6 +1101,7 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, > { > switch (sub->type) { > case V4L2_EVENT_EOS: > + case V4L2_EVENT_SOURCE_CHANGE: > return v4l2_event_subscribe(fh, sub, 0, NULL); > default: > return v4l2_ctrl_subscribe_event(fh, sub); > @@ -1058,7 +1210,64 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) > { > struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); > struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); > + unsigned int sz = vb2_get_plane_payload(&vbuf->vb2_buf, 0); > + u8 *p_src = vb2_plane_vaddr(&vbuf->vb2_buf, 0); > + u8 *p = p_src; > + struct vb2_queue *vq_out = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, > + V4L2_BUF_TYPE_VIDEO_OUTPUT); > + struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, > + V4L2_BUF_TYPE_VIDEO_CAPTURE); > + > + /* buf_queue handles only the first source change event */ > + if (ctx->first_source_change_sent) { > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); > + return; > + } > + /* > + * if both queues are streaming, the source change event is > + * handled in job_ready > + */ > + if (vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out)) { > + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); > + return; > + } > + > + if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { Do this instead: if (ctx->is_enc || !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } Now you can shift the remainder of the code one tab to the left. > + bool header_valid = false; > + static const struct v4l2_event rs_event = { > + .type = V4L2_EVENT_SOURCE_CHANGE, > + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, > + }; > + > + do { > + enum vb2_buffer_state state = get_next_header(ctx, p_src, sz, > + (u8 *)&ctx->state.header, > + &p); > + > + if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { > + v4l2_m2m_buf_done(vbuf, state); > + return; > + } > + header_valid = is_header_valid(ctx->state.header); > + /* > + * p points right after the end of the header in the > + * buffer. If the header is invalid we set p to point > + * to the next byte after the start of the header > + */ > + if (!header_valid) { > + p = p - sizeof(struct fwht_cframe_hdr) + 1; > + ctx->header_size = 0; > + ctx->comp_magic_cnt = 0; > + } > + > + } while (!header_valid); > + if (p < p_src + sz) > + ctx->cur_buf_offset = p - p_src; > > + update_capture_data_from_header(ctx); > + ctx->first_source_change_sent = true; > + v4l2_event_queue_fh(&ctx->fh, &rs_event); > + } > v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); > } > > @@ -1087,73 +1296,80 @@ static int vicodec_start_streaming(struct vb2_queue *q, > struct vicodec_q_data *q_data = get_q_data(ctx, q->type); > struct v4l2_fwht_state *state = &ctx->state; > const struct v4l2_fwht_pixfmt_info *info = q_data->info; > - unsigned int size = q_data->coded_width * q_data->coded_height; > - unsigned int chroma_div = info->width_div * info->height_div; > - unsigned int total_planes_size; > > - /* > - * we don't know ahead how many components are in the encoding type > - * V4L2_PIX_FMT_FWHT, so we will allocate space for 4 planes. > - */ > - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) > - total_planes_size = 2 * size + 2 * (size / chroma_div); > - else if (info->components_num == 3) > - total_planes_size = size + 2 * (size / chroma_div); > - else > - total_planes_size = size; > + if (!info) > + return -EINVAL; > > q_data->sequence = 0; > > - if (!V4L2_TYPE_IS_OUTPUT(q->type)) { > - if (!ctx->is_enc) { > - state->visible_width = q_data->visible_width; > - state->visible_height = q_data->visible_height; > - state->coded_width = q_data->coded_width; > - state->coded_height = q_data->coded_height; > - state->stride = q_data->coded_width * info->bytesperline_mult; > + ctx->last_src_buf = NULL; > + ctx->last_dst_buf = NULL; > + state->gop_cnt = 0; > + > + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || > + (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { > + unsigned int size = q_data->coded_width * q_data->coded_height; > + unsigned int chroma_div = info->width_div * info->height_div; > + unsigned int total_planes_size; > + u8 *new_comp_frame; > + > + if (!info || info->id == V4L2_PIX_FMT_FWHT) { > + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); > + return -EINVAL; > } > - return 0; > - } > + if (info->components_num == 4) > + total_planes_size = 2 * size + 2 * (size / chroma_div); > + else if (info->components_num == 3) > + total_planes_size = size + 2 * (size / chroma_div); > + else > + total_planes_size = size; > > - if (ctx->is_enc) { > state->visible_width = q_data->visible_width; > state->visible_height = q_data->visible_height; > state->coded_width = q_data->coded_width; > state->coded_height = q_data->coded_height; > state->stride = q_data->coded_width * info->bytesperline_mult; > - } > - state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); > - ctx->comp_max_size = total_planes_size; > - state->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); > - if (!state->ref_frame.luma || !state->compressed_frame) { > - kvfree(state->ref_frame.luma); > - kvfree(state->compressed_frame); > - vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); > - return -ENOMEM; > - } > - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num >= 3) { > - state->ref_frame.cb = state->ref_frame.luma + size; > - state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; > - } else { > - state->ref_frame.cb = NULL; > - state->ref_frame.cr = NULL; > - } > > - if (info->id == V4L2_PIX_FMT_FWHT || info->components_num == 4) > - state->ref_frame.alpha = > - state->ref_frame.cr + size / chroma_div; > - else > - state->ref_frame.alpha = NULL; > + state->ref_frame.luma = kvmalloc(total_planes_size, GFP_KERNEL); > + ctx->comp_max_size = total_planes_size; > + new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); > > - ctx->last_src_buf = NULL; > - ctx->last_dst_buf = NULL; > - state->gop_cnt = 0; > - ctx->cur_buf_offset = 0; > - ctx->comp_size = 0; > - ctx->header_size = 0; > - ctx->comp_magic_cnt = 0; > - ctx->comp_has_frame = false; > + if (!state->ref_frame.luma || !new_comp_frame) { > + kvfree(state->ref_frame.luma); > + kvfree(new_comp_frame); > + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); > + return -ENOMEM; > + } > + /* > + * if state->compressed_frame was already allocated then > + * it contain data of the first frame of the new resolution > + */ > + if (state->compressed_frame) { > + if (ctx->comp_size > ctx->comp_max_size) { > + ctx->comp_size = ctx->comp_max_size; > + ctx->comp_frame_size = ctx->comp_max_size; > + } > + memcpy(new_comp_frame, > + state->compressed_frame, ctx->comp_size); > + } > + > + kvfree(state->compressed_frame); > + state->compressed_frame = new_comp_frame; > + > + if (info->components_num >= 3) { > + state->ref_frame.cb = state->ref_frame.luma + size; > + state->ref_frame.cr = state->ref_frame.cb + size / chroma_div; > + } else { > + state->ref_frame.cb = NULL; > + state->ref_frame.cr = NULL; > + } > > + if (info->components_num == 4) > + state->ref_frame.alpha = > + state->ref_frame.cr + size / chroma_div; > + else > + state->ref_frame.alpha = NULL; > + } > return 0; > } > > @@ -1163,11 +1379,21 @@ static void vicodec_stop_streaming(struct vb2_queue *q) > > vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); > > - if (!V4L2_TYPE_IS_OUTPUT(q->type)) > - return; > - > - kvfree(ctx->state.ref_frame.luma); > - kvfree(ctx->state.compressed_frame); > + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || > + (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { > + kvfree(ctx->state.ref_frame.luma); > + ctx->source_changed = false; > + } > + if (V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) { > + ctx->cur_buf_offset = 0; > + ctx->comp_max_size = 0; > + ctx->comp_size = 0; > + ctx->header_size = 0; > + ctx->comp_magic_cnt = 0; > + ctx->comp_frame_size = 0; > + ctx->comp_has_frame = 0; > + ctx->comp_has_next_frame = 0; > + } > } > > static const struct vb2_ops vicodec_qops = { > @@ -1319,16 +1545,17 @@ static int vicodec_open(struct file *file) > else > ctx->q_data[V4L2_M2M_SRC].sizeimage = > size + sizeof(struct fwht_cframe_hdr); > - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; > - ctx->q_data[V4L2_M2M_DST].info = > - ctx->is_enc ? &pixfmt_fwht : v4l2_fwht_get_pixfmt(0); > - size = 1280 * 720 * ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / > - ctx->q_data[V4L2_M2M_DST].info->sizeimage_div; > - if (ctx->is_enc) > - ctx->q_data[V4L2_M2M_DST].sizeimage = > - size + sizeof(struct fwht_cframe_hdr); > - else > - ctx->q_data[V4L2_M2M_DST].sizeimage = size; > + if (ctx->is_enc) { > + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; > + ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; > + ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 * > + ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / > + ctx->q_data[V4L2_M2M_DST].info->sizeimage_div + > + sizeof(struct fwht_cframe_hdr); > + } else { > + ctx->q_data[V4L2_M2M_DST].info = NULL; > + } > + > ctx->state.colorspace = V4L2_COLORSPACE_REC709; > > if (ctx->is_enc) { > To be honest, this patch is hard to follow. Not your fault, it simply is difficult code. I think that I will merge a v3 (with all the comments fixed) and then see if I can refactor things to make the code easier to understand. And while I do that, you can start work on the stateless codec, which is after all the goal of your project :-) Regards, Hans