360 lines
12 KiB
C
360 lines
12 KiB
C
#include "avi.h"
|
||
#include "dac.h"
|
||
#include "ff.h"
|
||
#include "lcd_rgb.h"
|
||
#include "mjpeg.h"
|
||
#include "mymem.h"
|
||
#include "timer.h"
|
||
#include "touch_043.h"
|
||
#include "usart.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;
|
||
UINT 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;
|
||
}
|