doubao_chat/apps/wifi_camera/wifi/video_preview.c

697 lines
20 KiB
C

#include "app_config.h"
#ifdef CONFIG_ENABLE_VLIST
#include "sock_api/sock_api.h"
#include "os/os_api.h"
#include "server/net_server.h"
#include "server/server_core.h"
#include "server/ctp_server.h"
#include "server/rt_stream_pkg.h"
#include "server/simple_mov_unpkg.h"
#include "packet.h"
#include "generic/list.h"
#define VIDEO_PREVIEW_TASK_NAME "video_preview"
#define VIDEO_PREVIEW_TASK_STK 0x1000
#define VIDEO_PREVIEW_TASK_PRIO 22
struct __preview {
struct list_head cli_head;
u32 id;
u8 state;
void *video_preview_sock_hdl;
int (*cb)(void *priv, u8 *data, size_t len);
u8 kill_flag;
OS_MUTEX mutex;
};
static struct __preview preview_info;
#define preview_info_hander (&preview_info)
struct __preview_cli_info {
struct list_head entry;
char (*filename)[64];
u32 num;
int offset;
u16 timeinv;
u8 direct;
u8 state;
struct sockaddr_in remote_addr;
struct __packet_info pinfo;
u8 kill_flag;
int pid;
u32 tmp_is_30fps;
struct net_req *req;
};
static void video_preview_cli_thread(void *arg)
{
int ret;
FILE *fd;
char name[32];
char buf[256];
int i = 0;
int count = 0;
u32 msec = 0;
struct __preview_cli_info *cli = (struct __preview_cli_info *)arg;
puts("\nstart video_preview_cli_thread\n");
os_mutex_pend(&preview_info_hander->mutex, 0);
cli->pinfo.data = (u8 *)zalloc(IMAGE_SIZE);
cli->pinfo.len = IMAGE_SIZE;
if (cli->pinfo.data == NULL) {
printf("%s err , no mem !!!\n\n", __func__);
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "MULTI_COVER_FIGURE", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
cli->pinfo.type = PREVIEW_TYPE;
cli->pinfo.fd = NULL;
cli->pinfo.fast_play_mask = 0;
cli->pinfo.state = 0;//预览模式
while (1) {
if (!strstr(cli->filename[i], "storage")) {
send_end_packet(&cli->pinfo);
sprintf(buf, "status:%d", 1);
CTP_CMD_COMBINED(NULL, CTP_NO_ERR, "MULTI_COVER_FIGURE", "NOTIFY", buf);
break;
}
if (cli->kill_flag) {
printf("wanning : cli->kill_flag : %d \n", cli->kill_flag);
goto err2;
}
printf("filename[%d]:%s \n", i, cli->filename[i]);
fd = fopen(cli->filename[i], "r");
if (fd == NULL) {
printf("%s fopen fail\n", __func__);
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "MULTI_COVER_FIGURE", "NOTIFY", CTP_OPEN_FILE_MSG);
i++;
continue;
}
#if (defined CONFIG_NET_PKG_H264)
if (!is_vaild_mov_file(fd)) {
/* sprintf(buf, "path_%d:%s", i, cli->filename[i]); */
printf("%s is Invalid MOV\n", __func__);
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "MULTI_COVER_FIGURE", "NOTIFY", CTP_OPEN_FILE_MSG);
i++;
fclose(fd);
continue;
}
#elif (defined CONFIG_NET_PKG_JPEG)
avi_net_preview_unpkg_init(fd, cli->pinfo.state);
if (!is_vaild_avi_file(fd, cli->pinfo.state)) {
printf("err %s is Invalid AVI!!!\n", __func__);
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "MULTI_COVER_FIGURE", "NOTIFY", CTP_OPEN_FILE_MSG);
i++;
fclose(fd);
continue;
}
#endif
cli->pinfo.fd = fd;
strcpy(cli->pinfo.file_name, cli->filename[i]);
if (update_data(&cli->pinfo)) {
printf("err get media info fail\n");
goto err2;
}
ret = send_media_packet(&cli->pinfo);
if (ret <= 0) {
printf("err send_media_packet\n");
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "MULTI_COVER_FIGURE", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
ret = send_date_packet(&cli->pinfo, 0);
if (ret <= 0) {
printf("err send_data_packet\n");
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "MULTI_COVER_FIGURE", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
ret = send_video_packet(&cli->pinfo, 0);
if (ret <= 0) {
printf("send video preview err !!!!!\n\n");
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "MULTI_COVER_FIGURE", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
#if (defined CONFIG_NET_PKG_H264)
msec = i * cli->pinfo.info.sample_duration * 1000 / cli->pinfo.info.scale;
if (msec) {
incr_date_time(&cli->pinfo.time, msec / 1000);
}
#elif (defined CONFIG_NET_PKG_JPEG)
printf("avi_net_unpkg_exit...\n");
avi_net_unpkg_exit(fd, cli->pinfo.state);
#endif
printf("--%s get preview ok--\n", cli->pinfo.file_name);
fclose(cli->pinfo.fd);
cli->pinfo.fd = NULL;
i++;
}
err2:
#if (defined CONFIG_NET_PKG_JPEG)
avi_net_unpkg_exit(fd, cli->pinfo.state);
#endif
if (cli->filename != NULL) {
free(cli->filename);
}
if (cli->pinfo.info.stco_tab != NULL) {
free(cli->pinfo.info.stco_tab);
cli->pinfo.info.stco_tab = NULL;
}
if (cli->pinfo.info.stsz_tab != NULL) {
free(cli->pinfo.info.stsz_tab);
cli->pinfo.info.stsz_tab = NULL;
}
if (cli->pinfo.info.audio_stco_tab != NULL) {
free(cli->pinfo.info.audio_stco_tab);
cli->pinfo.info.audio_stco_tab = NULL;
}
if (cli->pinfo.fd != NULL) {
fclose(cli->pinfo.fd);
cli->pinfo.fd = NULL;
}
if (cli->pinfo.data) {
free(cli->pinfo.data);
}
if (cli->req) {
free(cli->req);
}
list_del(&cli->entry);
sock_unreg(cli->pinfo.sock);
cli->pinfo.data = NULL;
cli->kill_flag = 0;
free(cli);
os_mutex_post(&preview_info_hander->mutex);
}
//后期需要换找I帧的方法
static int find_idr_frame(struct __packet_info *pinfo, u32 offset)
{
int i = 0;
char buf[5];
size_t sample_offset;
size_t sample_size;
int ret;
float frame_per_ts = 0 ;
#if (defined CONFIG_NET_H264)
i = offset * pinfo->info.scale / (pinfo->info.sample_duration * 1000);
while (1) {
if (i >= pinfo->info.video_sample_count) {
return pinfo->info.video_sample_count - 1;//必须要减1
}
sample_size = get_sample_size(pinfo->info.stsz_tab, i);
sample_offset = get_chunk_offset(pinfo->info.stco_tab, i);
fseek(pinfo->fd, sample_offset, SEEK_SET);
ret = fread(pinfo->fd, buf, 5);
if (buf[4] != 0x67) {
i++;
} else {
return i;
}
}
#elif (defined CONFIG_NET_JPEG)
return avi_get_video_num(pinfo->fd, offset, pinfo->state); //返回seq
#endif
return 0;
}
static int send_date_per_fps(struct __preview_cli_info *cli, u32 i)
{
int ret = 0;
int is_30fps = 0;
u32 sec = (i - cli->tmp_is_30fps) / (cli->pinfo.info.scale / cli->pinfo.info.sample_duration);
if (sec) {
ret = send_date_packet(&cli->pinfo, sec);
cli->tmp_is_30fps = i;
}
return ret;
}
//成功:返回起始帧或结束帧 失败;-1
static int switch_direct_and_file(struct __preview_cli_info *cli)
{
int i = 0;
if (cli->direct) {
//behind
if (cli->offset <= 0) {
send_end_packet(&cli->pinfo);
cli->pinfo.fd = play_prev(&cli->pinfo);
if (cli->pinfo.fd == NULL) {
return -1;
}
update_data(&cli->pinfo);
cli->offset = ((cli->pinfo.info.durition / cli->pinfo.info.scale) * 1000);
i = cli->pinfo.info.video_sample_count - 1;
}
} else {
//forward
if (cli->offset >= ((cli->pinfo.info.durition / cli->pinfo.info.scale) * 1000)) {
send_end_packet(&cli->pinfo);
cli->pinfo.fd = play_next(&cli->pinfo);
if (cli->pinfo.fd == NULL) {
return -1;
}
update_data(&cli->pinfo);
i = 0;
cli->offset = 0;
}
}
if (cli->direct) {
cli->offset -= cli->timeinv;
} else {
cli->offset += cli->timeinv;
}
return i;
}
static void video_thus_cli_thread(void *arg)
{
int ret;
FILE *fd;
char name[32];
char buf[64];
int i = 0;
int count = 0;
u32 msec = 0;
struct __preview_cli_info *cli = (struct __preview_cli_info *)arg;
printf("\n %s \n", __func__);
os_mutex_pend(&preview_info_hander->mutex, 0);
cli->pinfo.data = (u8 *)zalloc(IMAGE_SIZE);
cli->pinfo.len = IMAGE_SIZE;
if (cli->pinfo.data == NULL) {
printf("%s err , no mem !!!\n\n", __func__);
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "MULTI_COVER_FIGURE", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
cli->pinfo.type = PREVIEW_TYPE;
cli->pinfo.fd = NULL;
cli->pinfo.fast_play_mask = 1;
cli->pinfo.state = 0;//预览模式
fd = fopen(cli->filename[i], "r");
if (fd == NULL) {
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "THUMBNAILS", "NOTIFY", CTP_OPEN_FILE_MSG);
goto err2;
}
#if (defined CONFIG_NET_PKG_H264)
if (!is_vaild_mov_file(fd)) {
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "THUMBNAILS", "NOTIFY", CTP_OPEN_FILE_MSG);
fclose(fd);
goto err2;
}
#elif (defined CONFIG_NET_PKG_JPEG)
avi_net_playback_unpkg_init(fd, cli->pinfo.state);
if (!is_vaild_avi_file(fd, cli->pinfo.state)) {
CTP_CMD_COMBINED(NULL, CTP_OPEN_FILE, "THUMBNAILS", "NOTIFY", CTP_OPEN_FILE_MSG);
fclose(fd);
goto err2;
}
#endif
cli->pinfo.fd = fd;
strcpy(cli->pinfo.file_name, cli->filename[0]);
if (update_data(&cli->pinfo)) {
printf("get media info fail\n");
goto err2;
}
ret = send_media_packet(&cli->pinfo);
if (ret <= 0) {
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "THUMBNAILS", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
incr_date_time(&cli->pinfo.time, cli->offset / 1000);
while (1) {
if (cli->kill_flag) {
printf("kill by other thread \n");
goto err2;
}
i = find_idr_frame(&cli->pinfo, cli->offset);
#if 0 //暂时不用,当时间偏移量大于当前,切换到下视频,AVI不能打开!!!!
i = switch_direct_and_file(cli);
if (i < 0) {
goto err2;
}
#endif
if ((count + 1) == cli->num) {
send_end_packet(&cli->pinfo);
}
ret = send_date_packet(&cli->pinfo, cli->timeinv / 1000);
if (ret <= 0) {
printf("send_data_packet errr !!\n");
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "THUMBNAILS", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
ret = send_video_packet(&cli->pinfo, i);
if (ret < 0) {
printf("send_video_packet errr !!\n");
/* sprintf(buf, "path:%s", cli->filename[i]); */
CTP_CMD_COMBINED(NULL, CTP_NET_ERR, "THUMBNAILS", "NOTIFY", CTP_NET_ERR_MSG);
goto err2;
}
count++;
cli->offset += cli->timeinv;
if (cli->num != 0 && count == cli->num) {
sprintf(buf, "status:%d", 1);
CTP_CMD_COMBINED(NULL, CTP_NO_ERR, "THUMBNAILS", "NOTIFY", buf);
break;
}
}
puts("------------get thus preview ok----------------\n");
err2:
#ifdef CONFIG_NET_PKG_JPEG
avi_net_unpkg_exit(fd, cli->pinfo.state);
#endif
if (cli->pinfo.info.stco_tab != NULL) {
free(cli->pinfo.info.stco_tab);
cli->pinfo.info.stco_tab = NULL;
}
if (cli->pinfo.info.stsz_tab != NULL) {
free(cli->pinfo.info.stsz_tab);
cli->pinfo.info.stsz_tab = NULL;
}
if (cli->pinfo.info.audio_stco_tab != NULL) {
free(cli->pinfo.info.audio_stco_tab);
cli->pinfo.info.audio_stco_tab = NULL;
}
if (cli->pinfo.fd != NULL) {
fclose(cli->pinfo.fd);
cli->pinfo.fd = NULL;
}
if (cli->pinfo.data) {
free(cli->pinfo.data);
}
if (cli->req) {
free(cli->req);
}
list_del(&cli->entry);
sock_unreg(cli->pinfo.sock);
cli->pinfo.data = NULL;
cli->kill_flag = 0;
free(cli);
os_mutex_post(&preview_info_hander->mutex);
}
static int create_app_thread(struct net_req *req, void *sock, struct sockaddr_in *remote_addr)
{
char name[32];
static u32 count = 0;
int ret;
struct __preview_cli_info *cli = zalloc(sizeof(struct __preview_cli_info));
if (cli == NULL) {
goto __err1;
}
cli->filename = req->pre.filename;
cli->pinfo.sock = sock;
cli->pinfo.data = NULL;
cli->pinfo.len = 0;
cli->req = req;
printf("create cli thread\n");
memcpy(&cli->remote_addr, remote_addr, sizeof(struct sockaddr_in));
os_mutex_pend(&preview_info_hander->mutex, 0);
list_add_tail(&cli->entry, &preview_info_hander->cli_head);
os_mutex_post(&preview_info_hander->mutex);
switch (req->pre.type) {
case PREVIEW:
sprintf(name, "preview_cli_%d", count++);
ret = thread_fork(name, 25, 2048, 0, &cli->pid, video_preview_cli_thread, (void *)cli);
if (ret != OS_NO_ERR) {
free(cli->req);
free(cli);
}
break;
case THUS:
cli->num = req->pre.num;
cli->offset = req->pre.offset;
cli->timeinv = req->pre.timeinv;
sprintf(name, "thus_cli_%d", count++);
ret = thread_fork(name, 25, 2048, 0, &cli->pid, video_thus_cli_thread, (void *)cli);
if (ret != OS_NO_ERR) {
free(cli->req);
free(cli);
}
break;
default:
free(cli->req);
free(cli);
break;
}
msleep(100);
return 0;
__err1:
return -1;
}
int video_preview_req_handler2(void *msg)
{
struct sockaddr_in remote_addr;
fd_set rdset;
struct timeval tv = {5, 0};//1s不来连接返回错误
void *sock_hdl = NULL;
int ret;
char buf[64];
socklen_t addrlen = sizeof(remote_addr);
if (msg == NULL) {
goto __err1;
}
struct net_req *req = (struct net_req *)msg;
strcpy(buf, "status:0");
if (req->pre.type == PREVIEW) {
CTP_CMD_COMBINED(NULL, CTP_NO_ERR, "MULTI_COVER_FIGURE", "NOTIFY", buf);
} else if (req->pre.type == THUS) {
CTP_CMD_COMBINED(NULL, CTP_NO_ERR, "THUMBNAILS", "NOTIFY", buf);
}
while (1) {
FD_ZERO(&rdset);
FD_SET(sock_get_socket(preview_info_hander->video_preview_sock_hdl), &rdset);
ret = sock_select(preview_info_hander->video_preview_sock_hdl, &rdset, NULL, NULL, &tv);
if (ret < 0) {
goto __err1;
} else if (ret == 0) {
puts("accept time out\n");
if (req->pre.type == PREVIEW) {
CTP_CMD_COMBINED(NULL, CTP_REQUEST, "MULTI_COVER_FIGURE", "NOTIFY", CTP_REQUEST_MSG);
} else if (req->pre.type == THUS) {
CTP_CMD_COMBINED(NULL, CTP_REQUEST, "THUMBNAILS", "NOTIFY", CTP_REQUEST_MSG);
}
free(req);
return -1;
} else {
sock_hdl = sock_accept(preview_info_hander->video_preview_sock_hdl, (struct sockaddr *)&remote_addr, &addrlen, NULL, NULL);
if (sock_hdl == NULL) {
goto __err1;
}
if (create_app_thread(req, sock_hdl, &remote_addr)) {
goto __err1;
}
return 0;
}
}
__err1:
sock_unreg(preview_info_hander->video_preview_sock_hdl);
free(req);
return -1;
}
static void pre_thread(void *arg)
{
video_preview_req_handler2(arg);
}
int video_preview_post_msg(struct net_req *_req)
{
int ret = 0;
static u32 count = 0;
char buf[32];
struct net_req *req = zalloc(sizeof(struct net_req));
if (req == NULL) {
return -1;
}
memcpy(req, _req, sizeof(struct net_req));
sprintf(buf, "pre_thread%d", count++);
ret = thread_fork(buf, 25, 0x512, 0, 0, pre_thread, (void *)req);
if (ret != OS_NO_ERR) {
free(req);
return -1;
}
return 0;
}
int preview_init(u16 port, int callback(void *priv, u8 *data, size_t len))
{
puts("preview_init\n");
int ret;
struct sockaddr_in dest_addr;
preview_info_hander->video_preview_sock_hdl = sock_reg(AF_INET, SOCK_STREAM, 0, NULL, NULL);
if (preview_info_hander->video_preview_sock_hdl == NULL) {
printf("%s %d->Error in socket()\n", __func__, __LINE__);
goto EXIT;
}
if (sock_set_reuseaddr(preview_info_hander->video_preview_sock_hdl)) {
printf("%s %d->Error in sock_set_reuseaddr(),errno=%d\n", __func__, __LINE__, errno);
goto EXIT;
}
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_port = htons(port);
ret = sock_bind(preview_info_hander->video_preview_sock_hdl, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
if (ret) {
printf("%s %d->Error in bind(),errno=%d\n", __func__, __LINE__, errno);
goto EXIT;
}
ret = sock_listen(preview_info_hander->video_preview_sock_hdl, 0xff);
if (ret) {
printf("%s %d->Error in listen()\n", __func__, __LINE__);
goto EXIT;
}
os_mutex_create(&preview_info_hander->mutex);
INIT_LIST_HEAD(&preview_info_hander->cli_head);
return 0;
EXIT:
sock_unreg(preview_info_hander->video_preview_sock_hdl);
preview_info_hander->video_preview_sock_hdl = NULL;
return -1;
}
int video_preview_and_thus_all_disconnect(void)
{
puts("---------------video_cli_disconnect\n\n");
struct __preview_cli_info *cli = NULL;
struct list_head *pos = NULL, *node = NULL;
os_mutex_pend(&preview_info_hander->mutex, 0);
if (list_empty(&preview_info_hander->cli_head)) {
puts("video_preview cli is emtry\n");
os_mutex_post(&preview_info_hander->mutex);
return -1;
}
list_for_each_safe(pos, node, &preview_info_hander->cli_head) {
cli = list_entry(pos, struct __preview_cli_info, entry);
cli->kill_flag = 1;
sock_set_quit(cli->pinfo.sock);
}
os_mutex_post(&preview_info_hander->mutex);
return 0;
}
void preview_uninit(void)
{
video_preview_and_thus_all_disconnect();
sock_unreg(preview_info_hander->video_preview_sock_hdl);
os_mutex_del(&preview_info_hander->mutex, OS_DEL_ALWAYS);
}
int video_cli_slide(struct sockaddr_in *dst_addr, u8 direct)
{
struct __preview_cli_info *cli = NULL;
struct list_head *pos = NULL, *node = NULL;
os_mutex_pend(&preview_info_hander->mutex, 0);
if (list_empty(&preview_info_hander->cli_head)) {
puts("video_preview cli is emtry\n");
os_mutex_post(&preview_info_hander->mutex);
return -1;
}
list_for_each_safe(pos, node, &preview_info_hander->cli_head) {
cli = list_entry(pos, struct __preview_cli_info, entry);
if (cli->remote_addr.sin_addr.s_addr == dst_addr->sin_addr.s_addr
&& cli->remote_addr.sin_port == dst_addr->sin_port) {
cli->direct = direct;
os_mutex_post(&preview_info_hander->mutex);
return 0;
}
}
os_mutex_post(&preview_info_hander->mutex);
return -1;
}
int video_preview_and_thus_disconnect(struct sockaddr_in *dst_addr)
{
struct __preview_cli_info *cli = NULL;
struct list_head *pos = NULL, *node = NULL;
os_mutex_pend(&preview_info_hander->mutex, 0);
if (list_empty(&preview_info_hander->cli_head)) {
puts("video_preview cli is emtry\n");
os_mutex_post(&preview_info_hander->mutex);
return -1;
}
list_for_each_safe(pos, node, &preview_info_hander->cli_head) {
cli = list_entry(pos, struct __preview_cli_info, entry);
if (dst_addr != NULL
&& cli->remote_addr.sin_addr.s_addr == dst_addr->sin_addr.s_addr) {
cli->kill_flag = 1;
sock_set_quit(cli->pinfo.sock);
os_mutex_post(&preview_info_hander->mutex);
return 0;
}
}
os_mutex_post(&preview_info_hander->mutex);
printf("%s not find it\n", __func__);
return -1;
}
#endif