blob: aebc889c532679ca894e32f7f497bf6260370395 [file] [log] [blame]
henryhsu58be50c2014-10-30 11:49:19 +08001/* Copyright 2014 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
Wu-Cheng Lice8947e2014-11-13 17:34:16 +08006#include <assert.h>
7#include <errno.h>
8#include <linux/videodev2.h>
9#include <pthread.h>
henryhsu58be50c2014-10-30 11:49:19 +080010#include <unistd.h>
Wu-Cheng Lice8947e2014-11-13 17:34:16 +080011#include <stdbool.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/queue.h>
henryhsu58be50c2014-10-30 11:49:19 +080016#include <sys/syscall.h>
Wu-Cheng Lice8947e2014-11-13 17:34:16 +080017#include "config.h" /* For HAVE_VISIBILITY */
henryhsu58be50c2014-10-30 11:49:19 +080018#include "libv4l-plugin.h"
Wu-Cheng Lidcec4332014-11-13 15:49:21 +080019#include "libvpu/rk_vepu_interface.h"
henryhsu58be50c2014-10-30 11:49:19 +080020
Wu-Cheng Lice8947e2014-11-13 17:34:16 +080021#define VLOG(log_level, str, ...) ((g_log_level >= log_level) ? \
22 (void) fprintf(stderr, "%s: " str "\n", __func__, ##__VA_ARGS__) \
23 : (void) 0)
24
25#define VLOG_FD(log_level, str, ...) ((g_log_level >= log_level) ? \
26 (void) fprintf(stderr, \
27 "%s: fd=%d. " str "\n", __func__, fd, ##__VA_ARGS__) : (void) 0)
28
29#define SYS_IOCTL(fd, cmd, arg) ({ \
30 int ret = syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), \
31 (void *)(arg)); \
32 if (g_log_level >= 2) \
33 fprintf(stderr, "SYS_ioctl: %s(%lu): fd=%d, ret=%d\n", \
34 v4l_cmd2str(cmd), _IOC_NR((unsigned long)cmd), fd, ret); \
35 ret; \
36 })
henryhsu58be50c2014-10-30 11:49:19 +080037
38#if HAVE_VISIBILITY
39#define PLUGIN_PUBLIC __attribute__ ((__visibility__("default")))
40#else
41#define PLUGIN_PUBLIC
42#endif
43
Wu-Cheng Lice8947e2014-11-13 17:34:16 +080044/* TODO: use frame rate and bitrate from applications. */
45#define DEFAULT_FRAME_RATE 30
46#define DEFAULT_BITRATE 1000000
47
48
49struct pending_buffer {
50 struct v4l2_buffer buffer;
51 struct v4l2_plane planes[VIDEO_MAX_PLANES];
52 TAILQ_ENTRY(pending_buffer) entries;
53};
54TAILQ_HEAD(pending_buffer_queue, pending_buffer);
55
56/*
57 * struct encoder_context - the context of an encoder instance.
58 * @enc: Encoder instance returned from rk_vepu_create().
59 * @mutex: The mutex to protect encoder_context.
60 * @param: The encoding parameters like input format, bitrate, and etc.
61 * @output_streamon_type: Type of output interface when it streams on.
62 * @capture_streamon_type: Type of capture interface when it streams on.
63 * @pending_buffers: The pending v4l2 buffers waiting for the encoding
64 * configuration. After a previous buffer is dequeued,
65 * one buffer from the queue can be queued.
66 * @can_qbuf: Indicate that we can queue one source buffer. This is true only
67 * when the parameters to pass together with the source buffer are
68 * ready; those params are received on dequeing the previous
69 * destination buffer.
70 * @get_param_payload: Payload of V4L2_CID_PRIVATE_RK3288_GET_PARAMS. This is
71 * used to update the encoder configuration by
72 * rk_vepu_update_config().
73 * @get_param_payload_size: The size of get_param_payload.
74 * @v4l2_ctrls: v4l2 controls for VIDIOC_S_EXT_CTRLS.
75 */
76struct encoder_context {
77 void *enc;
78 pthread_mutex_t mutex;
79 struct rk_vepu_param param;
80 enum v4l2_buf_type output_streamon_type;
81 enum v4l2_buf_type capture_streamon_type;
82 struct pending_buffer_queue pending_buffers;
83 bool can_qbuf;
84 void *get_param_payload;
85 size_t get_param_payload_size;
86 struct v4l2_ext_control v4l2_ctrls[MAX_NUM_GET_CONFIG_CTRLS];
87};
88
89static void *plugin_init(int fd);
90static void plugin_close(void *dev_ops_priv);
91static int plugin_ioctl(void *dev_ops_priv, int fd, unsigned long int cmd,
92 void *arg);
93
94/* Functions to handle various ioctl. */
95static int ioctl_streamon_locked(
96 struct encoder_context *ctx, int fd, enum v4l2_buf_type *type);
97static int ioctl_streamoff_locked(
98 struct encoder_context *ctx, int fd, enum v4l2_buf_type *type);
99static int ioctl_qbuf_locked(struct encoder_context *ctx, int fd,
100 struct v4l2_buffer *buffer);
101static int ioctl_dqbuf_locked(struct encoder_context *ctx, int fd,
102 struct v4l2_buffer *buffer);
103
104/* Helper functions to manipulate the pending buffer queue. */
105
106/* Insert a buffer to the tail of the queue. */
107static int queue_insert_tail(struct pending_buffer_queue *queue,
108 struct v4l2_buffer *buffer);
109/* Remove a buffer from the queue and free the memory. */
110static void queue_remove(struct pending_buffer_queue *queue,
111 struct pending_buffer *element);
112
113/* Set encoder configuration to the driver. */
114int set_encoder_config(struct encoder_context *ctx, int fd,
115 uint32_t buffer_index, size_t num_ctrls, uint32_t ctrls_ids[],
116 void **payloads, uint32_t payload_sizes[]);
117/* QBUF a buffer from the pending buffer queue if it is not empty. */
118static int qbuf_if_pending_buffer_exists(struct encoder_context *ctx, int fd);
119/* Get the encoder parameters using G_FMT and initialize libvpu. */
120static int initialize_libvpu(struct encoder_context *ctx, int fd);
121/* Return the string represenation of a libv4l command for debugging. */
122static const char *v4l_cmd2str(unsigned long int cmd);
123/*
124 * The current log level for VLOG. This is read from environment variable
125 * LIBV4L_PLUGIN_LOG_LEVEL every time plugin_init is called.
126 */
127static int g_log_level = 0;
128/* Get the log level from the environment variable. */
129static void get_log_level();
130static pthread_once_t g_get_log_level_once = PTHREAD_ONCE_INIT;
131
henryhsu58be50c2014-10-30 11:49:19 +0800132static void *plugin_init(int fd)
133{
Wu-Cheng Lice8947e2014-11-13 17:34:16 +0800134 int ret;
135 struct v4l2_query_ext_ctrl ext_ctrl;
136
137 pthread_once(&g_get_log_level_once, get_log_level);
138
139 VLOG_FD(1, "");
140 /* TODO: Query the driver and verify it's a Rockchip encoder. */
141 struct encoder_context *ctx = (struct encoder_context *)
142 calloc(1, sizeof(struct encoder_context));
143 if (ctx == NULL) {
144 errno = ENOMEM;
145 goto fail;
146 }
147 ret = pthread_mutex_init(&ctx->mutex, NULL);
148 if (ret)
149 goto fail;
150 TAILQ_INIT(&ctx->pending_buffers);
151
152 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
153 ext_ctrl.id = V4L2_CID_PRIVATE_RK3288_GET_PARAMS;
154 ret = SYS_IOCTL(fd, VIDIOC_QUERY_EXT_CTRL, &ext_ctrl);
155 if (ret) {
156 VLOG_FD(0, "Failed to query GET_PARAMS size. errno=%d", errno);
157 goto fail;
158 }
159 ctx->get_param_payload_size = ext_ctrl.elem_size;
160 ctx->get_param_payload = calloc(1, ctx->get_param_payload_size);
161 if (ctx->get_param_payload == NULL) {
162 errno = ENOMEM;
163 goto fail;
164 }
165 VLOG_FD(1, "Success. ctx=%p", ctx);
166 return ctx;
167
168fail:
169 plugin_close(ctx);
henryhsu58be50c2014-10-30 11:49:19 +0800170 return NULL;
171}
172
173static void plugin_close(void *dev_ops_priv)
174{
Wu-Cheng Lice8947e2014-11-13 17:34:16 +0800175 struct encoder_context *ctx = (struct encoder_context *)dev_ops_priv;
176
177 VLOG(1, "ctx=%p", ctx);
178 if (ctx == NULL)
179 return;
180
181 pthread_mutex_lock(&ctx->mutex);
182 if (ctx->enc)
183 rk_vepu_deinit(ctx->enc);
184 while (!TAILQ_EMPTY(&ctx->pending_buffers)) {
185 queue_remove(&ctx->pending_buffers,
186 TAILQ_FIRST(&ctx->pending_buffers));
187 }
188 free(ctx->get_param_payload);
189 ctx->get_param_payload = NULL;
190 pthread_mutex_unlock(&ctx->mutex);
191 pthread_mutex_destroy(&ctx->mutex);
192
193 free(ctx);
henryhsu58be50c2014-10-30 11:49:19 +0800194}
195
196static int plugin_ioctl(void *dev_ops_priv, int fd,
197 unsigned long int cmd, void *arg)
198{
Wu-Cheng Lice8947e2014-11-13 17:34:16 +0800199 /* TODO: Query the driver and verify it's a Rockchip encoder. */
200 int ret;
201 struct encoder_context *ctx = (struct encoder_context *)dev_ops_priv;
202
203 VLOG_FD(1, "%s(%lu)", v4l_cmd2str(cmd), _IOC_NR(cmd));
204
205 pthread_mutex_lock(&ctx->mutex);
206 switch (cmd) {
207 case VIDIOC_STREAMON:
208 ret = ioctl_streamon_locked(ctx, fd, (enum v4l2_buf_type *)arg);
209 break;
210
211 case VIDIOC_STREAMOFF:
212 ret = ioctl_streamoff_locked(ctx, fd, (enum v4l2_buf_type *)arg);
213 break;
214
215 case VIDIOC_QBUF:
216 ret = ioctl_qbuf_locked(ctx, fd, (struct v4l2_buffer *)arg);
217 break;
218
219 case VIDIOC_DQBUF:
220 ret = ioctl_dqbuf_locked(ctx, fd, (struct v4l2_buffer *)arg);
221 break;
222
223 default:
224 ret = SYS_IOCTL(fd, cmd, arg);
225 break;
226 }
227 pthread_mutex_unlock(&ctx->mutex);
228 return ret;
229}
230
231static int ioctl_streamon_locked(
232 struct encoder_context *ctx, int fd, enum v4l2_buf_type *type)
233{
234 int ret = SYS_IOCTL(fd, VIDIOC_STREAMON, type);
235 if (ret)
236 return ret;
237
238 if (V4L2_TYPE_IS_OUTPUT(*type))
239 ctx->output_streamon_type = *type;
240 else
241 ctx->capture_streamon_type = *type;
242 if (ctx->output_streamon_type && ctx->capture_streamon_type) {
243 ret = initialize_libvpu(ctx, fd);
244 if (ret)
245 return ret;
246 ctx->can_qbuf = true;
247 return qbuf_if_pending_buffer_exists(ctx, fd);
248 }
249 return 0;
250}
251
252static int ioctl_streamoff_locked(
253 struct encoder_context *ctx, int fd, enum v4l2_buf_type *type)
254{
255 int ret = SYS_IOCTL(fd, VIDIOC_STREAMOFF, type);
256 if (ret)
257 return ret;
258
259 if (V4L2_TYPE_IS_OUTPUT(*type))
260 ctx->output_streamon_type = 0;
261 else
262 ctx->capture_streamon_type = 0;
263 return 0;
264}
265
266static int ioctl_qbuf_locked(struct encoder_context *ctx, int fd,
267 struct v4l2_buffer *buffer)
268{
269 size_t num_ctrls = 0;
270 uint32_t *ctrl_ids = NULL, *payload_sizes = NULL;
271 void **payloads = NULL;
272 int ret;
273
274 if (!V4L2_TYPE_IS_OUTPUT(buffer->type)) {
275 return SYS_IOCTL(fd, VIDIOC_QBUF, buffer);
276 }
277
278 if (!ctx->can_qbuf) {
279 VLOG_FD(1, "Put buffer (%d) in the pending queue.",
280 buffer->index);
281 /*
282 * The last frame is not encoded yet. Put the buffer to the
283 * pending queue.
284 */
285 return queue_insert_tail(&ctx->pending_buffers, buffer);
286 }
287 /* Get the encoder configuration from the library. */
288 if (rk_vepu_get_config(ctx->enc, &num_ctrls, &ctrl_ids, &payloads,
289 &payload_sizes)) {
290 VLOG_FD(0, "rk_vepu_get_config failed");
291 return -EIO;
292 }
293 /* Set the encoder configuration to the driver. */
294 ret = set_encoder_config(ctx, fd, buffer->index, num_ctrls, ctrl_ids,
295 payloads, payload_sizes);
296 if (ret)
297 return ret;
298
299 ret = SYS_IOCTL(fd, VIDIOC_QBUF, buffer);
300 if (ret == 0)
301 ctx->can_qbuf = false;
302 else
303 VLOG(0, "QBUF failed. errno=%d", errno);
304 return ret;
305}
306
307static int ioctl_dqbuf_locked(struct encoder_context *ctx, int fd,
308 struct v4l2_buffer *buffer)
309{
310 struct v4l2_ext_controls ext_ctrls;
311 struct v4l2_ext_control v4l2_ctrl;
312 int ret;
313
314 if (V4L2_TYPE_IS_OUTPUT(buffer->type)) {
315 return SYS_IOCTL(fd, VIDIOC_DQBUF, buffer);
316 }
317
318 assert(!ctx->can_qbuf);
319
320 ret = SYS_IOCTL(fd, VIDIOC_DQBUF, buffer);
321 if (ret)
322 return ret;
323
324 /* Get the encoder configuration and update the library. */
325 memset(ctx->get_param_payload, 0, ctx->get_param_payload_size);
326 memset(&v4l2_ctrl, 0, sizeof(v4l2_ctrl));
327 v4l2_ctrl.id = V4L2_CID_PRIVATE_RK3288_GET_PARAMS;
328 v4l2_ctrl.size = ctx->get_param_payload_size;
329 v4l2_ctrl.ptr = &ctx->get_param_payload;
330 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
331 /* TODO: change this to config_store after the header is updated. */
332 ext_ctrls.ctrl_class = buffer->index + 1;
333 ext_ctrls.count = 1;
334 ext_ctrls.controls = &v4l2_ctrl;
335 ret = SYS_IOCTL(fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
336 if (ret) {
337 VLOG_FD(0, "G_EXT_CTRLS failed. errno=%d", errno);
338 return ret;
339 }
340 if (rk_vepu_update_config(ctx->enc, v4l2_ctrl.ptr, v4l2_ctrl.size,
341 buffer->m.planes[0].bytesused)) {
342 VLOG_FD(0, "rk_vepu_update_config failed.");
343 return -EIO;
344 }
345 ctx->can_qbuf = true;
346 return qbuf_if_pending_buffer_exists(ctx, fd);
347}
348
349int set_encoder_config(struct encoder_context *ctx, int fd,
350 uint32_t buffer_index, size_t num_ctrls, uint32_t ctrl_ids[],
351 void **payloads, uint32_t payload_sizes[]) {
352 size_t i;
353 struct v4l2_ext_controls ext_ctrls;
354
355 if (num_ctrls <= 0)
356 return 0;
357
358 assert(num_ctrls <= MAX_NUM_GET_CONFIG_CTRLS);
359 if (num_ctrls > MAX_NUM_GET_CONFIG_CTRLS)
360 num_ctrls = MAX_NUM_GET_CONFIG_CTRLS;
361 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
362 /* TODO: change this to config_store after the header is updated. */
363 ext_ctrls.ctrl_class = buffer_index + 1;
364 ext_ctrls.count = num_ctrls;
365 ext_ctrls.controls = ctx->v4l2_ctrls;
366 memset(ctx->v4l2_ctrls, 0, sizeof(ctx->v4l2_ctrls));
367 for (i = 0; i < num_ctrls; ++i) {
368 ctx->v4l2_ctrls[i].id = ctrl_ids[i];
369 ctx->v4l2_ctrls[i].ptr = payloads[i];
370 ctx->v4l2_ctrls[i].size = payload_sizes[i];
371 }
372 int ret = SYS_IOCTL(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
373 if (ret) {
374 VLOG(0, "S_EXT_CTRLS failed. errno=%d", errno);
375 return ret;
376 }
377 return 0;
378}
379
380static int qbuf_if_pending_buffer_exists(struct encoder_context *ctx, int fd) {
381 if (!TAILQ_EMPTY(&ctx->pending_buffers)) {
382 int ret;
383 struct pending_buffer *element =
384 TAILQ_FIRST(&ctx->pending_buffers);
385 VLOG_FD(1, "QBUF a buffer (%d) from the pending queue.",
386 element->buffer.index);
387 ret = ioctl_qbuf_locked(ctx, fd, &element->buffer);
388 if (ret)
389 return ret;
390 queue_remove(&ctx->pending_buffers, element);
391 }
392 return 0;
393}
394
395static int initialize_libvpu(struct encoder_context *ctx, int fd) {
396 struct rk_vepu_param param;
397 memset(&param, 0, sizeof(param));
398 param.framerate_numer = DEFAULT_FRAME_RATE;
399 param.framerate_denom = 1;
400 param.bitrate = DEFAULT_BITRATE;
401
402 struct v4l2_format format;
403 memset(&format, 0, sizeof(format));
404 format.type = ctx->output_streamon_type;
405 int ret = SYS_IOCTL(fd, VIDIOC_G_FMT, &format);
406 if (ret) {
407 VLOG_FD(0, "Fail to get output format. errno=%d", errno);
408 return ret;
409 }
410 param.width = format.fmt.pix_mp.width;
411 param.height = format.fmt.pix_mp.height;
412 param.input_format = format.fmt.pix_mp.pixelformat;
413
414 memset(&format, 0, sizeof(format));
415 format.type = ctx->capture_streamon_type;
416 ret = SYS_IOCTL(fd, VIDIOC_G_FMT, &format);
417 if (ret) {
418 VLOG_FD(0, "Fail to get capture format. errno=%d", errno);
419 return ret;
420 }
421 ctx->param.output_format = format.fmt.pix_mp.pixelformat;
422
423 /*
424 * If the encoder library has initialized and parameters have not
425 * changed, skip the initialization.
426 */
427 if (ctx->enc) {
428 if (memcmp(&param, &ctx->param, sizeof(param)) == 0)
429 return 0;
430 rk_vepu_deinit(ctx->enc);
431 }
432 ctx->enc = rk_vepu_init(&ctx->param);
433 if (ctx->enc == NULL) {
434 VLOG_FD(0, "Failed to initialize encoder library.");
435 return -EIO;
436 }
437 return 0;
438}
439
440static int queue_insert_tail(struct pending_buffer_queue *queue,
441 struct v4l2_buffer *buffer)
442{
443 struct pending_buffer *entry =
444 (struct pending_buffer *)calloc(sizeof(*entry), 1);
445 if (entry == NULL)
446 return -ENOMEM;
447 memcpy(&entry->buffer, buffer, sizeof(*buffer));
448 if (V4L2_TYPE_IS_MULTIPLANAR(buffer->type)) {
449 memset(entry->planes, 0,
450 sizeof(struct v4l2_plane) * VIDEO_MAX_PLANES);
451 memcpy(entry->planes, buffer->m.planes,
452 sizeof(struct v4l2_plane) * buffer->length);
453 entry->buffer.m.planes = entry->planes;
454 }
455 TAILQ_INSERT_TAIL(queue, entry, entries);
456 return 0;
457}
458
459static void queue_remove(struct pending_buffer_queue *queue,
460 struct pending_buffer *element)
461{
462 TAILQ_REMOVE(queue, element, entries);
463 free(element);
464}
465
466static void get_log_level()
467{
468 char *log_level_str = getenv("LIBV4L_PLUGIN_LOG_LEVEL");
469 if (log_level_str != NULL)
470 g_log_level = strtol(log_level_str, NULL, 10);
471}
472
473static const char* v4l_cmd2str(unsigned long int cmd)
474{
475 switch (cmd) {
476 case VIDIOC_QUERYCAP:
477 return "VIDIOC_QUERYCAP";
478 case VIDIOC_TRY_FMT:
479 return "VIDIOC_TRY_FMT";
480 case VIDIOC_S_FMT:
481 return "VIDIOC_S_FMT";
482 case VIDIOC_G_FMT:
483 return "VIDIOC_G_FMT";
484 case VIDIOC_ENUM_FMT:
485 return "VIDIOC_ENUM_FMT";
486 case VIDIOC_S_PARM:
487 return "VIDIOC_S_PARM";
488 case VIDIOC_G_PARM:
489 return "VIDIOC_G_PARM";
490 case VIDIOC_QBUF:
491 return "VIDIOC_QBUF";
492 case VIDIOC_DQBUF:
493 return "VIDIOC_DQBUF";
494 case VIDIOC_PREPARE_BUF:
495 return "VIDIOC_PREPARE_BUF";
496 case VIDIOC_CREATE_BUFS:
497 return "VIDIOC_CREATE_BUFS";
498 case VIDIOC_REQBUFS:
499 return "VIDIOC_REQBUFS";
500 case VIDIOC_STREAMON:
501 return "VIDIOC_STREAMON";
502 case VIDIOC_STREAMOFF:
503 return "VIDIOC_STREAMOFF";
504 case VIDIOC_S_CROP:
505 return "VIDIOC_S_CROP";
506 case VIDIOC_S_CTRL:
507 return "VIDIOC_S_CTRL";
508 case VIDIOC_G_EXT_CTRLS:
509 return "VIDIOC_G_EXT_CTRLS";
510 case VIDIOC_S_EXT_CTRLS:
511 return "VIDIOC_S_EXT_CTRLS";
512 case VIDIOC_QUERYBUF:
513 return "VIDIOC_QUERYBUF";
514 default:
515 return "UNKNOWN";
516 }
henryhsu58be50c2014-10-30 11:49:19 +0800517}
518
519PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = {
520 .init = &plugin_init,
521 .close = &plugin_close,
522 .ioctl = &plugin_ioctl,
523};