370 lines
12 KiB
C
370 lines
12 KiB
C
|
|
#include "touch_043.h"
|
|||
|
|
#include "usart.h"
|
|||
|
|
#include "timer.h"
|
|||
|
|
#include "avi.h"
|
|||
|
|
#include "mjpeg.h"
|
|||
|
|
#include "lcd_rgb.h"
|
|||
|
|
#include "mymem.h"
|
|||
|
|
#include "ff.h"
|
|||
|
|
#include "dac.h"
|
|||
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
|
|||
|
|
//ALIENTEK STM32F407开发板
|
|||
|
|
//AVI视频格式解析 代码
|
|||
|
|
//正点原子@ALIENTEK
|
|||
|
|
//技术论坛:www.openedv.com
|
|||
|
|
//创建日期:2014/7/20
|
|||
|
|
//版本:V1.0
|
|||
|
|
//版权所有,盗版必究。
|
|||
|
|
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
|
|||
|
|
//All rights reserved
|
|||
|
|
//*******************************************************************************
|
|||
|
|
//修改信息
|
|||
|
|
//无
|
|||
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|
|||
|
|
AVI_INFO avix; //avi文件相关信息
|
|||
|
|
u8*const AVI_VIDS_FLAG_TBL[2]={"00dc","01dc"};//视频编码标志字符串,00dc/01dc
|
|||
|
|
u8*const AVI_AUDS_FLAG_TBL[2]={"00wb","01wb"};//音频编码标志字符串,00wb/01wb
|
|||
|
|
|
|||
|
|
static vu8 g_i2splaybuf; //即将播放的音频帧缓冲编号
|
|||
|
|
static u8* g_i2sbuf[4]; //音频缓冲帧,共4帧,4*5K=20K
|
|||
|
|
|
|||
|
|
//音频数据I2S DMA传输回调函数
|
|||
|
|
void audio_i2s_dma_callback(DAC_UserStruct *dac)
|
|||
|
|
{
|
|||
|
|
g_i2splaybuf++;
|
|||
|
|
if(g_i2splaybuf>3)g_i2splaybuf=0;
|
|||
|
|
if(dac->buff_useing==1)
|
|||
|
|
{
|
|||
|
|
DMA_MemoryTargetConfig(DMA1_Stream5,(u32)g_i2sbuf[g_i2splaybuf], DMA_Memory_0);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
DMA_MemoryTargetConfig(DMA1_Stream5,(u32)g_i2sbuf[g_i2splaybuf], DMA_Memory_1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
//avi解码初始化
|
|||
|
|
//buf:输入缓冲区
|
|||
|
|
//size:缓冲区大小
|
|||
|
|
//返回值:AVI_OK,avi文件解析成功
|
|||
|
|
// 其他,错误代码
|
|||
|
|
AVISTATUS avi_init(u8 *buf,int size)
|
|||
|
|
{
|
|||
|
|
u16 offset;
|
|||
|
|
u8 *tbuf;
|
|||
|
|
AVISTATUS res=AVI_OK;
|
|||
|
|
AVI_HEADER *aviheader;
|
|||
|
|
LIST_HEADER *listheader;
|
|||
|
|
AVIH_HEADER *avihheader;
|
|||
|
|
STRH_HEADER *strhheader;
|
|||
|
|
|
|||
|
|
STRF_BMPHEADER *bmpheader;
|
|||
|
|
STRF_WAVHEADER *wavheader;
|
|||
|
|
|
|||
|
|
tbuf=buf;
|
|||
|
|
aviheader=(AVI_HEADER*)buf;
|
|||
|
|
if(aviheader->RiffID!=AVI_RIFF_ID)return AVI_RIFF_ERR; //RIFF ID错误
|
|||
|
|
if(aviheader->AviID!=AVI_AVI_ID)return AVI_AVI_ERR; //AVI ID错误
|
|||
|
|
buf+=sizeof(AVI_HEADER); //偏移
|
|||
|
|
listheader=(LIST_HEADER*)(buf);
|
|||
|
|
if(listheader->ListID!=AVI_LIST_ID)return AVI_LIST_ERR; //LIST ID错误
|
|||
|
|
if(listheader->ListType!=AVI_HDRL_ID)return AVI_HDRL_ERR; //HDRL ID错误
|
|||
|
|
buf+=sizeof(LIST_HEADER); //偏移
|
|||
|
|
avihheader=(AVIH_HEADER*)(buf);
|
|||
|
|
if(avihheader->BlockID!=AVI_AVIH_ID)return AVI_AVIH_ERR; //AVIH ID错误
|
|||
|
|
avix.SecPerFrame=avihheader->SecPerFrame; //得到帧间隔时间
|
|||
|
|
avix.TotalFrame=avihheader->TotalFrame; //得到总帧数
|
|||
|
|
buf+=avihheader->BlockSize+8; //偏移
|
|||
|
|
listheader=(LIST_HEADER*)(buf);
|
|||
|
|
if(listheader->ListID!=AVI_LIST_ID)return AVI_LIST_ERR; //LIST ID错误
|
|||
|
|
if(listheader->ListType!=AVI_STRL_ID)return AVI_STRL_ERR; //STRL ID错误
|
|||
|
|
strhheader=(STRH_HEADER*)(buf+12);
|
|||
|
|
if(strhheader->BlockID!=AVI_STRH_ID)return AVI_STRH_ERR; //STRH ID错误
|
|||
|
|
if(strhheader->StreamType==AVI_VIDS_STREAM) //视频帧在前
|
|||
|
|
{
|
|||
|
|
if(strhheader->Handler!=AVI_FORMAT_MJPG)return AVI_FORMAT_ERR; //非MJPG视频流,不支持
|
|||
|
|
avix.VideoFLAG=(u8*)AVI_VIDS_FLAG_TBL[0]; //视频流标记 "00dc"
|
|||
|
|
avix.AudioFLAG=(u8*)AVI_AUDS_FLAG_TBL[1]; //音频流标记 "01wb"
|
|||
|
|
bmpheader=(STRF_BMPHEADER*)(buf+12+strhheader->BlockSize+8);//strf
|
|||
|
|
if(bmpheader->BlockID!=AVI_STRF_ID)return AVI_STRF_ERR; //STRF ID错误
|
|||
|
|
avix.Width=bmpheader->bmiHeader.Width;
|
|||
|
|
avix.Height=bmpheader->bmiHeader.Height;
|
|||
|
|
buf+=listheader->BlockSize+8; //偏移
|
|||
|
|
listheader=(LIST_HEADER*)(buf);
|
|||
|
|
if(listheader->ListID!=AVI_LIST_ID)//是不含有音频帧的视频文件
|
|||
|
|
{
|
|||
|
|
avix.SampleRate=0; //音频采样率
|
|||
|
|
avix.Channels=0; //音频通道数
|
|||
|
|
avix.AudioType=0; //音频格式
|
|||
|
|
|
|||
|
|
}else
|
|||
|
|
{
|
|||
|
|
if(listheader->ListType!=AVI_STRL_ID)return AVI_STRL_ERR; //STRL ID错误
|
|||
|
|
strhheader=(STRH_HEADER*)(buf+12);
|
|||
|
|
if(strhheader->BlockID!=AVI_STRH_ID)return AVI_STRH_ERR; //STRH ID错误
|
|||
|
|
if(strhheader->StreamType!=AVI_AUDS_STREAM)return AVI_FORMAT_ERR;//格式错误
|
|||
|
|
wavheader=(STRF_WAVHEADER*)(buf+12+strhheader->BlockSize+8);//strf
|
|||
|
|
if(wavheader->BlockID!=AVI_STRF_ID)return AVI_STRF_ERR; //STRF ID错误
|
|||
|
|
avix.SampleRate=wavheader->SampleRate; //音频采样率
|
|||
|
|
avix.Channels=wavheader->Channels; //音频通道数
|
|||
|
|
avix.AudioType=wavheader->FormatTag; //音频格式
|
|||
|
|
}
|
|||
|
|
}else if(strhheader->StreamType==AVI_AUDS_STREAM) //音频帧在前
|
|||
|
|
{
|
|||
|
|
avix.VideoFLAG=(u8*)AVI_VIDS_FLAG_TBL[1]; //视频流标记 "01dc"
|
|||
|
|
avix.AudioFLAG=(u8*)AVI_AUDS_FLAG_TBL[0]; //音频流标记 "00wb"
|
|||
|
|
wavheader=(STRF_WAVHEADER*)(buf+12+strhheader->BlockSize+8);//strf
|
|||
|
|
if(wavheader->BlockID!=AVI_STRF_ID)return AVI_STRF_ERR; //STRF ID错误
|
|||
|
|
avix.SampleRate=wavheader->SampleRate; //音频采样率
|
|||
|
|
avix.Channels=wavheader->Channels; //音频通道数
|
|||
|
|
avix.AudioType=wavheader->FormatTag; //音频格式
|
|||
|
|
buf+=listheader->BlockSize+8; //偏移
|
|||
|
|
listheader=(LIST_HEADER*)(buf);
|
|||
|
|
if(listheader->ListID!=AVI_LIST_ID)return AVI_LIST_ERR; //LIST ID错误
|
|||
|
|
if(listheader->ListType!=AVI_STRL_ID)return AVI_STRL_ERR; //STRL ID错误
|
|||
|
|
strhheader=(STRH_HEADER*)(buf+12);
|
|||
|
|
if(strhheader->BlockID!=AVI_STRH_ID)return AVI_STRH_ERR; //STRH ID错误
|
|||
|
|
if(strhheader->StreamType!=AVI_VIDS_STREAM)return AVI_FORMAT_ERR;//格式错误
|
|||
|
|
bmpheader=(STRF_BMPHEADER*)(buf+12+strhheader->BlockSize+8);//strf
|
|||
|
|
if(bmpheader->BlockID!=AVI_STRF_ID)return AVI_STRF_ERR; //STRF ID错误
|
|||
|
|
if(bmpheader->bmiHeader.Compression!=AVI_FORMAT_MJPG)return AVI_FORMAT_ERR;//格式错误
|
|||
|
|
avix.Width=bmpheader->bmiHeader.Width;
|
|||
|
|
avix.Height=bmpheader->bmiHeader.Height;
|
|||
|
|
}
|
|||
|
|
offset=avi_srarch_id(tbuf,size,"movi"); //查找movi ID
|
|||
|
|
if(offset==0)return AVI_MOVI_ERR; //MOVI ID错误
|
|||
|
|
if(avix.SampleRate)//有音频流,才查找
|
|||
|
|
{
|
|||
|
|
tbuf+=offset;
|
|||
|
|
offset=avi_srarch_id(tbuf,size,avix.AudioFLAG); //查找音频流标记
|
|||
|
|
if(offset==0)return AVI_STREAM_ERR; //流错误
|
|||
|
|
tbuf+=offset+4;
|
|||
|
|
avix.AudioBufSize=*((u16*)tbuf); //得到音频流buf大小.
|
|||
|
|
}
|
|||
|
|
// printf("avi init ok\r\n");
|
|||
|
|
// printf("avix.SecPerFrame:%d\r\n",avix.SecPerFrame);
|
|||
|
|
// printf("avix.TotalFrame:%d\r\n",avix.TotalFrame);
|
|||
|
|
// printf("avix.Width:%d\r\n",avix.Width);
|
|||
|
|
// printf("avix.Height:%d\r\n",avix.Height);
|
|||
|
|
// printf("avix.AudioType:%d\r\n",avix.AudioType);
|
|||
|
|
// printf("avix.SampleRate:%d\r\n",avix.SampleRate);
|
|||
|
|
// printf("avix.Channels:%d\r\n",avix.Channels);
|
|||
|
|
// printf("avix.AudioBufSize:%d\r\n",avix.AudioBufSize);
|
|||
|
|
// printf("avix.VideoFLAG:%s\r\n",avix.VideoFLAG);
|
|||
|
|
// printf("avix.AudioFLAG:%s\r\n",avix.AudioFLAG);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//查找 ID
|
|||
|
|
//buf:待查缓存区
|
|||
|
|
//size:缓存大小
|
|||
|
|
//id:要查找的id,必须是4字节长度
|
|||
|
|
//返回值:0,查找失败,其他:movi ID偏移量
|
|||
|
|
u16 avi_srarch_id(u8* buf,int size,u8 *id)
|
|||
|
|
{
|
|||
|
|
u16 i;
|
|||
|
|
size-=4;
|
|||
|
|
for(i=0;i<size;i++)
|
|||
|
|
{
|
|||
|
|
if(buf[i]==id[0])
|
|||
|
|
if(buf[i+1]==id[1])
|
|||
|
|
if(buf[i+2]==id[2])
|
|||
|
|
if(buf[i+3]==id[3])return i;//找到"id"所在的位置
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
//得到stream流信息
|
|||
|
|
//buf:流开始地址(必须是01wb/00wb/01dc/00dc开头)
|
|||
|
|
AVISTATUS avi_get_streaminfo(u8* buf)
|
|||
|
|
{
|
|||
|
|
int offset=0;
|
|||
|
|
avix.StreamID=MAKEWORD(buf+2); //得到流类型
|
|||
|
|
avix.StreamSize=MAKEDWORD(buf+4); //得到流大小
|
|||
|
|
if(avix.StreamSize%2)avix.StreamSize++; //奇数加1(avix.StreamSize,必须是偶数)
|
|||
|
|
if(avix.StreamID==AVI_VIDS_FLAG||avix.StreamID==AVI_AUDS_FLAG)return AVI_OK;
|
|||
|
|
return AVI_STREAM_ERR;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
static vu8 g_frame_en=0;
|
|||
|
|
|
|||
|
|
|
|||
|
|
void avi_timer_irq (void)
|
|||
|
|
{
|
|||
|
|
g_frame_en=1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//参数是avix.SecPerFrame
|
|||
|
|
void avi_timer_init (u32 t)
|
|||
|
|
{
|
|||
|
|
TIMER_InitStruct timer_init={0};
|
|||
|
|
timer_init.Cycle=t/1;
|
|||
|
|
timer_init.Tim=TIM3;
|
|||
|
|
timer_init.UpdataCall=avi_timer_irq;
|
|||
|
|
TIMER_InitNormal(&timer_init);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
void avi_tiemr_delete (void)
|
|||
|
|
{
|
|||
|
|
TIMER_DeInit (TIM3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
#define AVI_AUDIO_BUF_SIZE 1024*5 //定义avi解码时,音频buf大小.
|
|||
|
|
#define AVI_VIDEO_BUF_SIZE 1024*200 //定义avi解码时,视频buf大小.
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
//播放视频
|
|||
|
|
int video_play (char *pname,int loop)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
u8 listhead[8]={0};//数据头
|
|||
|
|
u32 fresh=0;
|
|||
|
|
u8* framebuf; //视频解码buf
|
|||
|
|
u8* pbuf; //buf指针
|
|||
|
|
FIL *favi;
|
|||
|
|
u8 res=0;
|
|||
|
|
u16 offset=0;
|
|||
|
|
u32 nr;
|
|||
|
|
u8 key;
|
|||
|
|
u8 i2ssavebuf;
|
|||
|
|
DAC_UserStruct dac={0};
|
|||
|
|
|
|||
|
|
u8 *audio_buff=mymalloc (AVI_AUDIO_BUF_SIZE);
|
|||
|
|
framebuf=mymalloc(AVI_VIDEO_BUF_SIZE); //申请视频buf
|
|||
|
|
favi=(FIL*)mymalloc(sizeof(FIL)); //申请favi内存
|
|||
|
|
g_i2sbuf[0]=mymalloc(AVI_AUDIO_BUF_SIZE); //申请音频内存
|
|||
|
|
g_i2sbuf[1]=mymalloc(AVI_AUDIO_BUF_SIZE); //申请音频内存
|
|||
|
|
g_i2sbuf[2]=mymalloc(AVI_AUDIO_BUF_SIZE); //申请音频内存
|
|||
|
|
g_i2sbuf[3]=mymalloc(AVI_AUDIO_BUF_SIZE); //申请音频内存
|
|||
|
|
mymemset(g_i2sbuf[0],0,AVI_AUDIO_BUF_SIZE);
|
|||
|
|
mymemset(g_i2sbuf[1],0,AVI_AUDIO_BUF_SIZE);
|
|||
|
|
mymemset(g_i2sbuf[2],0,AVI_AUDIO_BUF_SIZE);
|
|||
|
|
mymemset(g_i2sbuf[3],0,AVI_AUDIO_BUF_SIZE);
|
|||
|
|
while(res==0)
|
|||
|
|
{
|
|||
|
|
res=f_open(favi,(char *)pname,FA_READ);
|
|||
|
|
if(res==0)
|
|||
|
|
{
|
|||
|
|
pbuf=framebuf;
|
|||
|
|
res=f_read(favi,pbuf,AVI_VIDEO_BUF_SIZE,&nr);//开始读取
|
|||
|
|
if(res)
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
//开始avi解析
|
|||
|
|
res=avi_init(pbuf,AVI_VIDEO_BUF_SIZE); //avi解析
|
|||
|
|
if(res)
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
//avi_timer_init (avix.SecPerFrame);
|
|||
|
|
offset=avi_srarch_id(pbuf,AVI_VIDEO_BUF_SIZE,(u8*)"movi");//寻找movi ID
|
|||
|
|
avi_get_streaminfo(pbuf+offset+4); //获取流信息
|
|||
|
|
f_lseek(favi,offset+12-8); //跳过标志ID,读地址偏移到流数据开始处
|
|||
|
|
extern void jdmerge_setvideo_xsize (int xsize,int lcd_xsize);
|
|||
|
|
jdmerge_setvideo_xsize (avix.Width,LCD_GetLcdSizeX());//设置地址自动自增
|
|||
|
|
res=mjpegdec_init((LCD_GetLcdSizeX()-avix.Width)/2,(LCD_GetLcdSizeY()-avix.Height)/2);//JPG解码初始化
|
|||
|
|
if(avix.SampleRate) //有音频信息,才初始化
|
|||
|
|
{
|
|||
|
|
//初始化DAC
|
|||
|
|
dac.buff1=(u32 *)g_i2sbuf[1];
|
|||
|
|
dac.buff2=(u32 *)g_i2sbuf[2];
|
|||
|
|
dac.buff_size=avix.AudioBufSize;
|
|||
|
|
dac.rate=DAC_GetRate(avix.SampleRate);
|
|||
|
|
dac.call_back=audio_i2s_dma_callback;
|
|||
|
|
DAC_NormalInit (&dac);
|
|||
|
|
}
|
|||
|
|
while(1)//播放循环
|
|||
|
|
{
|
|||
|
|
f_read(favi,listhead,8,&nr);
|
|||
|
|
if(avi_get_streaminfo(listhead))//读取下一帧 流标志
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
if (Touch_GetState()->x[1]!=0) break; //屏幕两点触摸返回
|
|||
|
|
if (USART3_GetKeyPressed()&(0x08)) break;//按下返回键返回
|
|||
|
|
if(avix.StreamID==AVI_VIDS_FLAG) //视频流
|
|||
|
|
{
|
|||
|
|
pbuf=framebuf;
|
|||
|
|
//mymemset (pbuf,0,AVI_VIDEO_BUF_SIZE);
|
|||
|
|
f_read(favi,pbuf,avix.StreamSize+8-8,&nr); //读入整帧+下一数据流ID信息
|
|||
|
|
//等待帧时间
|
|||
|
|
//while (g_frame_en==0){} g_frame_en=0;
|
|||
|
|
while (LCD_GetLayerUpdataStat ()==0);
|
|||
|
|
LCD_SwitchLayerBuff();
|
|||
|
|
res=mjpegdec_decode(pbuf,avix.StreamSize);
|
|||
|
|
if (res==0)
|
|||
|
|
{
|
|||
|
|
LCD_ExitLayerBuff();
|
|||
|
|
}
|
|||
|
|
if(res)
|
|||
|
|
{
|
|||
|
|
//break;
|
|||
|
|
}
|
|||
|
|
}else //音频流
|
|||
|
|
{
|
|||
|
|
// f_read(favi,audio_buff,avix.StreamSize+8-8,&nr);//填充i2sbuf
|
|||
|
|
// pbuf=audio_buff;
|
|||
|
|
i2ssavebuf++;
|
|||
|
|
if(i2ssavebuf>3)i2ssavebuf=0;
|
|||
|
|
do
|
|||
|
|
{
|
|||
|
|
nr=g_i2splaybuf;
|
|||
|
|
if(nr)nr--;
|
|||
|
|
else nr=3;
|
|||
|
|
}while(i2ssavebuf==nr);//碰撞等待.
|
|||
|
|
f_read(favi,g_i2sbuf[i2ssavebuf],avix.StreamSize,&nr);//填充i2sbuf
|
|||
|
|
u16 *p=(u16 *)g_i2sbuf[i2ssavebuf];
|
|||
|
|
for (int i=0;i<avix.StreamSize/2;i++)
|
|||
|
|
p[i]=(p[i]+0x8000)>>4;
|
|||
|
|
pbuf=g_i2sbuf[i2ssavebuf];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//avi_tiemr_delete();
|
|||
|
|
mjpegdec_free(); //释放内存
|
|||
|
|
f_close(favi);
|
|||
|
|
}
|
|||
|
|
if (loop==0)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
myfree(framebuf);
|
|||
|
|
myfree(favi);
|
|||
|
|
myfree(audio_buff);
|
|||
|
|
myfree(g_i2sbuf[0]);
|
|||
|
|
myfree(g_i2sbuf[1]);
|
|||
|
|
myfree(g_i2sbuf[2]);
|
|||
|
|
myfree(g_i2sbuf[3]);
|
|||
|
|
DAC_NormalDeInit (&dac);
|
|||
|
|
return (int)res;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|