From 017c324242c2e9724938ef193ac6cb4ec5131778 Mon Sep 17 00:00:00 2001 From: Hugues Fruchet Date: Thu, 2 Feb 2017 12:59:51 -0200 Subject: [PATCH] [media] st-delta: EOS (End Of Stream) support EOS (End Of Stream) support allows user to get all the potential decoded frames remaining in decoder pipeline after having reached the end of video bitstream. To do so, user calls VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) which will drain the decoder and get the drained frames that are then returned to user. User is informed of EOS completion in two ways: - dequeue of an empty frame flagged to V4L2_BUF_FLAG_LAST - reception of a V4L2_EVENT_EOS event. If, unfortunately, no buffer is available on CAPTURE queue to return the empty frame, EOS is delayed till user queue one CAPTURE buffer. Acked-by: Peter Griffin Signed-off-by: Hugues Fruchet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sti/delta/delta-v4l2.c | 146 +++++++++++++++++- drivers/media/platform/sti/delta/delta.h | 23 +++ 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 237a938a9e6b..c959614d6171 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -106,7 +106,8 @@ static void delta_frame_done(struct delta_ctx *ctx, struct delta_frame *frame, vbuf->sequence = ctx->frame_num++; v4l2_m2m_buf_done(vbuf, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - ctx->output_frames++; + if (frame->info.size) /* ignore EOS */ + ctx->output_frames++; } static void requeue_free_frames(struct delta_ctx *ctx) @@ -762,6 +763,135 @@ static int delta_g_selection(struct file *file, void *fh, return 0; } +static void delta_complete_eos(struct delta_ctx *ctx, + struct delta_frame *frame) +{ + struct delta_dev *delta = ctx->dev; + const struct v4l2_event ev = {.type = V4L2_EVENT_EOS}; + + /* + * Send EOS to user: + * - by returning an empty frame flagged to V4L2_BUF_FLAG_LAST + * - and then send EOS event + */ + + /* empty frame */ + frame->info.size = 0; + + /* set the last buffer flag */ + frame->flags |= V4L2_BUF_FLAG_LAST; + + /* release frame to user */ + delta_frame_done(ctx, frame, 0); + + /* send EOS event */ + v4l2_event_queue_fh(&ctx->fh, &ev); + + dev_dbg(delta->dev, "%s EOS completed\n", ctx->name); +} + +static int delta_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *cmd) +{ + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) + return -EINVAL; + + if (!(cmd->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && + (cmd->stop.pts != 0)) + return -EINVAL; + + return 0; +} + +static int delta_decoder_stop_cmd(struct delta_ctx *ctx, void *fh) +{ + const struct delta_dec *dec = ctx->dec; + struct delta_dev *delta = ctx->dev; + struct delta_frame *frame = NULL; + int ret = 0; + + dev_dbg(delta->dev, "%s EOS received\n", ctx->name); + + if (ctx->state != DELTA_STATE_READY) + return 0; + + /* drain the decoder */ + call_dec_op(dec, drain, ctx); + + /* release to user drained frames */ + while (1) { + frame = NULL; + ret = call_dec_op(dec, get_frame, ctx, &frame); + if (ret == -ENODATA) { + /* no more decoded frames */ + break; + } + if (frame) { + dev_dbg(delta->dev, "%s drain frame[%d]\n", + ctx->name, frame->index); + + /* pop timestamp and mark frame with it */ + delta_pop_dts(ctx, &frame->dts); + + /* release decoded frame to user */ + delta_frame_done(ctx, frame, 0); + } + } + + /* try to complete EOS */ + ret = delta_get_free_frame(ctx, &frame); + if (ret) + goto delay_eos; + + /* new frame available, EOS can now be completed */ + delta_complete_eos(ctx, frame); + + ctx->state = DELTA_STATE_EOS; + + return 0; + +delay_eos: + /* + * EOS completion from driver is delayed because + * we don't have a free empty frame available. + * EOS completion is so delayed till next frame_queue() call + * to be sure to have a free empty frame available. + */ + ctx->state = DELTA_STATE_WF_EOS; + dev_dbg(delta->dev, "%s EOS delayed\n", ctx->name); + + return 0; +} + +static int delta_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *cmd) +{ + struct delta_ctx *ctx = to_ctx(fh); + int ret = 0; + + ret = delta_try_decoder_cmd(file, fh, cmd); + if (ret) + return ret; + + return delta_decoder_stop_cmd(ctx, fh); +} + +static int delta_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); + default: + return -EINVAL; + } + + return 0; +} + /* v4l2 ioctl ops */ static const struct v4l2_ioctl_ops delta_ioctl_ops = { .vidioc_querycap = delta_querycap, @@ -782,6 +912,10 @@ static const struct v4l2_ioctl_ops delta_ioctl_ops = { .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = delta_g_selection, + .vidioc_try_decoder_cmd = delta_try_decoder_cmd, + .vidioc_decoder_cmd = delta_decoder_cmd, + .vidioc_subscribe_event = delta_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -1376,6 +1510,16 @@ static void delta_vb2_frame_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct delta_frame *frame = to_frame(vbuf); + if (ctx->state == DELTA_STATE_WF_EOS) { + /* new frame available, EOS can now be completed */ + delta_complete_eos(ctx, frame); + + ctx->state = DELTA_STATE_EOS; + + /* return, no need to recycle this buffer to decoder */ + return; + } + /* recycle this frame */ delta_recycle(ctx, frame); } diff --git a/drivers/media/platform/sti/delta/delta.h b/drivers/media/platform/sti/delta/delta.h index d4a401b87f8b..60c073246a01 100644 --- a/drivers/media/platform/sti/delta/delta.h +++ b/drivers/media/platform/sti/delta/delta.h @@ -27,11 +27,19 @@ *@DELTA_STATE_READY: * Decoding instance is ready to decode compressed access unit. * + *@DELTA_STATE_WF_EOS: + * Decoding instance is waiting for EOS (End Of Stream) completion. + * + *@DELTA_STATE_EOS: + * EOS (End Of Stream) is completed (signaled to user). Decoding instance + * should then be closed. */ enum delta_state { DELTA_STATE_WF_FORMAT, DELTA_STATE_WF_STREAMINFO, DELTA_STATE_READY, + DELTA_STATE_WF_EOS, + DELTA_STATE_EOS }; /* @@ -237,6 +245,7 @@ struct delta_ctx; * @get_frame: get the next decoded frame available, see below * @recycle: recycle the given frame, see below * @flush: (optional) flush decoder, see below + * @drain: (optional) drain decoder, see below */ struct delta_dec { const char *name; @@ -371,6 +380,18 @@ struct delta_dec { * decoding logic. */ int (*flush)(struct delta_ctx *ctx); + + /* + * drain() - drain decoder + * @ctx: (in) instance + * + * Optional. + * Mark decoder pending frames (decoded but not yet output) as ready + * so that they can be output to client at EOS (End Of Stream). + * get_frame() is to be called in a loop right after drain() to + * get all those pending frames. + */ + int (*drain)(struct delta_ctx *ctx); }; struct delta_dev; @@ -497,6 +518,8 @@ static inline char *frame_type_str(u32 flags) return "P"; if (flags & V4L2_BUF_FLAG_BFRAME) return "B"; + if (flags & V4L2_BUF_FLAG_LAST) + return "EOS"; return "?"; } -- 2.20.1