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

696
sdk/app/playback/avi.c Normal file
View File

@@ -0,0 +1,696 @@
#include "basic_include.h"
#include "osal_file.h"
#include "avi.h"
#include "utlist.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
//目前没有做兼容性,所以只是识别strh和strl,也不考虑顺序,读取完这两个段就退出
void read_strh_strf(void *fp,struct avi_msg_s *avi_msg)
{
struct chunk_s str;
AVIStreamHeader stream;
WAVEFORMATEX wav;
uint32_t offset;
uint8_t video_or_audio = 0;
while(1)
{
osal_fread(&str,1,sizeof(str),fp);
offset = osal_ftell(fp);
if(strncasecmp((void*)&(str.dwFourCC) ,"strh",4) == 0)
{
//分析流AVIStreamHeader
osal_fread(&stream,1,sizeof(stream),fp);
if(strncasecmp((void*)&(stream.fccType) ,"vids",4) == 0)
{
if(avi_msg->offset == 0)
{
avi_msg->v_id = 0x63643030; // "00dc"
}
else
{
avi_msg->v_id = 0x63643130; // "01dc"
}
video_or_audio = 0;
avi_msg->fps = (stream.dwRate + (stream.dwScale >> 2))/stream.dwScale;
avi_msg->offset++;
}
else if(strncasecmp((void*)&(stream.fccType) ,"auds",4) == 0)
{
if(avi_msg->offset == 0)
{
avi_msg->a_id = 0x62773030; // "00wb"
}
else
{
avi_msg->a_id = 0x62773130; // "01wb"
}
video_or_audio = 1;
avi_msg->offset++;
}
//偏移
osal_fseek(fp,offset + str.size);
}
else if(strncasecmp((void*)&(str.dwFourCC) ,"strf",4) == 0)
{
//视频就不要读取了
if(video_or_audio == 0)
{
}
//音频就读取看看采样率
else if(video_or_audio == 1)
{
osal_fread(&wav,1,sizeof(wav),fp);
//仅仅支持pcm
if(wav.wFormatTag == 1)
{
avi_msg->sample = wav.nSamplesPerSec;
}
else
{
avi_msg->sample = 0;
}
}
//分析流
osal_fseek(fp,offset + str.size);
//默认读取到strf就退出,所以遇到strf在前面,就会有异常咯
break;
}
else
{
break;
}
}
}
void *avi_read_init(const char *filename)
{
struct avi_msg_s *avi_msg = NULL;
int err = 1;
char data[32];
char chunk_name[5] = {0};
uint32_t readlen;
uint32_t chunk_size;
uint32_t seek_offset = 0;
uint32_t hdrl_offset = 0;
uint32_t offset;
void *fp = osal_fopen(filename,"rb");
if(!fp)
{
goto avi_read_init_end;
}
readlen = osal_fread(data,1,12,fp);
if(readlen < 12)
{
goto avi_read_init_end;
}
if( strncasecmp(data ,"RIFF",4) !=0 ||strncasecmp(data+8,"AVI ",4) !=0 )
{
os_printf("avi file err\n");
goto avi_read_init_end;
}
avi_msg = (struct avi_msg_s*)STREAM_LIBC_ZALLOC(sizeof(struct avi_msg_s));
if(!avi_msg)
{
err = 2;
goto avi_read_init_end;
}
avi_msg->fp = fp;
while(1)
{
readlen = osal_fread(data,1,8,fp);
if(readlen<8)
{
if(readlen)
{
os_printf("%s:%d err!!!!!!!!!!!!!\t%d\tosal_ftell:%d\n",__FUNCTION__,__LINE__,readlen,osal_ftell(fp));
goto avi_read_init_end;
}
else
{
err = 0;
goto avi_read_init_continue;
}
}
os_memcpy(&chunk_size,data+4,4);
chunk_size = (chunk_size+1)&(~1);
os_printf("chunk_size:%X\t%d\ttell:%X\n",chunk_size,chunk_size,osal_ftell(fp));
os_memcpy(chunk_name,data,4);
if(strncasecmp(data ,"LIST",4) == 0)
{
readlen = osal_fread(data,1,4,fp);
chunk_size -= 4;
os_memcpy(chunk_name,data,4);
//os_printf("chunk_name[%d]:%s\n",__LINE__,chunk_name);
if(strncasecmp(data ,"hdrl",4) == 0)
{
seek_offset = chunk_size + osal_ftell(fp);
hdrl_offset = osal_ftell(fp);
//readlen = osal_fread(&avi_msg->avi,1,sizeof(AVIMainHeader),fp);
//os_printf("width:%d\theight:%d\n",avi_msg->avi.dwWidth,avi_msg->avi.dwHeight);
//os_printf("seek_offset:%X\n",seek_offset);
osal_fseek(fp,seek_offset);
}
else if(strncasecmp(data ,"INFO",4) == 0)
{
seek_offset = chunk_size + osal_ftell(fp);
osal_fseek(fp,seek_offset);
}
else if(strncasecmp(data ,"movi",4) == 0)
{
avi_msg->movi_addr = osal_ftell(fp) - 4;
//os_printf("movi_addr:%X\n",avi_msg->movi_addr);
seek_offset = chunk_size + osal_ftell(fp);
osal_fseek(fp,seek_offset);
}
else
{
os_printf("%s:%d err!!!!!!!!!!!!!\n",__FUNCTION__,__LINE__);
goto avi_read_init_end;
}
}
else if(strncasecmp(data ,"JUNK",4) == 0)
{
seek_offset = chunk_size + osal_ftell(fp);
osal_fseek(fp,seek_offset);
}
else if(strncasecmp(data ,"idx1",4) == 0)
{
avi_msg->idx1_addr = osal_ftell(fp);
avi_msg->idx1_size = chunk_size;
os_printf("idx1_addr:%X\tidx1_size:%d\n",avi_msg->idx1_addr,avi_msg->idx1_size);
seek_offset = chunk_size + osal_ftell(fp);
osal_fseek(fp,seek_offset);
}
else
{
os_printf("%s:%d err!!!!!!!!!!!!!\n",__FUNCTION__,__LINE__);
goto avi_read_init_end;
}
}
avi_read_init_continue:
if(hdrl_offset)
{
os_printf("hdrl_offset:%d\n",hdrl_offset);
osal_fseek(fp,hdrl_offset);
readlen = osal_fread(&avi_msg->avi,1,sizeof(AVIMainHeader),fp);
//开始解析是否有音频、视频.
struct chunk_s List_msg;
avi_read_init_List_again:
readlen = osal_fread(&List_msg,1,sizeof(List_msg),fp);
offset = osal_ftell(fp);
//有流,去分析一下有哪些流
if(strncasecmp((void*)&(List_msg.dwFourCC) ,"LIST",4) == 0)
{
//尝试读取strl
readlen = osal_fread(data,1,4,fp);
os_memcpy(chunk_name,data,4);
os_printf("chunk_name[%d]:%s\n",__LINE__,chunk_name);
//如果有strl,代表有流
if(strncasecmp(data ,"strl",4) == 0)
{
//读取一下流,读取完毕后要seek到后面的块,然后重复读取是否有下一个流
read_strh_strf(fp,avi_msg);
//跳过List,重复读取
osal_fseek(fp,offset+List_msg.size);
goto avi_read_init_List_again;
}
}
}
avi_read_init_end:
if(err)
{
if(avi_msg)
{
STREAM_LIBC_FREE(avi_msg);
avi_msg = NULL;
}
if(fp)
{
osal_fclose(fp);
}
}
else
{
read_msg_init(avi_msg);
avi_msg->video_frame_num = 0;
avi_msg->audio_frame_num = 0;
avi_msg->video_file_addr = avi_msg->idx1_addr;
avi_msg->video_not_read_size = avi_msg->idx1_size;
avi_msg->video_cache_remain = 0;
avi_msg->audio_file_addr = avi_msg->idx1_addr;
avi_msg->audio_not_read_size = avi_msg->idx1_size;
avi_msg->audio_cache_remain = 0;
}
os_printf("err:%d\n",err);
return avi_msg;
}
struct fast_index_list *save_fast_index_list(struct fast_index_list *fast_index,uint32_t avi_index,uint32_t audio_index,uint32_t offset)
{
struct fast_index_list *list = (struct fast_index_list*)STREAM_LIBC_MALLOC(sizeof(struct fast_index_list));
if(!list)
{
return fast_index;
}
list->next = NULL;
list->prev = NULL;
list->avi_index = avi_index;
list->audio_index = audio_index;
list->offset = offset;
DL_APPEND(fast_index, list);
return fast_index;
}
static struct fast_index_list * parse_idx1_continue(struct fast_index_list *fast_index,struct avi_msg_s *avi_msg,char *buf,uint32_t size)
{
if(size%16 != 0)
{
return fast_index;
}
struct idx1_s *idx1 = (struct idx1_s*)buf;
struct avi_read_msg_s *avi_read_msg = avi_msg->avi_read_msg;
uint32_t offset = avi_read_msg->addr_offset;
for(int i=0;i<size/16;i++)
{
//idx1->dwFlags = 0;
//音频
if(idx1->dwChunkId == avi_msg->a_id && idx1->dwFlags)
{
avi_read_msg->audio_index += idx1->dwSize;
}
//视频
else if(idx1->dwChunkId == avi_msg->v_id)
{
if(avi_read_msg->avi_index%512 == 0 && avi_read_msg->avi_index>0)
{
fast_index = save_fast_index_list(fast_index,avi_read_msg->avi_index,avi_read_msg->audio_index,offset);
}
avi_read_msg->avi_index++;
}
idx1++;
offset+=16;
}
return fast_index;
}
//实现快进快退功能(跳转帧)
void avi_jmp_index(struct avi_msg_s *avi_msg,uint32_t index)
{
//索引快速索引表,看看最接近的帧,如果是0,则从头开始,如果非0,就索引
}
//往下读取到下一帧到对应的视频帧
void avi_read_next_index(struct avi_msg_s *avi_msg,uint32_t *offset,uint32_t *size,uint32_t *frame_num)
{
uint32_t read_size;
avi_read_next_index_again:
if(avi_msg->video_cache_remain > 0)
{
//不符合视频,就继续去读取,知道读取到视频帧
if(!(avi_msg->video_idx1->dwChunkId == avi_msg->v_id && avi_msg->video_idx1->dwFlags))
{
avi_msg->video_idx1++;
avi_msg->video_cache_remain -= sizeof(struct idx1_s);
goto avi_read_next_index_again;
}
//继续读取索引
*offset = avi_msg->video_idx1->dwOffset + avi_msg->movi_addr+8;
*size = avi_msg->video_idx1->dwSize;
//进行偏移
avi_msg->video_frame_num++;
avi_msg->video_idx1++;
avi_msg->video_cache_remain -= sizeof(struct idx1_s);
//还没有读取到对应的帧,就要继续读取
if(*frame_num >= avi_msg->video_frame_num)
{
goto avi_read_next_index_again;
}
*frame_num = avi_msg->video_frame_num;
return;
}
else
{
if(avi_msg->video_not_read_size >= 512)
{
read_size = 512;
}
else
{
read_size = avi_msg->video_not_read_size;
}
if(read_size)
{
osal_fseek(avi_msg->fp,avi_msg->video_file_addr);
osal_fread(avi_msg->video_buf,1,read_size,avi_msg->fp);
avi_msg->video_not_read_size -= read_size;
avi_msg->video_file_addr += read_size;
avi_msg->video_cache_remain = read_size;
avi_msg->video_idx1 = (struct idx1_s *)avi_msg->video_buf;
goto avi_read_next_index_again;
}
//读取结束
else
{
*offset = 0;
*size = 0;
return;
}
}
}
void avi_read_next_audio_index(struct avi_msg_s *avi_msg,uint32_t *offset,uint32_t *size,uint32_t *frame_num)
{
uint32_t read_size;
avi_read_next_audio_index_again:
if(avi_msg->audio_cache_remain > 0)
{
//不符合视频,就继续去读取,知道读取到视频帧
//os_printf("avi_msg->audio_idx1->dwChunkId:%08X\n",avi_msg->audio_idx1->dwChunkId);
if(!(avi_msg->audio_idx1->dwChunkId == avi_msg->a_id && avi_msg->audio_idx1->dwFlags))
{
avi_msg->audio_idx1++;
avi_msg->audio_cache_remain -= sizeof(struct idx1_s);
goto avi_read_next_audio_index_again;
}
//继续读取索引
*offset = avi_msg->audio_idx1->dwOffset + avi_msg->movi_addr+8;
*size = avi_msg->audio_idx1->dwSize;
//os_printf("offset:%X\tsize:%X\n",*offset,*size);
//进行偏移
avi_msg->audio_frame_num += (*size);
avi_msg->audio_idx1++;
avi_msg->audio_cache_remain -= sizeof(struct idx1_s);
//还没有读取到对应的帧,就要继续读取
if(*frame_num >= avi_msg->audio_frame_num)
{
goto avi_read_next_audio_index_again;
}
*frame_num = avi_msg->audio_frame_num;
return;
}
else
{
if(avi_msg->audio_not_read_size >= 512)
{
read_size = 512;
}
else
{
read_size = avi_msg->audio_not_read_size;
}
if(read_size)
{
osal_fseek(avi_msg->fp,avi_msg->audio_file_addr);
osal_fread(avi_msg->audio_buf,1,read_size,avi_msg->fp);
avi_msg->audio_not_read_size -= read_size;
avi_msg->audio_file_addr += read_size;
avi_msg->audio_cache_remain = read_size;
avi_msg->audio_idx1 = (struct idx1_s *)avi_msg->audio_buf;
goto avi_read_next_audio_index_again;
}
//读取结束
else
{
*offset = 0;
*size = 0;
return;
}
}
}
void gen_fast_index_list(struct avi_msg_s *avi_msg)
{
uint32_t read_size = 0;
struct avi_read_msg_s *read_msg = avi_msg->avi_read_msg;
//读取完毕后,需要释放空间,保留快速索引表
if(read_msg && read_msg->remain_read_size)
{
osal_fseek(avi_msg->fp,read_msg->addr_offset);
if(read_msg->remain_read_size >= 512)
{
read_size = 512;
}
else
{
read_size = read_msg->remain_read_size;
}
osal_fread((void*)read_msg->buf,1,read_size,avi_msg->fp);
avi_msg->fast_index = parse_idx1_continue(avi_msg->fast_index,avi_msg,(char*)read_msg->buf,read_size);
read_msg->addr_offset += read_size;
read_msg->remain_read_size -= read_size;
}
else
{
if(read_msg)
{
read_msg_deinit(avi_msg);
}
}
}
//定位索引点,重新计算
void locate_avi_index(struct avi_msg_s *avi_msg,uint32_t index)
{
if(index == 0)
{
avi_msg->video_frame_num = 0;
avi_msg->video_cache_remain = 0;
avi_msg->video_not_read_size = avi_msg->idx1_size;
avi_msg->video_file_addr = avi_msg->idx1_addr;
avi_msg->audio_frame_num = 0;
avi_msg->audio_cache_remain = 0;
avi_msg->audio_file_addr = avi_msg->idx1_addr;
avi_msg->audio_not_read_size = avi_msg->idx1_size;
}
else
{
struct fast_index_list *fast_index = avi_msg->fast_index;
struct fast_index_list *list,*tmp;
struct fast_index_list *last_index = NULL;
DL_FOREACH_SAFE(fast_index,list,tmp)
{
if(list->avi_index > index)
{
break;
}
//完全匹配,保存last_index;
else if(list->avi_index == index)
{
last_index = list;
break;
}
last_index = list;
}
//如果存在,则就从这里开始索引,一直索引到对应的index
if(last_index)
{
avi_msg->video_frame_num = last_index->avi_index;
avi_msg->video_cache_remain = 0;
//通过偏移,计算还有多少数据没有被读取
avi_msg->video_not_read_size = (avi_msg->idx1_size-(last_index->offset-avi_msg->idx1_addr));
avi_msg->video_file_addr = last_index->offset;
}
//从0开始索引
else
{
avi_msg->video_frame_num = 0;
avi_msg->video_cache_remain = 0;
avi_msg->video_not_read_size = avi_msg->idx1_size;
avi_msg->video_file_addr = avi_msg->idx1_addr;
avi_msg->audio_frame_num = 0;
avi_msg->audio_cache_remain = 0;
avi_msg->audio_file_addr = avi_msg->idx1_addr;
avi_msg->audio_not_read_size = avi_msg->idx1_size;
}
//os_printf("last_index:%X\tsample:%X\n",last_index,avi_msg->sample);
//索引到了视频,那么就要开始索引音频了,不过需要先计算当前视频的时间,然后计算出音频位置,最后定位
if(avi_msg->sample)
{
//index是要快进或者快退到的视频点
uint32_t video_time = index*1000/avi_msg->fps;
uint32_t audio_offset = video_time*avi_msg->sample*2/1000;
//os_printf("audio want to offset time:%d\toffset:%d\n",video_time,audio_offset);
last_index = NULL;
//查找最近的快速索引位置
DL_FOREACH_SAFE(fast_index,list,tmp)
{
//os_printf("list->audio_index:%d\n",list->audio_index);
if(list->audio_index > audio_offset)
{
break;
}
//完全匹配,保存last_index;
else if(list->audio_index == index)
{
last_index = list;
break;
}
last_index = list;
}
//索引到了音频位置
if(last_index)
{
//计算当前索引的音频位置
//("index audio_offset:%d\n",list->audio_index);
//这个代表当前缓冲区的索引位置
avi_msg->audio_frame_num = list->audio_index;
avi_msg->audio_cache_remain = 0;
//通过偏移,计算还有多少数据没有被读取
avi_msg->audio_not_read_size = (avi_msg->idx1_size-(last_index->offset-avi_msg->idx1_addr));
avi_msg->audio_file_addr = last_index->offset;
}
else
{
avi_msg->audio_frame_num = 0;
avi_msg->audio_cache_remain = 0;
avi_msg->audio_not_read_size = avi_msg->idx1_size;
avi_msg->audio_file_addr = avi_msg->idx1_addr;
}
}
}
}
static void free_fast_index_list(struct fast_index_list *fast_index)
{
struct fast_index_list *list,*tmp;
DL_FOREACH_SAFE(fast_index,list,tmp)
{
//从已有链表释放
DL_DELETE(fast_index,list);
STREAM_LIBC_FREE(list);
}
}
void avi_deinit(struct avi_msg_s *avi_msg)
{
if(avi_msg)
{
os_printf("%s %d\n",__FUNCTION__,__LINE__);
osal_fclose(avi_msg->fp);
//如果没有释放,就会释放(快速索引在索引完后,会自动释放,但是如果在没有索引完毕又要关闭视频,就需要在这里释放)
read_msg_deinit(avi_msg);
if(avi_msg->fast_index)
{
free_fast_index_list(avi_msg->fast_index);
}
STREAM_LIBC_FREE(avi_msg);
}
}
void parse_idx1(char *buf,uint32_t size)
{
if(size%16 != 0)
{
return;
}
struct idx1_s *idx1 = (struct idx1_s*)buf;
for(int i=0;i<size/16;i++)
{
idx1->dwFlags = 0;
os_printf("id:%s\tflags:00000010\toffset:%08X\tsize:%08X\n",(char*)&idx1->dwChunkId,idx1->dwOffset,idx1->dwSize);
idx1+=1;
}
}
struct avi_read_msg_s *read_msg_init(struct avi_msg_s *avi_msg)
{
struct avi_read_msg_s *read_msg = STREAM_LIBC_MALLOC(sizeof(struct avi_read_msg_s));
if(read_msg)
{
read_msg->addr_offset = avi_msg->idx1_addr;
read_msg->avi_index = 0;
read_msg->audio_index = 0;
read_msg->remain_read_size = avi_msg->idx1_size;
}
avi_msg->avi_read_msg = read_msg;
return read_msg;
}
void read_msg_deinit(struct avi_msg_s *avi_msg)
{
if(avi_msg->avi_read_msg)
{
STREAM_LIBC_FREE(avi_msg->avi_read_msg);
avi_msg->avi_read_msg = NULL;
}
}

142
sdk/app/playback/avi.h Normal file
View File

@@ -0,0 +1,142 @@
#ifndef __AVI_H
#define __AVI_H
#include "typesdef.h"
#include "stream_frame.h"
typedef struct {
uint32_t dwFourCC; // 必须为'avih'
uint32_t dwSize; // 本数据结构的大小不包括最初的8个字节(dwFourCC和dwSize两个字段)
uint32_t dwMicroSecPerFrame; // 每帧的持续时间(以毫秒ms为单位)定义avi的显示速率
uint32_t dwMaxBytesPerSec; // 最大的数据传输率
uint32_t dwPaddingGranularity; // 数据填充的粒度
uint32_t dwFlages; // AVI文件的特殊属性如是否包含索引块音视频数据是否交叉存储
uint32_t dwTotalFrame; // 文件中的总帧数
uint32_t dwInitialFrames; // 说明在开始播放前需要多少桢
uint32_t dwStreams; // 文件中包含的数据流个数
uint32_t dwSuggestedBufferSize; // 建议使用的缓冲区的大小,
// 通常为存储一桢图像以及同步声音所需要的数据之和
uint32_t dwWidth; // 图像宽
uint32_t dwHeight; // 图像高
uint32_t dwReserved[4]; // 保留值
} AVIMainHeader;
typedef struct {
uint32_t fccType; // 4字节表示数据流的种类vids表示视频数据流auds表示音频数据流
uint32_t fccHandler; // 4字节 ,表示数据流解压缩的驱动程序代号
uint32_t dwFlags; // 数据流属性
uint16_t wPriority; // 此数据流的播放优先级
uint16_t wLanguage; // 音频的语言代号
uint32_t dwInitalFrames; // 说明在开始播放前需要多少桢
uint32_t dwScale; // 数据量,视频每桢的大小或者音频的采样大小
uint32_t dwRate; // dwScale/dwRate = 每秒的采样数
uint32_t dwStart; // 数据流开始播放的位置以dwScale为单位
uint32_t dwLength; // 数据流的数据量以dwScale为单位
uint32_t dwSuggestedBufferSize; // 建议缓冲区的大小
uint32_t dwQuality; // 解压缩质量参数,值越大,质量越好
uint32_t dwSampleSize; // 音频的采样大小
uint16_t rcFrame[4]; // 视频图像所占的矩形
}AVIStreamHeader;
//实际标准后面可能还有信息,这里就不解析了
typedef struct tWAVEFORMATEX {
uint16_t wFormatTag;
uint16_t nChannels;
uint32_t nSamplesPerSec;
uint32_t nAvgBytesPerSec;
uint16_t nBlockAlign;
uint16_t wBitsPerSample;
uint16_t cbSize;
} WAVEFORMATEX;
struct avi_list
{
uint32_t offset;
uint32_t size;
};
typedef struct avi_list avi_list_audio_s;
typedef struct avi_list avi_list_photo_s;
struct idx1_s
{
uint32_t dwChunkId;
uint32_t dwFlags;
uint32_t dwOffset;
uint32_t dwSize;
};
//快速索引表
struct fast_index_list
{
struct fast_index_list *next;
struct fast_index_list *prev;
uint32_t avi_index; //当前视频index的值,这个记录的是视频的帧数
uint32_t audio_index; //当前音频index,这个记录的是音频的长度
uint32_t offset; //当前的偏移值,读取的第一个index就是对应avi或者index
};
struct avi_msg_s
{
AVIMainHeader avi;
void *fp;
uint32_t fps;
uint32_t sample;
uint32_t movi_addr;
uint32_t idx1_addr;
uint32_t idx1_size;
struct avi_read_msg_s *avi_read_msg;
struct fast_index_list *fast_index;
//当前读取视频帧的buf
uint32_t video_buf[512/4]; //当前读取视频帧的索引的缓冲区
uint32_t video_file_addr; //当前读取视频帧的地址
int32_t video_frame_num; //视频帧数
uint32_t video_not_read_size; //剩余长度
int32_t video_cache_remain; //缓存剩余长度
struct idx1_s *video_idx1;
//当前读取音频帧的buf
uint32_t audio_buf[512/4]; //当前读取音频帧的索引的缓冲区
uint32_t audio_file_addr; //当前读取音频帧的地址
int32_t audio_frame_num; //音频的读取偏移长度
uint32_t audio_not_read_size; //剩余长度
int32_t audio_cache_remain; //缓存剩余长度
struct idx1_s *audio_idx1;
uint8_t offset;
uint32_t v_id,a_id;
};
struct chunk_s
{
uint32_t dwFourCC;
uint32_t size;
};
struct avi_read_msg_s
{
uint32_t addr_offset;
uint32_t avi_index;
uint32_t audio_index;
uint32_t remain_read_size;
uint32_t buf[512/4];
};
void *avi_read_init(const char *filename);
void parse_idx1(char *buf,uint32_t size);
void avi_deinit(struct avi_msg_s *avi_msg);
struct avi_read_msg_s *read_msg_init(struct avi_msg_s *avi_msg);
void gen_fast_index_list(struct avi_msg_s *avi_msg);
void read_msg_deinit(struct avi_msg_s *avi_msg);
void locate_avi_index(struct avi_msg_s *avi_msg,uint32_t index);
void avi_read_next_index(struct avi_msg_s *avi_msg,uint32_t *offset,uint32_t *size,uint32_t *frame_num);
void avi_read_next_audio_index(struct avi_msg_s *avi_msg,uint32_t *offset,uint32_t *size,uint32_t *frame_num);
#endif

View File

@@ -0,0 +1,422 @@
#include "avi_player_msi.h"
/*****************************************************************************
* AVI1.0
*****************************************************************************/
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#define AVI_PLAYER_MAX_TX_NUM 8
#define AVI_STREAM_NAME (64)
#define AUDIO_TMP_BUF_SIZE (1024)
struct player_msg_s
{
void *task;
struct msi *msi;
struct fbpool tx_pool;
uint32_t magic;
struct avi_msg_s *avi_msg;
struct fast_index_list *g_fast_index_list;
uint32_t audio_frame_number; //读取索引的长度
uint32_t audio_frame_number_send; //已经播放的长度
uint32_t frame_number; //当前读取的帧
uint32_t not_read_size; //剩余未读取的空间长度
uint32_t cache_addr; //读取下一次的偏移地址
struct framebuff *data_tmp;
struct framebuff *audio_data_tmp;
int32_t locate_frame_number; //跳转视频的偏移
uint8_t running: 1, thread_stop : 1, speed_flag:1, rev: 5;
};
static void avi_player_thread(void *d)
{
struct player_msg_s *msg = (struct player_msg_s *)d;
struct msi *msi = msg->msi;
struct avi_msg_s *avi_msg = msg->avi_msg;
uint32_t magic = 0;
uint8_t *audio_tmp_buf = (uint8_t *)STREAM_LIBC_MALLOC(AUDIO_TMP_BUF_SIZE);
uint32_t now_audio_offset = 0;
uint32_t video_time = 0;
uint32_t audio_time = 0;
if (!avi_msg->sample)
{
audio_time = ~0; //没有音频
}
while (!msg->thread_stop)
{
if (!msi->enable)
{
//这里是否可以考虑先去建立部分快速索引?
goto avi_player_thread_end;
}
//正常这里去建立快速索引
gen_fast_index_list(avi_msg);
//判断一下是否要快进或者快退?
//所以先设置locate_frame_number,再配置speed_flag
if (msg->speed_flag)
{
msg->speed_flag = 0;
locate_avi_index(avi_msg,msg->locate_frame_number);
msg->frame_number = msg->locate_frame_number;
magic = msg->magic;
video_time = msg->frame_number * 1000 / avi_msg->fps;
//os_printf("msg->frame_number:%d\n",msg->frame_number);
if (avi_msg->sample)
{
//重置音频,音频是跟着视频走的,表示音频和video同步
msg->audio_frame_number_send = video_time * avi_msg->sample * 2 / 1000;;
audio_time = msg->audio_frame_number_send * 500 / avi_msg->sample;
now_audio_offset = 0;
//实际音频索引的位置应该在前面,意思是快速索引在前面,实际播放时间可能在后面,后面需要慢慢去轮询读取到对应的音频
msg->audio_frame_number = avi_msg->audio_frame_num;
}
}
//如果视频比音频播放前,就需要去读取音频了
if (video_time > audio_time)
{
//os_printf("goto avi_player_thread_audio_start:%d\n",__LINE__);
goto avi_player_thread_audio_start;
}
if (!msg->data_tmp)
{
msg->data_tmp = fbpool_get(&msg->tx_pool, 0, msi);
if (!msg->data_tmp)
{
goto avi_player_thread_audio_start;
}
}
//理论要判断当前播放的视频帧是否已经最大,最大就直接退出
uint32_t offset;
uint32_t size = 0;
avi_read_next_index(avi_msg, &offset, &size, &msg->frame_number);
if (!size)
{
// os_printf("goto avi_player_thread_audio_start:%d\n",__LINE__);
goto avi_player_thread_audio_start;
}
uint8_t *fb_video_data = (uint8_t *)STREAM_MALLOC(size);
if (fb_video_data)
{
osal_fseek(avi_msg->fp, offset);
osal_fread(fb_video_data, 1, size, avi_msg->fp);
sys_dcache_clean_range((uint32_t*)fb_video_data, size);
msg->data_tmp->data = (uint8_t *)fb_video_data;
msg->data_tmp->mtype = F_JPG;
msg->data_tmp->stype = FSTYPE_YUV_P0;
//转换成ms
//os_printf("msg->avi_msg->fps:%d\t%d\n",msg->avi_msg->fps,msg->frame_number*1000/msg->avi_msg->fps);
msg->data_tmp->time = msg->frame_number * 1000 / msg->avi_msg->fps;
msg->data_tmp->len = size;
video_time = msg->frame_number * 1000 / msg->avi_msg->fps;
msg->data_tmp->datatag = magic;
msi_output_fb(msi, msg->data_tmp);
msg->data_tmp = NULL;
_os_printf("(V:%d)",size);
}
avi_player_thread_audio_start:
if (!avi_msg->sample)
{
goto avi_player_thread_end;
}
if (video_time < audio_time)
{
//os_printf("audio_time:%d\tvideo_time:%d\n",audio_time,video_time);
goto avi_player_thread_end;
}
if (!msg->audio_data_tmp)
{
msg->audio_data_tmp = fbpool_get(&msg->tx_pool, 0, msi);
if (!msg->audio_data_tmp)
{
goto avi_player_thread_end;
}
}
//先去获取一个音频节点
if (msg->audio_data_tmp)
{
//os_printf("audio_time:%d\tvideo_time:%d\n",audio_time,video_time);
//为stream申请音频的空间,然后读取音频发送
uint8_t *fb_audio_data = (uint8_t *)STREAM_LIBC_MALLOC(AUDIO_TMP_BUF_SIZE);
//申请到空间,就将音频先读取到缓冲区,没有空间,则下次重新尝试
if (fb_audio_data)
{
uint32_t max_read_len;
uint32_t read_len = 0;
max_read_len = AUDIO_TMP_BUF_SIZE - now_audio_offset;
while (max_read_len)
{
//读取够音频数据长度,然后通过流发送,然后再将剩下的拷贝到缓冲区,由于
avi_read_next_audio_index(avi_msg, &offset, &size, &msg->audio_frame_number);
//音频已经读取完毕了
if (!size)
{
break;
}
osal_fseek(avi_msg->fp, offset);
read_len = (max_read_len > size) ? size : max_read_len;
osal_fread(audio_tmp_buf + now_audio_offset, 1, read_len, avi_msg->fp);
now_audio_offset += read_len;
max_read_len -= read_len;
}
if (!now_audio_offset) {
avi_msg->sample = 0;
audio_time = ~0;
msi_delete_fb(NULL, msg->audio_data_tmp);
msg->audio_data_tmp = NULL;
goto avi_player_thread_end;
}
//流发送,先拷贝数据
hw_memcpy(fb_audio_data, audio_tmp_buf, now_audio_offset);
msg->audio_data_tmp->data = (uint8_t *)fb_audio_data;
msg->audio_data_tmp->mtype = F_AUDIO;
msg->audio_data_tmp->stype = FSTYPE_AUDIO_PCM;
//转换成ms
//os_printf("msg->avi_msg->fps:%d\t%d\n",msg->avi_msg->fps,msg->frame_number*1000/msg->avi_msg->fps);
//设置音频的时间
msg->audio_data_tmp->time = msg->audio_frame_number_send * 500 / avi_msg->sample;
msg->audio_data_tmp->len = now_audio_offset;
msg->audio_data_tmp->datatag = magic;
_os_printf("(A:%d\t%d)",now_audio_offset,avi_msg->sample);
msi_output_fb(msi, msg->audio_data_tmp);
msg->audio_data_tmp = NULL;
msg->audio_frame_number_send += now_audio_offset;
audio_time = msg->audio_frame_number_send * 500 / avi_msg->sample;
//将剩余的数据拷贝
now_audio_offset = size - read_len;
//os_printf("remain:%d\n",now_audio_offset);
//如果有剩余,紧接着读取数据就可以了
if (now_audio_offset)
{
osal_fread(audio_tmp_buf, 1, now_audio_offset, avi_msg->fp);
}
}
}
avi_player_thread_end:
os_sleep_ms(1);
}
msg->running = 0;
if (audio_tmp_buf)
{
STREAM_LIBC_FREE(audio_tmp_buf);
}
}
static int32_t avi_player_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct player_msg_s *msg = (struct player_msg_s *)msi->priv;
switch (cmd_id)
{
case MSI_CMD_PRE_DESTROY:
{
if(msg) {
msg->thread_stop = 1;
while(msg->running)
{
os_sleep_ms(1);
}
if(msg->data_tmp)
{
msi_delete_fb(NULL, msg->data_tmp);
msg->data_tmp = NULL;
}
if(msg->audio_data_tmp)
{
msi_delete_fb(NULL, msg->audio_data_tmp);
msg->audio_data_tmp = NULL;
}
}
}
break;
case MSI_CMD_POST_DESTROY:
{
if(msg) {
if(msg->avi_msg)
{
avi_deinit(msg->avi_msg);
msg->avi_msg = NULL;
}
fbpool_destroy(&msg->tx_pool);
STREAM_LIBC_FREE(msg);
STREAM_LIBC_FREE((void*)msi->name);
msi->name = NULL;
msi->priv = NULL;
}
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
//os_printf("player free fb:0x%x\n",fb);
if (fb->data)
{
if (fb->mtype == F_AUDIO) {
STREAM_LIBC_FREE(fb->data);
} else if (fb->mtype == F_JPG) {
STREAM_FREE(fb->data);
}
fb->data = NULL;
}
fbpool_put(&msg->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
break;
case MSI_CMD_PLAYER:
{
uint32_t opcode = param1;
uint32_t arg = param2;
switch(opcode)
{
case MSI_FORWARD_INDEX:
{
//快进3s的时间
uint32_t play_time = arg + 3000;
msg->locate_frame_number = play_time * msg->avi_msg->fps / 1000;
//要判断是否已经到最后
if (msg->locate_frame_number > msg->avi_msg->avi.dwTotalFrame )
{
msg->locate_frame_number = msg->avi_msg->avi.dwTotalFrame;
}
//os_printf("msg->locate_frame_number:%d\targ:%d\n",msg->locate_frame_number,arg);
ret = msg->locate_frame_number * 1000 / msg->avi_msg->fps;
}
break;
case MSI_REWIND_INDEX:
{
uint32_t play_time = 0;
if (arg > 3000)
{
play_time = arg - 3000;
}
msg->locate_frame_number = play_time * msg->avi_msg->fps / 1000;
//os_printf("msg->locate_frame_number:%d\targ:%d\n",msg->locate_frame_number,arg);
ret = msg->locate_frame_number * 1000 / msg->avi_msg->fps;
}
break;
case MSI_PLAYER_MAGIC:
{
msg->magic = arg;
msg->speed_flag = 1;
}
break;
default:
break;
}
}
default:
break;
}
return ret;
}
struct msi *avi_player_init(const char *stream_name, const char *filename)
{
struct avi_msg_s *avi_msg = NULL;
struct msi *msi = NULL;
uint8_t * stream_name_buf = (uint8_t *)STREAM_LIBC_ZALLOC(AVI_STREAM_NAME);
if (!stream_name_buf)
{
return NULL;
}
os_memcpy(stream_name_buf, stream_name, os_strlen(stream_name)+1);
avi_msg = avi_read_init(filename);
os_printf("avi_msg:%X\n",avi_msg);
//创建workqueue去不停读取视频数据,然后发送出去
msi = msi_new((char*)stream_name_buf, 0, NULL);
if (!msi)
{
avi_deinit(avi_msg);
return NULL;
}
struct player_msg_s *msg = (struct player_msg_s *)msi->priv;
if (!msg)
{
msg = (struct player_msg_s*)STREAM_LIBC_ZALLOC(sizeof(struct player_msg_s));
msi->priv = (void*)msg;
msg->msi = msi;
msg->avi_msg = (struct avi_msg_s*)avi_msg;
msg->magic = 0;
msg->not_read_size = msg->avi_msg->idx1_size;
msg->cache_addr = msg->avi_msg->idx1_addr;
msg->running = 1;
fbpool_init(&msg->tx_pool, AVI_PLAYER_MAX_TX_NUM);
msi->action = (msi_action)avi_player_action;
msi->enable = 1;
msg->task = os_task_create(msi->name, avi_player_thread, (void*)msg, OS_TASK_PRIORITY_NORMAL, 0, NULL, 1024);
}
else
{
msi_destroy(msi);
msi = NULL;
avi_deinit(avi_msg);
}
return msi;
}

View File

@@ -0,0 +1,16 @@
#ifndef __AVI_PLAYER_MSI_H
#define __AVI_PLAYER_MSI_H
#include "basic_include.h"
#include "utlist.h"
#include "avi.h"
#include "stream_define.h"
#include "osal_file.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/multimedia/msi.h"
#include "lib/multimedia/framebuff.h"
struct msi *avi_player_init(const char *stream_name, const char *filename);
#endif