From: Dafna Hirschfeld <dafna3@gmail.com>
To: linux-media@vger.kernel.org
Cc: hverkuil@xs4all.nl, helen.koike@collabora.com,
Dafna Hirschfeld <dafna3@gmail.com>
Subject: [PATCH v2 6/6] media: vicodec: Add support for resolution change event.
Date: Wed, 16 Jan 2019 07:25:27 -0800 [thread overview]
Message-ID: <20190116152527.34411-7-dafna3@gmail.com> (raw)
In-Reply-To: <20190116152527.34411-1-dafna3@gmail.com>
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@gmail.com>
---
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;
+
+ 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,
+ 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);
+ 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)) {
+ 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) {
--
2.17.1
next prev parent reply other threads:[~2019-01-16 15:25 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-16 15:25 [PATCH v2 0/6] Adding support to resolution change and apdding Dafna Hirschfeld
2019-01-16 15:25 ` [PATCH v2 1/6] media: vicodec: bugfix - replace '=' with '|=' Dafna Hirschfeld
2019-01-16 15:25 ` [PATCH v2 2/6] media: vicodec: Add num_planes field to v4l2_fwht_pixfmt_info Dafna Hirschfeld
2019-01-16 15:25 ` [PATCH v2 3/6] media: vicodec: add support for CROP and COMPOSE selection Dafna Hirschfeld
2019-01-16 15:25 ` [PATCH v2 4/6] media: vicodec: Add pixel encoding flags to fwht header Dafna Hirschfeld
2019-01-17 10:57 ` Hans Verkuil
2019-01-16 15:25 ` [PATCH v2 5/6] media: vicodec: Separate fwht header from the frame data Dafna Hirschfeld
2019-01-17 10:29 ` Hans Verkuil
2019-01-16 15:25 ` Dafna Hirschfeld [this message]
2019-01-17 11:41 ` [PATCH v2 6/6] media: vicodec: Add support for resolution change event Hans Verkuil
2019-01-17 11:51 ` Hans Verkuil
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190116152527.34411-7-dafna3@gmail.com \
--to=dafna3@gmail.com \
--cc=helen.koike@collabora.com \
--cc=hverkuil@xs4all.nl \
--cc=linux-media@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).