--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
--- /dev/null
+/*
+ * hal_backend_codec_v4l2.c
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glob.h>
+#include <dlog.h>
+#include <sched.h>
+#include "hal_backend_codec_v4l2_private.h"
+#include <tbm_surface.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif /* LOG_TAG */
+#define LOG_TAG "CODEC_HAL"
+
+#ifndef LOGV
+#define LOGV(fmt, arg...) dlog_print(DLOG_VERBOSE, LOG_TAG, "%s (%d) %s : " fmt, __FILE__, __LINE__, __FUNCTION__, ##arg)
+#endif
+#ifndef LOGD
+#define LOGD(fmt, arg...) dlog_print(DLOG_DEBUG, LOG_TAG, "%s (%d) %s : " fmt, __FILE__, __LINE__, __FUNCTION__, ##arg)
+#endif
+#ifndef LOGI
+#define LOGI(fmt, arg...) dlog_print(DLOG_INFO, LOG_TAG, "%s (%d) %s : " fmt, __FILE__, __LINE__, __FUNCTION__, ##arg)
+#endif
+#ifndef LOGW
+#define LOGW(fmt, arg...) dlog_print(DLOG_WARN, LOG_TAG, "%s (%d) %s : " fmt, __FILE__, __LINE__, __FUNCTION__, ##arg)
+#endif
+#ifndef LOGE
+#define LOGE(fmt, arg...) dlog_print(DLOG_ERROR, LOG_TAG, "%s (%d) %s : " fmt, __FILE__, __LINE__, __FUNCTION__, ##arg)
+#endif
+
+#define VIRTUAL_CODEC_FMT_MAX 2
+#define VIRTUAL_CODEC_RES_MAX 3
+#define DEVICE_NODE_PATH_PREFIX "/dev/video"
+#define FOURCC_FORMAT "%c%c%c%c"
+#define FOURCC_CONVERT(fourcc) \
+ fourcc & 0xff,\
+ (fourcc >> 8) & 0xff,\
+ (fourcc >> 16) & 0xff,\
+ (fourcc >> 24) & 0xff
+
+#define DEVICE_NODE_PATH_LENGTH_MAX 32
+#define DEVICE_MAX 8
+#define DEVICE_FORMAT_MAX 16
+#define ERROR_STRING_LENGTH 64
+
+typedef struct _codec_v4l2_format_list_s {
+ uint32_t count;
+ enum v4l2_buf_type buf_type;
+ codec_format_type_e format_type;
+ codec_format_e formats[DEVICE_FORMAT_MAX];
+} codec_v4l2_format_list_s;
+
+typedef struct _codec_v4l2_device_s {
+ char node_path[DEVICE_NODE_PATH_LENGTH_MAX];
+ uint32_t capabilities;
+ codec_v4l2_format_list_s in_format;
+ codec_v4l2_format_list_s out_format;
+} codec_v4l2_device_s;
+
+typedef struct _codec_v4l2_device_list_s {
+ uint32_t count;
+ codec_v4l2_device_s *devices[DEVICE_MAX];
+} codec_v4l2_device_list_s;
+
+
+/* global variables */
+static codec_v4l2_device_list_s *g_codec_device_list;
+
+
+/* local functions */
+static void __codec_v4l2_constructor(void) __attribute__((constructor));
+static void __codec_v4l2_destructor(void) __attribute__((destructor));
+
+static int __codec_v4l2_probe_device(void);
+static void __codec_v4l2_release_device_list(void);
+
+static void __codec_init_buffer_control(codec_buffer_control_s *buffer_control);
+static void __codec_deinit_buffer_control(codec_buffer_control_s *buffer_control);
+
+static int __codec_v4l2_get_format(guint32 fourcc, codec_format_e *format);
+static int __codec_get_fourcc_plane_num(codec_format_e format, uint32_t *fourcc, uint32_t *plane_num);
+static void __codec_v4l2_send_message(hal_codec_handle_s *handle, codec_message_type_e type, int value);
+
+
+
+static void __codec_v4l2_constructor(void)
+{
+ int ret = __codec_v4l2_probe_device();
+ LOGI("Codec HAL Loaded - probe device[0x%x]", ret);
+}
+
+
+static void __codec_v4l2_destructor(void)
+{
+ __codec_v4l2_release_device_list();
+ LOGI("Codec HAL Unloaded");
+}
+
+
+static void __codec_v4l2_get_supported_format_list(int device_fd,
+ enum v4l2_buf_type buf_type,
+ codec_v4l2_format_list_s *format_list)
+{
+ int index = 0;
+ uint32_t count = 0;
+ codec_format_e format = CODEC_FORMAT_H264;
+ struct v4l2_fmtdesc fmtdesc;
+
+ if (device_fd < 0 || !format_list) {
+ LOGE("invalid param[%d,%p]", device_fd, format_list);
+ return;
+ }
+
+ LOGI("device fd [%d], buf type [0x%08x]", device_fd, buf_type);
+
+ for (index = 0 ; ; index++) {
+ memset(&fmtdesc, 0x0, sizeof(struct v4l2_fmtdesc));
+
+ fmtdesc.index = index;
+ fmtdesc.type = buf_type;
+
+ if (ioctl(device_fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0) {
+ LOGI("no more format index[%d]", index);
+ break;
+ }
+
+ if (__codec_v4l2_get_format(fmtdesc.pixelformat, &format) == CODEC_ERROR_NONE) {
+ format_list->formats[count] = format;
+ count++;
+
+ if (format & CODEC_FORMAT_TYPE_ENCODED)
+ format_list->format_type = CODEC_FORMAT_TYPE_ENCODED;
+ else
+ format_list->format_type = CODEC_FORMAT_TYPE_RAW;
+ }
+ }
+
+ format_list->count = count;
+ format_list->buf_type = buf_type;
+
+ LOGI("format count[%u]", count);
+}
+
+
+static int __codec_v4l2_probe_device(void)
+{
+ int ret = 0;
+ uint32_t device_count = 0;
+
+ size_t i = 0;
+ glob_t glob_buf;
+
+ struct v4l2_capability v4l2_cap;
+ guint32 device_caps;
+
+ memset(&glob_buf, 0x0, sizeof(glob_t));
+
+ ret = glob(DEVICE_NODE_PATH_PREFIX"*", 0, 0, &glob_buf);
+ if (ret != 0) {
+ switch (ret) {
+ case GLOB_NOSPACE:
+ LOGE("out of memory");
+ return CODEC_ERROR_OUT_OF_MEMORY;
+ case GLOB_ABORTED:
+ LOGE("read error");
+ return CODEC_ERROR_INTERNAL;
+ case GLOB_NOMATCH:
+ LOGE("match not found");
+ return CODEC_ERROR_INTERNAL;
+ default:
+ LOGE("unknown error[%d]", ret);
+ return CODEC_ERROR_INTERNAL;
+ }
+ }
+
+ g_codec_device_list = g_new0(codec_v4l2_device_list_s, 1);
+
+ LOGD("device node count[%zu]", glob_buf.gl_pathc);
+
+ for (i = 0 ; i < glob_buf.gl_pathc ; i++) {
+ int device_fd = 0;
+ bool is_mplane = false;
+ codec_v4l2_device_s *codec_device = NULL;
+
+ LOGD("[%zu] check device [%s]", i, glob_buf.gl_pathv[i]);
+
+ device_fd = open(glob_buf.gl_pathv[i], O_RDWR);
+ if (device_fd < 0) {
+ LOGE("open failed [%s] errno %d", glob_buf.gl_pathv[i], errno);
+ continue;
+ }
+
+ memset(&v4l2_cap, 0x0, sizeof(struct v4l2_capability));
+
+ if (ioctl(device_fd, VIDIOC_QUERYCAP, &v4l2_cap) < 0) {
+ LOGE("querycap failed. errno %d", errno);
+ close(device_fd);
+ continue;
+ }
+
+ LOGI("driver [%s], card [%s], bus_info [%s]",
+ v4l2_cap.driver, v4l2_cap.card, v4l2_cap.bus_info);
+
+ if (v4l2_cap.capabilities & V4L2_CAP_DEVICE_CAPS)
+ device_caps = v4l2_cap.device_caps;
+ else
+ device_caps = v4l2_cap.capabilities;
+
+ if (device_caps & V4L2_CAP_VIDEO_M2M_MPLANE) {
+ LOGI("V4L2_CAP_VIDEO_M2M_MPLANE");
+ is_mplane = true;
+ } else if (device_caps & V4L2_CAP_VIDEO_M2M) {
+ LOGI("V4L2_CAP_VIDEO_M2M");
+ is_mplane = false;
+ } else {
+ LOGW("not M2M device [%s]", glob_buf.gl_pathv[i]);
+ close(device_fd);
+ continue;
+ }
+
+ codec_device = g_new0(codec_v4l2_device_s, 1);
+
+ __codec_v4l2_get_supported_format_list(device_fd,
+ (is_mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT),
+ &codec_device->in_format);
+
+ __codec_v4l2_get_supported_format_list(device_fd,
+ (is_mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE),
+ &codec_device->out_format);
+
+ close(device_fd);
+
+ if (codec_device->in_format.count > 0 && codec_device->out_format.count > 0 &&
+ ((codec_device->in_format.format_type == CODEC_FORMAT_TYPE_ENCODED &&
+ codec_device->out_format.format_type == CODEC_FORMAT_TYPE_RAW) ||
+ (codec_device->in_format.format_type == CODEC_FORMAT_TYPE_RAW &&
+ codec_device->out_format.format_type == CODEC_FORMAT_TYPE_ENCODED))) {
+ strncpy(codec_device->node_path, glob_buf.gl_pathv[i], DEVICE_NODE_PATH_LENGTH_MAX - 1);
+ codec_device->capabilities = device_caps;
+
+ g_codec_device_list->devices[device_count] = codec_device;
+
+ LOGI("add device[%u] %p", device_count, codec_device);
+
+ device_count++;
+ } else {
+ LOGW("invalid format count[in:%u,out:%u] or format type[in:0x%08x,out:0x%08x]",
+ codec_device->in_format.count, codec_device->out_format.count,
+ codec_device->in_format.format_type, codec_device->out_format.format_type);
+
+ g_free(codec_device);
+ }
+ }
+
+ if (device_count > 0) {
+ g_codec_device_list->count = device_count;
+ LOGI("device count[%u]", device_count);
+ } else {
+ LOGW("no device");
+ g_free(g_codec_device_list);
+ g_codec_device_list = NULL;
+ }
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static void __codec_v4l2_release_device_list(void)
+{
+ uint32_t device_index = 0;
+ uint32_t device_count = 0;
+ codec_v4l2_device_s *codec_device = NULL;
+
+ if (!g_codec_device_list) {
+ LOGW("NULL device list");
+ return;
+ }
+
+ LOGI("release device list [%p]", g_codec_device_list);
+
+ for (device_index = 0 ; device_index < device_count ; device_index++) {
+ codec_device = g_codec_device_list->devices[device_index];
+ if (!codec_device) {
+ LOGW("NULL codec_device for index[%u]", device_index);
+ continue;
+ }
+
+ LOGI("release codec_device[%u] %p", device_index, codec_device);
+
+ memset(codec_device, 0x0, sizeof(codec_v4l2_device_s));
+ g_free(codec_device);
+ }
+
+ memset(g_codec_device_list, 0x0, sizeof(codec_v4l2_device_list_s));
+ g_free(g_codec_device_list);
+
+ g_codec_device_list = NULL;
+
+ LOGI("done");
+}
+
+
+static int __codec_v4l2_get_buf_type_and_node_path(codec_format_e in_format, codec_format_e out_format,
+ enum v4l2_buf_type *in_buf_type, enum v4l2_buf_type *out_buf_type, char **path)
+{
+ uint32_t device_index = 0;
+ uint32_t format_index = 0;
+ codec_v4l2_device_s *codec_device = NULL;
+ codec_v4l2_format_list_s *format_list = NULL;
+
+ if (!g_codec_device_list) {
+ LOGE("no codec device list");
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ if (!path || !in_buf_type || !out_buf_type) {
+ LOGE("NULL param [%p,%p,%p]", path, in_buf_type, out_buf_type);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ for (device_index = 0 ; device_index < g_codec_device_list->count ; device_index++) {
+ codec_device = g_codec_device_list->devices[device_index];
+ if (!codec_device) {
+ LOGW("No codec device for index[%d]", device_index);
+ continue;
+ }
+
+ format_list = &codec_device->in_format;
+ for (format_index = 0 ; format_index < format_list->count ; format_index++) {
+ if (format_list->formats[format_index] == in_format) {
+ *in_buf_type = format_list->buf_type;
+ break;
+ }
+ }
+
+ if (format_index == format_list->count) {
+ LOGW("in_format[%d] not found in device[%d][%s]",
+ in_format, device_index, codec_device->node_path);
+ continue;
+ }
+
+ format_list = &codec_device->out_format;
+ for (format_index = 0 ; format_index < format_list->count ; format_index++) {
+ if (format_list->formats[format_index] == out_format) {
+ *out_buf_type = format_list->buf_type;
+ *path = codec_device->node_path;
+
+ LOGI("found node path[%s], buf_type[in:%d, out:%d] for format[in:%d, out:%d]",
+ *path, *in_buf_type, *out_buf_type, in_format, out_format);
+
+ return CODEC_ERROR_NONE;
+ }
+ }
+ }
+
+ LOGE("node path not found for format[in:%d,out:%d]", in_format, out_format);
+
+ return CODEC_ERROR_NOT_SUPPORTED;
+}
+
+
+static void __codec_v4l2_send_message(hal_codec_handle_s *handle, codec_message_type_e type, int value)
+{
+ codec_message_s *message = NULL;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return;
+ }
+
+ locker = g_mutex_locker_new(&handle->msg_cb_lock);
+
+ switch (type) {
+ case CODEC_MESSAGE_TYPE_INPUT_BUFFER_USED:
+ break;
+ case CODEC_MESSAGE_TYPE_OUTPUT_BUFFER:
+ break;
+ case CODEC_MESSAGE_TYPE_RESOLUTION_CHANGED:
+ break;
+ case CODEC_MESSAGE_TYPE_ERROR:
+ message->error_code = (codec_error_e)value;
+ break;
+ default:
+ LOGW("unhandled type[%d]", type);
+ return;
+ }
+
+ message = g_new0(codec_message_s, 1);
+
+ message->type = type;
+
+ LOGD("type[%d], value[0x%x]", type, value);
+
+ g_queue_push_tail(handle->msg_list, message);
+ g_cond_signal(&handle->msg_cb_cond);
+}
+
+
+static int __codec_v4l2_wait_frame(int device_fd, int wait_time)
+{
+ int ret = CODEC_ERROR_NONE;
+ fd_set fds;
+ struct timeval timeout;
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(device_fd, &fds);
+
+ memset(&timeout, 0x0, sizeof(struct timeval));
+
+ timeout.tv_sec = wait_time;
+ timeout.tv_usec = 0;
+
+ /*LOGD("select : %d sec", wait_time);*/
+
+ ret = select(device_fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret == -1) {
+ if (EINTR == errno) {
+ LOGD("select error : EINTR");
+ return CODEC_ERROR_NONE;
+ }
+ LOGE("select failed. errno %d", errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ if (ret == 0) {
+ LOGE("select timeout.");
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ /*LOGD("select done");*/
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_g_ctrl(int device_fd, int cid, int *value)
+{
+ int ret = 0;
+ struct v4l2_control ctrl;
+
+ if (!value) {
+ LOGE("NULL param");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&ctrl, 0x0, sizeof(struct v4l2_control));
+
+ ctrl.id = cid;
+
+ ret = ioctl(device_fd, VIDIOC_G_CTRL, &ctrl);
+
+ if (ret == 0)
+ *value = ctrl.value;
+
+ LOGD("G_CTRL id 0x%x, value %d, ret %d", cid, *value, ret);
+
+ return ret;
+}
+
+
+static int __codec_v4l2_s_ctrl(int device_fd, int cid, int value)
+{
+ int ret = 0;
+ struct v4l2_control ctrl;
+
+ memset(&ctrl, 0x0, sizeof(struct v4l2_control));
+
+ ctrl.id = cid;
+ ctrl.value = value;
+
+ ret = ioctl(device_fd, VIDIOC_S_CTRL, &ctrl);
+
+ LOGD("S_CTRL id 0x%x, value %d, ret %d", cid, value, ret);
+
+ return ret;
+}
+
+
+static int __codec_v4l2_s_fmt(int device_fd,
+ codec_format_e format, enum v4l2_buf_type buf_type,
+ int width, int height)
+{
+ uint32_t fourcc = 0;
+ uint32_t plane_num = 0;
+ struct v4l2_format v4l2_fmt;
+
+ memset(&v4l2_fmt, 0x0, sizeof(struct v4l2_format));
+
+ LOGI("S_FMT : format[%d], buf_type[%d], resolution[%dx%d]",
+ format, buf_type, width, height);
+
+ if (__codec_get_fourcc_plane_num(format, &fourcc, &plane_num) != CODEC_ERROR_NONE) {
+ LOGE("get fourcc failed for format [%d]", format);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_fmt, 0x0, sizeof(struct v4l2_format));
+
+ v4l2_fmt.type = buf_type;
+ if (V4L2_TYPE_IS_MULTIPLANAR(buf_type)) {
+ v4l2_fmt.fmt.pix_mp.width = width;
+ v4l2_fmt.fmt.pix_mp.height = height;
+ v4l2_fmt.fmt.pix_mp.pixelformat = fourcc;
+ v4l2_fmt.fmt.pix_mp.num_planes = plane_num;
+ } else {
+ v4l2_fmt.fmt.pix.width = width;
+ v4l2_fmt.fmt.pix.height = height;
+ v4l2_fmt.fmt.pix.pixelformat = fourcc;
+ v4l2_fmt.fmt.pix.bytesperline = width;
+ }
+
+ if (ioctl(device_fd, VIDIOC_S_FMT, &v4l2_fmt) < 0) {
+ LOGE("S_FMT failed. errno %d", errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ LOGI("done");
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_stream(int device_fd, int type, gboolean onoff)
+{
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ if (ioctl(device_fd, onoff ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type) < 0) {
+ LOGE("stream %d failed. [t:%d] errno %d", onoff, type, errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ LOGD("stream %d done [t:%d]", onoff, type);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_reqbufs(int device_fd, int type, int memory, uint32_t count, uint32_t *result_count)
+{
+ struct v4l2_requestbuffers v4l2_reqbuf;
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGI("type[%d], memory[%d], count[%u]", type, memory, count);
+
+ memset(&v4l2_reqbuf, 0x0, sizeof(struct v4l2_requestbuffers));
+
+ v4l2_reqbuf.type = type;
+ v4l2_reqbuf.memory = memory;
+ v4l2_reqbuf.count = count;
+
+ if (ioctl(device_fd, VIDIOC_REQBUFS, &v4l2_reqbuf) < 0) {
+ LOGE("REQBUFS[count %d] failed. errno %d", count, errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ if (v4l2_reqbuf.count != count)
+ LOGW("different count [req:%d, result:%d]", count, v4l2_reqbuf.count);
+
+ if (result_count)
+ *result_count = v4l2_reqbuf.count;
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_qbuf(int device_fd, int type, int memory, int index, int dmabuf_fd)
+{
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.index = index;
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+
+ if (dmabuf_fd > CODEC_HAL_INITIAL_FD) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(type))
+ v4l2_buf.m.planes[0].m.fd = dmabuf_fd;
+ else
+ v4l2_buf.m.fd = dmabuf_fd;
+ }
+
+ if (ioctl(device_fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
+ LOGE("qbuf failed. [i: %d, t: %d, m: %d] errno %d",
+ index, type, memory, errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ LOGD("QBUF done [i:%d, t:%d, m:%d, fd:%d]",
+ index, type, memory, dmabuf_fd);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_dqbuf(int device_fd, int type, int memory, int *index, uint32_t *bytesused)
+{
+ int ret = CODEC_ERROR_NONE;
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!index) {
+ LOGE("NULL parameter");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+
+ ret = ioctl(device_fd, VIDIOC_DQBUF, &v4l2_buf);
+ if (ret < 0) {
+ LOGE("dqbuf failed. [t: %d, m: %d] errno %d",
+ type, memory, errno);
+ return CODEC_ERROR_DEVICE_READ;
+ }
+
+ LOGD("DQBUF[i:%d], bytesused[%u], length[%u]",
+ v4l2_buf.index, v4l2_buf.bytesused, v4l2_buf.length);
+
+ *index = v4l2_buf.index;
+ if (bytesused)
+ *bytesused = v4l2_buf.bytesused;
+
+ /*LOGD("dqbuf index %d", *index);*/
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_v4l2_get_format(guint32 fourcc, codec_format_e *format)
+{
+ if (!format) {
+ LOGE("NULL parameter");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
+ *format = CODEC_FORMAT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV12MT:
+ *format = CODEC_FORMAT_NV12T;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV21M:
+ *format = CODEC_FORMAT_NV21;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ *format = CODEC_FORMAT_I420;
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ *format = CODEC_FORMAT_YV12;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ *format = CODEC_FORMAT_YUYV;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ *format = CODEC_FORMAT_UYVY;
+ break;
+ case V4L2_PIX_FMT_H264:
+ *format = CODEC_FORMAT_H264;
+ break;
+ default:
+ LOGW("unhandled fourcc ["FOURCC_FORMAT"]", FOURCC_CONVERT(fourcc));
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ LOGI("fourcc ["FOURCC_FORMAT"] -> format [%d]",
+ FOURCC_CONVERT(fourcc), *format);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_get_fourcc_plane_num(codec_format_e format, uint32_t *fourcc, uint32_t *plane_num)
+{
+ if (!fourcc || !plane_num) {
+ LOGE("NULL parameter %p %p", fourcc, plane_num);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (format) {
+ case CODEC_FORMAT_NV12:
+ *fourcc = V4L2_PIX_FMT_NV12;
+ *plane_num = 2;
+ break;
+ case CODEC_FORMAT_NV12T:
+ *fourcc = V4L2_PIX_FMT_NV12MT;
+ *plane_num = 2;
+ break;
+ case CODEC_FORMAT_NV21:
+ *fourcc = V4L2_PIX_FMT_NV21;
+ *plane_num = 2;
+ break;
+ case CODEC_FORMAT_I420:
+ *fourcc = V4L2_PIX_FMT_YUV420;
+ *plane_num = 3;
+ break;
+ case CODEC_FORMAT_YV12:
+ *fourcc = V4L2_PIX_FMT_YVU420;
+ *plane_num = 3;
+ break;
+ case CODEC_FORMAT_YUYV:
+ *fourcc = V4L2_PIX_FMT_YUYV;
+ *plane_num = 1;
+ break;
+ case CODEC_FORMAT_UYVY:
+ *fourcc = V4L2_PIX_FMT_UYVY;
+ *plane_num = 1;
+ break;
+ case CODEC_FORMAT_H264:
+ *fourcc = V4L2_PIX_FMT_H264;
+ *plane_num = 1;
+ break;
+ default:
+ LOGE("unhandled format [%d]", format);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ LOGD("format [%d] -> fourcc ["FOURCC_FORMAT"]",
+ format, FOURCC_CONVERT(*fourcc));
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __codec_stop_stream(hal_codec_handle_s *handle)
+{
+ int ret = CODEC_ERROR_NONE;
+#if 0
+ int i = 0;
+ codec_buffer_s *buffer = NULL;
+#endif
+#ifdef TIZEN_FEATURE_ZERO_COPY_SUPPORT
+ codec_tbm_buffer_s *tbm_buffer = NULL;
+#endif
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ /* stream off */
+ ret = __codec_v4l2_stream(handle->device_fd, handle->input_buffer_control.buf_type, FALSE);
+ ret = __codec_v4l2_stream(handle->device_fd, handle->output_buffer_control.buf_type, FALSE);
+
+ LOGI("stream off[0x%x]", ret);
+
+ /* release buffer */
+#if 0
+ for (i = 0 ; i < buffer_count ; i++) {
+ buffer = &handle->codec_buffers[i];
+
+ LOGI("release buffer[%d]", i);
+
+#ifdef TIZEN_FEATURE_ZERO_COPY_SUPPORT
+ tbm_buffer = &handle->tbm_buffers[i];
+
+ if (tbm_buffer->dmabuf_fd > CODEC_HAL_INITIAL_FD) {
+ LOGI(" close dmabuf fd[%d]", tbm_buffer->dmabuf_fd);
+ close(tbm_buffer->dmabuf_fd);
+ tbm_buffer->dmabuf_fd = CODEC_HAL_INITIAL_FD;
+ }
+
+ if (tbm_buffer->bo) {
+ LOGI(" unref tbm bo[%p]", tbm_buffer->bo);
+ tbm_bo_unref(tbm_buffer->bo);
+ tbm_buffer->bo = NULL;
+ }
+
+ tbm_buffer->bo_handle.ptr = NULL;
+#endif
+
+ if (buffer->planes[0].data) {
+#ifndef TIZEN_FEATURE_ZERO_COPY_SUPPORT
+ LOGI(" munmap %p", buffer->planes[0].data);
+ munmap(buffer->planes[0].data, buffer->planes[0].size);
+#endif
+ buffer->planes[0].data = NULL;
+ buffer->planes[0].size = 0;
+ } else {
+ LOGI(" NULL data [i:%d]", i);
+ }
+ }
+#endif
+
+ ret = __codec_v4l2_reqbufs(handle->device_fd,
+ handle->input_buffer_control.buf_type, V4L2_MEMORY_MMAP, 0, NULL);
+
+ ret = __codec_v4l2_reqbufs(handle->device_fd,
+ handle->output_buffer_control.buf_type, V4L2_MEMORY_MMAP, 0, NULL);
+
+ LOGI("reqbufs 0 [0x%x]", ret);
+
+ return ret;
+}
+
+
+static int __codec_start_stream(hal_codec_handle_s *handle,
+ uint32_t in_buffer_count, uint32_t out_buffer_count)
+{
+ int ret = CODEC_ERROR_NONE;
+#if 0
+ int i = 0;
+ codec_buffer_s *buffer = NULL;
+#endif
+#ifdef TIZEN_FEATURE_ZERO_COPY_SUPPORT
+ uint32_t buffer_size = 0;
+ codec_tbm_buffer_s *tbm_buffer = NULL;
+#else
+#if 0
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+#endif
+#endif
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ /* request buffer */
+ ret = __codec_v4l2_reqbufs(handle->device_fd,
+ handle->input_buffer_control.buf_type, V4L2_MEMORY_MMAP,
+ in_buffer_count, &handle->input_buffer_control.count);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("REQBUFS for input failed [0x%x]", ret);
+ return ret;
+ }
+
+ LOGI("input buffer count : request %d -> result %d",
+ in_buffer_count, handle->input_buffer_control.count);
+
+ ret = __codec_v4l2_reqbufs(handle->device_fd,
+ handle->output_buffer_control.buf_type, V4L2_MEMORY_MMAP,
+ out_buffer_count, &handle->output_buffer_control.count);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("REQBUFS for output failed [0x%x]", ret);
+ return ret;
+ }
+
+ LOGI("output buffer count : request %d -> result %d",
+ out_buffer_count, handle->output_buffer_control.count);
+
+#if 0
+ /* alloc and queue buffer */
+ for (i = 0 ; i < handle->buffer_count ; i++) {
+ buffer = &handle->codec_buffers[i];
+
+ buffer->index = i;
+ buffer->format = format;
+ buffer->resolution.width = resolution->width;
+ buffer->resolution.height = resolution->height;
+ buffer->num_planes = plane_num;
+
+#ifdef TIZEN_FEATURE_ZERO_COPY_SUPPORT
+ tbm_buffer = &handle->tbm_buffers[i];
+
+ tbm_buffer->bo = tbm_bo_alloc(handle->bufmgr, (int)buffer_size, TBM_BO_DEFAULT);
+ if (!tbm_buffer->bo) {
+ LOGE("bo alloc failed[size:%u]", buffer_size);
+ ret = CODEC_ERROR_OUT_OF_MEMORY;
+ goto _START_STREAM_FAILED;
+ }
+
+ tbm_buffer->bo_handle = tbm_bo_get_handle(tbm_buffer->bo, TBM_DEVICE_CPU);
+ if (!tbm_buffer->bo_handle.ptr) {
+ LOGE("bo[%p] get handle failed", tbm_buffer->bo);
+ ret = CODEC_ERROR_INTERNAL;
+ goto _START_STREAM_FAILED;
+ }
+
+ tbm_buffer->dmabuf_fd = tbm_bo_export_fd(tbm_buffer->bo);
+ if (tbm_buffer->dmabuf_fd < 0) {
+ LOGE("export fd failed from bo[%p]", tbm_buffer->bo);
+ ret = CODEC_ERROR_INTERNAL;
+ goto _START_STREAM_FAILED;
+ }
+
+ buffer->total_size = buffer_size;
+ buffer->planes[0].size = buffer_size;
+ buffer->planes[0].data = tbm_buffer->bo_handle.ptr;
+ buffer->num_bos = 1;
+ buffer->bos[0] = tbm_buffer->bo;
+
+ LOGD("buffer[%d], size[%d], data[%p], bo[%p], fd[%d]",
+ i, buffer->planes[0].size, buffer->planes[0].data,
+ tbm_buffer->bo, tbm_buffer->dmabuf_fd);
+
+ ret = __codec_v4l2_qbuf(handle->device_fd,
+ handle->buffer_type, handle->memory_type,
+ i, tbm_buffer->dmabuf_fd);
+#else /* TIZEN_FEATURE_ZERO_COPY_SUPPORT */
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = handle->buffer_type;
+ v4l2_buf.memory = handle->memory_type;
+ v4l2_buf.index = i;
+ v4l2_buf.m.planes = v4l2_planes;
+ v4l2_buf.length = plane_num;
+
+ if (ioctl(handle->device_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0) {
+ LOGE("[%d] query buf failed. errno %d", i, errno);
+ ret = CODEC_ERROR_INTERNAL;
+ goto _START_STREAM_FAILED;
+ }
+
+ buffer->index = i;
+ buffer->format = format;
+ buffer->resolution.width = resolution->width;
+ buffer->resolution.height = resolution->height;
+ buffer->total_size = v4l2_buf.length;
+ buffer->num_planes = plane_num;
+ buffer->planes[0].size = v4l2_buf.length;
+ buffer->planes[0].data = mmap(0,
+ v4l2_buf.length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ handle->device_fd,
+ (off_t)v4l2_buf.m.offset);
+
+ if (buffer->planes[0].data == MAP_FAILED) {
+ LOGE("[%d] mmap failed (errno %d)", i, errno);
+ goto _START_STREAM_FAILED;
+ }
+
+ LOGI("buffer[%d], size[%d], data[%p]",
+ i, buffer->planes[0].size, buffer->planes[0].data);
+
+ ret = __codec_v4l2_qbuf(handle->device_fd,
+ handle->buffer_type, handle->memory_type,
+ i, CODEC_HAL_INITIAL_FD);
+#endif /* TIZEN_FEATURE_ZERO_COPY_SUPPORT */
+
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("[%d] qbuf failed (errno %d)", i, errno);
+ goto _START_STREAM_FAILED;
+ }
+ }
+#endif
+
+ /* stream on */
+ ret = __codec_v4l2_stream(handle->device_fd, handle->input_buffer_control.buf_type, TRUE);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("intput stream on failed");
+ goto _START_STREAM_FAILED;
+ }
+
+ ret = __codec_v4l2_stream(handle->device_fd, handle->output_buffer_control.buf_type, TRUE);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("output stream on failed");
+ goto _START_STREAM_FAILED;
+ }
+
+ LOGI("done");
+
+ return CODEC_ERROR_NONE;
+
+_START_STREAM_FAILED:
+ __codec_stop_stream(handle);
+ return ret;
+}
+
+
+static gboolean __codec_h264_is_delta_frame(guint8 *data, uint32_t size)
+{
+ uint32_t offset = 0;
+ uint32_t zero_count = 0;
+ uint32_t nal_unit_type = 0;
+
+ if (!data) {
+ LOGW("NULL data");
+ return FALSE;
+ }
+
+CHECK_AGAIN:
+ zero_count = 0;
+ while (offset < size) {
+ if (data[offset] == 0x0) {
+ zero_count++;
+ } else if (data[offset] == 0x1) {
+ if (zero_count == 2 || zero_count == 3) {
+ /* break to check NAL unit type */
+ offset++;
+ break;
+ }
+ zero_count = 0;
+ } else {
+ zero_count = 0;
+ }
+
+ offset++;
+ }
+
+ if (offset >= size) {
+ LOGD("invalid offset[%u], size[%u]", offset, size);
+ return FALSE;
+ }
+
+ nal_unit_type = data[offset] & 0x1F;
+ offset++;
+
+ switch (nal_unit_type) {
+ case 1:
+ LOGD("non-IDR picture");
+ return TRUE;
+ case 5:
+ LOGD("IDR picture");
+ return FALSE;
+ case 7:
+ LOGD("SPS(Sequence Parameter Set)");
+ goto CHECK_AGAIN;
+ case 8:
+ LOGD("PPS(Picture Parameter Set)");
+ goto CHECK_AGAIN;
+ default:
+ LOGD("unhandled NAL unit type[%u]", nal_unit_type);
+ return FALSE;
+ }
+}
+
+
+static void __codec_message_release_func(gpointer data)
+{
+ codec_message_s *message = (codec_message_s *)data;
+
+ if (!message) {
+ LOGW("NULL message");
+ return;
+ }
+
+ LOGD("release message %p, type %d", message, message->type);
+
+ g_free(message);
+
+ return;
+}
+
+
+static void *_codec_message_handler_func(gpointer data)
+{
+ int i = 0;
+ codec_message_s *message = NULL;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)data;
+
+ if (!handle) {
+ LOGE("NULL handle for capture thread");
+ return NULL;
+ }
+
+ LOGD("enter - message thread");
+
+ g_mutex_lock(&handle->msg_cb_lock);
+
+ while (handle->msg_cb_run) {
+ if (g_queue_is_empty(handle->msg_list)) {
+ LOGD("wait for message");
+ g_cond_wait(&handle->msg_cb_cond, &handle->msg_cb_lock);
+ LOGD("message signal received");
+ }
+
+ if (!handle->msg_cb_run) {
+ LOGW("break message thread");
+ break;
+ }
+
+ message = g_queue_pop_head(handle->msg_list);
+ if (!message) {
+ LOGW("NULL message");
+ continue;
+ }
+
+ g_mutex_unlock(&handle->msg_cb_lock);
+
+ if (handle->msg_cb) {
+ LOGD("call message callback[%d] type[%d]", i, message->type);
+ handle->msg_cb(message, handle->msg_cb_data);
+ }
+
+ g_free(message);
+
+ g_mutex_lock(&handle->msg_cb_lock);
+ }
+
+ g_mutex_unlock(&handle->msg_cb_lock);
+
+ LOGD("leave - message thread");
+
+ return NULL;
+}
+
+
+static void __codec_release_handle(hal_codec_handle_s *handle)
+{
+ if (!handle) {
+ LOGW("NULL handle");
+ return;
+ }
+
+ LOGD("release codec HAL handle [%p]", handle);
+
+ if (handle->msg_thread) {
+ g_mutex_lock(&handle->msg_cb_lock);
+ handle->msg_cb_run = FALSE;
+ g_cond_signal(&handle->msg_cb_cond);
+ g_mutex_unlock(&handle->msg_cb_lock);
+ g_thread_join(handle->msg_thread);
+ g_queue_free_full(handle->msg_list, (GDestroyNotify)__codec_message_release_func);
+ handle->msg_list = NULL;
+ }
+
+ __codec_deinit_buffer_control(&handle->input_buffer_control);
+ __codec_deinit_buffer_control(&handle->output_buffer_control);
+
+ g_mutex_clear(&handle->lock);
+ g_mutex_clear(&handle->msg_cb_lock);
+ g_cond_clear(&handle->msg_cb_cond);
+
+ if (handle->bufmgr) {
+ tbm_bufmgr_deinit(handle->bufmgr);
+ handle->bufmgr = NULL;
+ }
+
+ g_free(handle);
+
+ return;
+}
+
+
+static void __codec_init_buffer_control(codec_buffer_control_s *buffer_control)
+{
+ if (!buffer_control) {
+ LOGE("NULL control");
+ return;
+ }
+
+ g_mutex_init(&buffer_control->lock);
+ g_cond_init(&buffer_control->cond);
+}
+
+static void __codec_deinit_buffer_control(codec_buffer_control_s *buffer_control)
+{
+ if (!buffer_control) {
+ LOGE("NULL control");
+ return;
+ }
+
+ g_mutex_clear(&buffer_control->lock);
+ g_cond_clear(&buffer_control->cond);
+}
+
+
+int codec_v4l2_init(codec_type_e type, void **codec_handle)
+{
+ hal_codec_handle_s *new_handle = NULL;
+
+ tbm_bufmgr bufmgr = NULL;
+
+ LOGD("enter");
+
+ if (!codec_handle) {
+ LOGE("NULL pointer for handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ bufmgr = tbm_bufmgr_init(-1);
+ if (bufmgr == NULL) {
+ LOGE("get tbm bufmgr failed");
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ new_handle = g_new0(hal_codec_handle_s, 1);
+ if (!new_handle) {
+ LOGE("failed to alloc codec HAL handle");
+ tbm_bufmgr_deinit(bufmgr);
+ return CODEC_ERROR_OUT_OF_MEMORY;
+ }
+
+ new_handle->bufmgr = bufmgr;
+
+ g_mutex_init(&new_handle->lock);
+ g_cond_init(&new_handle->cond);
+
+ /* buffer */
+ __codec_init_buffer_control(&new_handle->input_buffer_control);
+ __codec_init_buffer_control(&new_handle->output_buffer_control);
+
+ /* message */
+ g_mutex_init(&new_handle->msg_cb_lock);
+ g_cond_init(&new_handle->msg_cb_cond);
+
+ new_handle->msg_list = g_queue_new();
+ new_handle->msg_cb_run = TRUE;
+ new_handle->msg_thread = g_thread_try_new("codecHAL:msg",
+ _codec_message_handler_func, (gpointer)new_handle, NULL);
+ if (!new_handle->msg_thread) {
+ LOGE("failed to create message thread");
+ __codec_release_handle(new_handle);
+ return CODEC_ERROR_INTERNAL;
+ }
+
+ new_handle->device_index = CODEC_HAL_INITIAL_INDEX;
+ new_handle->device_fd = CODEC_HAL_INITIAL_FD;
+ new_handle->state = CODEC_STATE_INITIALIZED;
+
+ *codec_handle = new_handle;
+
+ LOGD("codec HAL handle [%p]", new_handle);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_deinit(void *codec_handle)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ g_mutex_lock(&handle->lock);
+
+ if (handle->state != CODEC_STATE_INITIALIZED) {
+ LOGE("invalid state [%d]", handle->state);
+ g_mutex_unlock(&handle->lock);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ g_mutex_unlock(&handle->lock);
+
+ __codec_release_handle(handle);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_configure(void *codec_handle, int width, int height, codec_format_e in_format, codec_format_e out_format, bool is_secure)
+{
+ int ret = 0;
+ int device_fd = CODEC_HAL_INITIAL_FD;
+ char *node_path = NULL;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ enum v4l2_buf_type in_buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ enum v4l2_buf_type out_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ if (handle->state != CODEC_STATE_INITIALIZED) {
+ LOGE("invalid state %d", handle->state);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ if (((in_format & CODEC_FORMAT_TYPE_ENCODED) && (out_format & CODEC_FORMAT_TYPE_ENCODED)) ||
+ ((in_format & CODEC_FORMAT_TYPE_RAW) && (out_format & CODEC_FORMAT_TYPE_RAW))) {
+ LOGE("invalid format : in[%d], out[%d]", in_format, out_format);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = __codec_v4l2_get_buf_type_and_node_path(in_format, out_format,
+ &in_buf_type, &out_buf_type, &node_path);
+ if (ret != CODEC_ERROR_NONE)
+ return ret;
+
+ /* device open */
+ device_fd = open(node_path, O_RDWR);
+ if (device_fd < 0) {
+ char error_string[ERROR_STRING_LENGTH] = {'\0',};
+ strerror_r(errno, error_string, ERROR_STRING_LENGTH);
+ LOGE("[%s] open failed [%s]", node_path, error_string);
+ return CODEC_ERROR_DEVICE_OPEN;
+ }
+
+ /* set format for input */
+ ret = __codec_v4l2_s_fmt(device_fd, in_format, in_buf_type, width, height);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("set format[%d] for input failed - [0x%x]", in_format, ret);
+ close(device_fd);
+ return ret;
+ }
+
+ /* set format for output */
+ ret = __codec_v4l2_s_fmt(device_fd, out_format, out_buf_type, width, height);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("set format[%d] for output failed - [0x%x]", in_format, ret);
+ close(device_fd);
+ return ret;
+ }
+
+ handle->input_buffer_control.buf_type = in_buf_type;
+ handle->output_buffer_control.buf_type = out_buf_type;
+
+ handle->device_fd = device_fd;
+ handle->state = CODEC_STATE_CONFIGURED;
+
+ LOGD("done");
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_release(void *codec_handle)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ if (handle->state != CODEC_STATE_CONFIGURED) {
+ LOGE("invalid state %d", handle->state);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ if (handle->device_fd > CODEC_HAL_INITIAL_FD) {
+ LOGI("close device fd [%d]", handle->device_fd);
+ close(handle->device_fd);
+ } else {
+ LOGW("invalid device fd [%d]", handle->device_fd);
+ }
+
+ handle->device_fd = CODEC_HAL_INITIAL_FD;
+ handle->state = CODEC_STATE_INITIALIZED;
+
+ LOGD("done");
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_start(void *codec_handle, hal_codec_message_cb callback, void *user_data)
+{
+ int ret = 0;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle || !callback) {
+ LOGE("NULL param %p %p", handle, callback);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ if (handle->state != CODEC_STATE_CONFIGURED) {
+ LOGE("invalid state %d", handle->state);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ ret = __codec_start_stream(handle, BUFFER_MIN, BUFFER_MIN);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("__codec_start_stream failed[0x%x]", ret);
+ return ret;
+ }
+
+ handle->state = CODEC_STATE_STARTED;
+
+ LOGD("start preview done");
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_stop(void *codec_handle)
+{
+ int ret = CODEC_ERROR_NONE;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGD("start");
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ if (handle->state != CODEC_STATE_STARTED) {
+ LOGE("invalid state %d", handle->state);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ ret = __codec_stop_stream(handle);
+
+ handle->state = CODEC_STATE_CONFIGURED;
+
+ LOGD("stop done [0x%x]", ret);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_flush(void *codec_handle)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_push_input_buffer(void *codec_handle, codec_buffer_s *buffer)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_release_output_buffer(void *codec_handle, int buffer_index)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_get_state(void *codec_handle, codec_state_e *state)
+{
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+
+ if (!handle) {
+ LOGE("NULL handle");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ return CODEC_ERROR_NONE;
+}
+
+
+static int __set_command(hal_codec_handle_s *handle, int64_t command, void *value)
+{
+ int cid = 0;
+ int ctrl_ret = 0;
+ int set_value = 0;
+
+ if (!handle || !value) {
+ LOGE("NULL param %p %p", handle, value);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ set_value = *(int *)value;
+
+ if (handle->state < CODEC_STATE_OPENED) {
+ LOGE("invalid state %d", handle->state);
+ return CODEC_ERROR_INVALID_STATE;
+ }
+
+ LOGI("command[%"PRIx64"] -> [%d]", command, set_value);
+
+ switch (command) {
+ case CODEC_COMMAND_BITRATE:
+ cid = V4L2_CID_MPEG_VIDEO_BITRATE;
+ set_value = *(int *)value;
+ break;
+ default:
+ LOGE("NOT_SUPPORTED command %"PRIx64, command);
+ return CODEC_ERROR_NOT_SUPPORTED;
+ }
+
+ ctrl_ret = __codec_v4l2_s_ctrl(handle->device_fd, cid, set_value);
+ if (ctrl_ret < 0) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ LOGE("Permission denied %d", errno);
+ return CODEC_ERROR_PERMISSION_DENIED;
+ case EINVAL:
+ LOGE("Invalid argument");
+ return CODEC_ERROR_INVALID_PARAMETER;
+ case EBUSY:
+ LOGE("Device busy");
+ return CODEC_ERROR_DEVICE_BUSY;
+ case ENOTSUP:
+ LOGE("Not supported");
+ return CODEC_ERROR_NOT_SUPPORTED;
+ default:
+ LOGE("Unknown errro %d", errno);
+ return CODEC_ERROR_INTERNAL;
+ }
+ }
+
+ return CODEC_ERROR_NONE;
+}
+
+
+int codec_v4l2_set_command(void *codec_handle, int64_t command, void *value)
+{
+ int ret = CODEC_ERROR_NONE;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ ret = __set_command(handle, command, value);
+
+ return ret;
+}
+
+
+int codec_v4l2_get_command(void *codec_handle, int64_t command, void **value)
+{
+ int ret = CODEC_ERROR_NONE;
+ int cid = 0;
+ int ctrl_ret = 0;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ if (!handle || !value) {
+ LOGE("NULL param %p %p", handle, value);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ LOGI("command[%"PRIx64"]", command);
+
+ switch (command) {
+ case CODEC_COMMAND_BITRATE:
+ cid = V4L2_CID_MPEG_VIDEO_BITRATE;
+ break;
+ default:
+ LOGE("Not supported command[%"PRIx64"]", command);
+ return CODEC_ERROR_NOT_SUPPORTED;
+ }
+
+ ctrl_ret = __codec_v4l2_g_ctrl(handle->device_fd, cid, (int *)*value);
+ if (ctrl_ret < 0) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ LOGE("Permission denied %d", errno);
+ ret = CODEC_ERROR_PERMISSION_DENIED;
+ break;
+ case EINVAL:
+ LOGE("Invalid argument");
+ ret = CODEC_ERROR_INVALID_PARAMETER;
+ break;
+ case EBUSY:
+ LOGE("Device busy");
+ ret = CODEC_ERROR_DEVICE_BUSY;
+ break;
+ case ENOTSUP:
+ LOGE("Not supported");
+ ret = CODEC_ERROR_NOT_SUPPORTED;
+ break;
+ default:
+ LOGE("Unknown errro %d", errno);
+ ret = CODEC_ERROR_INTERNAL;
+ break;
+ }
+ }
+
+ if (ret == CODEC_ERROR_NONE)
+ LOGI("get[%d]", **(int **)value);
+
+ return ret;
+}
+
+
+static void __dump_batch_command(codec_batch_command_control_s *batch_command)
+{
+ if (!batch_command) {
+ LOGE("NULL batch command");
+ return;
+ }
+
+#if 0
+ LOGI("[WHITE_BALANCE] %d", batch_command->white_balance);
+#endif
+}
+
+
+int codec_v4l2_set_batch_command(void *codec_handle, codec_batch_command_control_s *batch_command, int64_t *error_command)
+{
+ int ret = CODEC_ERROR_NONE;
+ int i = 0;
+ int support_count = 0;
+ hal_codec_handle_s *handle = (hal_codec_handle_s *)codec_handle;
+ g_autoptr(GMutexLocker) locker = NULL;
+ set_batch_table_s set_table[] = {
+ {CODEC_COMMAND_BITRATE, batch_command ? (void *)&batch_command->bitrate : NULL},
+ {CODEC_COMMAND_REQUEST_CODECDATA, NULL},
+ {CODEC_COMMAND_REQUEST_SYNCFRAME, NULL}
+ };
+
+ if (!handle || !batch_command) {
+ LOGE("NULL param %p %p", handle, batch_command);
+ return CODEC_ERROR_INVALID_PARAMETER;
+ }
+
+ locker = g_mutex_locker_new(&handle->lock);
+
+ support_count = sizeof(set_table) / sizeof(set_batch_table_s);
+
+ LOGD("set batch command - support count %d", support_count);
+
+ __dump_batch_command(batch_command);
+
+ for (i = 0 ; i < support_count ; i++) {
+ if (!(batch_command->command_set_flag & set_table[i].command))
+ continue;
+
+ ret = __set_command(handle, set_table[i].command, set_table[i].value);
+ if (ret != CODEC_ERROR_NONE) {
+ LOGE("failed command %"PRIx64", ret 0x%x", set_table[i].command, ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+static int codec_v4l2_backend_init(void **data)
+{
+ hal_backend_codec_funcs *funcs = NULL;
+
+ if (!data || !*data) {
+ LOGE("NULL data[%p]", data);
+ return -1;
+ }
+
+ funcs = (hal_backend_codec_funcs *)*data;
+
+ LOGI("codec HAL funcs[%p]", funcs);
+
+ funcs->init = codec_v4l2_init;
+ funcs->deinit = codec_v4l2_deinit;
+ funcs->configure = codec_v4l2_configure;
+ funcs->release = codec_v4l2_release;
+ funcs->start = codec_v4l2_start;
+ funcs->stop = codec_v4l2_stop;
+ funcs->flush = codec_v4l2_flush;
+ funcs->push_input_buffer = codec_v4l2_push_input_buffer;
+ funcs->release_output_buffer = codec_v4l2_release_output_buffer;
+ funcs->get_state = codec_v4l2_get_state;
+ funcs->set_command = codec_v4l2_set_command;
+ funcs->get_command = codec_v4l2_get_command;
+ funcs->set_batch_command = codec_v4l2_set_batch_command;
+
+ return 0;
+}
+
+
+static int codec_v4l2_backend_exit(void *data)
+{
+ if (!data) {
+ LOGW("NULL data");
+ return 0;
+ }
+
+ LOGI("clear function pointer[%p] set by backend", data);
+
+ memset(data, 0x0, sizeof(hal_backend_codec_funcs));
+
+ return 0;
+}
+
+
+hal_backend hal_backend_codec_data = {
+ .name = "codec-v4l2",
+ .vendor = "TIZEN",
+ .init = codec_v4l2_backend_init,
+ .exit = codec_v4l2_backend_exit,
+ .major_version = 1,
+ .minor_version = 0,
+};