Files
TAIXIN/sdk/app/playback/avi.c

697 lines
20 KiB
C

#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;
}
}