On Wed, 01 Feb 2017, Hugues Fruchet wrote:

> 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.
> 
> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>

Acked-by: Peter Griffin <peter.grif...@linaro.org>

> ---
>  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 237a938..c959614 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 int delta_g_selection(struct file *file, void *fh,
>       .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 d4a401b..60c07324 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_ipc_param {
>   * @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 "?";
>  }
>  
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to