/* * 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 #include #include #include #include #include struct uvc_buffer { void* buffer; size_t size; size_t total_size; int width; int height; int video_id; }; 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; 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, int id) { 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; buffer->buffer = calloc(1, buffer->size); if (!buffer->buffer) { free(buffer); return NULL; } buffer->total_size = buffer->size; buffer->video_id = 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 buffer = NULL; pthread_mutex_unlock(&uvc_buffer->mutex); return buffer; } 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) { int *id = (int *)arg; prctl(PR_SET_NAME, "uvc_gadget_pthread", 0, 0, 0); uvc_gadget_main(*id); uvc_set_user_run_state(true, *id); pthread_exit(NULL); } int uvc_gadget_pthread_create(int *id) { pthread_t *pid = NULL; uvc_memset_uvc_user(*id); if ((pid = uvc_video_get_uvc_pid(*id))) { if (pthread_create(pid, NULL, uvc_gadget_pthread, id)) { printf("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(int id) { int ret = 0; printf("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(&v->id); pthread_mutex_lock(&mtx_v); pthread_mutex_init(&v->buffer_mutex, NULL); pthread_mutex_init(&v->user_mutex, NULL); ret = 0; } else { printf("%s: %d: memory alloc fail.\n", __func__, __LINE__); ret = -1; } } else { printf("%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); printf("UVC_BUFFER_NUM = %d\n", UVC_BUFFER_NUM); for (i = 0; i < UVC_BUFFER_NUM; i++) { buffer = uvc_buffer_create(width, height, v->id); if (!buffer) { ret = -1; goto exit; } uvc_buffer_push_back(&v->uvc->write, buffer); } _uvc_video_set_uvc_process(v, 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); uvc_buffer_destroy(&v->uvc->write); uvc_buffer_destroy(&v->uvc->read); delete v->uvc; v->uvc = NULL; } pthread_mutex_unlock(&v->buffer_mutex); } void uvc_buffer_deinit(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_deinit(l); break; } } } pthread_mutex_unlock(&mtx_v); } 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; } #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: 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); } 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; printf("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; } 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_user_fill_buffer(struct uvc_video *v, struct uvc_device *dev, struct v4l2_buffer *buf) { struct uvc_buffer* buffer = NULL; while (!(buffer = uvc_buffer_front(&v->uvc->read)) && _uvc_get_user_run_state(v)) { pthread_mutex_unlock(&mtx_v); usleep(1000); pthread_mutex_lock(&mtx_v); } if (buffer) { if (!_uvc_buffer_check(v, buffer)) return; if (_uvc_get_user_run_state(v) && _uvc_video_get_uvc_process(v)) { if (buf->length >= buffer->size && buffer->buffer) { buf->bytesused = buffer->size; memcpy(dev->mem[buf->index].start, buffer->buffer, buffer->size); } } else { buf->bytesused = buf->length; } buffer = uvc_buffer_pop_front(&v->uvc->read); if (!v->buffer_s) { v->buffer_s = buffer; } else { uvc_buffer_push_back(&v->uvc->write, v->buffer_s); v->buffer_s = buffer; } } 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; memcpy(dev->mem[buf->index].start, v->buffer_s->buffer, v->buffer_s->size); } } } else { buf->bytesused = buf->length; memset(dev->mem[buf->index].start, 0, buf->length); } } 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); }