/* * 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 "uvc_video.h" #include "uvc-gadget.h" #include "yuv.h" #include "mpi_enc.h" #include "uvc_log.h" #include #include #include #include #include #include #include #include #include #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER #include "drm.h" #endif #if UVC_DYNAMIC_DEBUG_FPS struct uvc_debug_info_def uvc_debug_info; #endif struct uvc_buffer_list { std::list buffer_list; pthread_mutex_t mutex; }; struct video_uvc { struct uvc_buffer_list write; struct uvc_buffer_list read; struct uvc_buffer_list all; pthread_t id; bool run; int video_id; }; static std::list lst_v; static pthread_mutex_t mtx_v = PTHREAD_MUTEX_INITIALIZER; static struct uvc_buffer *uvc_buffer_create(int width, int height, struct uvc_video *v) { struct uvc_buffer *buffer = NULL; buffer = (struct uvc_buffer *)calloc(1, sizeof(struct uvc_buffer)); if (!buffer) return NULL; buffer->width = width; buffer->height = height; buffer->size = buffer->width * buffer->height * 2; #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER unsigned int drm_flag = 0; buffer->drm_buf_size = buffer->size; if (v->drm_fd == -1) v->drm_fd = drm_open(); if (v->drm_fd < 0) return NULL; if (v->uvc_user.fcc == V4L2_PIX_FMT_YUYV || v->uvc_user.fcc == V4L2_PIX_FMT_NV12) drm_flag = ROCKCHIP_BO_CACHABLE; int ret = drm_alloc(v->drm_fd, buffer->drm_buf_size, 16, &buffer->handle, drm_flag); if (ret) { LOG_ERROR("drm_alloc fail\n"); return NULL; } ret = drm_handle_to_fd(v->drm_fd, buffer->handle, &buffer->fd, DRM_CLOEXEC | DRM_RDWR); if (ret) { LOG_ERROR("drm_handle_to_fd fail\n"); return NULL; } buffer->buffer = (void *)drm_map_buffer(v->drm_fd, buffer->handle, buffer->drm_buf_size); LOG_DEBUG("v->drm_fd=%d,buffer->handle=%d,size=%d\n", v->drm_fd, buffer->handle, buffer->drm_buf_size); #else buffer->buffer = calloc(1, buffer->size); #endif if (!buffer->buffer) { free(buffer); return NULL; } buffer->total_size = buffer->size; buffer->video_id = v->id; return buffer; } static void uvc_buffer_push_back(struct uvc_buffer_list *uvc_buffer, struct uvc_buffer *buffer) { pthread_mutex_lock(&uvc_buffer->mutex); uvc_buffer->buffer_list.push_back(buffer); pthread_mutex_unlock(&uvc_buffer->mutex); } static struct uvc_buffer *uvc_buffer_pop_front( struct uvc_buffer_list *uvc_buffer) { struct uvc_buffer *buffer = NULL; pthread_mutex_lock(&uvc_buffer->mutex); if (!uvc_buffer->buffer_list.empty()) { buffer = uvc_buffer->buffer_list.front(); uvc_buffer->buffer_list.pop_front(); } pthread_mutex_unlock(&uvc_buffer->mutex); return buffer; } static struct uvc_buffer *uvc_buffer_front(struct uvc_buffer_list *uvc_buffer) { struct uvc_buffer *buffer = NULL; pthread_mutex_lock(&uvc_buffer->mutex); if (!uvc_buffer->buffer_list.empty()) buffer = uvc_buffer->buffer_list.front(); else //{printf("buff null\n"); buffer = NULL; // } pthread_mutex_unlock(&uvc_buffer->mutex); return buffer; } #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER static void uvc_drm_buffer_destroy(struct uvc_video *v, struct uvc_buffer_list *uvc_buffer) { struct uvc_buffer *buffer = NULL; pthread_mutex_lock(&uvc_buffer->mutex); while (!uvc_buffer->buffer_list.empty()) { buffer = uvc_buffer->buffer_list.front(); LOG_INFO("uvc_drm_buffer_destroy buffer->handle=%d size=%d\n", buffer->handle, buffer->drm_buf_size); drm_unmap_buffer(buffer->buffer, buffer->drm_buf_size); close(buffer->fd); drm_free(v->drm_fd, buffer->handle); free(buffer); uvc_buffer->buffer_list.pop_front(); } pthread_mutex_unlock(&uvc_buffer->mutex); pthread_mutex_destroy(&uvc_buffer->mutex); } #endif static void uvc_buffer_destroy(struct uvc_buffer_list *uvc_buffer) { struct uvc_buffer *buffer = NULL; pthread_mutex_lock(&uvc_buffer->mutex); while (!uvc_buffer->buffer_list.empty()) { buffer = uvc_buffer->buffer_list.front(); free(buffer->buffer); free(buffer); uvc_buffer->buffer_list.pop_front(); } pthread_mutex_unlock(&uvc_buffer->mutex); pthread_mutex_destroy(&uvc_buffer->mutex); } static void uvc_buffer_clear(struct uvc_buffer_list *uvc_buffer) { pthread_mutex_lock(&uvc_buffer->mutex); uvc_buffer->buffer_list.clear(); pthread_mutex_unlock(&uvc_buffer->mutex); } static void *uvc_gadget_pthread(void *arg) { struct uvc_function_config *fc = (struct uvc_function_config *)arg; int *id = (int *)arg; prctl(PR_SET_NAME, "uvc_gadget_pthread", 0, 0, 0); uvc_gadget_main(fc); uvc_set_user_run_state(true, fc->video); pthread_exit(NULL); } int uvc_gadget_pthread_create(struct uvc_function_config *fc) { pthread_t *pid = NULL; int id = fc->video; uvc_memset_uvc_user(id); if ((pid = uvc_video_get_uvc_pid(id))) { if (pthread_create(pid, NULL, uvc_gadget_pthread, fc)) { LOG_ERROR("create uvc_gadget_pthread fail!\n"); return -1; } } return 0; } static int _uvc_video_id_check(int id) { int ret = 0; if (!lst_v.empty()) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { ret = -1; break; } } } return ret; } int uvc_video_id_check(int id) { int ret = 0; pthread_mutex_lock(&mtx_v); ret = _uvc_video_id_check(id); pthread_mutex_unlock(&mtx_v); return ret; } int uvc_video_id_add(struct uvc_function_config *fc) { int ret = 0; int id = fc->video; LOG_INFO("add uvc video id: %d\n", id); pthread_mutex_lock(&mtx_v); if (!_uvc_video_id_check(id)) { struct uvc_video *v = (struct uvc_video *)calloc(1, sizeof(struct uvc_video)); if (v) { v->id = id; lst_v.push_back(v); pthread_mutex_unlock(&mtx_v); uvc_gadget_pthread_create(fc); pthread_mutex_lock(&mtx_v); pthread_mutex_init(&v->buffer_mutex, NULL); pthread_mutex_init(&v->user_mutex, NULL); ret = 0; } else { LOG_ERROR("%s: %d: memory alloc fail.\n", __func__, __LINE__); ret = -1; } } else { LOG_WARN("%s: %d: %d already exist.\n", __func__, __LINE__, id); ret = -1; } pthread_mutex_unlock(&mtx_v); return ret; } void uvc_video_id_remove(int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { pthread_mutex_destroy(&l->buffer_mutex); pthread_mutex_destroy(&l->user_mutex); free(l); lst_v.erase(i); break; } } } pthread_mutex_unlock(&mtx_v); } int uvc_video_id_get(unsigned int seq) { int ret = -1; pthread_mutex_lock(&mtx_v); if (!lst_v.empty()) { unsigned int cnt = 0; for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { if (cnt++ == seq) { struct uvc_video *l = *i; ret = l->id; break; } } } pthread_mutex_unlock(&mtx_v); return ret; } static void uvc_gadget_pthread_exit(int id); static int uvc_video_id_exit(int id) { if (uvc_video_id_check(id)) { uvc_gadget_pthread_exit(id); uvc_video_id_remove(id); return 0; } return -1; } static int _uvc_video_id_exit_all() { int ret = -1; pthread_mutex_lock(&mtx_v); if (!lst_v.empty()) { struct uvc_video *l = lst_v.front(); pthread_mutex_unlock(&mtx_v); uvc_video_id_exit(l->id); pthread_mutex_lock(&mtx_v); ret = 0; } pthread_mutex_unlock(&mtx_v); return ret; } void uvc_video_id_exit_all() { while (!_uvc_video_id_exit_all()) continue; } static void _uvc_video_set_uvc_process(struct uvc_video *v, bool state) { v->uvc_process = state; } void uvc_video_set_uvc_process(int id, bool state) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_video_set_uvc_process(l, state); break; } } } pthread_mutex_unlock(&mtx_v); } static bool _uvc_video_get_uvc_process(struct uvc_video *v) { return v->uvc_process; } bool uvc_video_get_uvc_process(int id) { bool state = false; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { state = _uvc_video_get_uvc_process(l); break; } } } pthread_mutex_unlock(&mtx_v); return state; } pthread_t *uvc_video_get_uvc_pid(int id) { pthread_t *tid = NULL; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { tid = &l->uvc_pid; break; } } } pthread_mutex_unlock(&mtx_v); return tid; } void uvc_video_join_uvc_pid(int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { if (l->uvc_pid) { pthread_mutex_unlock(&mtx_v); pthread_join(l->uvc_pid, NULL); l->uvc_pid = 0; pthread_mutex_lock(&mtx_v); } } } } pthread_mutex_unlock(&mtx_v); } static void uvc_gadget_pthread_exit(int id) { while (!uvc_get_user_run_state(id)) pthread_yield(); uvc_set_user_run_state(false, id); uvc_video_join_uvc_pid(id); } static void _uvc_get_user_resolution(struct uvc_video *v, int *width, int *height); static int _uvc_buffer_init(struct uvc_video *v) { int i = 0; int ret = 0; struct uvc_buffer *buffer = NULL; int width, height; _uvc_get_user_resolution(v, &width, &height); pthread_mutex_lock(&v->buffer_mutex); v->uvc = new video_uvc(); if (!v->uvc) { ret = -1; goto exit; } v->uvc->id = 0; v->uvc->video_id = v->id; v->uvc->run = 1; v->buffer_s = NULL; pthread_mutex_init(&v->uvc->write.mutex, NULL); pthread_mutex_init(&v->uvc->read.mutex, NULL); uvc_buffer_clear(&v->uvc->write); uvc_buffer_clear(&v->uvc->read); uvc_buffer_clear(&v->uvc->all); LOG_DEBUG("UVC_BUFFER_NUM = %d\n", UVC_BUFFER_NUM); v->drm_fd = -1; for (i = 0; i < UVC_BUFFER_NUM; i++) { buffer = uvc_buffer_create(width, height, v); if (!buffer) { ret = -1; goto exit; } #if (UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF) uvc_buffer_push_back(&v->uvc->all, buffer); #endif uvc_buffer_push_back(&v->uvc->write, buffer); } _uvc_video_set_uvc_process(v, true); v->can_exit = true; exit: pthread_mutex_unlock(&v->buffer_mutex); return ret; } int uvc_buffer_init(int id) { int ret = -1; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { ret = _uvc_buffer_init(l); break; } } } pthread_mutex_unlock(&mtx_v); return ret; } static void _uvc_buffer_deinit(struct uvc_video *v) { pthread_mutex_lock(&v->buffer_mutex); if (v->uvc) { v->uvc->run = 0; _uvc_video_set_uvc_process(v, false); if (v->buffer_s) uvc_buffer_push_back(&v->uvc->write, v->buffer_s); #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER #if UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF LOG_DEBUG("_uvc_buffer_deinit all\n"); uvc_drm_buffer_destroy(v, &v->uvc->all); #else LOG_DEBUG("_uvc_buffer_deinit write\n"); uvc_drm_buffer_destroy(v, &v->uvc->write); LOG_DEBUG("_uvc_buffer_deinit read\n"); uvc_drm_buffer_destroy(v, &v->uvc->read); #endif drm_close(v->drm_fd); LOG_DEBUG("_uvc_buffer_deinit drm_close drm_fd:%d\n", v->drm_fd); v->drm_fd = -1; #else uvc_buffer_destroy(&v->uvc->write); uvc_buffer_destroy(&v->uvc->read); #endif delete v->uvc; v->uvc = NULL; } pthread_mutex_unlock(&v->buffer_mutex); } void uvc_buffer_deinit(int id) { #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER struct uvc_video *l; if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { l = *i; if (id == l->id) { break; } } } int wait_cnt = 0; while (l->uvc_process && !l->can_exit) { wait_cnt++; usleep(1000); if (wait_cnt > 30) { LOG_INFO("uvc_buffer_deinit timeout 30ms,force exit!\n"); break; } } pthread_mutex_lock(&mtx_v); _uvc_buffer_deinit(l); pthread_mutex_unlock(&mtx_v); #else pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_buffer_deinit(l); break; } } } pthread_mutex_unlock(&mtx_v); #endif } static bool _uvc_buffer_write_enable(struct uvc_video *v) { bool ret = false; if (pthread_mutex_trylock(&v->buffer_mutex)) return ret; if (v->uvc && uvc_buffer_front(&v->uvc->write)) ret = true; pthread_mutex_unlock(&v->buffer_mutex); return ret; } bool uvc_buffer_write_enable(int id) { bool ret = false; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { ret = _uvc_buffer_write_enable(l); break; } } } pthread_mutex_unlock(&mtx_v); return ret; } static bool _uvc_buffer_read_enable(struct uvc_video *v) { bool ret = false; if (pthread_mutex_trylock(&v->buffer_mutex)) return ret; if (v->uvc && uvc_buffer_front(&v->uvc->read)) ret = true; pthread_mutex_unlock(&v->buffer_mutex); return ret; } bool uvc_buffer_read_enable(int id) { bool ret = false; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { ret = _uvc_buffer_read_enable(l); break; } } } pthread_mutex_unlock(&mtx_v); return ret; } static bool _uvc_mpp_buffer_write_enable(struct uvc_video *v) { bool ret = false; //if (pthread_mutex_trylock(&v->buffer_mutex)) // return ret; if (v->uvc && uvc_buffer_front(&v->uvc->write)) ret = true; // pthread_mutex_unlock(&v->buffer_mutex); return ret; } bool uvc_mpp_buffer_write_enable(int id) { bool ret = false; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { ret = _uvc_buffer_write_enable(l); break; } } } pthread_mutex_unlock(&mtx_v); return ret; } #define EX_MAX_LEN 65535 #define EX_DATA_LEN (EX_MAX_LEN - 2) static void _uvc_buffer_write(struct uvc_video *v, unsigned short stamp, void *extra_data, size_t extra_size, void *data, size_t size, unsigned int fcc) { const size_t cnt = extra_size / (EX_DATA_LEN + 1) + 1; pthread_mutex_lock(&v->buffer_mutex); if (v->uvc && data) { struct uvc_buffer *buffer = uvc_buffer_pop_front(&v->uvc->write); if (buffer && buffer->buffer) { if (buffer->total_size >= extra_size + size) { switch (fcc) { case V4L2_PIX_FMT_YUYV: #if YUYV_AS_RAW #ifdef USE_RK_MODULE raw16_to_raw8(buffer->width, buffer->height, data, buffer->buffer); #else memcpy(buffer->buffer, data, size); #endif #else NV12_to_YUYV(buffer->width, buffer->height, data, buffer->buffer); #endif break; case V4L2_PIX_FMT_MJPEG: if (extra_data && buffer->total_size >= extra_size + size + 4 * cnt) { size_t index = 4;// FF D8 FF E0 size_t len = *((unsigned char *)data + index); len = len * 256 + *((unsigned char *)data + index + 1); index = index + len; memcpy(buffer->buffer, data, index); size_t ind = index; for (size_t i = 1; i < cnt; i++) { memset((char *)buffer->buffer + ind, 0xFF, 1); ind++; memset((char *)buffer->buffer + ind, 0xE2, 1); ind++; memset((char *)buffer->buffer + ind, EX_MAX_LEN / 256, 1); ind++; memset((char *)buffer->buffer + ind, EX_MAX_LEN % 256, 1); ind++; memcpy((char *)buffer->buffer + ind, (char *)extra_data + (i - 1) * EX_DATA_LEN, EX_DATA_LEN); ind += EX_DATA_LEN; } memset((char *)buffer->buffer + ind, 0xFF, 1); ind ++; memset((char *)buffer->buffer + ind, 0xE2, 1); ind++; memset((char *)buffer->buffer + ind, (2 + extra_size - EX_DATA_LEN * (cnt - 1)) / 256, 1); ind++; memset((char *)buffer->buffer + ind, (2 + extra_size - EX_DATA_LEN * (cnt - 1)) % 256, 1); ind++; memcpy((char *)buffer->buffer + ind, (char *)extra_data + EX_DATA_LEN * (cnt - 1), extra_size - EX_DATA_LEN * (cnt - 1)); ind += extra_size - EX_DATA_LEN * (cnt - 1); memcpy((char *)buffer->buffer + ind, (char *)data + index, size - index); extra_size += 4 * cnt; } else { memcpy(buffer->buffer, data, size); } //memcpy((char*)buffer->buffer + size, &stamp, sizeof(stamp)); //size += sizeof(stamp); break; case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_H265: if (extra_data && extra_size > 0) memcpy(buffer->buffer, extra_data, extra_size); if (extra_size >= 0) memcpy((char *)buffer->buffer + extra_size, data, size); break; } buffer->size = extra_size + size; uvc_buffer_push_back(&v->uvc->read, buffer); } else { uvc_buffer_push_back(&v->uvc->write, buffer); } } } pthread_mutex_unlock(&v->buffer_mutex); } void uvc_buffer_write(unsigned short stamp, void *extra_data, size_t extra_size, void *data, size_t size, unsigned int fcc, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_buffer_write(l, stamp, extra_data, extra_size, data, size, fcc); break; } } } pthread_mutex_unlock(&mtx_v); } #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER struct uvc_buffer *uvc_buffer_write_get(int id) { struct uvc_buffer *buffer = NULL; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { l->can_exit = false; pthread_mutex_lock(&l->buffer_mutex); buffer = uvc_buffer_pop_front(&l->uvc->write); pthread_mutex_unlock(&l->buffer_mutex); break; } } } pthread_mutex_unlock(&mtx_v); return buffer; } struct uvc_buffer *uvc_buffer_write_get_nolock(int id) { struct uvc_buffer *buffer = NULL; if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { l->can_exit = false; pthread_mutex_lock(&l->buffer_mutex); buffer = uvc_buffer_pop_front(&l->uvc->write); //if (l->) pthread_mutex_unlock(&l->buffer_mutex); break; } } } return buffer; } void uvc_buffer_read_set_nolock(int id, struct uvc_buffer *buf) { #if 1 //uvc_ipc_test_write(); #endif if (buf->abandon) { LOG_INFO("uvc_buffer_read_set_nolock abandon\n"); } else if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { l->can_exit = true; pthread_mutex_lock(&l->buffer_mutex); uvc_buffer_push_back(&l->uvc->read, buf); pthread_mutex_unlock(&l->buffer_mutex); // l->shm_control->sendUVCBuffer(); break; } } } } void uvc_buffer_read_set(int id, struct uvc_buffer *buf) { pthread_mutex_lock(&mtx_v); if (buf->abandon) { LOG_INFO("uvc_buffer_read_set abandon\n"); } else if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { l->can_exit = true; pthread_mutex_lock(&l->buffer_mutex); uvc_buffer_push_back(&l->uvc->read, buf); pthread_mutex_unlock(&l->buffer_mutex); break; } } } pthread_mutex_unlock(&mtx_v); } void uvc_buffer_write_set(int id, struct uvc_buffer *buf) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { // l->can_exit = true; pthread_mutex_lock(&l->buffer_mutex); uvc_buffer_push_back(&l->uvc->write, buf); pthread_mutex_unlock(&l->buffer_mutex); break; } } } pthread_mutex_unlock(&mtx_v); } void uvc_buffer_all_set(int id, struct uvc_buffer *buf) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { // l->can_exit = true; pthread_mutex_lock(&l->buffer_mutex); uvc_buffer_push_back(&l->uvc->all, buf); pthread_mutex_unlock(&l->buffer_mutex); break; } } } pthread_mutex_unlock(&mtx_v); } #endif static void _uvc_set_user_resolution(struct uvc_video *v, int width, int height) { pthread_mutex_lock(&v->user_mutex); v->uvc_user.width = width; v->uvc_user.height = height; LOG_DEBUG("uvc_user.width = %u, uvc_user.height = %u\n", v->uvc_user.width, v->uvc_user.height); pthread_mutex_unlock(&v->user_mutex); } void uvc_set_user_resolution(int width, int height, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_set_user_resolution(l, width, height); break; } } } pthread_mutex_unlock(&mtx_v); } static void _uvc_get_user_resolution(struct uvc_video *v, int *width, int *height) { pthread_mutex_lock(&v->user_mutex); *width = v->uvc_user.width; *height = v->uvc_user.height; pthread_mutex_unlock(&v->user_mutex); } void uvc_get_user_resolution(int *width, int *height, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_get_user_resolution(l , width, height); break; } } } pthread_mutex_unlock(&mtx_v); } static bool _uvc_get_user_run_state(struct uvc_video *v) { bool ret; pthread_mutex_lock(&v->user_mutex); ret = v->uvc_user.run; pthread_mutex_unlock(&v->user_mutex); return ret; } bool uvc_get_user_run_state(int id) { bool state = false; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { state = _uvc_get_user_run_state(l); break; } } } pthread_mutex_unlock(&mtx_v); return state; } #if UVC_SEND_BUF_WHEN_ENC_READY #ifdef RK_MPP_USE_UVC_VIDEO_BUFFER int uvc_video_qbuf_index(struct uvc_device *dev, struct uvc_buffer *send_buf, int index, int len) { #if 0 LOG_INFO("uvc_video_qbuf_index enter send_buf=%p,index:%d len=%d,dev->nbufs=%d\n", send_buf, index, len, dev->nbufs); #endif unsigned int i; int ret; struct v4l2_requestbuffers req; memset(&req, 0, sizeof (req)); req.count = dev->nbufs; req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; req.memory = V4L2_MEMORY_DMABUF; if (index == 0) { uvc_video_reqbufs(dev, dev->nbufs); uvc_video_stream(dev, 1); dev->vbuf_info = (struct v4l2_buffer_info *)calloc(dev->nbufs, sizeof(struct v4l2_buffer_info)); } /* UVC standalone setup. */ if (dev->run_standalone) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = req.type; buf.index = index; buf.memory = req.memory; struct uvc_buffer *uvc_buf = send_buf;//uvc_buffer_write_get(dev->video_id); buf.m.fd = uvc_buf->fd; buf.length = uvc_buf->total_size; buf.bytesused = len; dev->ubuf.bytesused = len; dev->vbuf_info[index].fd = buf.m.fd; dev->vbuf_info[index].uvc_buf = uvc_buf; dev->vbuf_info[index].index = index; if (ioctl(dev->uvc_fd, VIDIOC_QBUF, &buf) < 0) { LOG_ERROR("%s ioctl(VIDIOC_QBUF): %m,buf.m.fd=%d,buf.length=%d\n", dev, buf.m.fd, buf.length); return -1; } else { LOG_INFO("V4L2: %u buffers allocated index:%d\n", dev->nbufs, index); } } return 0; } #endif #endif static void _uvc_set_user_run_state(struct uvc_video *v, bool state) { pthread_mutex_lock(&v->user_mutex); v->uvc_user.run = state; pthread_mutex_unlock(&v->user_mutex); } void uvc_set_user_run_state(bool state, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_set_user_run_state(l, state); break; } } } pthread_mutex_unlock(&mtx_v); } static void _uvc_set_user_fcc(struct uvc_video *v, unsigned int fcc) { v->uvc_user.fcc = fcc; } void uvc_set_user_fcc(unsigned int fcc, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_set_user_fcc(l, fcc); break; } } } pthread_mutex_unlock(&mtx_v); } static unsigned int _uvc_get_user_fcc(struct uvc_video *v) { return v->uvc_user.fcc; } unsigned int uvc_get_user_fcc(int id) { unsigned int fcc = 0; pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { fcc = _uvc_get_user_fcc(l); break; } } } pthread_mutex_unlock(&mtx_v); return fcc; } static void _uvc_memset_uvc_user(struct uvc_video *v) { memset(&v->uvc_user, 0, sizeof(struct uvc_user)); } void uvc_memset_uvc_user(int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_memset_uvc_user(l); break; } } } pthread_mutex_unlock(&mtx_v); } static bool _uvc_buffer_check(struct uvc_video *v, struct uvc_buffer *buffer) { int width = 0, height = 0; _uvc_get_user_resolution(v, &width, &height); if (buffer->width == width && buffer->height == height) return true; else return false; } static void uvc_delay_time_calcu_before_get(struct uvc_device *dev, struct uvc_video *v) { #if UVC_DYNAMIC_DEBUG_USE_TIME if (!access(UVC_DYNAMIC_DEBUG_USE_TIME_CHECK, 0)) { int32_t use_time_us, now_time_us; struct timespec now_tm = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &now_tm); now_time_us = now_tm.tv_sec * 1000000LL + now_tm.tv_nsec / 1000; // us use_time_us = now_time_us - v->last_pts; //usb send ok and calcu the latency time use last pts LOG_INFO("isp->mpp->usb_ready->usb_send_ok seq:%d latency time:%d us, %d ms\n", v->last_seq, use_time_us, use_time_us / 1000); } if (dev->usb_state == USB_STATE_FIRST_GET_READY) { int32_t use_time_us; struct timespec now_tm = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &now_tm); dev->usb_state = USB_STATE_FIRST_GET_OK; dev->first_usb_get_ready_pts = now_tm.tv_sec * 1000000LL + now_tm.tv_nsec / 1000; use_time_us = dev->first_usb_get_ready_pts - dev->stream_on_pts; LOG_INFO("steamon->get_ready latency time:%d us, %d ms\n", use_time_us, use_time_us / 1000); } else if (dev->usb_state == USB_STATE_FIRST_SEND_OK) { struct timespec now_tm = {0, 0}; int32_t use_time_us; clock_gettime(CLOCK_MONOTONIC, &now_tm); dev->first_usb_send_ok_pts = now_tm.tv_sec * 1000000LL + now_tm.tv_nsec / 1000; dev->usb_state = USB_STATE_NORMAL_RUN; use_time_us = dev->first_usb_send_ok_pts - dev->stream_on_pts; LOG_INFO("steamon->get_ready->get_ok->send_ok latency time:%d us, %d ms\n", use_time_us, use_time_us / 1000); use_time_us = dev->first_usb_send_ok_pts - v->last_pts; LOG_INFO("isp->mpp->usb_ready->usb_send_ok seq:%d latency time:%d us, %d ms\n", v->last_seq, use_time_us, use_time_us / 1000); } #endif } static void uvc_delay_time_calcu_after_get(struct uvc_device *dev, struct uvc_video *v, struct uvc_buffer *buffer) { #if UVC_DYNAMIC_DEBUG_USE_TIME if (dev->usb_state == USB_STATE_FIRST_GET_OK) { struct timespec now_tm = {0, 0}; int32_t use_time_us; clock_gettime(CLOCK_MONOTONIC, &now_tm); dev->first_usb_get_ok_pts = now_tm.tv_sec * 1000000LL + now_tm.tv_nsec / 1000; dev->usb_state = USB_STATE_FIRST_SEND_OK; use_time_us = dev->first_usb_get_ok_pts - dev->stream_on_pts; LOG_INFO("steamon->get_ready->get_ok seq:%d time:%d us, %d ms\n", buffer->seq, use_time_us, use_time_us / 1000); } v->last_pts = buffer->pts; v->now_pts = buffer->pts; v->last_seq = buffer->seq; if (!access(UVC_DYNAMIC_DEBUG_USE_TIME_CHECK, 0)) { int32_t use_time_us, now_time_us; struct timespec now_tm = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &now_tm); now_time_us = now_tm.tv_sec * 1000000LL + now_tm.tv_nsec / 1000; // us use_time_us = now_time_us - v->now_pts; LOG_INFO("isp->mpp->usb_ready seq:%d latency time:%d us, %d ms\n", buffer->seq, use_time_us, use_time_us / 1000); } #endif } static int drop_frame_cnt = 0; struct uvc_buffer *uvc_get_enc_data(struct uvc_device *dev, struct uvc_video *v, bool init) { struct uvc_buffer *buffer = NULL; v->idle_cnt = 0; int time_out = 60; if (init) time_out = 30; while (!(buffer = uvc_buffer_front(&v->uvc->read)) && _uvc_get_user_run_state(v)) { pthread_mutex_unlock(&mtx_v); usleep(1000); v->idle_cnt++; pthread_mutex_lock(&mtx_v); if (v->idle_cnt > time_out) { if(!(buffer = uvc_buffer_front(&v->uvc->read)) && _uvc_get_user_run_state(v)) // onece more check it. { if (init == false) { if(drop_frame_cnt == 0) { LOG_DEBUG("fill buf timeout %d ms, abandon this write buf %d\n",time_out, v->buffer_s->fd); } //v->buffer_s->abandon = true; //uvc_buffer_pop_front(&v->uvc->write); } else { if(drop_frame_cnt == 0) { LOG_DEBUG("init:%d,fill buf timeout %d ms\n", init, time_out); } } drop_frame_cnt++; if(drop_frame_cnt > 200) { if (access("/tmp/uvc_camera_no_buf", 0)) { LOG_INFO("it's already 200 frames buf no to get from CAMERA,tell watchdog now\n"); system("touch /tmp/uvc_camera_no_buf &"); } drop_frame_cnt = 0; } } break; } } // LOG_INFO("init:%d,uvc_get_enc_data: %p\n", init, buffer); #if UVC_SEND_BUF_WHEN_ENC_READY if (init && buffer) { uvc_video_qbuf_index(dev, buffer, dev->get_buf_count, buffer->size); } #endif if (buffer) uvc_delay_time_calcu_after_get(dev, v, buffer); return buffer; } #if UVC_SEND_BUF_WHEN_ENC_READY struct uvc_buffer *uvc_user_fill_buffer_init(struct uvc_device *dev) { pthread_mutex_lock(&mtx_v); struct uvc_buffer *buffer = NULL; if (_uvc_video_id_check(dev->video_id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (dev->video_id == l->id) { uvc_delay_time_calcu_before_get(dev, l); if(uvc_get_enc_data(dev, l, true)) { buffer = uvc_buffer_pop_front(&l->uvc->read); } break; } } } pthread_mutex_unlock(&mtx_v); return buffer; } #endif static void _uvc_user_fill_buffer(struct uvc_video *v, struct uvc_device *dev, struct v4l2_buffer *buf) { struct uvc_buffer *buffer = NULL; bool get_ok = false; uvc_delay_time_calcu_before_get(dev, v); #if 0//UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF for (int i = 0; i < dev->nbufs; i++) { if (dev->vbuf_info[i].fd == buf->m.fd) { v->buffer_s = dev->vbuf_info[i].uvc_buf; v->buffer_s->abandon = false; break; } } uvc_buffer_push_back(&v->uvc->write, v->buffer_s); #endif buffer = uvc_get_enc_data(dev, v, false); if (buffer) { for (int i = 0; i < dev->nbufs; i++) { if (buffer == dev->vbuf_info[i].uvc_buf) { buf->index = dev->vbuf_info[i].index; buf->sequence = buffer->seq; buf->m.fd = dev->vbuf_info[i].fd; get_ok = true; break; } } if (get_ok == false || !_uvc_buffer_check(v, buffer)) { LOG_ERROR("fail:%d\n", get_ok); return; } if (_uvc_get_user_run_state(v) && _uvc_video_get_uvc_process(v)) { if (buf->length >= buffer->size && buffer->buffer) { // buf->bytesused = 550*1024;//for test buf->bytesused = buffer->size; #if UVC_IO_METHOD == UVC_IO_METHOD_MMAP memcpy(dev->mem[buf->index].start, buffer->buffer, buffer->size); #elif UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF #else dev->mem[buf->index].start = buffer->buffer; #endif drop_frame_cnt = 0; } } else { LOG_WARN("_uvc_user_fill_buffer no go here \n"); #if UVC_IO_METHOD == UVC_IO_METHOD_MMAP buf->bytesused = buf->length; #elif UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF buf->bytesused = 0; #else buf->bytesused = buf->length; #endif } buffer = uvc_buffer_pop_front(&v->uvc->read); #if UVC_IO_METHOD == UVC_IO_METHOD_MMAP if (!v->buffer_s) { v->buffer_s = buffer; } else { uvc_buffer_push_back(&v->uvc->write, v->buffer_s); v->buffer_s = buffer; } #elif UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF #else #endif } else if (v->buffer_s) { if (!_uvc_buffer_check(v, v->buffer_s)) return; if (_uvc_get_user_run_state(v) && _uvc_video_get_uvc_process(v)) { if (buf->length >= v->buffer_s->size && v->buffer_s->buffer) { buf->bytesused = v->buffer_s->size; #if UVC_IO_METHOD == UVC_IO_METHOD_MMAP memcpy(dev->mem[buf->index].start, v->buffer_s->buffer, v->buffer_s->size); #elif UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF buf->bytesused = 0; buf->m.fd = 0; #else dev->mem[buf->index].start = v->buffer_s->buffer; #endif } } } else { buf->bytesused = 0; buf->m.fd = 0; #if UVC_IO_METHOD == UVC_IO_METHOD_MMAP memset(dev->mem[buf->index].start, 0, buf->length); #elif UVC_IO_METHOD == UVC_IO_METHOD_DMA_BUFF #else #endif } } void uvc_user_fill_buffer(struct uvc_device *dev, struct v4l2_buffer *buf, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_user_fill_buffer(l, dev, buf); break; } } } pthread_mutex_unlock(&mtx_v); } static void _uvc_user_set_write_buffer(struct uvc_video *v, struct uvc_device *dev, struct v4l2_buffer *buf) { struct uvc_buffer *buffer = NULL; bool get_ok = false; // uvc_delay_time_calcu_before_get(dev, v); for (int i = 0; i < dev->nbufs; i++) { if (dev->vbuf_info[i].fd == buf->m.fd) { v->buffer_s = dev->vbuf_info[i].uvc_buf; v->buffer_s->abandon = false; get_ok = true; break; } } if (get_ok) uvc_buffer_push_back(&v->uvc->write, v->buffer_s); else LOG_ERROR("fail:%d\n", buf->m.fd); } void uvc_user_set_write_buffer(struct uvc_device *dev, struct v4l2_buffer *buf, int id) { pthread_mutex_lock(&mtx_v); if (_uvc_video_id_check(id)) { for (std::list::iterator i = lst_v.begin(); i != lst_v.end(); ++i) { struct uvc_video *l = *i; if (id == l->id) { _uvc_user_set_write_buffer(l, dev, buf); break; } } } pthread_mutex_unlock(&mtx_v); } void uvc_user_lock() { pthread_mutex_lock(&mtx_v); } void uvc_user_unlock() { pthread_mutex_unlock(&mtx_v); }