linuxOS_AP06/external/uvc_app/uvc/uvc_control.c
2025-06-03 12:28:32 +08:00

315 lines
8.4 KiB
C

/*
* Copyright (C) 2019 Rockchip Electronics Co., Ltd.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL), available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <pthread.h>
#include "uvc_control.h"
#include "uvc_encode.h"
#include "uvc_video.h"
#include "uevent.h"
#define SYS_ISP_NAME "isp"
#define SYS_CIF_NAME "cif"
#define UVC_STREAMING_INTF_PATH "/sys/kernel/config/usb_gadget/rockchip/functions/uvc.gs6/streaming/bInterfaceNumber"
struct uvc_ctrl {
int id;
int width;
int height;
int fps;
};
static struct uvc_ctrl uvc_ctrl[2];
struct uvc_encode uvc_enc;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int uvc_streaming_intf = -1;
static pthread_t run_id = 0;
static bool run_flag = true;
static pthread_mutex_t run_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t run_cond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t video_added = PTHREAD_COND_INITIALIZER;
static uvc_open_camera_callback uvc_open_camera_cb = NULL;
static uvc_close_camera_callback uvc_close_camera_cb = NULL;
void register_uvc_open_camera(uvc_open_camera_callback cb)
{
uvc_open_camera_cb = cb;
}
void register_uvc_close_camera(uvc_close_camera_callback cb)
{
uvc_close_camera_cb = cb;
}
static bool is_uvc_video(void *buf)
{
if (strstr(buf, "usb") || strstr(buf, "gadget"))
return true;
else
return false;
}
static void query_uvc_streaming_intf(void)
{
int fd;
fd = open(UVC_STREAMING_INTF_PATH, O_RDONLY);
if (fd >= 0) {
char intf[32] = {0};
read(fd, intf, sizeof(intf) - 1);
uvc_streaming_intf = atoi(intf);
printf("uvc_streaming_intf = %d\n", uvc_streaming_intf);
close(fd);
} else {
printf("open %s failed!\n", UVC_STREAMING_INTF_PATH);
}
}
int get_uvc_streaming_intf(void)
{
return uvc_streaming_intf;
}
int get_max_video_number() {
const char *dir_path = "/sys/class/video4linux/";
struct dirent *entry;
int max_video_number = -1;
DIR *dir = opendir(dir_path);
if (dir == NULL) {
printf("open %s failed.\n", dir_path);
return -1;
}
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, "video", 5) == 0) {
int video_number = atoi(entry->d_name + 5);
if (video_number > max_video_number) {
max_video_number = video_number;
}
}
}
closedir(dir);
return max_video_number;
}
int check_uvc_video_id(void)
{
FILE *fp = NULL;
char buf[1024];
int i;
char cmd[128];
int max = -1;
int uvc_cnt = 1;
int find_cnt = 0;
if (getenv("UVC_CNT"))
uvc_cnt = atoi(getenv("UVC_CNT"));
memset(&uvc_ctrl, 0, sizeof(uvc_ctrl));
uvc_ctrl[0].id = -1;
uvc_ctrl[1].id = -1;
max = get_max_video_number();
if (max < 0)
return -1;
for (i = max; i >= 0; i--) {
snprintf(cmd, sizeof(cmd), "/sys/class/video4linux/video%d/name", i);
if (access(cmd, F_OK))
continue;
snprintf(cmd, sizeof(cmd), "cat /sys/class/video4linux/video%d/name", i);
fp = popen(cmd, "r");
if (fp) {
if (fgets(buf, sizeof(buf), fp)) {
if (is_uvc_video(buf)) {
find_cnt++;
if (uvc_ctrl[1].id < 0)
uvc_ctrl[1].id = i;
else if (uvc_ctrl[0].id < 0)
uvc_ctrl[0].id = i;
}
}
pclose(fp);
}
if (find_cnt >= uvc_cnt)
break;
}
if (uvc_ctrl[0].id < 0 && uvc_ctrl[1].id < 0) {
printf("Please configure uvc...\n");
return -1;
}
if (uvc_ctrl[0].id < 0 && uvc_ctrl[1].id >= 0) {
uvc_ctrl[0].id = uvc_ctrl[1].id;
uvc_ctrl[1].id = -1;
}
query_uvc_streaming_intf();
return 0;
}
void add_uvc_video()
{
if (uvc_ctrl[0].id >= 0)
uvc_video_id_add(uvc_ctrl[0].id);
if (uvc_ctrl[1].id >= 0)
uvc_video_id_add(uvc_ctrl[1].id);
}
void uvc_control_init(int width, int height, int fcc, int fps)
{
pthread_mutex_lock(&lock);
memset(&uvc_enc, 0, sizeof(uvc_enc));
if (uvc_encode_init(&uvc_enc, width, height, fcc)) {
printf("%s fail!\n", __func__);
abort();
}
pthread_mutex_unlock(&lock);
if (uvc_open_camera_cb)
uvc_open_camera_cb(width, height, fcc, fps);
}
void uvc_control_exit()
{
if (uvc_close_camera_cb)
uvc_close_camera_cb();
pthread_mutex_lock(&lock);
uvc_encode_exit(&uvc_enc);
memset(&uvc_enc, 0, sizeof(uvc_enc));
pthread_mutex_unlock(&lock);
}
void uvc_read_camera_buffer(void *cam_buf, int cam_fd, size_t cam_size,
void* extra_data, size_t extra_size)
{
pthread_mutex_lock(&lock);
if (cam_size <= uvc_enc.width * uvc_enc.height * 2) {
uvc_enc.video_id = uvc_video_id_get(0);
uvc_enc.extra_data = extra_data;
uvc_enc.extra_size = extra_size;
uvc_encode_process(&uvc_enc, cam_buf, cam_fd, cam_size);
} else if (uvc_enc.width > 0 && uvc_enc.height > 0) {
printf("%s: cam_size = %u, uvc_enc.width = %d, uvc_enc.height = %d\n",
__func__, cam_size, uvc_enc.width, uvc_enc.height);
}
pthread_mutex_unlock(&lock);
}
static void uvc_control_wait(void)
{
pthread_mutex_lock(&run_mutex);
if (run_flag)
pthread_cond_wait(&run_cond, &run_mutex);
pthread_mutex_unlock(&run_mutex);
}
void uvc_control_signal(void)
{
pthread_mutex_lock(&run_mutex);
pthread_cond_signal(&run_cond);
pthread_mutex_unlock(&run_mutex);
}
void uvc_added_signal(void)
{
pthread_mutex_lock(&run_mutex);
pthread_cond_signal(&video_added);
pthread_mutex_unlock(&run_mutex);
}
static void uvc_added_wait(void)
{
pthread_mutex_lock(&run_mutex);
if (run_flag)
pthread_cond_wait(&video_added, &run_mutex);
pthread_mutex_unlock(&run_mutex);
}
static void *uvc_control_thread(void *arg)
{
uint32_t flag = *(uint32_t *)arg;
while (run_flag) {
if (!check_uvc_video_id()) {
add_uvc_video();
/* Ensure main was waiting for this signal */
usleep(500);
uvc_added_signal();
if (flag & UVC_CONTROL_LOOP_ONCE)
break;
uvc_control_wait();
uvc_video_id_exit_all();
} else {
uvc_control_wait();
}
}
pthread_exit(NULL);
}
int uvc_control_run(uint32_t flags)
{
if (flags & UVC_CONTROL_CHECK_STRAIGHT) {
if (!check_uvc_video_id())
add_uvc_video();
else
return -1;
} else {
uevent_monitor_run(flags);
if (pthread_create(&run_id, NULL, uvc_control_thread, &flags)) {
printf("%s: pthread_create failed!\n", __func__);
return -1;
}
uvc_added_wait();
}
return 0;
}
void uvc_control_join(uint32_t flags)
{
if (flags & UVC_CONTROL_CHECK_STRAIGHT) {
uvc_video_id_exit_all();
} else {
run_flag = false;
uvc_control_signal();
pthread_join(run_id, NULL);
if (flags & UVC_CONTROL_LOOP_ONCE);
uvc_video_id_exit_all();
}
}