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