Files
player/Project/Src/MJPEG/avi.c
2025-07-06 17:11:38 +08:00

360 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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