Files
TAIXIN/sdk/lib/video/miniMP4/time_lapse_mp4.c

439 lines
12 KiB
C
Raw Normal View History

/**********************************************************************
* demo
*********************************************************************/
#include "basic_include.h"
#include "fatfs/osal_file.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/multimedia/msi.h"
#include "osal/string.h"
#include "stream_define.h"
#include "audio_media_ctrl/audio_code_ctrl.h"
#include "audio_msi/audio_adc.h"
#include "mp4/mp4_encode.h"
// 结构体申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC os_malloc
#define STREAM_LIBC_FREE os_free
#define STREAM_LIBC_ZALLOC os_zalloc
enum
{
MSI_MP4_STOP = BIT(0),
MSI_MP4_THREAD_DEAD = BIT(1),
};
#define TIME_LAPSE_DIR "0:/DCIM"
#define MP4_FILE_NAME "mp4"
struct mp4_encode_msi_s
{
struct msi *msi;
struct os_event evt;
uint16_t rec_time;
uint16_t filter_type;
};
#define MIN(a, b) ((a) > (b) ? b : a)
#define MAX(a, b) ((a) > (b) ? a : b)
static uint32_t a2i(char *str)
{
uint32_t ret = 0;
uint32_t indx = 0;
char str_buf[32];
memset(str_buf, 0, 32);
while (str[indx] != '\0')
{
if (str[indx] == '.')
{
break;
}
str_buf[indx] = str[indx];
indx++;
}
// printf("str_buf:%s str:%s\r\n",str_buf,str);
indx = 0;
while (str_buf[indx] != '\0')
{
if (str_buf[indx] >= '0' && str_buf[indx] <= '9')
{
ret = ret * 10 + str_buf[indx] - '0';
}
indx++;
}
return ret;
}
static void *creat_mp4_file(char *dir_name)
{
void *fp;
char filepath[64];
uint32_t indx = os_jiffies() % 99999999;
void *dir = osal_opendir(dir_name);
if (!dir)
{
osal_fmkdir(dir_name);
}
else
{
osal_closedir(dir);
}
sprintf(filepath, "%s/%08d.%s", dir_name, indx, MP4_FILE_NAME);
printf("filepath:%s\r\n", filepath);
fp = osal_fopen(filepath, "wb+");
return fp;
}
static void *creat_mp4_file2(char *dir_name, char *filename)
{
void *fp;
char filepath[64];
uint32_t indx = os_jiffies();
void *dir = osal_opendir(dir_name);
if (!dir)
{
osal_fmkdir(dir_name);
}
else
{
osal_closedir(dir);
}
sprintf(filename, "%016d.jpg", indx);
sprintf(filepath, "%s/%016d.%s", dir_name, indx, MP4_FILE_NAME);
printf("filepath:%s\r\n", filepath);
fp = osal_fopen(filepath, "wb+");
return fp;
}
// 获取nal的size,从0开始搜索,返回的是的nal头的size,offset相对于头的偏移(通过多次调用,可以用于计算nal_size)
// 返回0代表搜索不到nal的头
static uint8_t get_nal_size(uint8_t *buf, uint32_t size, uint32_t *offset)
{
uint32_t pos = 0;
while ((size - pos) > 3)
{
if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 1)
{
*offset = pos;
return 3;
}
if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 0 && buf[pos + 3] == 1)
{
*offset = pos;
return 4;
}
pos++;
}
return 0;
}
// 只是获取pps和sps的nalsize
static uint8_t *get_sps_pps_nal_size(uint8_t *buf, uint32_t size, uint32_t *nal_size, uint8_t *head_size)
{
uint32_t offset;
uint8_t nal_head_size = get_nal_size(buf, size, &offset);
uint8_t nal_type;
uint8_t *ret_buf = NULL;
// 找到头部,检查类型
if (nal_head_size && offset + nal_head_size < size)
{
nal_type = buf[nal_head_size + offset] & 0x1f;
// 找到sps和pps就返回长度和偏移(相对buf的偏移)
if (nal_type == 7 || nal_type == 8)
{
// 查找下一个nal
nal_head_size = get_nal_size(buf + offset + nal_head_size, size - (offset + nal_head_size), nal_size);
if (nal_head_size)
{
// 偏移到nal的头部
ret_buf = buf + offset;
// 返回nal的头size
*head_size = nal_head_size;
}
}
}
return ret_buf;
}
static int mp4_encode_running(struct msi *msi, uint32_t save_time)
{
int ret = 1;
struct mp4_encode_msi_s *mp4_encode = (struct mp4_encode_msi_s *) msi->priv;
//struct msi *mp4_thumb_msi = NULL;
void *mp4_msg = NULL;
void *fp = NULL;
struct framebuff *fb = NULL;
int error = 0;
uint8_t nal_head_size;
uint32_t nal_size;
char h264_filename[64];
uint8_t *buf;
uint8_t *nal_head_buf;
uint8_t *last_nal_head_buf;
int write_size = 0;
uint32_t MP4_status = 0;
uint32_t write_start_time = os_jiffies();
//uint8_t sps_pps_flag = 0;
uint8_t last_num = 0;
(void) last_num;
uint32_t v_count = 0;
//uint32_t audio_save_time = 0;
// 参考minimp4的值去配置
//uint8_t asps_data[2] = {0x15, 0x88};
fp = creat_mp4_file2(TIME_LAPSE_DIR, h264_filename);
if (!fp)
{
os_sleep_ms(1);
goto mp4_encode_thread_end;
}
mp4_msg = MP4_open_init(fp, 0);
if (!mp4_msg)
{
goto mp4_encode_thread_end;
}
mp4_video_cfg_init(mp4_msg, 1280, 720);
ret = 0;
os_printf(KERN_INFO "%s:%d\n", __FUNCTION__, __LINE__);
while (fp)
{
//mp4_encode_running_again:
os_event_wait(&mp4_encode->evt, MSI_MP4_STOP, &MP4_status, OS_EVENT_WMODE_OR, 0);
// 结束写卡
if (MP4_status & MSI_MP4_STOP)
{
ret = 1;
goto mp4_encode_thread_end;
}
fb = msi_get_fb(msi, 0);
if (fb && (fb->mtype == F_H264))
{
buf = fb->data;
nal_head_size = 0;
nal_head_buf = buf;
// 记录最后一次nal的头位置,如果没有pps或者sps,这个就是I帧或者P帧
last_nal_head_buf = nal_head_buf;
mp4_encode_thread_again:
// 检查对应头部是否是nal
// nal_head_size = get_nal_size(buf + buf_offset, 64, 0,&cur_offset);
// 检查是否存在pps或者sps,只是检查64byte就好了,硬件产生
nal_head_buf = get_sps_pps_nal_size(nal_head_buf, 64, &nal_size, &nal_head_size);
// 找到pps或者sps
if (nal_head_buf)
{
error |= write_h264_pps_sps(mp4_msg, nal_head_buf, nal_size + nal_head_size);
if (0 != error)
{
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
goto mp4_encode_thread_end;
}
// 查看下一个nal是否为pps或者sps
nal_head_buf += (nal_size + nal_head_size);
last_nal_head_buf = nal_head_buf;
goto mp4_encode_thread_again;
}
// 写剩余的nal数据
_os_printf(KERN_INFO "T");
v_count++;
error |= write_h264_data(mp4_msg, last_nal_head_buf, fb->len - (last_nal_head_buf - fb->data), 120);
if (0 != error)
{
goto mp4_encode_thread_end;
}
write_size += (fb->len - (last_nal_head_buf - fb->data));
msi_delete_fb(NULL, fb);
fb = NULL;
if (os_jiffies() - write_start_time >= save_time)
{
goto mp4_encode_thread_end;
}
// 同步,缩时录影直接同步,不需要考虑定时,因为本身就是很久才录制一次
error |= mp4_syn(mp4_msg);
if (0 != error)
{
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
goto mp4_encode_thread_end;
}
}
else
{
msi_delete_fb(NULL, fb);
fb = NULL;
}
os_sleep_ms(1);
}
mp4_encode_thread_end:
if (fb)
{
msi_delete_fb(NULL, fb);
}
if (mp4_msg)
{
mp4_deinit(mp4_msg);
}
if (fp)
{
osal_fclose(fp);
fp = NULL;
}
// 如果遇到写入错误之类,直接删除缓冲区的吧,防止写卡慢导致后面的帧不是连续的
if (error)
{
while (1)
{
fb = msi_get_fb(msi, 0);
if (fb)
{
msi_delete_fb(NULL, fb);
}
else
{
break;
}
}
}
os_printf("mp4 encode end\n");
return ret;
}
// 测试文件保存,保存一分钟吧,一直录卡,直到关闭msi
static void mp4_encode_thread(void *d)
{
struct msi *msi = (struct msi *) d;
struct mp4_encode_msi_s *mp4_encode = (struct mp4_encode_msi_s *) msi->priv;
int ret = 0;
//int res;
msi_get(msi);
while (msi)
{
msi->enable = 1;
ret = mp4_encode_running(msi, 60 * 1000 * mp4_encode->rec_time);
msi->enable = 0;
os_printf("%s:%d end\n", __FUNCTION__, __LINE__);
if (ret)
{
break;
}
}
//mp4_encode_thread_end:
os_event_set(&mp4_encode->evt, MSI_MP4_THREAD_DEAD, NULL);
msi_put(msi);
os_printf("%s:%d end\n", __FUNCTION__, __LINE__);
}
static int32_t MP4_encode_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct mp4_encode_msi_s *mp4_encode = (struct mp4_encode_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
os_event_wait(&mp4_encode->evt, MSI_MP4_THREAD_DEAD, NULL, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
os_event_del(&mp4_encode->evt);
STREAM_LIBC_FREE(mp4_encode);
break;
case MSI_CMD_PRE_DESTROY:
os_event_set(&mp4_encode->evt, MSI_MP4_STOP, NULL);
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype == F_H264 && mp4_encode->filter_type != (uint16_t) ~0)
{
ret = RET_ERR;
if (mp4_encode->filter_type == fb->stype)
{
struct fb_h264_s *h264_s = (struct fb_h264_s *) fb->priv;
// 只是支持I帧
if (h264_s->type == 1)
{
// os_printf(KERN_INFO"recv I frame\n");
ret = RET_OK;
}
}
}
}
break;
case MSI_CMD_GET_RUNNING:
{
uint32_t rflags = 0;
os_event_wait(&mp4_encode->evt, MSI_MP4_THREAD_DEAD | MSI_MP4_STOP, &rflags, OS_EVENT_WMODE_OR, 0);
if (param1)
{
*(uint32_t *) param1 = (rflags & (MSI_MP4_THREAD_DEAD | MSI_MP4_STOP)) ? 0 : 1;
}
}
break;
}
return ret;
}
struct msi *time_lapse_mp4_encode_msi2_init(const char *mp4_msi_name, uint16_t filter_type, uint16_t rec_time)
{
struct mp4_encode_msi_s *mp4_encode = NULL;
struct msi *msi = msi_new(mp4_msi_name, 64, NULL);
if (msi && !msi->priv)
{
mp4_encode = (struct mp4_encode_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct mp4_encode_msi_s));
ASSERT(mp4_encode);
mp4_encode->filter_type = filter_type;
mp4_encode->rec_time = rec_time;
msi->priv = mp4_encode;
os_event_init(&mp4_encode->evt);
mp4_encode->msi = msi;
msi->action = MP4_encode_msi_action;
msi->enable = 1;
}
else
{
// 不要重复打开,同一个名称需要等待上一次写入完成后并且关闭后才可以重新打开
// 因为这里new了一次,所以要destroy一次(new和destroy要配对,实际msi的destroy是在另一个地方)
if (msi)
{
msi_destroy(msi);
msi = NULL;
goto mp4_encode_msi_init_end;
}
}
void *mp4_hdl = os_task_create("time_lapse_mp4", mp4_encode_thread, msi, OS_TASK_PRIORITY_NORMAL, 0, NULL, 2048);
os_printf("mp4_hdl:%X\n", mp4_hdl);
if (!mp4_hdl && mp4_encode)
{
os_event_set(&mp4_encode->evt, MSI_MP4_THREAD_DEAD, NULL);
}
mp4_encode_msi_init_end:
return msi;
}