diff --git a/Project/Src/MJPEG/avi.c b/Project/Src/MJPEG/avi.c index 42ced67..146d994 100644 --- a/Project/Src/MJPEG/avi.c +++ b/Project/Src/MJPEG/avi.c @@ -1,369 +1,359 @@ +#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" -#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 +////////////////////////////////////////////////////////////////////////////////// +// 本程序只供学习使用,未经作者许可,不得用于其它任何用途 +// 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 +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); - } -} +static vu8 g_i2splaybuf; // 即将播放的音频帧缓冲编号 +static u8 *g_i2sbuf[4]; // 音频缓冲帧,共4帧,4*5K=20K - -//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; +// 音频数据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); + } } -//查找 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;iRiffID != 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; } - - - - - -static vu8 g_frame_en=0; - - -void avi_timer_irq (void) -{ - g_frame_en=1; +// 查找 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; } -//参数是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); +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); } -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; } - - - - - - - - - - -#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>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; - -} - - - - - - - - - - diff --git a/Project/Src/MJPEG/avi.h b/Project/Src/MJPEG/avi.h index e079760..b323e76 100644 --- a/Project/Src/MJPEG/avi.h +++ b/Project/Src/MJPEG/avi.h @@ -1,227 +1,205 @@ #ifndef __AVI_H -#define __AVI_H -#include -////////////////////////////////////////////////////////////////////////////////// -//本程序只供学习使用,未经作者许可,不得用于其它任何用途 -//ALIENTEK STM32F407开发板 -//AVI视频格式解析 代码 -//正点原子@ALIENTEK -//技术论坛:www.openedv.com -//创建日期:2014/7/20 -//版本:V1.0 -//版权所有,盗版必究。 -//Copyright(C) 广州市星翼电子科技有限公司 2009-2019 -//All rights reserved +#define __AVI_H +#include +////////////////////////////////////////////////////////////////////////////////// +// 本程序只供学习使用,未经作者许可,不得用于其它任何用途 +// ALIENTEK STM32F407开发板 +// AVI视频格式解析 代码 +// 正点原子@ALIENTEK +// 技术论坛:www.openedv.com +// 创建日期:2014/7/20 +// 版本:V1.0 +// 版权所有,盗版必究。 +// Copyright(C) 广州市星翼电子科技有限公司 2009-2019 +// All rights reserved //******************************************************************************* -//修改信息 -//无 -////////////////////////////////////////////////////////////////////////////////// +// 修改信息 +// 无 +////////////////////////////////////////////////////////////////////////////////// -//错误类型 +// 错误类型 typedef enum { - AVI_OK=0, //0,成功 - AVI_RIFF_ERR, //1,RIFF ID读取失败 - AVI_AVI_ERR, //2,AVI ID读取失败 - AVI_LIST_ERR, //3,LIST ID读取失败 - AVI_HDRL_ERR, //4,HDRL ID读取失败 - AVI_AVIH_ERR, //5,AVIH ID读取失败 - AVI_STRL_ERR, //6,STRL ID读取失败 - AVI_STRH_ERR, //7,STRH ID读取失败 - AVI_STRF_ERR, //8,STRF ID读取失败 - AVI_MOVI_ERR, //9,MOVI ID读取失败 - AVI_FORMAT_ERR, //10,格式错误 - AVI_STREAM_ERR, //11,流错误 -}AVISTATUS; + AVI_OK = 0, // 0,成功 + AVI_RIFF_ERR, // 1,RIFF ID读取失败 + AVI_AVI_ERR, // 2,AVI ID读取失败 + AVI_LIST_ERR, // 3,LIST ID读取失败 + AVI_HDRL_ERR, // 4,HDRL ID读取失败 + AVI_AVIH_ERR, // 5,AVIH ID读取失败 + AVI_STRL_ERR, // 6,STRL ID读取失败 + AVI_STRH_ERR, // 7,STRH ID读取失败 + AVI_STRF_ERR, // 8,STRF ID读取失败 + AVI_MOVI_ERR, // 9,MOVI ID读取失败 + AVI_FORMAT_ERR, // 10,格式错误 + AVI_STREAM_ERR, // 11,流错误 +} AVISTATUS; +#define AVI_RIFF_ID 0X46464952 +#define AVI_AVI_ID 0X20495641 +#define AVI_LIST_ID 0X5453494C +#define AVI_HDRL_ID 0X6C726468 // 信息块标志 +#define AVI_MOVI_ID 0X69766F6D // 数据块标志 +#define AVI_STRL_ID 0X6C727473 // strl标志 +#define AVI_AVIH_ID 0X68697661 // avih子块∈AVI_HDRL_ID +#define AVI_STRH_ID 0X68727473 // strh(流头)子块∈AVI_STRL_ID +#define AVI_STRF_ID 0X66727473 // strf(流格式)子块∈AVI_STRL_ID +#define AVI_STRD_ID 0X64727473 // strd子块∈AVI_STRL_ID (可选的) -#define AVI_RIFF_ID 0X46464952 -#define AVI_AVI_ID 0X20495641 -#define AVI_LIST_ID 0X5453494C -#define AVI_HDRL_ID 0X6C726468 //信息块标志 -#define AVI_MOVI_ID 0X69766F6D //数据块标志 -#define AVI_STRL_ID 0X6C727473 //strl标志 +#define AVI_VIDS_STREAM 0X73646976 // 视频流 +#define AVI_AUDS_STREAM 0X73647561 // 音频流 -#define AVI_AVIH_ID 0X68697661 //avih子块∈AVI_HDRL_ID -#define AVI_STRH_ID 0X68727473 //strh(流头)子块∈AVI_STRL_ID -#define AVI_STRF_ID 0X66727473 //strf(流格式)子块∈AVI_STRL_ID -#define AVI_STRD_ID 0X64727473 //strd子块∈AVI_STRL_ID (可选的) - -#define AVI_VIDS_STREAM 0X73646976 //视频流 -#define AVI_AUDS_STREAM 0X73647561 //音频流 - - -#define AVI_VIDS_FLAG 0X6463 //视频流标志 -#define AVI_AUDS_FLAG 0X7762 //音频流标志 +#define AVI_VIDS_FLAG 0X6463 // 视频流标志 +#define AVI_AUDS_FLAG 0X7762 // 音频流标志 ////////////////////////////////////////////////////////////////////////////////////////// -#define AVI_FORMAT_MJPG 0X47504A4D +#define AVI_FORMAT_MJPG 0X47504A4D +// AVI 信息结构体 +// 将一些重要的数据,存放在这里,方便解码 +typedef struct __packed { + u32 SecPerFrame; // 视频帧间隔时间(单位为us) + u32 TotalFrame; // 文件总帧数 + u32 Width; // 图像宽 + u32 Height; // 图像高 + u32 SampleRate; // 音频采样率 + u16 Channels; // 声道数,一般为2,表示立体声 + u16 AudioBufSize; // 音频缓冲区大小 + u16 AudioType; // 音频类型:0X0001=PCM;0X0050=MP2;0X0055=MP3;0X2000=AC3; + u16 StreamID; // 流类型ID,StreamID=='dc'==0X6463 /StreamID=='wb'==0X7762 + u32 StreamSize; // 流大小,必须是偶数,如果读取到为奇数,则加1.补为偶数. + u8 *VideoFLAG; // 视频帧标记,VideoFLAG="00dc"/"01dc" + u8 *AudioFLAG; // 音频帧标记,AudioFLAG="00wb"/"01wb" +} AVI_INFO; -//AVI 信息结构体 -//将一些重要的数据,存放在这里,方便解码 -typedef struct __packed -{ - u32 SecPerFrame; //视频帧间隔时间(单位为us) - u32 TotalFrame; //文件总帧数 - u32 Width; //图像宽 - u32 Height; //图像高 - u32 SampleRate; //音频采样率 - u16 Channels; //声道数,一般为2,表示立体声 - u16 AudioBufSize; //音频缓冲区大小 - u16 AudioType; //音频类型:0X0001=PCM;0X0050=MP2;0X0055=MP3;0X2000=AC3; - u16 StreamID; //流类型ID,StreamID=='dc'==0X6463 /StreamID=='wb'==0X7762 - u32 StreamSize; //流大小,必须是偶数,如果读取到为奇数,则加1.补为偶数. - u8* VideoFLAG; //视频帧标记,VideoFLAG="00dc"/"01dc" - u8* AudioFLAG; //音频帧标记,AudioFLAG="00wb"/"01wb" -}AVI_INFO; - -//extern AVI_INFO avix; //avi文件相关信息 +// extern AVI_INFO avix; //avi文件相关信息 ////////////////////////////////////////////////////////////////////////////////////////// -//AVI 块信息 -typedef struct -{ - u32 RiffID; //RiffID=='RIFF'==0X61766968 - u32 FileSize; //AVI文件大小(不包含最初的8字节,也RIFFID和FileSize不计算在内) - u32 AviID; //AviID=='AVI '==0X41564920 -}AVI_HEADER; +// AVI 块信息 +typedef struct { + u32 RiffID; // RiffID=='RIFF'==0X61766968 + u32 FileSize; // AVI文件大小(不包含最初的8字节,也RIFFID和FileSize不计算在内) + u32 AviID; // AviID=='AVI '==0X41564920 +} AVI_HEADER; -//AVI 块信息 -typedef struct -{ - u32 FrameID; //帧ID,FrameID=='RIFF'==0X61766968 - u32 FrameSize; //帧大小 -}FRAME_HEADER; +// AVI 块信息 +typedef struct { + u32 FrameID; // 帧ID,FrameID=='RIFF'==0X61766968 + u32 FrameSize; // 帧大小 +} FRAME_HEADER; +// LIST 块信息 +typedef struct { + u32 ListID; // ListID=='LIST'==0X4c495354 + u32 BlockSize; // 块大小(不包含最初的8字节,也ListID和BlockSize不计算在内) + u32 ListType; // LIST子块类型:hdrl(信息块)/movi(数据块)/idxl(索引块,非必须,是可选的) +} LIST_HEADER; -//LIST 块信息 -typedef struct -{ - u32 ListID; //ListID=='LIST'==0X4c495354 - u32 BlockSize; //块大小(不包含最初的8字节,也ListID和BlockSize不计算在内) - u32 ListType; //LIST子块类型:hdrl(信息块)/movi(数据块)/idxl(索引块,非必须,是可选的) -}LIST_HEADER; +// avih 子块信息 +typedef struct { + u32 BlockID; // 块标志:avih==0X61766968 + u32 BlockSize; // 块大小(不包含最初的8字节,也就是BlockID和BlockSize不计算在内) + u32 SecPerFrame; // 视频帧间隔时间(单位为us) + u32 MaxByteSec; // 最大数据传输率,字节/秒 + u32 PaddingGranularity; // 数据填充的粒度 + u32 Flags; // AVI文件的全局标记,比如是否含有索引块等 + u32 TotalFrame; // 文件总帧数 + u32 InitFrames; // 为交互格式指定初始帧数(非交互格式应该指定为0) + u32 Streams; // 包含的数据流种类个数,通常为2 + u32 RefBufSize; // 建议读取本文件的缓存大小(应能容纳最大的块)默认可能是1M字节!!! + u32 Width; // 图像宽 + u32 Height; // 图像高 + u32 Reserved[4]; // 保留 +} AVIH_HEADER; -//avih 子块信息 -typedef struct -{ - u32 BlockID; //块标志:avih==0X61766968 - u32 BlockSize; //块大小(不包含最初的8字节,也就是BlockID和BlockSize不计算在内) - u32 SecPerFrame; //视频帧间隔时间(单位为us) - u32 MaxByteSec; //最大数据传输率,字节/秒 - u32 PaddingGranularity; //数据填充的粒度 - u32 Flags; //AVI文件的全局标记,比如是否含有索引块等 - u32 TotalFrame; //文件总帧数 - u32 InitFrames; //为交互格式指定初始帧数(非交互格式应该指定为0) - u32 Streams; //包含的数据流种类个数,通常为2 - u32 RefBufSize; //建议读取本文件的缓存大小(应能容纳最大的块)默认可能是1M字节!!! - u32 Width; //图像宽 - u32 Height; //图像高 - u32 Reserved[4]; //保留 -}AVIH_HEADER; +// strh 流头子块信息(strh∈strl) +typedef struct { + u32 BlockID; // 块标志:strh==0X73747268 + u32 BlockSize; // 块大小(不包含最初的8字节,也就是BlockID和BlockSize不计算在内) + u32 StreamType; // 数据流种类,vids(0X73646976):视频;auds(0X73647561):音频 + u32 Handler; // 指定流的处理者,对于音视频来说就是解码器,比如MJPG/H264之类的. + u32 Flags; // 标记:是否允许这个流输出?调色板是否变化? + u16 Priority; // 流的优先级(当有多个相同类型的流时优先级最高的为默认流) + u16 Language; // 音频的语言代号 + u32 InitFrames; // 为交互格式指定初始帧数 + u32 Scale; // 数据量, 视频每桢的大小或者音频的采样大小 + u32 Rate; // Scale/Rate=每秒采样数 + u32 Start; // 数据流开始播放的位置,单位为Scale + u32 Length; // 数据流的数据量,单位为Scale + u32 RefBufSize; // 建议使用的缓冲区大小 + u32 Quality; // 解压缩质量参数,值越大,质量越好 + u32 SampleSize; // 音频的样本大小 + struct // 视频帧所占的矩形 + { + short Left; + short Top; + short Right; + short Bottom; + } Frame; +} STRH_HEADER; -//strh 流头子块信息(strh∈strl) -typedef struct -{ - u32 BlockID; //块标志:strh==0X73747268 - u32 BlockSize; //块大小(不包含最初的8字节,也就是BlockID和BlockSize不计算在内) - u32 StreamType; //数据流种类,vids(0X73646976):视频;auds(0X73647561):音频 - u32 Handler; //指定流的处理者,对于音视频来说就是解码器,比如MJPG/H264之类的. - u32 Flags; //标记:是否允许这个流输出?调色板是否变化? - u16 Priority; //流的优先级(当有多个相同类型的流时优先级最高的为默认流) - u16 Language; //音频的语言代号 - u32 InitFrames; //为交互格式指定初始帧数 - u32 Scale; //数据量, 视频每桢的大小或者音频的采样大小 - u32 Rate; //Scale/Rate=每秒采样数 - u32 Start; //数据流开始播放的位置,单位为Scale - u32 Length; //数据流的数据量,单位为Scale - u32 RefBufSize; //建议使用的缓冲区大小 - u32 Quality; //解压缩质量参数,值越大,质量越好 - u32 SampleSize; //音频的样本大小 - struct //视频帧所占的矩形 - { - short Left; - short Top; - short Right; - short Bottom; - }Frame; -}STRH_HEADER; +// BMP结构体 +typedef struct { + u32 BmpSize; // bmp结构体大小,包含(BmpSize在内) + long Width; // 图像宽 + long Height; // 图像高 + u16 Planes; // 平面数,必须为1 + u16 BitCount; // 像素位数,0X0018表示24位 + u32 Compression; // 压缩类型,比如:MJPG/H264等 + u32 SizeImage; // 图像大小 + long XpixPerMeter; // 水平分辨率 + long YpixPerMeter; // 垂直分辨率 + u32 ClrUsed; // 实际使用了调色板中的颜色数,压缩格式中不使用 + u32 ClrImportant; // 重要的颜色 +} BMP_HEADER; -//BMP结构体 -typedef struct -{ - u32 BmpSize; //bmp结构体大小,包含(BmpSize在内) - long Width; //图像宽 - long Height; //图像高 - u16 Planes; //平面数,必须为1 - u16 BitCount; //像素位数,0X0018表示24位 - u32 Compression; //压缩类型,比如:MJPG/H264等 - u32 SizeImage; //图像大小 - long XpixPerMeter; //水平分辨率 - long YpixPerMeter; //垂直分辨率 - u32 ClrUsed; //实际使用了调色板中的颜色数,压缩格式中不使用 - u32 ClrImportant; //重要的颜色 -}BMP_HEADER; +// 颜色表 +typedef struct { + u8 rgbBlue; // 蓝色的亮度(值范围为0-255) + u8 rgbGreen; // 绿色的亮度(值范围为0-255) + u8 rgbRed; // 红色的亮度(值范围为0-255) + u8 rgbReserved; // 保留,必须为0 +} AVIRGBQUAD; -//颜色表 -typedef struct -{ - u8 rgbBlue; //蓝色的亮度(值范围为0-255) - u8 rgbGreen; //绿色的亮度(值范围为0-255) - u8 rgbRed; //红色的亮度(值范围为0-255) - u8 rgbReserved; //保留,必须为0 -}AVIRGBQUAD; - -//对于strh,如果是视频流,strf(流格式)使STRH_BMPHEADER块 -typedef struct -{ - u32 BlockID; //块标志,strf==0X73747266 - u32 BlockSize; //块大小(不包含最初的8字节,也就是BlockID和本BlockSize不计算在内) - BMP_HEADER bmiHeader; //位图信息头 - AVIRGBQUAD bmColors[1]; //颜色表 -}STRF_BMPHEADER; - -//对于strh,如果是音频流,strf(流格式)使STRH_WAVHEADER块 -typedef struct -{ - u32 BlockID; //块标志,strf==0X73747266 - u32 BlockSize; //块大小(不包含最初的8字节,也就是BlockID和本BlockSize不计算在内) - u16 FormatTag; //格式标志:0X0001=PCM,0X0055=MP3... - u16 Channels; //声道数,一般为2,表示立体声 - u32 SampleRate; //音频采样率 - u32 BaudRate; //波特率 - u16 BlockAlign; //数据块对齐标志 - u16 Size; //该结构大小 -}STRF_WAVHEADER; - -#define MAKEWORD(ptr) (u16)(((u16)*((u8*)(ptr))<<8)|(u16)*(u8*)((ptr)+1)) -#define MAKEDWORD(ptr) (u32)(((u16)*(u8*)(ptr)|(((u16)*(u8*)(ptr+1))<<8)|\ - (((u16)*(u8*)(ptr+2))<<16)|(((u16)*(u8*)(ptr+3))<<24))) - - -AVISTATUS avi_init(u8 *buf,int size); //初始化avi解码器 -u16 avi_srarch_id(u8* buf,int size,u8* id); //查找ID,ID必须是4个字节长度 -AVISTATUS avi_get_streaminfo(u8* buf); //获取流信息 - - - -//播放视频,loop==1,循环播放 -int video_play (char *pname,int loop); +// 对于strh,如果是视频流,strf(流格式)使STRH_BMPHEADER块 +typedef struct { + u32 BlockID; // 块标志,strf==0X73747266 + u32 BlockSize; // 块大小(不包含最初的8字节,也就是BlockID和本BlockSize不计算在内) + BMP_HEADER bmiHeader; // 位图信息头 + AVIRGBQUAD bmColors[1]; // 颜色表 +} STRF_BMPHEADER; +// 对于strh,如果是音频流,strf(流格式)使STRH_WAVHEADER块 +typedef struct { + u32 BlockID; // 块标志,strf==0X73747266 + u32 BlockSize; // 块大小(不包含最初的8字节,也就是BlockID和本BlockSize不计算在内) + u16 FormatTag; // 格式标志:0X0001=PCM,0X0055=MP3... + u16 Channels; // 声道数,一般为2,表示立体声 + u32 SampleRate; // 音频采样率 + u32 BaudRate; // 波特率 + u16 BlockAlign; // 数据块对齐标志 + u16 Size; // 该结构大小 +} STRF_WAVHEADER; +#define MAKEWORD(ptr) \ + (u16)(((u16) * ((u8 *)(ptr)) << 8) | (u16) * (u8 *)((ptr) + 1)) +#define MAKEDWORD(ptr) \ + (u32)(((u16) * (u8 *)(ptr) | (((u16) * (u8 *)(ptr + 1)) << 8) | \ + (((u16) * (u8 *)(ptr + 2)) << 16) | \ + (((u16) * (u8 *)(ptr + 3)) << 24))) +AVISTATUS avi_init(u8 *buf, int size); // 初始化avi解码器 +u16 avi_srarch_id(u8 *buf, int size, u8 *id); // 查找ID,ID必须是4个字节长度 +AVISTATUS avi_get_streaminfo(u8 *buf); // 获取流信息 +// 播放视频,loop==1,循环播放 +int video_play(char *pname, int loop); u8 Avih_Parser(u8 *buffer); u8 Strl_Parser(u8 *buffer); u8 Strf_Parser(u8 *buffer); - - -u16 Search_Movi(u8* buffer); -u16 Search_Fram(u8* buffer); -u32 ReadUnit(u8 *buffer,u8 index,u8 Bytes,u8 Format); +u16 Search_Movi(u8 *buffer); +u16 Search_Fram(u8 *buffer); +u32 ReadUnit(u8 *buffer, u8 index, u8 Bytes, u8 Format); #endif - diff --git a/Project/Src/MJPEG/mjpeg.c b/Project/Src/MJPEG/mjpeg.c index 23942cb..700f81d 100644 --- a/Project/Src/MJPEG/mjpeg.c +++ b/Project/Src/MJPEG/mjpeg.c @@ -1,223 +1,186 @@ -#include "mjpeg.h" +#include "mjpeg.h" +#include "ff.h" +#include "lcd_rgb.h" #include "mymem.h" -#include "ff.h" -#include "lcd_rgb.h" -////////////////////////////////////////////////////////////////////////////////// -//本程序只供学习使用,未经作者许可,不得用于其它任何用途 -//ALIENTEK STM32F407开发板 -//MJPEG视频处理 代码 -//正点原子@ALIENTEK -//技术论坛:www.openedv.com -//创建日期:2014/7/20 -//版本:V1.0 -//版权所有,盗版必究。 -//Copyright(C) 广州市星翼电子科技有限公司 2009-2019 -//All rights reserved +////////////////////////////////////////////////////////////////////////////////// +// 本程序只供学习使用,未经作者许可,不得用于其它任何用途 +// ALIENTEK STM32F407开发板 +// MJPEG视频处理 代码 +// 正点原子@ALIENTEK +// 技术论坛:www.openedv.com +// 创建日期:2014/7/20 +// 版本:V1.0 +// 版权所有,盗版必究。 +// Copyright(C) 广州市星翼电子科技有限公司 2009-2019 +// All rights reserved //******************************************************************************* -//修改信息 -//无 -////////////////////////////////////////////////////////////////////////////////// - +// 修改信息 +// 无 +////////////////////////////////////////////////////////////////////////////////// struct jpeg_decompress_struct *cinfo; struct my_error_mgr *jerr; -u8 *jpegbuf; //jpeg数据缓存指针 -u32 jbufsize; //jpeg buf大小 -u16 imgoffx,imgoffy; //图像在x,y方向的偏移量 +u8 *jpegbuf; // jpeg数据缓存指针 +u32 jbufsize; // jpeg buf大小 +u16 imgoffx, imgoffy; // 图像在x,y方向的偏移量 - - //////////////////////////////////////////////////////////////////////////////// -//简单快速的内存分配,以提高速度 -#define MJPEG_MAX_MALLOC_SIZE 38*1024 //最大可以分配38K字节 +// 简单快速的内存分配,以提高速度 +#define MJPEG_MAX_MALLOC_SIZE 38 * 1024 // 最大可以分配38K字节 +u8 *jmembuf; // mjpeg解码的 内存池 +u32 jmempos; // 内存池指针 -u8 *jmembuf; //mjpeg解码的 内存池 -u32 jmempos; //内存池指针 - -//mjpeg申请内存 -void* mjpeg_malloc(u32 num) -{ - u32 curpos=jmempos; //此次分配的起始地址 - jmempos+=num; //下一次分配的起始地址 - if(jmempos>MJPEG_MAX_MALLOC_SIZE) - { - printf("mem error:%d,%d",curpos,num); - } - return (void *)&jmembuf[curpos]; //返回申请到的内存首地址 -} +// mjpeg申请内存 +void *mjpeg_malloc(u32 num) { + u32 curpos = jmempos; // 此次分配的起始地址 + jmempos += num; // 下一次分配的起始地址 + if (jmempos > MJPEG_MAX_MALLOC_SIZE) { + printf("mem error:%d,%d", curpos, num); + } + return (void *)&jmembuf[curpos]; // 返回申请到的内存首地址 +} //////////////////////////////////////////////////////////////////////////////// -//错误退出 -static void my_error_exit(j_common_ptr cinfo) -{ - my_error_ptr myerr=(my_error_ptr) cinfo->err; - (*cinfo->err->output_message) (cinfo); - longjmp(myerr->setjmp_buffer, 1); -} - -METHODDEF(void) my_emit_message(j_common_ptr cinfo, int msg_level) -{ - my_error_ptr myerr=(my_error_ptr) cinfo->err; - if(msg_level<0) - { - printf("emit msg:%d\r\n",msg_level); - longjmp(myerr->setjmp_buffer, 1); - } +// 错误退出 +static void my_error_exit(j_common_ptr cinfo) { + my_error_ptr myerr = (my_error_ptr)cinfo->err; + (*cinfo->err->output_message)(cinfo); + longjmp(myerr->setjmp_buffer, 1); } -//初始化资源,不执行任何操作 -static void init_source(j_decompress_ptr cinfo) -{ - //不需要做任何事情. - return; -} -//填充输入缓冲区,一次性读取整帧数据 -static boolean fill_input_buffer(j_decompress_ptr cinfo) -{ - if(jbufsize==0)//结束了 - { - //printf("jd read off\r\n"); - //填充结束符 - jpegbuf[0] = (u8) 0xFF; - jpegbuf[1] = (u8) JPEG_EOI; - cinfo->src->next_input_byte =jpegbuf; - cinfo->src->bytes_in_buffer = 2; - }else - { - cinfo->src->next_input_byte =jpegbuf; - cinfo->src->bytes_in_buffer = jbufsize; - jbufsize-=jbufsize; - } - return TRUE; +METHODDEF(void) +my_emit_message(j_common_ptr cinfo, int msg_level) { + my_error_ptr myerr = (my_error_ptr)cinfo->err; + if (msg_level < 0) { + printf("emit msg:%d\r\n", msg_level); + longjmp(myerr->setjmp_buffer, 1); + } } -//在文件里面,跳过num_bytes个数据 -static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) -{ - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) - { - while(num_bytes>(long) cinfo->src->bytes_in_buffer) - { - num_bytes-=(long)cinfo->src->bytes_in_buffer; - (void)cinfo->src->fill_input_buffer(cinfo); - /* note we assume that fill_input_buffer will never - * return FALSE, so suspension need not be handled. - */ - } - cinfo->src->next_input_byte += (size_t) num_bytes; - cinfo->src->bytes_in_buffer -= (size_t) num_bytes; + +// 初始化资源,不执行任何操作 +static void init_source(j_decompress_ptr cinfo) { + // 不需要做任何事情. + return; +} +// 填充输入缓冲区,一次性读取整帧数据 +static boolean fill_input_buffer(j_decompress_ptr cinfo) { + if (jbufsize == 0) // 结束了 + { + // printf("jd read off\r\n"); + // 填充结束符 + jpegbuf[0] = (u8)0xFF; + jpegbuf[1] = (u8)JPEG_EOI; + cinfo->src->next_input_byte = jpegbuf; + cinfo->src->bytes_in_buffer = 2; + } else { + cinfo->src->next_input_byte = jpegbuf; + cinfo->src->bytes_in_buffer = jbufsize; + jbufsize -= jbufsize; + } + return TRUE; +} +// 在文件里面,跳过num_bytes个数据 +static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long)cinfo->src->bytes_in_buffer) { + num_bytes -= (long)cinfo->src->bytes_in_buffer; + (void)cinfo->src->fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never + * return FALSE, so suspension need not be handled. + */ } -} -//在解码结束后,被jpeg_finish_decompress函数调用 -static void term_source(j_decompress_ptr cinfo) -{ - //不做任何处理 - return; + cinfo->src->next_input_byte += (size_t)num_bytes; + cinfo->src->bytes_in_buffer -= (size_t)num_bytes; + } } -//初始化jpeg解码数据源 -static void jpeg_filerw_src_init(j_decompress_ptr cinfo) -{ - if (cinfo->src == NULL) /* first time for this JPEG object? */ - { - cinfo->src = (struct jpeg_source_mgr *) - (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof(struct jpeg_source_mgr)); - } - cinfo->src->init_source = init_source; - cinfo->src->fill_input_buffer = fill_input_buffer; - cinfo->src->skip_input_data = skip_input_data; - cinfo->src->resync_to_restart = jpeg_resync_to_restart; /* use default method */ - cinfo->src->term_source = term_source; - cinfo->src->bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - cinfo->src->next_input_byte = NULL; /* until buffer loaded */ -} - - -//mjpeg 解码初始化 -//offx,offy:x,y方向的偏移 -//返回值:0,成功; -// 1,失败 -u8 mjpegdec_init(u16 offx,u16 offy) -{ - cinfo=mymalloc(sizeof(struct jpeg_decompress_struct)); - jerr=mymalloc(sizeof(struct my_error_mgr)); - //分配sram区的内存以提高解码速度--2020.6.2 - jmembuf=mymalloc_fast(MJPEG_MAX_MALLOC_SIZE);//MJPEG解码内存池申请 - if(cinfo==0||jerr==0||jmembuf==0) - { - mjpegdec_free(); - return 1; - } - //保存图像在x,y方向的偏移量 - imgoffx=offx; - imgoffy=offy; - return 0; +// 在解码结束后,被jpeg_finish_decompress函数调用 +static void term_source(j_decompress_ptr cinfo) { + // 不做任何处理 + return; } -//mjpeg结束,释放内存 -void mjpegdec_free(void) -{ - myfree(cinfo); - myfree(jerr); - myfree(jmembuf); +// 初始化jpeg解码数据源 +static void jpeg_filerw_src_init(j_decompress_ptr cinfo) { + if (cinfo->src == NULL) /* first time for this JPEG object? */ + { + cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)( + (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr)); + } + cinfo->src->init_source = init_source; + cinfo->src->fill_input_buffer = fill_input_buffer; + cinfo->src->skip_input_data = skip_input_data; + cinfo->src->resync_to_restart = + jpeg_resync_to_restart; /* use default method */ + cinfo->src->term_source = term_source; + cinfo->src->bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + cinfo->src->next_input_byte = NULL; /* until buffer loaded */ } -//解码一副JPEG图片 -//buf:jpeg数据流数组 -//bsize:数组大小 -//返回值:0,成功 -// 其他,错误 -u8 mjpegdec_decode(u8* buf,u32 bsize) -{ - JSAMPARRAY buffer; - if(bsize==0)return 1; - jpegbuf=buf; - jbufsize=bsize; - jmempos=0;//MJEPG解码,重新从0开始分配内存 - - cinfo->err=jpeg_std_error(&jerr->pub); - jerr->pub.error_exit = my_error_exit; - jerr->pub.emit_message = my_emit_message; - //if(bsize>20*1024)printf("s:%d\r\n",bsize); - if (setjmp(jerr->setjmp_buffer)) //错误处理 - { - jpeg_abort_decompress(cinfo); - jpeg_destroy_decompress(cinfo); - return 2; - } - jpeg_create_decompress(cinfo); - jpeg_filerw_src_init(cinfo); - jpeg_read_header(cinfo, TRUE); - cinfo->dct_method = JDCT_IFAST; - cinfo->do_fancy_upsampling = 0; - jpeg_start_decompress(cinfo); - extern void jdmerge_setlcdaddr (unsigned short *addr); - unsigned short *lcd_addr=(unsigned short *)LCD_GetDrawAddr(); -// lcd_addr+=LCD_GetLcdSizeX()*LCD_GetLcdSizeY()-1-(imgoffx+imgoffy*LCD_GetLcdSizeX()); - jdmerge_setlcdaddr(lcd_addr);//设置显示位置 - while (cinfo->output_scanline < cinfo->output_height) - { - jpeg_read_scanlines(cinfo, buffer, 1); - } - jpeg_finish_decompress(cinfo); - jpeg_destroy_decompress(cinfo); - return 0; +// mjpeg 解码初始化 +// offx,offy:x,y方向的偏移 +// 返回值:0,成功; +// 1,失败 +u8 mjpegdec_init(u16 offx, u16 offy) { + cinfo = mymalloc(sizeof(struct jpeg_decompress_struct)); + jerr = mymalloc(sizeof(struct my_error_mgr)); + // 分配sram区的内存以提高解码速度--2020.6.2 + jmembuf = mymalloc_fast(MJPEG_MAX_MALLOC_SIZE); // MJPEG解码内存池申请 + if (cinfo == 0 || jerr == 0 || jmembuf == 0) { + mjpegdec_free(); + return 1; + } + // 保存图像在x,y方向的偏移量 + imgoffx = offx; + imgoffy = offy; + return 0; +} +// mjpeg结束,释放内存 +void mjpegdec_free(void) { + myfree(cinfo); + myfree(jerr); + myfree(jmembuf); } +// 解码一副JPEG图片 +// buf:jpeg数据流数组 +// bsize:数组大小 +// 返回值:0,成功 +// 其他,错误 +u8 mjpegdec_decode(u8 *buf, u32 bsize) { + JSAMPARRAY buffer; + if (bsize == 0) + return 1; + jpegbuf = buf; + jbufsize = bsize; + jmempos = 0; // MJEPG解码,重新从0开始分配内存 - - - - - - - - - - - - - - + cinfo->err = jpeg_std_error(&jerr->pub); + jerr->pub.error_exit = my_error_exit; + jerr->pub.emit_message = my_emit_message; + // if(bsize>20*1024)printf("s:%d\r\n",bsize); + if (setjmp(jerr->setjmp_buffer)) // 错误处理 + { + jpeg_abort_decompress(cinfo); + jpeg_destroy_decompress(cinfo); + return 2; + } + jpeg_create_decompress(cinfo); + jpeg_filerw_src_init(cinfo); + jpeg_read_header(cinfo, TRUE); + cinfo->dct_method = JDCT_IFAST; + cinfo->do_fancy_upsampling = 0; + jpeg_start_decompress(cinfo); + extern void jdmerge_setlcdaddr(unsigned short *addr); + unsigned short *lcd_addr = (unsigned short *)LCD_GetDrawAddr(); + // lcd_addr+=LCD_GetLcdSizeX()*LCD_GetLcdSizeY()-1-(imgoffx+imgoffy*LCD_GetLcdSizeX()); + jdmerge_setlcdaddr(lcd_addr); // 设置显示位置 + while (cinfo->output_scanline < cinfo->output_height) { + jpeg_read_scanlines(cinfo, buffer, 1); + } + jpeg_finish_decompress(cinfo); + jpeg_destroy_decompress(cinfo); + return 0; +} diff --git a/Project/Src/MJPEG/mjpeg.h b/Project/Src/MJPEG/mjpeg.h index 3a2f869..51aeefc 100644 --- a/Project/Src/MJPEG/mjpeg.h +++ b/Project/Src/MJPEG/mjpeg.h @@ -1,36 +1,33 @@ #ifndef __MJPEG_H -#define __MJPEG_H -#include "stdio.h" -#include +#define __MJPEG_H +#include "stdio.h" #include "stm32f4xx.h" +#include #include -////////////////////////////////////////////////////////////////////////////////// -//本程序只供学习使用,未经作者许可,不得用于其它任何用途 -//ALIENTEK STM32F407开发板 -//MJPEG视频处理 代码 -//正点原子@ALIENTEK -//技术论坛:www.openedv.com -//创建日期:2014/7/20 -//版本:V1.0 -//版权所有,盗版必究。 -//Copyright(C) 广州市星翼电子科技有限公司 2009-2019 -//All rights reserved +////////////////////////////////////////////////////////////////////////////////// +// 本程序只供学习使用,未经作者许可,不得用于其它任何用途 +// ALIENTEK STM32F407开发板 +// MJPEG视频处理 代码 +// 正点原子@ALIENTEK +// 技术论坛:www.openedv.com +// 创建日期:2014/7/20 +// 版本:V1.0 +// 版权所有,盗版必究。 +// Copyright(C) 广州市星翼电子科技有限公司 2009-2019 +// All rights reserved //******************************************************************************* -//修改信息 -//无 -////////////////////////////////////////////////////////////////////////////////// - - +// 修改信息 +// 无 +////////////////////////////////////////////////////////////////////////////////// struct my_error_mgr { - struct jpeg_error_mgr pub; - jmp_buf setjmp_buffer; //for return to caller -}; -typedef struct my_error_mgr * my_error_ptr; + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; // for return to caller +}; +typedef struct my_error_mgr *my_error_ptr; -u8 mjpegdec_init(u16 offx,u16 offy); +u8 mjpegdec_init(u16 offx, u16 offy); void mjpegdec_free(void); -u8 mjpegdec_decode(u8* buf,u32 bsize); +u8 mjpegdec_decode(u8 *buf, u32 bsize); #endif - diff --git a/Project/Src/MP3/mp3play.c b/Project/Src/MP3/mp3play.c index 2e2151d..d0d1e2d 100644 --- a/Project/Src/MP3/mp3play.c +++ b/Project/Src/MP3/mp3play.c @@ -1,692 +1,658 @@ #include "mp3play.h" -#include "stm32f4xx.h" #include "dac.h" #include "delay.h" -#include "mymem.h" -#include "usart.h" -#include "string.h" -#include "rtthread.h" #include "ftfile.h" +#include "mymem.h" +#include "rtthread.h" +#include "stm32f4xx.h" +#include "string.h" +#include "usart.h" -//包含这个文件用于文字编码转换 +// 包含这个文件用于文字编码转换 #include "char_encode.h" - - - -////////////////////////////////////////////////////////////////////////////////// -//本程序移植自helix MP3解码库 -//ALIENTEK STM32F407开发板 -//MP3 解码代码 -//正点原子@ALIENTEK -//技术论坛:www.openedv.com -//创建日期:2014/6/29 -//版本:V1.0 +////////////////////////////////////////////////////////////////////////////////// +// 本程序移植自helix MP3解码库 +// ALIENTEK STM32F407开发板 +// MP3 解码代码 +// 正点原子@ALIENTEK +// 技术论坛:www.openedv.com +// 创建日期:2014/6/29 +// 版本:V1.0 //******************************************************************************** -//V1.0 说明 -//1,支持16位单声道/立体声MP3的解码 -//2,支持CBR/VBR格式MP3解码 -//3,支持ID3V1和ID3V2标签解析 -//4,支持所有比特率(MP3最高是320Kbps)解码 -////////////////////////////////////////////////////////////////////////////////// - -static __mp3ctrl * mp3ctrl; //mp3控制结构体 -static __audiodev audiodev; //音频控制结构体 -static vu8 g_buff_Invalid=0; //数据无效时要重新填充数据 -static vu8 g_buff_useing; //当前DMA在使用哪个缓冲区 -static int g_vol=5; //音量 0~10级 +// V1.0 说明 +// 1,支持16位单声道/立体声MP3的解码 +// 2,支持CBR/VBR格式MP3解码 +// 3,支持ID3V1和ID3V2标签解析 +// 4,支持所有比特率(MP3最高是320Kbps)解码 +////////////////////////////////////////////////////////////////////////////////// + +static __mp3ctrl *mp3ctrl; // mp3控制结构体 +static __audiodev audiodev; // 音频控制结构体 +static vu8 g_buff_Invalid = 0; // 数据无效时要重新填充数据 +static vu8 g_buff_useing; // 当前DMA在使用哪个缓冲区 +static int g_vol = 5; // 音量 0~10级 #define MP3_VOL_MAX 10 - -//MP3 DMA发送回调函数 -static void dma_tx_callback(DAC_UserStruct *dac) -{ - u16 i; - if (dac->buff_useing==1) - { - if((audiodev.status&0X01)==0)//暂停了,填充0 - { - for(i=0;i<2304*2;i++)audiodev.i2sbuf1[i]=0; - } - }else - { - if((audiodev.status&0X01)==0)//暂停了,填充0 - { - for(i=0;i<2304*2;i++)audiodev.i2sbuf2[i]=0; - } - } - g_buff_useing=dac->buff_useing; - g_buff_Invalid=1; -} -//填充PCM数据到DAC -//buf:PCM数据首地址 -//size:pcm数据量(16位为单位) -//nch:声道数(1,单声道,2立体声) -void mp3_fill_buffer(u16* buf,u16 size,u8 nch) -{ - u16 i; - s16 *p; - while(g_buff_Invalid==0)//等待传输完成 - { - rt_thread_delay(5);// - }; - if(g_buff_useing==0) - { - p=(s16*)audiodev.i2sbuf2; - }else - { - p=(s16*)audiodev.i2sbuf1; - } - if(nch==2) - { - for(i=0;i>4); - int temp=(int16_t)buf[i]; - temp=temp*g_vol/MP3_VOL_MAX; - p[i]=((temp+0x8000)>>4); - } - } - else //单声道 - { - for(i=0;i>4); - p[2*i+1]=p[2*i]; - } - } - //填充了数据之后设置为有效 - g_buff_Invalid=0; -} - - - -static void mp3_get_string (u8 type,u8 *out,u8 *in,int size); - - - - -//解析ID3V1 -//buf:输入数据缓存区(大小固定是128字节) -//pctrl:MP3控制器 -//返回值:0,获取正常 -// 其他,获取失败 -u8 mp3_id3v1_decode(u8* buf,__mp3ctrl *pctrl) -{ - ID3V1_Tag *id3v1tag; - id3v1tag=(ID3V1_Tag*)buf; - if (strncmp("TAG",(char*)id3v1tag->id,3)==0)//是MP3 ID3V1 TAG - { - mp3_get_string(0,pctrl->title,id3v1tag->title,30); - mp3_get_string(0,pctrl->artist,id3v1tag->artist,30); - }else return 1; - return 0; +// MP3 DMA发送回调函数 +static void dma_tx_callback(DAC_UserStruct *dac) { + u16 i; + if (dac->buff_useing == 1) { + if ((audiodev.status & 0X01) == 0) // 暂停了,填充0 + { + for (i = 0; i < 2304 * 2; i++) + audiodev.i2sbuf1[i] = 0; + } + } else { + if ((audiodev.status & 0X01) == 0) // 暂停了,填充0 + { + for (i = 0; i < 2304 * 2; i++) + audiodev.i2sbuf2[i] = 0; + } + } + g_buff_useing = dac->buff_useing; + g_buff_Invalid = 1; +} +// 填充PCM数据到DAC +// buf:PCM数据首地址 +// size:pcm数据量(16位为单位) +// nch:声道数(1,单声道,2立体声) +void mp3_fill_buffer(u16 *buf, u16 size, u8 nch) { + u16 i; + s16 *p; + while (g_buff_Invalid == 0) // 等待传输完成 + { + rt_thread_delay(5); // + }; + if (g_buff_useing == 0) { + p = (s16 *)audiodev.i2sbuf2; + } else { + p = (s16 *)audiodev.i2sbuf1; + } + if (nch == 2) { + for (i = 0; i < size; i++) { + // p[i]=((buf[i]+0x8000)>>4); + int temp = (int16_t)buf[i]; + temp = temp * g_vol / MP3_VOL_MAX; + p[i] = ((temp + 0x8000) >> 4); + } + } else // 单声道 + { + for (i = 0; i < size; i++) { + int temp = (int16_t)buf[i]; + temp = temp * g_vol / MP3_VOL_MAX; + p[2 * i] = ((temp + 0x8000) >> 4); + p[2 * i + 1] = p[2 * i]; + } + } + // 填充了数据之后设置为有效 + g_buff_Invalid = 0; } +static void mp3_get_string(u8 type, u8 *out, u8 *in, int size); - - - -static void mp3_get_string (u8 type,u8 *out,u8 *in,int size) -{ - if (type==1)//UNICODE16 - { - u8 *char_uni=mymalloc (128); - mymemset(char_uni,0,128); - strncpy((char*)char_uni,(char*)(in),size); - //Unicode转GBK - uni2gbk_str(char_uni,out); - myfree(char_uni); - } - else if (type==3)//UTF8 - { - u8 *char_utf8=mymalloc (128); - mymemset(char_utf8,0,128); - strncpy((char*)char_utf8,(char*)(in),size); - - //UTF8转GBK - utf82gbk_str (char_utf8,out); - - myfree(char_utf8); - } - else //(type==0)//ASCII - { - strncpy((char*)out,(char*)(in),size); - } +// 解析ID3V1 +// buf:输入数据缓存区(大小固定是128字节) +// pctrl:MP3控制器 +// 返回值:0,获取正常 +// 其他,获取失败 +u8 mp3_id3v1_decode(u8 *buf, __mp3ctrl *pctrl) { + ID3V1_Tag *id3v1tag; + id3v1tag = (ID3V1_Tag *)buf; + if (strncmp("TAG", (char *)id3v1tag->id, 3) == 0) // 是MP3 ID3V1 TAG + { + mp3_get_string(0, pctrl->title, id3v1tag->title, 30); + mp3_get_string(0, pctrl->artist, id3v1tag->artist, 30); + } else + return 1; + return 0; } +static void mp3_get_string(u8 type, u8 *out, u8 *in, int size) { + if (type == 1) // UNICODE16 + { + u8 *char_uni = mymalloc(128); + mymemset(char_uni, 0, 128); + strncpy((char *)char_uni, (char *)(in), size); + // Unicode转GBK + uni2gbk_str(char_uni, out); + myfree(char_uni); + } else if (type == 3) // UTF8 + { + u8 *char_utf8 = mymalloc(128); + mymemset(char_utf8, 0, 128); + strncpy((char *)char_utf8, (char *)(in), size); + // UTF8转GBK + utf82gbk_str(char_utf8, out); + myfree(char_utf8); + } else //(type==0)//ASCII + { + strncpy((char *)out, (char *)(in), size); + } +} +// 解析ID3V2 +// buf:输入数据缓存区 +// size:数据大小 +// pctrl:MP3控制器 +// 返回值:0,获取正常 +// 其他,获取失败 +u8 mp3_id3v2_decode(u8 *buf, u32 size, __mp3ctrl *pctrl) { + ID3V2_TagHead *taghead; + ID3V23_FrameHead *framehead; + u32 t; + u32 tagsize; // tag大小 + u32 frame_size; // 帧大小 + taghead = (ID3V2_TagHead *)buf; + if (strncmp("ID3", (const char *)taghead->id, 3) == 0) // 存在ID3? + { + tagsize = ((u32)taghead->size[0] << 21) | ((u32)taghead->size[1] << 14) | + ((u16)taghead->size[2] << 7) | taghead->size[3]; // 得到tag 大小 + pctrl->datastart = tagsize; // 得到mp3数据开始的偏移量 + if (tagsize > size) + tagsize = size; // tagsize大于输入bufsize的时候,只处理输入size大小的数据 + if (taghead->mversion < 3) { + printf("not supported mversion!\r\n"); + return 1; + } + t = 10; + while (t < tagsize) { + framehead = (ID3V23_FrameHead *)(buf + t); + frame_size = + ((u32)framehead->size[0] << 24) | ((u32)framehead->size[1] << 16) | + ((u32)framehead->size[2] << 8) | framehead->size[3]; // 得到帧大小 + if (strncmp("TT2", (char *)framehead->id, 3) == 0 || + strncmp("TIT2", (char *)framehead->id, 4) == + 0) // 找到歌曲标题帧,不支持unicode格式!! + { + u8 *dat_ptr = (buf + t + sizeof(ID3V23_FrameHead)); + int size = AUDIO_MIN(frame_size - 1, MP3_TITSIZE_MAX - 1); + mp3_get_string(dat_ptr[0], pctrl->title, dat_ptr + 1, size); + } + if (strncmp("TP1", (char *)framehead->id, 3) == 0 || + strncmp("TPE1", (char *)framehead->id, 4) == 0) // 找到歌曲艺术家帧 + { + u8 *dat_ptr = (buf + t + sizeof(ID3V23_FrameHead)); + int size = AUDIO_MIN(frame_size - 1, MP3_ARTSIZE_MAX - 1); + mp3_get_string(dat_ptr[0], pctrl->artist, dat_ptr + 1, size); + } + // if (strncmp("APIC",(char*)framehead->id,4)==0) + // { + // //专辑封面 + // u8 + //*dat_ptr=(buf+t+sizeof(ID3V23_FrameHead)); if (pctrl->jpeg_data) + // myfree(pctrl->jpeg_data); + // pctrl->jpeg_data=mymalloc(frame_size); pctrl->jpeg_len=frame_size; + // mymemcpy + //(pctrl->jpeg_data,dat_ptr,frame_size); + // } + t += frame_size + sizeof(ID3V23_FrameHead); + } + return 0; + } else { + pctrl->datastart = 0; // 不存在ID3,mp3数据是从0开始 + return (u8)-1; + } +} +// 获取MP3基本信息 +// pname:MP3文件路径 +// pctrl:MP3控制信息结构体 +// 返回值:0,成功 +// 其他,失败 +u8 mp3_get_info(u8 *data, u32 size, __mp3ctrl *pctrl) { + HMP3Decoder decoder = {0}; + MP3FrameInfo frame_info = {0}; + MP3_FrameXing *fxing = 0; + MP3_FrameVBRI *fvbri = 0; + u8 *buf = 0; + u8 *data_ptr = 0; + // u32 br; + u8 res = 0; + int offset = 0; + u32 p = 0; + short samples_per_frame = 0; // 一帧的采样个数 + u32 totframes = 0; // 总帧数 - - - -//解析ID3V2 -//buf:输入数据缓存区 -//size:数据大小 -//pctrl:MP3控制器 -//返回值:0,获取正常 -// 其他,获取失败 -u8 mp3_id3v2_decode(u8* buf,u32 size,__mp3ctrl *pctrl) -{ - ID3V2_TagHead *taghead; - ID3V23_FrameHead *framehead; - u32 t; - u32 tagsize; //tag大小 - u32 frame_size; //帧大小 - taghead=(ID3V2_TagHead*)buf; - if(strncmp("ID3",(const char*)taghead->id,3)==0)//存在ID3? - { - tagsize=((u32)taghead->size[0]<<21)|((u32)taghead->size[1]<<14)|((u16)taghead->size[2]<<7)|taghead->size[3];//得到tag 大小 - pctrl->datastart=tagsize; //得到mp3数据开始的偏移量 - if(tagsize>size)tagsize=size; //tagsize大于输入bufsize的时候,只处理输入size大小的数据 - if(taghead->mversion<3) - { - printf("not supported mversion!\r\n"); - return 1; - } - t=10; - while(tsize[0]<<24)|((u32)framehead->size[1]<<16)|((u32)framehead->size[2]<<8)|framehead->size[3];//得到帧大小 - if (strncmp("TT2",(char*)framehead->id,3)==0||strncmp("TIT2",(char*)framehead->id,4)==0)//找到歌曲标题帧,不支持unicode格式!! - { - u8 *dat_ptr=(buf+t+sizeof(ID3V23_FrameHead)); - int size=AUDIO_MIN(frame_size-1,MP3_TITSIZE_MAX-1); - mp3_get_string(dat_ptr[0],pctrl->title,dat_ptr+1,size); - } - if (strncmp("TP1",(char*)framehead->id,3)==0||strncmp("TPE1",(char*)framehead->id,4)==0)//找到歌曲艺术家帧 - { - u8 *dat_ptr=(buf+t+sizeof(ID3V23_FrameHead)); - int size=AUDIO_MIN(frame_size-1,MP3_ARTSIZE_MAX-1); - mp3_get_string(dat_ptr[0],pctrl->artist,dat_ptr+1,size); - } -// if (strncmp("APIC",(char*)framehead->id,4)==0) -// { -// //专辑封面 -// u8 *dat_ptr=(buf+t+sizeof(ID3V23_FrameHead)); -// if (pctrl->jpeg_data) myfree(pctrl->jpeg_data); -// pctrl->jpeg_data=mymalloc(frame_size); -// pctrl->jpeg_len=frame_size; -// mymemcpy (pctrl->jpeg_data,dat_ptr,frame_size); -// } - t+=frame_size+sizeof(ID3V23_FrameHead); - } - return 0; - }else - { pctrl->datastart=0;//不存在ID3,mp3数据是从0开始 - return (u8)-1; - } -} - -//获取MP3基本信息 -//pname:MP3文件路径 -//pctrl:MP3控制信息结构体 -//返回值:0,成功 -// 其他,失败 -u8 mp3_get_info(u8 *data,u32 size,__mp3ctrl* pctrl) -{ - HMP3Decoder decoder={0}; - MP3FrameInfo frame_info={0}; - MP3_FrameXing* fxing=0; - MP3_FrameVBRI* fvbri=0; - u8 *buf=0; - u8 *data_ptr=0; - //u32 br; - u8 res=0; - int offset=0; - u32 p=0; - short samples_per_frame=0; //一帧的采样个数 - u32 totframes=0; //总帧数 - - buf=mymalloc(MP3_FILE_BUF_SZ); //申请5K内存 - if(buf)//内存申请成功 - { - data_ptr=data; - mymemcpy (buf,data_ptr,MP3_FILE_BUF_SZ); - if(res==0)//读取文件成功,开始解析ID3V2/ID3V1以及获取MP3信息 - { - if (mp3_id3v2_decode(buf,MP3_FILE_BUF_SZ,pctrl)!=0) //解析ID3V2数据 - { - data_ptr=data+size-128; - mymemcpy (buf,data_ptr,128); - mp3_id3v1_decode(buf,pctrl); //解析ID3V1数据 - } - decoder=MP3InitDecoder(); //MP3解码申请内存 - data_ptr=data+pctrl->datastart; - mymemcpy (buf,data_ptr,MP3_FILE_BUF_SZ); - offset=MP3FindSyncWord(buf,MP3_FILE_BUF_SZ); //查找帧同步信息 - if(offset>=0&&MP3GetNextFrameInfo(decoder,&frame_info,&buf[offset])==0)//找到帧同步信息了,且下一阵信息获取正常 - { - p=offset+4+32; - fvbri=(MP3_FrameVBRI*)(buf+p); - if(strncmp("VBRI",(char*)fvbri->id,4)==0)//存在VBRI帧(VBR格式) - { - if (frame_info.version==MPEG1)samples_per_frame=1152;//MPEG1,layer3每帧采样数等于1152 - else samples_per_frame=576;//MPEG2/MPEG2.5,layer3每帧采样数等于576 - totframes=((u32)fvbri->frames[0]<<24)|((u32)fvbri->frames[1]<<16)|((u16)fvbri->frames[2]<<8)|fvbri->frames[3];//得到总帧数 - pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度 - }else //不是VBRI帧,尝试是不是Xing帧(VBR格式) - { - if (frame_info.version==MPEG1) //MPEG1 - { - p=frame_info.nChans==2?32:17; - samples_per_frame = 1152; //MPEG1,layer3每帧采样数等于1152 - }else - { - p=frame_info.nChans==2?17:9; - samples_per_frame=576; //MPEG2/MPEG2.5,layer3每帧采样数等于576 - } - p+=offset+4; - fxing=(MP3_FrameXing*)(buf+p); - if(strncmp("Xing",(char*)fxing->id,4)==0||strncmp("Info",(char*)fxing->id,4)==0)//是Xng帧 - { - if(fxing->flags[3]&0X01)//存在总frame字段 - { - totframes=((u32)fxing->frames[0]<<24)|((u32)fxing->frames[1]<<16)|((u16)fxing->frames[2]<<8)|fxing->frames[3];//得到总帧数 - pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度 - }else //不存在总frames字段 - { - pctrl->totsec=size/(frame_info.bitrate/8); - } - }else //CBR格式,直接计算总播放时间 - { - pctrl->totsec=size/(frame_info.bitrate/8); - } - } - pctrl->bitrate=frame_info.bitrate; //得到当前帧的码率 - mp3ctrl->samplerate=frame_info.samprate; //得到采样率. - if(frame_info.nChans==2)mp3ctrl->outsamples=frame_info.outputSamps; //输出PCM数据量大小 - else mp3ctrl->outsamples=frame_info.outputSamps*2; //输出PCM数据量大小,对于单声道MP3,直接*2,补齐为双声道输出 - }else res=0XFE;//未找到同步帧 - MP3FreeDecoder(decoder);//释放内存 - } - }else res=0XFF; - myfree(buf); - return res; -} -//得到当前播放时间 -//fx:文件指针 -//mp3x:mp3播放控制器 -//void mp3_get_curtime(FIL*fx, FILINFO *finfo,__mp3ctrl *mp3x) + buf = mymalloc(MP3_FILE_BUF_SZ); // 申请5K内存 + if (buf) // 内存申请成功 + { + data_ptr = data; + mymemcpy(buf, data_ptr, MP3_FILE_BUF_SZ); + if (res == 0) // 读取文件成功,开始解析ID3V2/ID3V1以及获取MP3信息 + { + if (mp3_id3v2_decode(buf, MP3_FILE_BUF_SZ, pctrl) != 0) // 解析ID3V2数据 + { + data_ptr = data + size - 128; + mymemcpy(buf, data_ptr, 128); + mp3_id3v1_decode(buf, pctrl); // 解析ID3V1数据 + } + decoder = MP3InitDecoder(); // MP3解码申请内存 + data_ptr = data + pctrl->datastart; + mymemcpy(buf, data_ptr, MP3_FILE_BUF_SZ); + offset = MP3FindSyncWord(buf, MP3_FILE_BUF_SZ); // 查找帧同步信息 + if (offset >= 0 && + MP3GetNextFrameInfo(decoder, &frame_info, &buf[offset]) == + 0) // 找到帧同步信息了,且下一阵信息获取正常 + { + p = offset + 4 + 32; + fvbri = (MP3_FrameVBRI *)(buf + p); + if (strncmp("VBRI", (char *)fvbri->id, 4) == 0) // 存在VBRI帧(VBR格式) + { + if (frame_info.version == MPEG1) + samples_per_frame = 1152; // MPEG1,layer3每帧采样数等于1152 + else + samples_per_frame = 576; // MPEG2/MPEG2.5,layer3每帧采样数等于576 + totframes = + ((u32)fvbri->frames[0] << 24) | ((u32)fvbri->frames[1] << 16) | + ((u16)fvbri->frames[2] << 8) | fvbri->frames[3]; // 得到总帧数 + pctrl->totsec = totframes * samples_per_frame / + frame_info.samprate; // 得到文件总长度 + } else // 不是VBRI帧,尝试是不是Xing帧(VBR格式) + { + if (frame_info.version == MPEG1) // MPEG1 + { + p = frame_info.nChans == 2 ? 32 : 17; + samples_per_frame = 1152; // MPEG1,layer3每帧采样数等于1152 + } else { + p = frame_info.nChans == 2 ? 17 : 9; + samples_per_frame = 576; // MPEG2/MPEG2.5,layer3每帧采样数等于576 + } + p += offset + 4; + fxing = (MP3_FrameXing *)(buf + p); + if (strncmp("Xing", (char *)fxing->id, 4) == 0 || + strncmp("Info", (char *)fxing->id, 4) == 0) // 是Xng帧 + { + if (fxing->flags[3] & 0X01) // 存在总frame字段 + { + totframes = ((u32)fxing->frames[0] << 24) | + ((u32)fxing->frames[1] << 16) | + ((u16)fxing->frames[2] << 8) | + fxing->frames[3]; // 得到总帧数 + pctrl->totsec = totframes * samples_per_frame / + frame_info.samprate; // 得到文件总长度 + } else // 不存在总frames字段 + { + pctrl->totsec = size / (frame_info.bitrate / 8); + } + } else // CBR格式,直接计算总播放时间 + { + pctrl->totsec = size / (frame_info.bitrate / 8); + } + } + pctrl->bitrate = frame_info.bitrate; // 得到当前帧的码率 + mp3ctrl->samplerate = frame_info.samprate; // 得到采样率. + if (frame_info.nChans == 2) + mp3ctrl->outsamples = frame_info.outputSamps; // 输出PCM数据量大小 + else + mp3ctrl->outsamples = + frame_info.outputSamps * + 2; // 输出PCM数据量大小,对于单声道MP3,直接*2,补齐为双声道输出 + } else + res = 0XFE; // 未找到同步帧 + MP3FreeDecoder(decoder); // 释放内存 + } + } else + res = 0XFF; + myfree(buf); + return res; +} +// 得到当前播放时间 +// fx:文件指针 +// mp3x:mp3播放控制器 +// void mp3_get_curtime(FIL*fx, FILINFO *finfo,__mp3ctrl *mp3x) //{ -// u32 fpos=0; -// if(fx->fptr>mp3x->datastart)fpos=fx->fptr-mp3x->datastart; //得到当前文件播放到的地方 -// mp3x->cursec=fpos*mp3x->totsec/(finfo->fsize-mp3x->datastart); //当前播放到第多少秒了? -//} -//mp3文件快进快退函数 -//pos:需要定位到的文件位置 -//返回值:当前文件位置(即定位后的结果) -u32 mp3_file_seek(u32 pos) -{ -// if(pos>audiodev.file->fsize) -// { -// pos=audiodev.file->fsize; -// } -// f_lseek(audiodev.file,pos); -// return audiodev.file->fptr; - return pos; +// u32 fpos=0; +// if(fx->fptr>mp3x->datastart)fpos=fx->fptr-mp3x->datastart; +////得到当前文件播放到的地方 +// mp3x->cursec=fpos*mp3x->totsec/(finfo->fsize-mp3x->datastart); +////当前播放到第多少秒了? +// } +// mp3文件快进快退函数 +// pos:需要定位到的文件位置 +// 返回值:当前文件位置(即定位后的结果) +u32 mp3_file_seek(u32 pos) { + // if(pos>audiodev.file->fsize) + // { + // pos=audiodev.file->fsize; + // } + // f_lseek(audiodev.file,pos); + // return audiodev.file->fptr; + return pos; } - - - -//判断mp3是否支持 -//读取 MP3_FILE_BUF_SZ 大小的数据就行了 -//返回0 支持,非0 不支持 -int mp3_get_support (u8* data,u32 size) -{ - int res=0; - __mp3ctrl *mp3ctrl=mymalloc(sizeof(__mp3ctrl)); - mymemset(mp3ctrl,0,sizeof(__mp3ctrl));//数据清零 - res=mp3_get_info(data,size,mp3ctrl); - if (mp3ctrl->jpeg_data) myfree(mp3ctrl->jpeg_data); - myfree(mp3ctrl); - mp3ctrl=0; - return res; +// 判断mp3是否支持 +// 读取 MP3_FILE_BUF_SZ 大小的数据就行了 +// 返回0 支持,非0 不支持 +int mp3_get_support(u8 *data, u32 size) { + int res = 0; + __mp3ctrl *mp3ctrl = mymalloc(sizeof(__mp3ctrl)); + mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零 + res = mp3_get_info(data, size, mp3ctrl); + if (mp3ctrl->jpeg_data) + myfree(mp3ctrl->jpeg_data); + myfree(mp3ctrl); + mp3ctrl = 0; + return res; } -//返回0 支持,非0 不支持 -int mp3_get_support_name (const char *name) -{ - //定义返回变量 - int res=0; - - //初始化mp3结构体 - __mp3ctrl *mp3ctrl=mymalloc(sizeof(__mp3ctrl)); - mymemset(mp3ctrl,0,sizeof(__mp3ctrl));//数据清零 - - //申请缓存 - u32 buf_size=1024*5; - u8 *buf=mymalloc (buf_size); - - //打开文件 - ft_file_struct *file=ft_if_fopen(name,"rw"); - - //获取头大小 - ft_if_fseek(file,0,SEEK_SET); - ft_if_fread(buf,1,buf_size,file); - ID3V2_TagHead *taghead; - u32 tagsize=0; //tag大小 - taghead=(ID3V2_TagHead*)buf; - if(strncmp("ID3",(const char*)taghead->id,3)==0)//存在ID3? - { - tagsize=((u32)taghead->size[0]<<21)|((u32)taghead->size[1]<<14)|((u16)taghead->size[2]<<7)|taghead->size[3];//得到tag 大小 - } - - //校验格式是否支持 - HMP3Decoder decoder={0}; - MP3FrameInfo frame_info={0}; - int offset=0; - decoder=MP3InitDecoder(); //MP3解码申请内存 - ft_if_fseek(file,tagsize,SEEK_SET); - ft_if_fread(buf,1,buf_size,file); - offset=MP3FindSyncWord(buf,buf_size); //查找帧同步信息 - if(offset>=0&&MP3GetNextFrameInfo(decoder,&frame_info,&buf[offset])==0)//找到帧同步信息了,且下一阵信息获取正常 - { res=0; } - else res=-1; - MP3FreeDecoder(decoder);//释放内存 +// 返回0 支持,非0 不支持 +int mp3_get_support_name(const char *name) { + // 定义返回变量 + int res = 0; - //关闭文件 - ft_if_fclose(file); - - //释放缓存 - myfree(buf); - - //释放mp3结构体 - myfree(mp3ctrl); - - //返回结果 - return res; + // 初始化mp3结构体 + __mp3ctrl *mp3ctrl = mymalloc(sizeof(__mp3ctrl)); + mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零 + + // 申请缓存 + u32 buf_size = 1024 * 5; + u8 *buf = mymalloc(buf_size); + + // 打开文件 + ft_file_struct *file = ft_if_fopen(name, "rw"); + + // 获取头大小 + ft_if_fseek(file, 0, SEEK_SET); + ft_if_fread(buf, 1, buf_size, file); + ID3V2_TagHead *taghead; + u32 tagsize = 0; // tag大小 + taghead = (ID3V2_TagHead *)buf; + if (strncmp("ID3", (const char *)taghead->id, 3) == 0) // 存在ID3? + { + tagsize = ((u32)taghead->size[0] << 21) | ((u32)taghead->size[1] << 14) | + ((u16)taghead->size[2] << 7) | taghead->size[3]; // 得到tag 大小 + } + + // 校验格式是否支持 + HMP3Decoder decoder = {0}; + MP3FrameInfo frame_info = {0}; + int offset = 0; + decoder = MP3InitDecoder(); // MP3解码申请内存 + ft_if_fseek(file, tagsize, SEEK_SET); + ft_if_fread(buf, 1, buf_size, file); + offset = MP3FindSyncWord(buf, buf_size); // 查找帧同步信息 + if (offset >= 0 && MP3GetNextFrameInfo(decoder, &frame_info, &buf[offset]) == + 0) // 找到帧同步信息了,且下一阵信息获取正常 + { + res = 0; + } else + res = -1; + MP3FreeDecoder(decoder); // 释放内存 + + // 关闭文件 + ft_if_fclose(file); + + // 释放缓存 + myfree(buf); + + // 释放mp3结构体 + myfree(mp3ctrl); + + // 返回结果 + return res; } - - - - -//播放一曲MP3音乐 -//fname:MP3文件路径. -//返回值:0,正常播放完成 +// 播放一曲MP3音乐 +// fname:MP3文件路径. +// 返回值:0,正常播放完成 //[b7]:0,正常状态;1,错误状态 -//[b6:0]:b7=0时,表示操作码 -// b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误) -u8 mp3_play_song(u8* data,u32 size) -{ - - if (mp3ctrl) return AP_ERR;//不可重入! - HMP3Decoder mp3decoder={0}; - MP3FrameInfo mp3frameinfo={0}; - u8 res=0; - u8* buffer=0; //输入buffer - u8* readptr=0; //MP3解码读指针 - u8 *data_ptr=0; - int offset=0; //偏移量 - int outofdata=0;//超出数据范围 - int bytesleft=0;//buffer还剩余的有效数据 - u32 br=0; - int err=0; - DAC_UserStruct dac={0}; - - mp3ctrl=mymalloc(sizeof(__mp3ctrl)); - buffer=mymalloc(MP3_FILE_BUF_SZ); //申请解码buf大小 - audiodev.i2sbuf1=mymalloc(2304*2); - audiodev.i2sbuf2=mymalloc(2304*2); - audiodev.tbuf=mymalloc(2304*2); - audiodev.file_seek=mp3_file_seek; - - if(!mp3ctrl||!buffer||!audiodev.i2sbuf1||!audiodev.i2sbuf2||!audiodev.tbuf)//内存申请失败 - { - myfree(mp3ctrl); - myfree(buffer); - myfree(audiodev.i2sbuf1); - myfree(audiodev.i2sbuf2); - myfree(audiodev.tbuf); - return AP_ERR; //错误 - } - mymemset(audiodev.i2sbuf1,0,2304*2); //数据清零 - mymemset(audiodev.i2sbuf2,0,2304*2); //数据清零 - mymemset(mp3ctrl,0,sizeof(__mp3ctrl));//数据清零 - res=mp3_get_info(data,size,mp3ctrl); - if(res==0) - { - - //初始化DAC - dac.buff1=(u32 *)audiodev.i2sbuf1; - dac.buff2=(u32 *)audiodev.i2sbuf2; - //dac.buff_size=2304*2; - dac.buff_size=mp3ctrl->outsamples*2; - dac.rate=DAC_GetRate(mp3ctrl->samplerate); - dac.call_back=dma_tx_callback; - DAC_NormalInit (&dac); - mp3decoder=MP3InitDecoder(); //MP3解码申请内存 - audiodev.status|=1<<1; //开启播放 - audiodev.status|=1<<0; //非暂停状态 - } - if(res==0&&mp3decoder!=0)//打开文件成功 - { - //f_lseek(audiodev.file,mp3ctrl->datastart); //跳过文件头中tag信息 - data_ptr=data+mp3ctrl->datastart; - //audio_start(); //开始播放 - while(res==0) - { - readptr=buffer; //MP3读指针指向buffer - offset=0; //偏移量为0 - outofdata=0; //数据正常 - bytesleft=0; - //res=f_read(audiodev.file,buffer,MP3_FILE_BUF_SZ,&br);//一次读取MP3_FILE_BUF_SZ字节 - br=MP3_FILE_BUF_SZ; - if (br>data+size-data_ptr) br=data+size-data_ptr; - mymemcpy (buffer,data_ptr,br); - data_ptr+=br; - if(res)//读数据出错了 - { - res=AP_ERR; - break; - } - if(br==0) //读数为0,说明解码完成了. - { - res=AP_OK; //播放完成 - break; - } - bytesleft+=br; //buffer里面有多少有效MP3数据? - err=0; - while(!outofdata)//没有出现数据异常(即可否找到帧同步字符) - { - offset=MP3FindSyncWord(readptr,bytesleft);//在readptr位置,开始查找同步字符 - if(offset<0) //没有找到同步字符,跳出帧解码循环 - { - outofdata=1;//没找到帧同步字符 - }else //找到同步字符了 - { - readptr+=offset; //MP3读指针偏移到同步字符处. - bytesleft-=offset; //buffer里面的有效数据个数,必须减去偏移量 - err=MP3Decode(mp3decoder,&readptr,&bytesleft,(short*)audiodev.tbuf,0);//解码一帧MP3数据 - - if(err!=0) - { - //printf("decode error:%d\r\n",err); - break; - }else - { - MP3GetLastFrameInfo(mp3decoder,&mp3frameinfo); //得到刚刚解码的MP3帧信息 - if(mp3ctrl->bitrate!=mp3frameinfo.bitrate) //更新码率 - { - mp3ctrl->bitrate=mp3frameinfo.bitrate; - } - mp3_fill_buffer((u16*)audiodev.tbuf,mp3frameinfo.outputSamps,mp3frameinfo.nChans);//填充pcm数据 - } - if(bytesleftdata+size-data_ptr) br=data+size-data_ptr; - mymemcpy (buffer+bytesleft,data_ptr,br); - data_ptr+=br; - if(brdata)fpos=data_ptr-data; //得到当前文件播放到的地方 - mp3ctrl->cursec=fpos*mp3ctrl->totsec/(size-mp3ctrl->datastart); //当前播放到第多少秒了? - audiodev.totsec=mp3ctrl->totsec; //参数传递 - audiodev.cursec=mp3ctrl->cursec; - audiodev.bitrate=mp3ctrl->bitrate; - audiodev.samplerate=mp3ctrl->samplerate; - audiodev.bps=16;//MP3仅支持16位 - if(audiodev.status&0X01)break;//没有按下暂停 - } - if((audiodev.status&(1<<1))==0)//请求结束播放/播放完成 - { - res=AP_NEXT;//跳出上上级循环 - outofdata=1;//跳出上一级循环 - break; - } - } - } - } - //audio_stop();//关闭音频输出 - }else res=AP_ERR;//错误 - MP3FreeDecoder(mp3decoder); //释放内存 - if (mp3ctrl->jpeg_data) myfree(mp3ctrl->jpeg_data); - myfree(mp3ctrl); - myfree(buffer); - myfree(audiodev.i2sbuf1); - myfree(audiodev.i2sbuf2); - myfree(audiodev.tbuf); - DAC_NormalDeInit (&dac); - mp3ctrl=0; - audiodev.cursec=0; - audiodev.totsec=0; - return res; -} +//[b6:0]:b7=0时,表示操作码 +// b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误) +u8 mp3_play_song(u8 *data, u32 size) { + if (mp3ctrl) + return AP_ERR; // 不可重入! + HMP3Decoder mp3decoder = {0}; + MP3FrameInfo mp3frameinfo = {0}; + u8 res = 0; + u8 *buffer = 0; // 输入buffer + u8 *readptr = 0; // MP3解码读指针 + u8 *data_ptr = 0; + int offset = 0; // 偏移量 + int outofdata = 0; // 超出数据范围 + int bytesleft = 0; // buffer还剩余的有效数据 + u32 br = 0; + int err = 0; + DAC_UserStruct dac = {0}; + + mp3ctrl = mymalloc(sizeof(__mp3ctrl)); + buffer = mymalloc(MP3_FILE_BUF_SZ); // 申请解码buf大小 + audiodev.i2sbuf1 = mymalloc(2304 * 2); + audiodev.i2sbuf2 = mymalloc(2304 * 2); + audiodev.tbuf = mymalloc(2304 * 2); + audiodev.file_seek = mp3_file_seek; + + if (!mp3ctrl || !buffer || !audiodev.i2sbuf1 || !audiodev.i2sbuf2 || + !audiodev.tbuf) // 内存申请失败 + { + myfree(mp3ctrl); + myfree(buffer); + myfree(audiodev.i2sbuf1); + myfree(audiodev.i2sbuf2); + myfree(audiodev.tbuf); + return AP_ERR; // 错误 + } + mymemset(audiodev.i2sbuf1, 0, 2304 * 2); // 数据清零 + mymemset(audiodev.i2sbuf2, 0, 2304 * 2); // 数据清零 + mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零 + res = mp3_get_info(data, size, mp3ctrl); + if (res == 0) { + + // 初始化DAC + dac.buff1 = (u32 *)audiodev.i2sbuf1; + dac.buff2 = (u32 *)audiodev.i2sbuf2; + // dac.buff_size=2304*2; + dac.buff_size = mp3ctrl->outsamples * 2; + dac.rate = DAC_GetRate(mp3ctrl->samplerate); + dac.call_back = dma_tx_callback; + DAC_NormalInit(&dac); + mp3decoder = MP3InitDecoder(); // MP3解码申请内存 + audiodev.status |= 1 << 1; // 开启播放 + audiodev.status |= 1 << 0; // 非暂停状态 + } + if (res == 0 && mp3decoder != 0) // 打开文件成功 + { + // f_lseek(audiodev.file,mp3ctrl->datastart); //跳过文件头中tag信息 + data_ptr = data + mp3ctrl->datastart; + // audio_start(); + // //开始播放 + while (res == 0) { + readptr = buffer; // MP3读指针指向buffer + offset = 0; // 偏移量为0 + outofdata = 0; // 数据正常 + bytesleft = 0; + // res=f_read(audiodev.file,buffer,MP3_FILE_BUF_SZ,&br);//一次读取MP3_FILE_BUF_SZ字节 + br = MP3_FILE_BUF_SZ; + if (br > data + size - data_ptr) + br = data + size - data_ptr; + mymemcpy(buffer, data_ptr, br); + data_ptr += br; + if (res) // 读数据出错了 + { + res = AP_ERR; + break; + } + if (br == 0) // 读数为0,说明解码完成了. + { + res = AP_OK; // 播放完成 + break; + } + bytesleft += br; // buffer里面有多少有效MP3数据? + err = 0; + while (!outofdata) // 没有出现数据异常(即可否找到帧同步字符) + { + offset = MP3FindSyncWord(readptr, + bytesleft); // 在readptr位置,开始查找同步字符 + if (offset < 0) // 没有找到同步字符,跳出帧解码循环 + { + outofdata = 1; // 没找到帧同步字符 + } else // 找到同步字符了 + { + readptr += offset; // MP3读指针偏移到同步字符处. + bytesleft -= offset; // buffer里面的有效数据个数,必须减去偏移量 + err = MP3Decode(mp3decoder, &readptr, &bytesleft, + (short *)audiodev.tbuf, 0); // 解码一帧MP3数据 + + if (err != 0) { + // printf("decode error:%d\r\n",err); + break; + } else { + MP3GetLastFrameInfo(mp3decoder, + &mp3frameinfo); // 得到刚刚解码的MP3帧信息 + if (mp3ctrl->bitrate != mp3frameinfo.bitrate) // 更新码率 + { + mp3ctrl->bitrate = mp3frameinfo.bitrate; + } + mp3_fill_buffer((u16 *)audiodev.tbuf, mp3frameinfo.outputSamps, + mp3frameinfo.nChans); // 填充pcm数据 + } + if (bytesleft < + MAINBUF_SIZE * + 2) // 当数组内容小于2倍MAINBUF_SIZE的时候,必须补充新的数据进来. + { + memmove( + buffer, readptr, + bytesleft); // 移动readptr所指向的数据到buffer里面,数据量大小为:bytesleft + // f_read(audiodev.file,buffer+bytesleft,MP3_FILE_BUF_SZ-bytesleft,&br);//补充余下的数据 + br = MP3_FILE_BUF_SZ - bytesleft; + if (br > data + size - data_ptr) + br = data + size - data_ptr; + mymemcpy(buffer + bytesleft, data_ptr, br); + data_ptr += br; + if (br < MP3_FILE_BUF_SZ - bytesleft) { + mymemset(buffer + bytesleft + br, 0, + MP3_FILE_BUF_SZ - bytesleft - br); + } + bytesleft = MP3_FILE_BUF_SZ; + readptr = buffer; + } + while (audiodev.status & (1 << 1)) // 正常播放中 + { + rt_thread_delay(5); + // mp3_get_curtime(audiodev.file,mp3ctrl); + u32 fpos = 0; + if (data_ptr > data) + fpos = data_ptr - data; // 得到当前文件播放到的地方 + mp3ctrl->cursec = + fpos * mp3ctrl->totsec / + (size - mp3ctrl->datastart); // 当前播放到第多少秒了? + audiodev.totsec = mp3ctrl->totsec; // 参数传递 + audiodev.cursec = mp3ctrl->cursec; + audiodev.bitrate = mp3ctrl->bitrate; + audiodev.samplerate = mp3ctrl->samplerate; + audiodev.bps = 16; // MP3仅支持16位 + if (audiodev.status & 0X01) + break; // 没有按下暂停 + } + if ((audiodev.status & (1 << 1)) == 0) // 请求结束播放/播放完成 + { + res = AP_NEXT; // 跳出上上级循环 + outofdata = 1; // 跳出上一级循环 + break; + } + } + } + } + // audio_stop();//关闭音频输出 + } else + res = AP_ERR; // 错误 + MP3FreeDecoder(mp3decoder); // 释放内存 + if (mp3ctrl->jpeg_data) + myfree(mp3ctrl->jpeg_data); + myfree(mp3ctrl); + myfree(buffer); + myfree(audiodev.i2sbuf1); + myfree(audiodev.i2sbuf2); + myfree(audiodev.tbuf); + DAC_NormalDeInit(&dac); + mp3ctrl = 0; + audiodev.cursec = 0; + audiodev.totsec = 0; + return res; +} // 获取当前播放的曲目名,返回非0成功 -char *mp3_get_name(void) -{ - if(mp3ctrl) - return (char *)mp3ctrl->title; - else return 0; +char *mp3_get_name(void) { + if (mp3ctrl) + return (char *)mp3ctrl->title; + else + return 0; } - // 获取当前播放的艺术家,返回非0成功 -char *mp3_get_artist(void) -{ - if(mp3ctrl) - return (char *)mp3ctrl->artist; - else return 0; +char *mp3_get_artist(void) { + if (mp3ctrl) + return (char *)mp3ctrl->artist; + else + return 0; } - // 获取当前播放的时间,返回1成功 -int mp3_get_time(int *totsec,int *cursec) -{ - if(mp3ctrl) - { - if(totsec) *totsec=mp3ctrl->totsec; - if(cursec) *cursec=mp3ctrl->cursec; - return 1; - } - else - { - return 0; - } +int mp3_get_time(int *totsec, int *cursec) { + if (mp3ctrl) { + if (totsec) + *totsec = mp3ctrl->totsec; + if (cursec) + *cursec = mp3ctrl->cursec; + return 1; + } else { + return 0; + } } +__audiodev *mp3_getAudiodev(void) { return &audiodev; } +__mp3ctrl *mp3_getMp3Info(void) { return mp3ctrl; } -__audiodev *mp3_getAudiodev (void) -{ - return &audiodev; +void mp3_stop(void) { mp3_getAudiodev()->status = 0; } + +void mp3_play(void) { + // 暂停中 + if (mp3_getAudiodev()->status & 1) { + mp3_getAudiodev()->status &= (~1); + } + // 播放中 + else { + mp3_getAudiodev()->status |= 1; + } } -__mp3ctrl *mp3_getMp3Info (void) -{ - return mp3ctrl; -} - - - -void mp3_stop (void) -{ - mp3_getAudiodev()->status=0; -} - - -void mp3_play (void) -{ - //暂停中 - if (mp3_getAudiodev()->status&1) - { - mp3_getAudiodev()->status&=(~1); - } - //播放中 - else - { - mp3_getAudiodev()->status|=1; - } -} - - -void mp3_suspend (void) -{ - mp3_getAudiodev()->status|=1; -} +void mp3_suspend(void) { mp3_getAudiodev()->status |= 1; } // 设置音量 -int mp3_set_vol(int vol) -{ - if(vol>MP3_VOL_MAX) vol=MP3_VOL_MAX; - else if(vol<0) vol=0; - - g_vol=vol; - return 0; +int mp3_set_vol(int vol) { + if (vol > MP3_VOL_MAX) + vol = MP3_VOL_MAX; + else if (vol < 0) + vol = 0; + g_vol = vol; + return 0; } // 增加音量 -int mp3_add_val(int vol) -{ - vol=vol+g_vol; - if(vol>MP3_VOL_MAX) vol=MP3_VOL_MAX; - else if(vol<0) vol=0; - - g_vol=vol; - return 0; +int mp3_add_val(int vol) { + vol = vol + g_vol; + if (vol > MP3_VOL_MAX) + vol = MP3_VOL_MAX; + else if (vol < 0) + vol = 0; + g_vol = vol; + return 0; } - // 减少音量 -int mp3_sub_val(int vol) -{ - vol=g_vol-vol; - if(vol>MP3_VOL_MAX) vol=MP3_VOL_MAX; - else if(vol<0) vol=0; - - g_vol=vol; - return 0; +int mp3_sub_val(int vol) { + vol = g_vol - vol; + if (vol > MP3_VOL_MAX) + vol = MP3_VOL_MAX; + else if (vol < 0) + vol = 0; + g_vol = vol; + return 0; } - - // 获取音量 -int mp3_get_vol(void) -{ - return g_vol; -} - - +int mp3_get_vol(void) { return g_vol; } diff --git a/Project/Src/MP3/mp3play.h b/Project/Src/MP3/mp3play.h index cee1119..9fe02b9 100644 --- a/Project/Src/MP3/mp3play.h +++ b/Project/Src/MP3/mp3play.h @@ -1,186 +1,151 @@ -#ifndef __MP3PLAY_H__ -#define __MP3PLAY_H__ -#include +#ifndef __MP3PLAY_H__ +#define __MP3PLAY_H__ #include -////////////////////////////////////////////////////////////////////////////////// -//本程序移植自helix MP3解码库 -//ALIENTEK STM32F407开发板 -//MP3 解码代码 -//正点原子@ALIENTEK -//技术论坛:www.openedv.com -//创建日期:2014/6/29 -//版本:V1.0 +#include +////////////////////////////////////////////////////////////////////////////////// +// 本程序移植自helix MP3解码库 +// ALIENTEK STM32F407开发板 +// MP3 解码代码 +// 正点原子@ALIENTEK +// 技术论坛:www.openedv.com +// 创建日期:2014/6/29 +// 版本:V1.0 //******************************************************************************** -//V1.0 说明 -//1,支持16位单声道/立体声MP3的解码 -//2,支持CBR/VBR格式MP3解码 -//3,支持ID3V1和ID3V2标签解析 -//4,支持所有比特率(MP3最高是320Kbps)解码 -////////////////////////////////////////////////////////////////////////////////// - -#define MP3_TITSIZE_MAX 40 //歌曲名字最大长度 -#define MP3_ARTSIZE_MAX 40 //歌曲名字最大长度 -#define MP3_FILE_BUF_SZ 20*1024 //MP3解码时,文件buf大小 - - - - - -//取2个值里面的较小值. -#ifndef AUDIO_MIN -#define AUDIO_MIN(x,y) ((x)<(y)? (x):(y)) +// V1.0 说明 +// 1,支持16位单声道/立体声MP3的解码 +// 2,支持CBR/VBR格式MP3解码 +// 3,支持ID3V1和ID3V2标签解析 +// 4,支持所有比特率(MP3最高是320Kbps)解码 +////////////////////////////////////////////////////////////////////////////////// + +#define MP3_TITSIZE_MAX 40 // 歌曲名字最大长度 +#define MP3_ARTSIZE_MAX 40 // 歌曲名字最大长度 +#define MP3_FILE_BUF_SZ 20 * 1024 // MP3解码时,文件buf大小 + +// 取2个值里面的较小值. +#ifndef AUDIO_MIN +#define AUDIO_MIN(x, y) ((x) < (y) ? (x) : (y)) #endif - - - - -//ID3V1 标签 -typedef struct __packed -{ - u8 id[3]; //ID,TAG三个字母 - u8 title[30]; //歌曲名字 - u8 artist[30]; //艺术家名字 - u8 year[4]; //年代 - u8 comment[30]; //备注 - u8 genre; //流派 -}ID3V1_Tag; -//ID3V2 标签头 -typedef struct __packed -{ - u8 id[3]; //ID - u8 mversion; //主版本号 - u8 sversion; //子版本号 - u8 flags; //标签头标志 - u8 size[4]; //标签信息大小(不包含标签头10字节).所以,标签大小=size+10. -}ID3V2_TagHead; +// ID3V1 标签 +typedef struct __packed { + u8 id[3]; // ID,TAG三个字母 + u8 title[30]; // 歌曲名字 + u8 artist[30]; // 艺术家名字 + u8 year[4]; // 年代 + u8 comment[30]; // 备注 + u8 genre; // 流派 +} ID3V1_Tag; -//ID3V2.3 版本帧头 -typedef struct __packed -{ - u8 id[4]; //帧ID - u8 size[4]; //帧大小 - u16 flags; //帧标志 -}ID3V23_FrameHead; +// ID3V2 标签头 +typedef struct __packed { + u8 id[3]; // ID + u8 mversion; // 主版本号 + u8 sversion; // 子版本号 + u8 flags; // 标签头标志 + u8 size[4]; // 标签信息大小(不包含标签头10字节).所以,标签大小=size+10. +} ID3V2_TagHead; -//MP3 Xing帧信息(没有全部列出来,仅列出有用的部分) -typedef struct __packed -{ - u8 id[4]; //帧ID,为Xing/Info - u8 flags[4]; //存放标志 - u8 frames[4]; //总帧数 - u8 fsize[4]; //文件总大小(不包含ID3) -}MP3_FrameXing; - -//MP3 VBRI帧信息(没有全部列出来,仅列出有用的部分) -typedef struct __packed -{ - u8 id[4]; //帧ID,为Xing/Info - u8 version[2]; //版本号 - u8 delay[2]; //延迟 - u8 quality[2]; //音频质量,0~100,越大质量越好 - u8 fsize[4]; //文件总大小 - u8 frames[4]; //文件总帧数 -}MP3_FrameVBRI; +// ID3V2.3 版本帧头 +typedef struct __packed { + u8 id[4]; // 帧ID + u8 size[4]; // 帧大小 + u16 flags; // 帧标志 +} ID3V23_FrameHead; +// MP3 Xing帧信息(没有全部列出来,仅列出有用的部分) +typedef struct __packed { + u8 id[4]; // 帧ID,为Xing/Info + u8 flags[4]; // 存放标志 + u8 frames[4]; // 总帧数 + u8 fsize[4]; // 文件总大小(不包含ID3) +} MP3_FrameXing; -//MP3控制结构体 -typedef struct __packed -{ - u8 title[MP3_TITSIZE_MAX]; //歌曲名字 - u8 artist[MP3_ARTSIZE_MAX]; //艺术家名字 - u32 totsec ; //整首歌时长,单位:秒 - u32 cursec ; //当前播放时长 - - u8 *jpeg_data; //专辑封面 - u32 jpeg_len; //专辑封面长度 +// MP3 VBRI帧信息(没有全部列出来,仅列出有用的部分) +typedef struct __packed { + u8 id[4]; // 帧ID,为Xing/Info + u8 version[2]; // 版本号 + u8 delay[2]; // 延迟 + u8 quality[2]; // 音频质量,0~100,越大质量越好 + u8 fsize[4]; // 文件总大小 + u8 frames[4]; // 文件总帧数 +} MP3_FrameVBRI; - u32 bitrate; //比特率 - u32 samplerate; //采样率 - u16 outsamples; //PCM输出数据量大小(以16位为单位),单声道MP3,则等于实际输出*2(方便DAC输出) +// MP3控制结构体 +typedef struct __packed { + u8 title[MP3_TITSIZE_MAX]; // 歌曲名字 + u8 artist[MP3_ARTSIZE_MAX]; // 艺术家名字 + u32 totsec; // 整首歌时长,单位:秒 + u32 cursec; // 当前播放时长 - u32 datastart; //数据帧开始的位置(在文件里面的偏移) -}__mp3ctrl; + u8 *jpeg_data; // 专辑封面 + u32 jpeg_len; // 专辑封面长度 -//extern __mp3ctrl * mp3ctrl; + u32 bitrate; // 比特率 + u32 samplerate; // 采样率 + u16 outsamples; // PCM输出数据量大小(以16位为单位),单声道MP3,则等于实际输出*2(方便DAC输出) + u32 datastart; // 数据帧开始的位置(在文件里面的偏移) +} __mp3ctrl; +// extern __mp3ctrl * mp3ctrl; - - - - -//音乐播放操作结果定义 +// 音乐播放操作结果定义 typedef enum { - AP_OK=0X00, //正常播放完成 - AP_NEXT, //播放下一曲 - AP_PREV, //播放上一曲 - AP_ERR=0X80, //播放有错误(没定义错误代码,仅仅表示出错) -}APRESULT; + AP_OK = 0X00, // 正常播放完成 + AP_NEXT, // 播放下一曲 + AP_PREV, // 播放上一曲 + AP_ERR = 0X80, // 播放有错误(没定义错误代码,仅仅表示出错) +} APRESULT; -//音乐播放控制器 -typedef struct __packed -{ - //2个I2S解码的BUF - u8 *i2sbuf1; - u8 *i2sbuf2; - u8 *tbuf; //零时数组 - u32(*file_seek)(u32);//文件快进快退函数 +// 音乐播放控制器 +typedef struct __packed { + // 2个I2S解码的BUF + u8 *i2sbuf1; + u8 *i2sbuf2; + u8 *tbuf; // 零时数组 + u32 (*file_seek)(u32); // 文件快进快退函数 - vu8 status; //bit0:0,暂停播放;1,继续播放 - //bit1:0,结束播放;1,开启播放 - //bit2~3:保留 - //bit4:0,无音乐播放;1,音乐播放中 (对外标记) - //bit5:0,无动作;1,执行了一次切歌操作(对外标记) - //bit6:0,无动作;1,请求终止播放(但是不删除音频播放任务),处理完成后,播放任务自动清零该位 - //bit7:0,音频播放任务已删除/请求删除;1,音频播放任务正在运行(允许继续执行) - - u8 mode; //播放模式 - //0,全部循环;1,单曲循环;2,随机播放; - - u8 *name; //当前播放的MP3歌曲名字 - u16 namelen; //name所占的点数. - u16 curnamepos; //当前的偏移 + vu8 status; // bit0:0,暂停播放;1,继续播放 + // bit1:0,结束播放;1,开启播放 + // bit2~3:保留 + // bit4:0,无音乐播放;1,音乐播放中 (对外标记) + // bit5:0,无动作;1,执行了一次切歌操作(对外标记) + // bit6:0,无动作;1,请求终止播放(但是不删除音频播放任务),处理完成后,播放任务自动清零该位 + // bit7:0,音频播放任务已删除/请求删除;1,音频播放任务正在运行(允许继续执行) - u32 totsec ; //整首歌时长,单位:秒 - u32 cursec ; //当前播放时长 - u32 bitrate; //比特率(位速) - u32 samplerate; //采样率 - u16 bps; //位数,比如16bit,24bit,32bit - - -}__audiodev; + u8 mode; // 播放模式 + // 0,全部循环;1,单曲循环;2,随机播放; + u8 *name; // 当前播放的MP3歌曲名字 + u16 namelen; // name所占的点数. + u16 curnamepos; // 当前的偏移 + u32 totsec; // 整首歌时长,单位:秒 + u32 cursec; // 当前播放时长 + u32 bitrate; // 比特率(位速) + u32 samplerate; // 采样率 + u16 bps; // 位数,比如16bit,24bit,32bit +} __audiodev; +u16 GetRBIT16(u16); +void mp3_i2s_dma_tx_callback(void); +void mp3_fill_buffer(u16 *buf, u16 size, u8 nch); +u8 mp3_id3v1_decode(u8 *buf, __mp3ctrl *pctrl); +u8 mp3_id3v2_decode(u8 *buf, u32 size, __mp3ctrl *pctrl); +u8 mp3_get_info(u8 *data, u32 size, __mp3ctrl *pctrl); - - -u16 GetRBIT16 (u16); - - - - -void mp3_i2s_dma_tx_callback(void) ; -void mp3_fill_buffer(u16* buf,u16 size,u8 nch); -u8 mp3_id3v1_decode(u8* buf,__mp3ctrl *pctrl); -u8 mp3_id3v2_decode(u8* buf,u32 size,__mp3ctrl *pctrl); -u8 mp3_get_info(u8 *data,u32 size,__mp3ctrl* pctrl); - -__audiodev *mp3_getAudiodev (void); -__mp3ctrl *mp3_getMp3Info (void); - - - +__audiodev *mp3_getAudiodev(void); +__mp3ctrl *mp3_getMp3Info(void); // 返回0 支持,非0 不支持 -int mp3_get_support (u8* data,u32 size) ; -int mp3_get_support_name (const char *name); +int mp3_get_support(u8 *data, u32 size); +int mp3_get_support_name(const char *name); // 播放内存中的mp3文件 -u8 mp3_play_song(u8* data,u32 size); +u8 mp3_play_song(u8 *data, u32 size); // 获取当前播放的曲目名,返回非0成功 char *mp3_get_name(void); @@ -189,11 +154,11 @@ char *mp3_get_name(void); char *mp3_get_artist(void); // 获取当前播放的时间,返回1成功 -int mp3_get_time(int *totsec,int *cursec); +int mp3_get_time(int *totsec, int *cursec); -void mp3_stop (void); -void mp3_play (void); -void mp3_suspend (void); +void mp3_stop(void); +void mp3_play(void); +void mp3_suspend(void); // 设置音量 int mp3_set_vol(int vol); @@ -208,31 +173,3 @@ int mp3_sub_val(int vol); int mp3_get_vol(void); #endif - - - - - - - - - - - - - - - - - - - - - - - - - - - -