/* * * SPDX-License-Identifier: GPL-2.0 * * Copyright (C) 2020 Amlogic or its affiliates * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include "camera.h" #include "handler.h" #include "video.h" #define IMAGE_DIR "/data/camera" static int video_mkdir_image(void) { int ret = 0; DIR *dir = NULL; dir = opendir(IMAGE_DIR); if (dir != NULL) { closedir(dir); return ret; } ret = mkdir(IMAGE_DIR, 0777); if (ret) { printf("Failed to mkdir %s: %s\n", IMAGE_DIR, strerror(errno)); return ret; } return ret; } static void video_buff_save(struct video_dev *vdev, struct buffer_t *buff) { int fd = -1; uint32_t i; uint8_t *data; uint32_t size; uint32_t frm_num = 0; char name[96] = {'\0'}; frm_num = buff->sequence; sprintf(name, "%s/%s_%s_ca_%d.raw", IMAGE_DIR, vdev->cam->name, vdev->entity.name, frm_num); fd = open(name, O_RDWR | O_CREAT, 0666); if (fd < 0) { printf("%s:Error open file\n", __func__); return; } for (i = 0 ; i < buff->nplane; i++) { data = buff->planes[i].data; size = buff->planes[i].size; write(fd, data, size); } printf("%s: %s save file %s\n", vdev->cam->name, vdev->entity.name, name); close(fd); } static uint64_t video_get_msec_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } static void video_get_frame_fps(struct video_dev *vdev, struct buffer_t *buff, int inter) { uint32_t fps = 0; uint32_t frm_num = 0; static uint64_t start = 0; static uint64_t end = 0; frm_num = buff->sequence; if (frm_num % inter == 1) start = video_get_msec_time(); if ((frm_num >= inter) && (frm_num % inter == 0)) { end = video_get_msec_time(); fps = inter * 1000 / (end - start); printf("%s: %s fps: %u\n", vdev->cam->name, vdev->entity.name, fps); } } static void *stats_buff_handler_thread(void *params) { struct buffer_t *sbuf = NULL; struct buffer_t *pbuf = NULL; struct video_dev *stats = params; struct camera_dev *cam = stats->cam; struct video_dev *para = &cam->param; while (cam->status == STATUS_ON) { pbuf = video_get_list_buff(para); sbuf = video_get_list_buff(stats); if (!pbuf || !sbuf) { usleep(5000); continue; } video_delete_list_buff(stats, sbuf); video_delete_list_buff(para, pbuf); alg_params_process(sbuf, pbuf, cam->cam_id); video_dev_queue_buff(stats, sbuf); video_dev_queue_buff(para, pbuf); } return NULL; } static void *video_buff_handler_thread(void *params) { struct buffer_t *vbuf = NULL; struct video_dev *vdev = params; while (vdev->cam->status == STATUS_ON) { vbuf = video_get_list_buff(vdev); if (vbuf == NULL) { usleep(5000); continue; } video_delete_list_buff(vdev, vbuf); video_get_frame_fps(vdev, vbuf, 70); video_dev_queue_buff(vdev, vbuf); } return NULL; } static int offtile_buff_handler(void *params) { struct buffer_t *imp_buff; struct buffer_t *vc0_buff; struct buffer_t *vc1_buff; struct video_dev *imp = params; struct camera_dev *cam = imp->cam; struct video_dev *vc0 = &cam->vc0; struct video_dev *vc1 = &cam->vc1; imp_buff = video_get_list_buff(imp); vc0_buff = video_get_list_buff(vc0); if (cam->isp.img_mode == C4_ISP_IMAGE_HDR) vc1_buff = video_get_list_buff(vc1); else vc1_buff = vc0_buff; if (!imp_buff || !vc0_buff || !vc1_buff) return -EINVAL; video_delete_list_buff(imp, imp_buff); video_delete_list_buff(vc0, vc0_buff); if (cam->isp.img_mode == C4_ISP_IMAGE_HDR) video_delete_list_buff(vc1, vc1_buff); imp_buff->planes[0].fd = vc0_buff->planes[0].fd; imp_buff->planes[0].bytesused = vc0_buff->planes[0].size; imp_buff->planes[1].fd = vc1_buff->planes[0].fd; imp_buff->planes[1].bytesused = vc1_buff->planes[0].size; /* Will wait until the ISP processing is completed */ video_dev_queue_buff(imp, imp_buff); video_dev_queue_buff(vc0, vc0_buff); if (cam->isp.img_mode == C4_ISP_IMAGE_HDR) video_dev_queue_buff(vc1, vc1_buff); return 0; } static void offpost_queue_imp_buff(struct video_dev *imp, struct video_dev *vc, struct buffer_t *imp_buff, struct buffer_t *vc_buff) { imp_buff->planes[0].fd = vc_buff->planes[0].fd; imp_buff->planes[0].bytesused = vc_buff->planes[0].size; imp_buff->planes[1].fd = vc_buff->planes[1].fd; imp_buff->planes[1].bytesused = vc_buff->planes[1].size; /* Will wait until the ISP processing is completed */ video_dev_queue_buff(imp, imp_buff); video_dev_queue_buff(vc, vc_buff); } static int offpost_buff_handler(void *params) { struct video_dev *imp = params; struct camera_dev *cam = imp->cam; struct video_dev *vc0 = &cam->vc0; struct video_dev *vc1 = &cam->vc1; struct video_dev *vc2 = &cam->vc2; struct video_dev *vc3 = &cam->vc3; struct buffer_t *imp_buff; struct buffer_t *vc0_buff; struct buffer_t *vc1_buff; struct buffer_t *vc2_buff; struct buffer_t *vc3_buff; if (cam->isp.img_mode != C4_ISP_IMAGE_SDR) return -EINVAL; imp_buff = video_get_list_buff(imp); vc0_buff = video_get_list_buff(vc0); vc1_buff = video_get_list_buff(vc1); vc2_buff = video_get_list_buff(vc2); vc3_buff = video_get_list_buff(vc3); if (!imp_buff || (!vc0_buff && !vc1_buff && !vc2_buff && !vc3_buff)) return -EINVAL; video_delete_list_buff(imp, imp_buff); if (vc0_buff) { video_delete_list_buff(vc0, vc0_buff); offpost_queue_imp_buff(imp, vc0, imp_buff, vc0_buff); } if (vc1_buff) { video_delete_list_buff(vc1, vc1_buff); offpost_queue_imp_buff(imp, vc1, imp_buff, vc1_buff); } if (vc2_buff) { video_delete_list_buff(vc2, vc2_buff); offpost_queue_imp_buff(imp, vc2, imp_buff, vc2_buff); } if (vc3_buff) { video_delete_list_buff(vc3, vc3_buff); offpost_queue_imp_buff(imp, vc3, imp_buff, vc3_buff); } return 0; } static void *offline_buff_handler_thread(void *params) { struct video_dev *imp = params; struct camera_dev *cam = imp->cam; int ret; while (cam->status == STATUS_ON) { if (cam->isp.work_mode == C4_ISP_OFFTILE_MODE) ret = offtile_buff_handler(params); else if (cam->isp.work_mode == C4_ISP_OFFPOST_MODE) ret = offpost_buff_handler(params); else { printf("Invalid offline work mode: %u\n", cam->isp.work_mode); break; } if (ret) { usleep(3000); continue; } } return NULL; } static int video_dev_create_thread(struct video_dev *vdev, void *func) { int ret; if (!vdev->used) return 0; pthread_mutex_init(&vdev->lock, NULL); ret = pthread_create(&vdev->tid, NULL, func, (void *)vdev); if (ret) printf("Failed to create %s handler thread\n", vdev->entity.name); return ret; } static void video_dev_destroy_thread(struct video_dev *vdev) { if (!vdev->used) return; pthread_join(vdev->tid, NULL); } void video_dev_buff_ready(struct video_dev *vdev, struct buffer_t *buff) { pthread_mutex_lock(&vdev->lock); local_list_add(&vdev->head, &buff->list); pthread_mutex_unlock(&vdev->lock); } struct buffer_t *video_get_list_buff(struct video_dev *vdev) { struct buffer_t *buff = NULL; pthread_mutex_lock(&vdev->lock); if (local_list_empty(&vdev->head)) { pthread_mutex_unlock(&vdev->lock); goto normal_return; } buff = local_list_first_entry(&vdev->head, struct buffer_t, list); pthread_mutex_unlock(&vdev->lock); normal_return: return buff; } void video_delete_list_buff(struct video_dev *vdev, struct buffer_t *buff) { pthread_mutex_lock(&vdev->lock); local_list_delete(&vdev->head, &buff->list); pthread_mutex_unlock(&vdev->lock); } void videos_dev_thread_create(struct camera_dev *cam) { pthread_mutex_init(&cam->param.lock, NULL); video_dev_create_thread(&cam->stats, stats_buff_handler_thread); video_dev_create_thread(&cam->imp, offline_buff_handler_thread); video_dev_create_thread(&cam->raw, video_buff_handler_thread); video_dev_create_thread(&cam->view, video_buff_handler_thread); video_dev_create_thread(&cam->cap, video_buff_handler_thread); video_dev_create_thread(&cam->record, video_buff_handler_thread); video_dev_create_thread(&cam->disp, video_buff_handler_thread); if (cam->isp.work_mode != C4_ISP_OFFTILE_MODE && cam->isp.work_mode != C4_ISP_OFFPOST_MODE) { video_dev_create_thread(&cam->vc0, video_buff_handler_thread); video_dev_create_thread(&cam->vc1, video_buff_handler_thread); video_dev_create_thread(&cam->vc2, video_buff_handler_thread); video_dev_create_thread(&cam->vc3, video_buff_handler_thread); } video_mkdir_image(); } void videos_dev_thread_destroy(struct camera_dev *cam) { video_dev_destroy_thread(&cam->stats); video_dev_destroy_thread(&cam->imp); video_dev_destroy_thread(&cam->raw); video_dev_destroy_thread(&cam->view); video_dev_destroy_thread(&cam->cap); video_dev_destroy_thread(&cam->record); video_dev_destroy_thread(&cam->disp); if (cam->isp.work_mode != C4_ISP_OFFTILE_MODE && cam->isp.work_mode != C4_ISP_OFFPOST_MODE) { video_dev_destroy_thread(&cam->vc0); video_dev_destroy_thread(&cam->vc1); video_dev_destroy_thread(&cam->vc2); video_dev_destroy_thread(&cam->vc3); } }