|
@@ -106,7 +106,8 @@ static void delta_frame_done(struct delta_ctx *ctx, struct delta_frame *frame,
|
|
vbuf->sequence = ctx->frame_num++;
|
|
vbuf->sequence = ctx->frame_num++;
|
|
v4l2_m2m_buf_done(vbuf, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
|
|
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)
|
|
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;
|
|
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 */
|
|
/* v4l2 ioctl ops */
|
|
static const struct v4l2_ioctl_ops delta_ioctl_ops = {
|
|
static const struct v4l2_ioctl_ops delta_ioctl_ops = {
|
|
.vidioc_querycap = delta_querycap,
|
|
.vidioc_querycap = delta_querycap,
|
|
@@ -782,6 +912,10 @@ static const struct v4l2_ioctl_ops delta_ioctl_ops = {
|
|
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
|
|
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
|
|
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
|
|
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
|
|
.vidioc_g_selection = delta_g_selection,
|
|
.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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
struct delta_frame *frame = to_frame(vbuf);
|
|
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 */
|
|
/* recycle this frame */
|
|
delta_recycle(ctx, frame);
|
|
delta_recycle(ctx, frame);
|
|
}
|
|
}
|