doubao_chat/apps/demo/demo_DevKitBoard/avi_replay.c

809 lines
21 KiB
C

#include "app_config.h"
#include "system/includes.h"
#include "device/device.h"
#include "lcd_drive.h"
#include "os/os_api.h"
#include "asm/includes.h"
#include "system/timer.h"
#include "sys_common.h"
#include "system/includes.h"
#include "simple_avi_unpkg.h"
#include "server/server_core.h"
#include "server/audio_server.h"
#include "server/video_dec_server.h"
#include "lcd_config.h"
#include "yuv_soft_scalling.h"
#include "storage_device.h"
#if 1
#define log_info(x, ...) printf("[AVI_PLAY][INFO] " x " ", ## __VA_ARGS__)
#define log_err(x, ...) printf("[AVI_PLAY][ERR] " x " ", ## __VA_ARGS__)
#else
#define log_info(...)
#define log_err(...)
#endif
#define TIMER_BASE (2)
#define AVI_OFFSET_THR (50)
enum {
MSG_VIDS_UPDATE,
MSG_VIDS_EXIT,
};
enum replay_speed_type {
REPLAY_SPEED_1X = 1,
REPLAY_SPEED_2X,
REPLAY_SPEED_3X,
REPLAY_SPEED_4X,
REPLAY_SPEED_0_5X,
REPLAY_SPEED_0_25X,
};
enum replay_status_type {
RE_ST_NULL = 0,
RE_ST_START,
RE_ST_EXIT,
RE_ST_PAUSE,
};
struct avi_hdl {
FILE *fd;
int play_time;
char *jpg_buf;
char *yuv_buf;
char *scale_buf;
char *rgb_buf;
u8 vids_drop;
u8 vids_exit;
u8 vids_busy;
u16 vids_inr;
int vids_idx;
int vids_total;
u8 auds_vol;
u8 auds_drop;
int auds_idx;
u8 auds_exit;
u16 auds_inr;
int auds_rate;
int auds_total;
char *auds_data;
char *auds_frame;
cbuffer_t auds_cbuf;
u8 dec_status;
struct server *dec_server;
int sync_timer;
OS_MUTEX mutex;
OS_SEM auds_rsem;
enum replay_speed_type speed;
enum replay_status_type status;
};
static struct avi_hdl hdl = {0};
#define __this (&hdl)
#define AUDS_CBUF_LEN (10 * 1024)
#define AUDS_FRAME_LEN (1 * 1024)
#define DEC_JPG_LEN 100*1024
#define IDX_00DC ntohl(0x30306463)
#define IDX_01WB ntohl(0x30317762)
#define IDX_00WB ntohl(0x30307762)
static void video_decode(char *buf, int buf_len)
{
int pix;
int ytype;
int yuv_len;
char *cy, *cb, *cr;
struct jpeg_decode_req req = {0};
struct jpeg_image_info info = {0};
info.input.data.buf = buf;
info.input.data.len = buf_len;
if (jpeg_decode_image_info(&info)) {
log_err("jpeg_decode_image_info err\n");
return;
}
switch (info.sample_fmt) {
case JPG_SAMP_FMT_YUV444:
ytype = 1;
break;//444
case JPG_SAMP_FMT_YUV420:
ytype = 4;
break;//420
default:
ytype = 2;
break;
}
pix = info.width * info.height;
yuv_len = pix + pix / ytype * 2;
//log_info("ytype = %d, width = %d , height = %d\n", ytype, info.width, info.height);
if (!__this->yuv_buf) {
if (!(__this->yuv_buf = malloc(yuv_len))) {
log_err("yuv_buf malloc error, size = %d\n", yuv_len);
return;
}
}
cy = __this->yuv_buf;
cb = cy + pix;
cr = cb + pix / ytype;
req.input_type = JPEG_INPUT_TYPE_DATA;
req.input.data.buf = info.input.data.buf;
req.input.data.len = info.input.data.len;
req.buf_y = cy;
req.buf_u = cb;
req.buf_v = cr;
req.buf_width = info.width;
req.buf_height = info.height;
req.out_width = info.width;
req.out_height = info.height;
req.output_type = JPEG_DECODE_TYPE_DEFAULT;
req.bits_mode = BITS_MODE_UNCACHE;
req.dec_query_mode = TRUE;
if (jpeg_decode_one_image(&req)) {
log_err("jpeg decode error\n");
return;
}
#if 0
if (!__this->scale_buf) {
if (!(__this->scale_buf = malloc(LCD_H * LCD_W * (1 + 1 / ytype * 2)))) {
log_err("scale_buf malloc error, size = %d\n", LCD_H * LCD_W * (1 + 1 / ytype * 2));
return;
}
}
YUV420p_Soft_Scaling(__this->yuv_buf, __this->scale_buf, info.width, info.height, LCD_W, LCD_H);
lcd_show_frame(__this->scale_buf, info.width * info.height * 3 / 2, info.width, info.height);
#else
lcd_show_frame(__this->yuv_buf, info.width * info.height * 3 / 2, info.width, info.height);
#endif
}
static int audio_vfs_fread(void *priv, void *data, u32 len)
{
int rlen = 0, ret;
int read_len, recv_len, remain_len;
if (__this->status == RE_ST_EXIT) {
return 0;
}
os_sem_pend(&__this->auds_rsem, 0);
//putchar(')');
if (cbuf_get_data_size(&__this->auds_cbuf)) {
rlen = cbuf_read(&__this->auds_cbuf, data, len);
}
return len;
}
static const struct audio_vfs_ops audio_vfs_ops = {
.fread = audio_vfs_fread,
};
static int open_audio_dec(void)
{
union audio_req req = {0};
req.dec.output_buf = NULL;
req.dec.volume = __this->auds_vol;
req.dec.output_buf_len = AUDS_CBUF_LEN;
req.dec.channel = 1;
req.dec.sample_rate = __this->auds_rate;
req.dec.priority = 1;
req.dec.vfs_ops = &audio_vfs_ops;
req.dec.file = (FILE *)__this;
req.dec.dec_type = "pcm";
req.dec.sample_source = "dac";
if (server_request(__this->dec_server, AUDIO_REQ_DEC, &req) != 0) {
return -1;
}
req.dec.cmd = AUDIO_DEC_START;
if (server_request(__this->dec_server, AUDIO_REQ_DEC, &req) != 0) {
return -1;
}
return 0;
}
static int close_audio_dec(void)
{
union audio_req req = {0};
if (__this->dec_server) {
req.dec.cmd = AUDIO_DEC_STOP;
server_request(__this->dec_server, AUDIO_REQ_DEC, &req);
server_close(__this->dec_server);
}
return 0;
}
static void audio_replay_task(void *priv)
{
u8 empty = 0;
int frame_len, msg[2];
for (;;) {
if (os_taskq_pend(NULL, msg, ARRAY_SIZE(msg)) != OS_TASKQ) {
continue;
}
switch (msg[0]) {
case MSG_VIDS_UPDATE:
//putchar('}');
break;
case MSG_VIDS_EXIT:
__this->auds_exit = 1;
return;
default:
continue;
}
os_mutex_pend(&__this->mutex, 0);
if ((frame_len = avi_audio_get_frame(__this->fd, msg[1], (u8 *)__this->auds_frame, AUDS_FRAME_LEN, 1)) <= 0) {
log_err("get audio frame error\n");
__this->vids_exit = 1;
os_mutex_post(&__this->mutex);
return;
}
os_mutex_post(&__this->mutex);
_audio_retry_:
if (cbuf_write(&__this->auds_cbuf, __this->auds_frame, frame_len) > 0) {
os_sem_set(&__this->auds_rsem, 0);
os_sem_post(&__this->auds_rsem);
//putchar('(');
if (!__this->dec_status) {
__this->dec_status = 1;
open_audio_dec();
}
} else {
os_time_dly(2);
//putchar('R');
goto _audio_retry_;
}
if (msg[1] == __this->auds_total) {
__this->auds_exit = 1;
return;
}
}
}
static void video_replay_task(void *priv)
{
int frame_len, msg[2];
for (;;) {
if (os_taskq_pend(NULL, msg, ARRAY_SIZE(msg)) != OS_TASKQ) {
continue;
}
switch (msg[0]) {
case MSG_VIDS_UPDATE:
//putchar('>');
break;
case MSG_VIDS_EXIT:
__this->vids_exit = 1;
return;
default:
continue;
}
os_mutex_pend(&__this->mutex, 0);
if ((frame_len = avi_video_get_frame(__this->fd, msg[1], (u8 *)__this->jpg_buf, DEC_JPG_LEN, 1)) <= 0) {
log_err("get video frame error\n");
__this->vids_exit = 1;
os_mutex_post(&__this->mutex);
return;
}
os_mutex_post(&__this->mutex);
if (*(u32 *)__this->jpg_buf == IDX_00DC || *(u32 *)__this->jpg_buf == IDX_01WB || *(u32 *)__this->jpg_buf == IDX_00WB) {
frame_len -= 8;
__this->jpg_buf += 8;
}
video_decode(__this->jpg_buf, frame_len);
__this->vids_busy = 0;
if (msg[1] == __this->vids_total) {
__this->vids_exit = 1;
return;
}
}
}
static void sync_timer_hdl(void *priv)
{
int offset;
static u32 time = 0, vids_time = 0, auds_time = 0, time_last = 0;
if (__this->auds_exit && __this->vids_exit) {
__this->status = RE_ST_EXIT;
os_sem_set(&__this->auds_rsem, 0);
os_sem_post(&__this->auds_rsem);
close_audio_dec();
free(__this->auds_data);
free(__this->auds_frame);
os_sem_del(&__this->auds_rsem, 0);
free(__this->jpg_buf);
free(__this->yuv_buf);
if (__this->scale_buf) {
free(__this->scale_buf);
}
avi_unpkg_exit(__this->fd, 1);
fclose(__this->fd);
sys_timer_del(__this->sync_timer);
memset(__this, 0, sizeof(struct avi_hdl));
log_info("STEP -> VIDEO COMPLETE\n");
return;
}
switch (__this->status) {
case RE_ST_NULL:
return;
case RE_ST_START:
break;
case RE_ST_EXIT:
break;
case RE_ST_PAUSE:
return;
default:
return;
}
offset = (__this->auds_idx * __this->auds_inr) - (__this->vids_idx * __this->vids_inr);
//if (offset >= AVI_OFFSET_THR) {
// log_info("vids_drop = %d, offset = %d\n", offset / __this->vids_inr, offset);
//}
if ((time - vids_time >= __this->vids_inr && !__this->vids_busy) || !time) {
vids_time = time;
if (__this->speed == REPLAY_SPEED_0_5X || __this->speed == REPLAY_SPEED_0_25X) {
__this->vids_idx += __this->vids_drop;
__this->vids_idx = (__this->vids_idx > __this->vids_total) ? __this->vids_total : __this->vids_idx;
__this->vids_busy = 1;
//putchar('<');
if (os_taskq_post_type("video_replay_task", MSG_VIDS_UPDATE, 1, &__this->vids_idx) != OS_NO_ERR) {
log_err("update vids error\n");
}
} else {
if (offset) {
__this->vids_idx += offset >= AVI_OFFSET_THR ? (offset / __this->vids_inr) : __this->vids_drop;
__this->vids_idx = (__this->vids_idx > __this->vids_total) ? __this->vids_total : __this->vids_idx;
__this->vids_busy = 1;
//putchar('<');
if (os_taskq_post_type("video_replay_task", MSG_VIDS_UPDATE, 1, &__this->vids_idx) != OS_NO_ERR) {
//log_err("update vids error\n");
}
}
}
}
if ((time - auds_time >= __this->auds_inr) || !time) {
auds_time = time;
if (__this->speed == REPLAY_SPEED_0_5X || __this->speed == REPLAY_SPEED_0_25X) {
if (offset < 0) {
__this->auds_idx += fabs(offset) >= AVI_OFFSET_THR ? (fabs(offset) / __this->auds_inr) : __this->auds_drop;
__this->auds_idx = (__this->auds_idx > __this->auds_total) ? __this->auds_total : __this->auds_idx;
//putchar('{');
if (os_taskq_post_type("audio_replay_task", MSG_VIDS_UPDATE, 1, &__this->auds_idx) != OS_NO_ERR) {
//log_err("update auds error\n");
__this->auds_idx -= __this->auds_drop;
}
} else {
}
} else {
__this->auds_idx += __this->auds_drop;
__this->auds_idx = (__this->auds_idx > __this->auds_total) ? __this->auds_total : __this->auds_idx;
//putchar('{');
if (os_taskq_post_type("audio_replay_task", MSG_VIDS_UPDATE, 1, &__this->auds_idx) != OS_NO_ERR) {
//log_err("update auds error\n");
__this->auds_idx -= __this->auds_drop;
}
}
}
time += TIMER_BASE;
if (time - time_last >= 1000) {
time_last = time;
log_info("time = %d, vids = %d / %d, auds = %d / %d\n", time, __this->vids_idx, __this->vids_idx * __this->vids_inr, __this->auds_idx, __this->auds_idx * __this->auds_inr);
}
}
char avi_replay_start(char *path)
{
if (!path) {
log_err("%s path error\n");
return -1;
}
if (__this->status != RE_ST_NULL) {
return -1;
}
if (!(__this->fd = fopen(path, "r"))) {
log_err("%s open error\n", path);
return -1;
}
if (avi_playback_unpkg_init(__this->fd, 1)) {
log_err("avi_playback_unpkg_init error\n");
return -1;
}
__this->jpg_buf = (char *)calloc(1, DEC_JPG_LEN);
__this->auds_data = (char *)calloc(1, AUDS_CBUF_LEN);
__this->auds_frame = (char *)calloc(1, AUDS_FRAME_LEN);
ASSERT(__this->auds_data || __this->auds_frame || __this->jpg_buf);
cbuf_init(&__this->auds_cbuf, __this->auds_data, AUDS_CBUF_LEN);
__this->auds_drop = 1;
__this->vids_drop = 1;
__this->auds_vol = 50;
__this->play_time = avi_get_file_time(__this->fd, 1);
__this->auds_total = avi_get_audio_chunk_num(__this->fd, 1);
__this->vids_total = avi_get_video_chunk_num(__this->fd, 1);
__this->auds_rate = avi_get_audio_sample_rate(__this->fd, 1);
__this->vids_inr = (__this->play_time * 1000) / __this->vids_total;
__this->auds_inr = (__this->play_time * 1000) / __this->auds_total;
__this->dec_server = server_open("audio_server", "dec");
if (!__this->dec_server) {
return -1;
}
log_info("total_time = %d, vids_num = %d, auds_num = %d, vids_inr = %d, auds_inr = %d, auds_rate = %d\n", __this->play_time, __this->vids_total, __this->auds_total, __this->vids_inr, __this->auds_inr, __this->auds_rate);
os_mutex_create(&__this->mutex);
os_sem_create(&__this->auds_rsem, 0);
thread_fork("audio_replay_task", 11, 1024, 1024, 0, audio_replay_task, NULL);
thread_fork("video_replay_task", 11, 1024, 256, 0, video_replay_task, NULL);
__this->status = RE_ST_START;
__this->sync_timer = sys_timer_add(NULL, sync_timer_hdl, TIMER_BASE);
log_info("STEP -> START\n");
return 0;
}
char avi_replay_exit(void)
{
if (__this->status == RE_ST_NULL || __this->status == RE_ST_EXIT) {
return -1;
}
if (os_taskq_post_type("audio_replay_task", MSG_VIDS_EXIT, 0, NULL) != OS_NO_ERR) {
log_err("exit auds error\n");
return -1;
}
if (os_taskq_post_type("video_replay_task", MSG_VIDS_EXIT, 0, NULL) != OS_NO_ERR) {
log_err("exit vids error\n");
return -1;
}
return 0;
}
char avi_replay_pause(u8 pause)
{
if (__this->status == RE_ST_START || __this->status == RE_ST_PAUSE) {
__this->status = pause ? RE_ST_PAUSE : RE_ST_START;
return 0;
}
return -1;
}
char avi_replay_change_speed(enum replay_speed_type speed)
{
char *speed_str[] = {
"",
"REPLAY_SPEED_1X",
"REPLAY_SPEED_2X",
"REPLAY_SPEED_3X",
"REPLAY_SPEED_4X",
"REPLAY_SPEED_0_5X",
"REPLAY_SPEED_0_25X",
};
if (!(__this->status == RE_ST_START || __this->status == RE_ST_PAUSE)) {
return -1;
}
switch (speed) {
case REPLAY_SPEED_1X:
case REPLAY_SPEED_2X:
case REPLAY_SPEED_3X:
case REPLAY_SPEED_4X:
__this->auds_drop = speed;
__this->auds_inr = (__this->play_time * 1000 * 1) / __this->auds_total;
__this->vids_drop = 1;
__this->vids_inr = (__this->play_time * 1000 * 1) / __this->vids_total;
break;
case REPLAY_SPEED_0_5X:
__this->auds_drop = 1;
__this->auds_inr = (__this->play_time * 1000 * 2) / __this->auds_total;
__this->vids_drop = 1;
__this->vids_inr = (__this->play_time * 1000 * 2) / __this->vids_total;
break;
case REPLAY_SPEED_0_25X:
__this->auds_drop = 1;
__this->auds_inr = (__this->play_time * 1000 * 4) / __this->auds_total;
__this->vids_drop = 1;
__this->vids_inr = (__this->play_time * 1000 * 4) / __this->vids_total;
break;
default:
return -1;
}
__this->speed = speed;
log_info("CHANTE_SPEED : %s, auds_inr = %d, auds_drop = %d\n", speed_str[speed], __this->auds_inr, __this->auds_drop);
return 0;
}
char avi_get_litimg(char *path, int idx, u8 *data, int *len, int width, int height)
{
FILE *fd;
int pix;
int ytype;
int jpg_len, yuv_len;
char *cy, *cb, *cr;
struct jpeg_decode_req req = {0};
struct jpeg_image_info info = {0};
u8 *jpg_data, *yuv_data;
if (!path || !data) {
log_err("params error\n");
return -1;
}
if (!(fd = fopen(path, "r"))) {
log_err("%s open error\n", path);
return -1;
}
if (avi_playback_unpkg_init(fd, 1)) {
log_err("avi_playback_unpkg_init error\n");
return -1;
}
if (!(jpg_data = malloc(DEC_JPG_LEN))) {
log_err("jpg_data malloc error, size = %d\n", DEC_JPG_LEN);
return -1;
}
jpg_len = avi_video_get_frame(fd, idx, jpg_data, DEC_JPG_LEN, 1);
avi_unpkg_exit(fd, 1);
fclose(fd);
info.input.data.buf = jpg_data;
info.input.data.len = jpg_len;
if (jpeg_decode_image_info(&info)) {
log_err("jpeg_decode_image_info err\n");
return -1;
}
switch (info.sample_fmt) {
case JPG_SAMP_FMT_YUV444:
ytype = 1;
break;//444
case JPG_SAMP_FMT_YUV420:
ytype = 4;
break;//420
default:
ytype = 2;
break;
}
pix = info.width * info.height;
yuv_len = pix + pix / ytype * 2;
if (!(yuv_data = malloc(yuv_len))) {
log_err("yuv_data malloc error, size = %d\n", yuv_len);
return -1;
}
cy = yuv_data;
cb = cy + pix;
cr = cb + pix / ytype;
req.input_type = JPEG_INPUT_TYPE_DATA;
req.input.data.buf = info.input.data.buf;
req.input.data.len = info.input.data.len;
req.buf_y = cy;
req.buf_u = cb;
req.buf_v = cr;
req.buf_width = info.width;
req.buf_height = info.height;
req.out_width = info.width;
req.out_height = info.height;
req.output_type = JPEG_DECODE_TYPE_DEFAULT;
req.bits_mode = BITS_MODE_UNCACHE;
req.dec_query_mode = TRUE;
if (jpeg_decode_one_image(&req)) {
log_err("jpeg decode error\n");
return -1;
}
YUV420p_Soft_Scaling(yuv_data, data, info.width, info.height, width, height);
pix = width * height;
*len = pix + pix / ytype * 2;
printf("len = %d\n", *len);
free(jpg_data);
free(yuv_data);
return 0;
}
#include "demo_config.h"
#ifdef AVI_REPLAY_DEMO
#include "event/device_event.h"
#include "event/key_event.h"
static void avi_litimg_show_task(void *priv)
{
int len;
FILE *fd;
u8 *data = malloc(LCD_W * LCD_H * 2);
avi_get_litimg("storage/sd0/C/SJ.avi", 1, data, &len, LCD_W, LCD_H);
if (fd = fopen("storage/sd0/C/litimg.yuv", "w+")) {
fwrite(data, len, 1, fd);
}
fclose(fd);
lcd_show_frame(data, len, LCD_W, LCD_H);
free(data);
}
static int key_event_handler(struct sys_event *event)
{
enum replay_speed_type speed;
static u32 k2_cnt = 0, k4_cnt = 0, k5_cnt = 0;
struct key_event *key = (struct key_event *)event->payload;
if (event->type == SYS_KEY_EVENT) {
printf("key : value = %d, action = %d", key->value, key->action);
if (key->action == KEY_EVENT_CLICK) {
switch (key->value) {
case 13://K1
avi_replay_start("storage/sd0/C/SJ.avi");
break;
case 6://K2
k2_cnt++;
avi_replay_pause(k2_cnt % 2);
break;
case 4://K3
avi_replay_exit();
break;
case 5://K4
k4_cnt++;
log_info("k4_cnt = %d, k4_cnt/4 = %d\n", k4_cnt, k4_cnt % 4);
switch (k4_cnt % 4) {
case 0:
speed = REPLAY_SPEED_1X;
break;
case 1:
speed = REPLAY_SPEED_2X;
break;
case 2:
speed = REPLAY_SPEED_3X;
break;
case 3:
speed = REPLAY_SPEED_4X;
break;
default:
speed = REPLAY_SPEED_1X;
break;
}
avi_replay_change_speed(speed);
break;
case 12://K5
k5_cnt++;
log_info("k5_cnt = %d, k5_cnt/3 = %d\n", k5_cnt, k5_cnt % 3);
switch (k5_cnt % 3) {
case 0:
speed = REPLAY_SPEED_1X;
break;
case 1:
speed = REPLAY_SPEED_0_5X;
break;
case 2:
speed = REPLAY_SPEED_0_25X;
break;
default:
speed = REPLAY_SPEED_1X;
break;
}
avi_replay_change_speed(speed);
break;
case 10://K6
thread_fork("avi_litimg_show_task", 6, 1024, 0, NULL, avi_litimg_show_task, NULL);
break;
default:
break;
}
}
}
}
static void avi_replay_test(void)
{
user_ui_lcd_init();
set_lcd_show_data_mode(CAMERA);
register_sys_event_handler(SYS_KEY_EVENT, 0, 1, key_event_handler);
while (!storage_device_ready()) {
os_time_dly(10);
putchar('S');
}
}
late_initcall(avi_replay_test);
#endif