From: Jeongmo Yang Date: Tue, 24 Sep 2024 11:10:32 +0000 (+0900) Subject: Initial release X-Git-Tag: accepted/tizen/unified/20250610.081745~17 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ef4a47f1915c9aaebc52b4b0c2b58026135f8d46;p=platform%2Fhal%2Fbackend%2Fcodec-v4l2.git Initial release - Build OK with hal-api-codec package. - Some functions are implemented, but not completed. : init() : deinit() : configure() : release() Signed-off-by: Jeongmo Yang --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7e88032 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Jeongmo Yang diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..8aa906c --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,205 @@ + + 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. + + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..af437a6 --- /dev/null +++ b/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..0e0f016 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE.APLv2 file for Apache License terms and conditions. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..f5469d7 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,7 @@ +#! /bin/sh + +libtoolize --copy --force +aclocal +autoheader +autoconf +automake -a -c diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e18bc23 --- /dev/null +++ b/configure.ac @@ -0,0 +1,54 @@ +AC_PREREQ(2.52) + +AC_INIT([hal-backend-codec-v4l2], [1.0.0]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_CONFIG_HEADERS([config.h:config.hin]) +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +AC_PROG_CC +AC_PROG_CXX +AM_PROG_CC_C_O +AC_C_CONST +AC_FUNC_MMAP +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_STAT +AC_FUNC_VPRINTF +AC_HEADER_STDBOOL +AC_HEADER_STDC +AC_HEADER_TIME +AC_PROG_GCC_TRADITIONAL +AC_PROG_LIBTOOL + +PKG_CHECK_MODULES(HAL_ROOTSTRAP, hal-rootstrap) +AC_SUBST(HAL_ROOTSTRAP_CFLAGS) +AC_SUBST(HAL_ROOTSTRAP_LIBS) + +PKG_CHECK_MODULES(HALAPI_CODEC, hal-api-codec) +AC_SUBST(HALAPI_CODEC_CFLAGS) +AC_SUBST(HALAPI_CODEC_LIBS) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h memory.h stdlib.h string.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_ALLOCA +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_SELECT_ARGTYPES +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([memset select]) +AC_CONFIG_FILES([ +Makefile +src/Makefile +]) +AC_OUTPUT diff --git a/hal-backend-codec-v4l2.manifest b/hal-backend-codec-v4l2.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/hal-backend-codec-v4l2.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/hal-backend-codec-v4l2.spec b/packaging/hal-backend-codec-v4l2.spec new file mode 100644 index 0000000..5b502c1 --- /dev/null +++ b/packaging/hal-backend-codec-v4l2.spec @@ -0,0 +1,42 @@ +Name: hal-backend-codec-v4l2 +Summary: Tizen Codec HAL using generic V4L2 interface +Version: 1.0.0 +Release: 0 +Group: Multimedia/Libraries +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: pkgconfig(hal-rootstrap) +BuildRequires: pkgconfig(hal-api-codec) + +%description +Tizen Codec HAL using generic V4L2 interface. + + +%prep +%setup -q + + +%build +./autogen.sh +%configure \ + --disable-static\ + --libdir=%{_hal_libdir} +make %{?jobs:-j%jobs} + +%install +%make_install +mkdir -p %{buildroot}%{_hal_licensedir}/%{name} +cp LICENSE.APLv2 %{buildroot}%{_hal_licensedir}/%{name} + + +%post +/sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%manifest %{name}.manifest +%{_hal_licensedir}/%{name}/* +%defattr(-,root,root,-) +%{_hal_libdir}/*.so + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a486df4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,20 @@ +ACLOCAL_AMFLAGS='-I m4' + +lib_LTLIBRARIES = libhal-backend-codec.la + +noinst_HEADERS = hal_backend_codec_v4l2_private.h + +libhal_backend_codec_la_SOURCES = hal_backend_codec_v4l2.c + +libhal_backend_codec_la_CFLAGS = \ + -I$(srcdir)/include \ + $(HAL_ROOTSTRAP_CFLAGS) \ + $(HALAPI_CODEC_CFLAGS) + +libhal_backend_codec_la_LIBADD = \ + $(HAL_ROOTSTRAP_LIBS) \ + $(HALAPI_CODEC_LIBS) + +libhal_backend_codec_la_CFLAGS += -fdata-sections -ffunction-sections -Wl,--gc-sections +libhal_backend_codec_la_LDFLAGS = -Wl,--gc-sections -avoid-version + diff --git a/src/hal_backend_codec_v4l2.c b/src/hal_backend_codec_v4l2.c new file mode 100644 index 0000000..61dfc2d --- /dev/null +++ b/src/hal_backend_codec_v4l2.c @@ -0,0 +1,1766 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hal_backend_codec_v4l2_private.h" +#include + +#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, +}; diff --git a/src/hal_backend_codec_v4l2_private.h b/src/hal_backend_codec_v4l2_private.h new file mode 100644 index 0000000..e4b12f8 --- /dev/null +++ b/src/hal_backend_codec_v4l2_private.h @@ -0,0 +1,105 @@ +/* + * hal_backend_codec_v4l2_private.h + * + * 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. + * + */ + +#ifndef __HAL_BACKEND_CODEC_V4L2_PRIVATE_H__ +#define __HAL_BACKEND_CODEC_V4L2_PRIVATE_H__ + +#include +#include +#include +#include +#include + +#define CODEC_HAL_INITIAL_INDEX -1 +#define CODEC_HAL_INITIAL_FD -1 +#define BUFFER_MIN 4 +#define BUFFER_MAX 16 +#define V4L2_PLANES_MAX 4 + + +typedef struct _set_batch_table_s { + int64_t command; + void *value; +} set_batch_table_s; + +typedef struct _codec_tbm_buffer_s { + tbm_bo bo; + tbm_bo_handle bo_handle; + int dmabuf_fd; +} codec_tbm_buffer_s; + +typedef struct _codec_hal_buffer_control_s { + gboolean is_run; + guint32 count; + guint32 queued_count; + GMutex lock; + GCond cond; + codec_format_e format; + enum v4l2_buf_type buf_type; + enum v4l2_memory memory_type; + codec_buffer_s buffers[BUFFER_MAX]; +} codec_buffer_control_s; + +typedef struct _codec_hal_handle_s { + /* tbm */ + tbm_bufmgr bufmgr; + + /* device */ + gint32 device_index; + gint32 device_fd; + + /* buffer */ + codec_buffer_control_s input_buffer_control; + codec_buffer_control_s output_buffer_control; + GThread *output_thread; + gboolean output_thread_run; + + /* message */ + GThread *msg_thread; + hal_codec_message_cb msg_cb; + gpointer msg_cb_data; + gboolean msg_cb_run; + GQueue *msg_list; + GMutex msg_cb_lock; + GCond msg_cb_cond; + + /* settings */ + gint32 bitrate; + + /* etc */ + GMutex lock; + GCond cond; + codec_state_e state; +} hal_codec_handle_s; + +int codec_v4l2_init(codec_type_e type, void **codec_handle); +int codec_v4l2_deinit(void *codec_handle); +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 codec_v4l2_release(void *codec_handle); +int codec_v4l2_start(void *codec_handle, hal_codec_message_cb callback, void *user_data); +int codec_v4l2_stop(void *codec_handle); +int codec_v4l2_flush(void *codec_handle); +int codec_v4l2_push_input_buffer(void *codec_handle, codec_buffer_s *buffer); +int codec_v4l2_release_output_buffer(void *codec_handle, int buffer_index); +int codec_v4l2_get_state(void *codec_handle, codec_state_e *state); +int codec_v4l2_set_command(void *codec_handle, int64_t command, void *value); +int codec_v4l2_get_command(void *codec_handle, int64_t command, void **value); +int codec_v4l2_set_batch_command(void *codec_handle, codec_batch_command_control_s *batch_command, int64_t *error_command); + +#endif /* __HAL_BACKEND_CODEC_V4L2_PRIVATE_H__ */