Files
player/Project/Src/MJPEG/avi.c

370 lines
12 KiB
C
Raw Normal View History

2025-06-27 00:32:57 +08:00
#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;
}