Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources

This commit is contained in:
2026-07-06 11:30:13 +08:00
commit e76462eeb7
3451 changed files with 1415300 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
/**
* @file adts.c
* @brief
* @author licaibiao
* @version 1.0
* @date 2023-12-06
*
* @copyright Copyright (c) 2023-2030 liwen01 Technology Co., Ltd
*
*/
#include "adts.h"
#include <math.h>
void set_fixed_header(adts_fixed_header *header) {
header->syncword = 0xFF;// 代表着一个帧的开始
header->id = 1;// MPEG Version: 0 for MPEG-4, 1 for MPEG-2
header->layer = 0;// always 00
header->protection_absent = 1;// 表示是否误码校验
header->profile = 1;// 表示使用哪个级别的AAC, 有些芯片只支持AAC LC.
header->sampling_frequency_index = 0xb;// 表示使用的采样率的下标
header->private_bit = 0;
header->channel_configuration = 1;// 表示声道数
header->original_copy = 0;
header->home = 0;
}
void set_variable_header(adts_variable_header *header, const int aac_raw_data_length) {
header->copyright_identification_bit = 0;
header->copyright_identification_start = 0;
header->aac_frame_length = aac_raw_data_length + 7;
header->adts_buffer_fullness = 0x7f;
header->number_of_raw_data_blocks_in_frame = 0;
}
void get_fixed_header(const unsigned char buff[7], adts_fixed_header *header) {
unsigned long long adts = 0;
const unsigned char *p = buff;
adts |= *p ++; adts <<= 8;
adts |= *p ++; adts <<= 8;
adts |= *p ++; adts <<= 8;
adts |= *p ++; adts <<= 8;
adts |= *p ++; adts <<= 8;
adts |= *p ++; adts <<= 8;
adts |= *p ++;
header->syncword = (adts >> 44);
header->id = (adts >> 43) & 0x01;
header->layer = (adts >> 41) & 0x03;
header->protection_absent = (adts >> 40) & 0x01;
header->profile = (adts >> 38) & 0x03;
header->sampling_frequency_index = (adts >> 34) & 0x0f;
header->private_bit = (adts >> 33) & 0x01;
header->channel_configuration = (adts >> 30) & 0x07;
header->original_copy = (adts >> 29) & 0x01;
header->home = (adts >> 28) & 0x01;
}
void get_variable_header(const unsigned char buff[7], adts_variable_header *header) {
unsigned long long adts = 0;
adts = buff[0]; adts <<= 8;
adts |= buff[1]; adts <<= 8;
adts |= buff[2]; adts <<= 8;
adts |= buff[3]; adts <<= 8;
adts |= buff[4]; adts <<= 8;
adts |= buff[5]; adts <<= 8;
adts |= buff[6];
header->copyright_identification_bit = (adts >> 27) & 0x01;
header->copyright_identification_start = (adts >> 26) & 0x01;
header->aac_frame_length = (adts >> 13) & ((int)pow(2, 14) - 1);
header->adts_buffer_fullness = (adts >> 2) & ((int)pow(2, 11) - 1);
header->number_of_raw_data_blocks_in_frame = adts & 0x03;
}
void convert_adts_header2int64(const adts_fixed_header *fixed_header, const adts_variable_header *variable_header, uint64_t *header) {
uint64_t ret_value = 0;
ret_value = ((fixed_header->syncword<<4) | 0x0F);
ret_value <<= 1;
ret_value |= fixed_header->id;
ret_value <<= 2;
ret_value |= fixed_header->layer;
ret_value <<= 1;
ret_value |= (fixed_header->protection_absent) & 0x01;
ret_value <<= 2;
ret_value |= fixed_header->profile;
ret_value <<= 4;
ret_value |= (fixed_header->sampling_frequency_index) & 0x0f;
ret_value <<= 1;
ret_value |= (fixed_header->private_bit) & 0x01;
ret_value <<= 3;
ret_value |= (fixed_header->channel_configuration) & 0x07;
ret_value <<= 1;
ret_value |= (fixed_header->original_copy) & 0x01;
ret_value <<= 1;
ret_value |= (fixed_header->home) & 0x01;
ret_value <<= 1;
ret_value |= (variable_header->copyright_identification_bit) & 0x01;
ret_value <<= 1;
ret_value |= (variable_header->copyright_identification_start) & 0x01;
ret_value <<= 13;
ret_value |= (variable_header->aac_frame_length) & ((int)pow(2, 13) - 1);
ret_value <<= 11;
ret_value |= ((variable_header->adts_buffer_fullness<<4) | 0x0F ) & ((int)pow(2, 11) - 1);
ret_value <<= 2;
ret_value |= (variable_header->number_of_raw_data_blocks_in_frame) & ((int)pow(2, 2) - 1);
*header = ret_value;
}
void convert_adts_header2char(const adts_fixed_header *fixed_header, const adts_variable_header *variable_header, unsigned char buffer[7]) {
uint64_t value = 0;
convert_adts_header2int64(fixed_header, variable_header, &value);
buffer[0] = (value >> 48) & 0xff;
buffer[1] = (value >> 40) & 0xff;
buffer[2] = (value >> 32) & 0xff;
buffer[3] = (value >> 24) & 0xff;
buffer[4] = (value >> 16) & 0xff;
buffer[5] = (value >> 8) & 0xff;
buffer[6] = (value) & 0xff;
}
void aac_dsi_to_adts(uint8_t *dsi, uint8_t *adts, uint32_t aac_data_length)
{
uint8_t audio_object_type = (dsi[0] >> 3) & 0x1F;
uint8_t profile = audio_object_type - 1;
uint8_t samplerate_index = ((dsi[0] & 0x07) << 1) | (dsi[1] >> 7);
uint8_t channels = (dsi[1] >> 3) & 0x0F;
;uint32_t frame_length = aac_data_length + 7;
adts[0] = 0xFF; //syncword hight 8 bits
adts[1] = 0xF0; //syncword low 4 bits
adts[1] |= (0x01 << 3); //ID 0:MPEG-4,1:MPEG-2
adts[1] |= (0x00 << 1); //layer:00
adts[1] |= (0x01 & 0x1); //1:no CRC,0:is CRC
adts[2] = (profile << 6); //profile 0x01:AAC-LC
adts[2] |= (samplerate_index << 2);
adts[2] |= (0x00 << 1); //private_bit (0)
adts[2] |= (channels & 0x4) >> 2;
adts[3] = (channels & 0x3) << 6;
adts[3] |= (frame_length >> 11); //frame_length bits12-bits13
adts[4] = (frame_length >> 3) & 0xFF; //frame_length bits4-bits11
adts[5] = (frame_length & 0x07) << 5; //frame_length low 3 bits first
adts[5] |= (0x7FF >> 6) & 0x1F; //adts_buffer_fullness high 5 bits
adts[6] = (0x7FF & 0x3F) << 2; //adts_buffer_fullness low 6 bits
adts[6] |= (0x00); //number_of_raw_data_blocks_in_frame (0)
}

View File

@@ -0,0 +1,67 @@
/**
* @file adts.h
* @brief
* @author licaibiao
* @version 1.0
* @date 2023-12-06
*
* @copyright Copyright (c) 2023-2030 liwen01 Technology Co., Ltd
*
*/
#ifndef __AdtsHeader__adts__
#define __AdtsHeader__adts__
#include <stdio.h>
#include <stdint.h>
/*
* ADTS 全称: Audio Data Transport Stream. 是 AAC音频的传输流格式.
* ADTS头中相对有用的信息包括: 采样率, 声道数, 帧长度. 每一个带ADTS头信息的AAC流都会清晰的告诉解码器他需要的这些信息.
* 一般情况下ADTS的头信息都是7个字节, 分为两部分: 前28bit是 adts_fixed_header, 后28bit是 adts_variable_header.
*/
typedef struct adts_fixed_header {
unsigned short syncword: 12;
unsigned char id: 1;
unsigned char layer: 2;
unsigned char protection_absent: 1;
unsigned char profile: 2;
unsigned char sampling_frequency_index: 4;
unsigned char private_bit: 1;
unsigned char channel_configuration: 3;
unsigned char original_copy: 1;
unsigned char home: 1;
} adts_fixed_header; // length : 28 bits
typedef struct adts_variable_header {
unsigned char copyright_identification_bit: 1;
unsigned char copyright_identification_start: 1;
unsigned short aac_frame_length: 13;
unsigned short adts_buffer_fullness: 11;
unsigned char number_of_raw_data_blocks_in_frame:2;
} adts_variable_header; // length : 28 bits
// 设置adts固定的一部分
extern void set_fixed_header(adts_fixed_header *header);
// 设置adts可变的一部分
extern void set_variable_header(adts_variable_header *header, const int aac_raw_data_length);
// 解析buff中的adts固定部分头信息
extern void get_fixed_header(const unsigned char buff[7], adts_fixed_header *header);
// 解析buff中的adts可变部分头信息
extern void get_variable_header(const unsigned char buff[7], adts_variable_header *header);
// 将adts头信息转化为一个64位的整数, PS: 前面8位空着
extern void convert_adts_header2int64(const adts_fixed_header *fixed_header, const adts_variable_header *variable_header, uint64_t *header);
// 将adts头信息转化为一个7字节的buff
extern void convert_adts_header2char(const adts_fixed_header *fixed_header, const adts_variable_header *variable_header, unsigned char buffer[7]);
extern void aac_dsi_to_adts(uint8_t *dsi, uint8_t *adts, uint32_t aac_data_length);
#endif /* defined(__AdtsHeader__adts__) */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
/**********************************************************************
* 缩时录影的基本代码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;
}