最简单的基于FFMPEG+SDL的音频播放器

FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手。

在此我把自己做项目过程中实现的一个非常简单的音频播放器(大约200行代码)源代码传上来,以作备忘,同时方便新手学习FFMPEG。

该播放器虽然简单,但是几乎包含了使用FFMPEG播放一个音频所有必备的API,并且使用SDL输出解码出来的音频。

并且支持流媒体等多种音频输入。
程序使用了新的FFMPEG类库,和早期版本的FFMPEG类库的API函数略有不同。

平台使用VC2010


注意:

1.程序输出的解码后PCM音频数据可以使用Audition打开播放

2.m4a,aac文件可以直接播放。mp3文件需要调整SDL音频帧大小为4608(默认是4096),否则播放会不流畅

3.也可以播放视频中的音频


贴上程序代码:

[cpp]  view plain copy
  1. //  
  2. //FFMPEG+SDL音频解码程序  
  3. //雷霄骅  
  4. //中国传媒大学/数字电视技术  
  5. //leixiaohua1020@126.com  
  6. //  
  7. //  
  8. #include <stdlib.h>  
  9. #include <string.h>  
  10. extern "C"  
  11. {  
  12. #include "libavcodec/avcodec.h"  
  13. #include "libavformat/avformat.h"  
  14. //SDL  
  15. #include "sdl/SDL.h"  
  16. #include "sdl/SDL_thread.h"  
  17. };  
  18. #include "decoder.h"  
  19. //#include "wave.h"  
  20.   
  21. //#define _WAVE_  
  22.   
  23. //全局变量---------------------  
  24.     static  Uint8  *audio_chunk;   
  25.     static  Uint32  audio_len;   
  26.     static  Uint8  *audio_pos;   
  27. //-----------------  
  28.     /*  The audio function callback takes the following parameters:  
  29.     stream: A pointer to the audio buffer to be filled  
  30.     len: The length (in bytes) of the audio buffer (这是固定的4096?) 
  31.     回调函数 
  32.     注意:mp3为什么播放不顺畅? 
  33.     len=4096;audio_len=4608;两个相差512!为了这512,还得再调用一次回调函数。。。 
  34.     m4a,aac就不存在此问题(都是4096)! 
  35.     */   
  36.     void  fill_audio(void *udata,Uint8 *stream,int len){   
  37.         /*  Only  play  if  we  have  data  left  */   
  38.     if(audio_len==0)   
  39.             return;   
  40.         /*  Mix  as  much  data  as  possible  */   
  41.     len=(len>audio_len?audio_len:len);   
  42.     SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);  
  43.     audio_pos += len;   
  44.     audio_len -= len;   
  45.     }   
  46. //-----------------  
  47.   
  48.   
  49. int decode_audio(char* no_use)  
  50. {  
  51.     AVFormatContext *pFormatCtx;  
  52.     int             i, audioStream;  
  53.     AVCodecContext  *pCodecCtx;  
  54.     AVCodec         *pCodec;  
  55.   
  56.     char url[300]={0};  
  57.     strcpy(url,no_use);  
  58.     //Register all available file formats and codecs  
  59.     av_register_all();  
  60.   
  61.     //支持网络流输入  
  62.     avformat_network_init();  
  63.     //初始化  
  64.     pFormatCtx = avformat_alloc_context();  
  65.     //有参数avdic  
  66.     //if(avformat_open_input(&pFormatCtx,url,NULL,&avdic)!=0){  
  67.     if(avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0){  
  68.         printf("Couldn't open file.\n");  
  69.         return -1;  
  70.     }  
  71.       
  72.     // Retrieve stream information  
  73.     if(av_find_stream_info(pFormatCtx)<0)  
  74.     {  
  75.         printf("Couldn't find stream information.\n");  
  76.         return -1;  
  77.     }  
  78.     // Dump valid information onto standard error  
  79.     av_dump_format(pFormatCtx, 0, url, false);  
  80.   
  81.     // Find the first audio stream  
  82.     audioStream=-1;  
  83.     for(i=0; i < pFormatCtx->nb_streams; i++)  
  84.         //原为codec_type==CODEC_TYPE_AUDIO  
  85.         if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)  
  86.         {  
  87.             audioStream=i;  
  88.             break;  
  89.         }  
  90.   
  91.     if(audioStream==-1)  
  92.     {  
  93.         printf("Didn't find a audio stream.\n");  
  94.         return -1;  
  95.     }  
  96.   
  97.     // Get a pointer to the codec context for the audio stream  
  98.     pCodecCtx=pFormatCtx->streams[audioStream]->codec;  
  99.   
  100.     // Find the decoder for the audio stream  
  101.     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  
  102.     if(pCodec==NULL)  
  103.     {  
  104.         printf("Codec not found.\n");  
  105.         return -1;  
  106.     }  
  107.   
  108.     // Open codec  
  109.     if(avcodec_open(pCodecCtx, pCodec)<0)  
  110.     {  
  111.         printf("Could not open codec.\n");  
  112.         return -1;  
  113.     }  
  114.   
  115.     /********* For output file ******************/  
  116.     FILE *pFile;  
  117. #ifdef _WAVE_  
  118.     pFile=fopen("output.wav""wb");  
  119.     fseek(pFile, 44, SEEK_SET); //预留文件头的位置  
  120. #else  
  121.     pFile=fopen("output.pcm""wb");  
  122. #endif  
  123.   
  124.     // Open the time stamp file  
  125.     FILE *pTSFile;  
  126.     pTSFile=fopen("audio_time_stamp.txt""wb");  
  127.     if(pTSFile==NULL)  
  128.     {  
  129.         printf("Could not open output file.\n");  
  130.         return -1;  
  131.     }  
  132.     fprintf(pTSFile, "Time Base: %d/%d\n", pCodecCtx->time_base.num, pCodecCtx->time_base.den);  
  133.   
  134.     /*** Write audio into file ******/  
  135.     //把结构体改为指针  
  136.     AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));  
  137.     av_init_packet(packet);  
  138.   
  139.     //音频和视频解码更加统一!  
  140.     //新加  
  141.     AVFrame *pFrame;  
  142.     pFrame=avcodec_alloc_frame();  
  143.   
  144.     //---------SDL--------------------------------------  
  145.     //初始化  
  146.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {    
  147.         printf( "Could not initialize SDL - %s\n", SDL_GetError());   
  148.         exit(1);  
  149.     }  
  150.   
  151.     //结构体,包含PCM数据的相关信息  
  152.     SDL_AudioSpec wanted_spec;  
  153.     wanted_spec.freq = pCodecCtx->sample_rate;   
  154.     wanted_spec.format = AUDIO_S16SYS;   
  155.     wanted_spec.channels = pCodecCtx->channels;   
  156.     wanted_spec.silence = 0;   
  157.     wanted_spec.samples = 1024; //播放AAC,M4a,缓冲区的大小  
  158.     //wanted_spec.samples = 1152; //播放MP3,WMA时候用  
  159.     wanted_spec.callback = fill_audio;   
  160.     wanted_spec.userdata = pCodecCtx;   
  161.   
  162.     if (SDL_OpenAudio(&wanted_spec, NULL)<0)//步骤(2)打开音频设备   
  163.     {   
  164.         printf("can't open audio.\n");   
  165.         return 0;   
  166.     }   
  167.     //-----------------------------------------------------  
  168.     printf("比特率 %3d\n", pFormatCtx->bit_rate);  
  169.     printf("解码器名称 %s\n", pCodecCtx->codec->long_name);  
  170.     printf("time_base  %d \n", pCodecCtx->time_base);  
  171.     printf("声道数  %d \n", pCodecCtx->channels);  
  172.     printf("sample per second  %d \n", pCodecCtx->sample_rate);  
  173.     //新版不再需要  
  174. //  short decompressed_audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];  
  175. //  int decompressed_audio_buf_size;  
  176.     uint32_t ret,len = 0;  
  177.     int got_picture;  
  178.     int index = 0;  
  179.     while(av_read_frame(pFormatCtx, packet)>=0)  
  180.     {  
  181.         if(packet->stream_index==audioStream)  
  182.         {  
  183.             //decompressed_audio_buf_size = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2;  
  184.             //原为avcodec_decode_audio2  
  185.                 //ret = avcodec_decode_audio4( pCodecCtx, decompressed_audio_buf,  
  186.                 //&decompressed_audio_buf_size, packet.data, packet.size );  
  187.             //改为  
  188.             ret = avcodec_decode_audio4( pCodecCtx, pFrame,  
  189.                 &got_picture, packet);  
  190.             if ( ret < 0 ) // if error len = -1  
  191.             {  
  192.                 printf("Error in decoding audio frame.\n");  
  193.                 exit(0);  
  194.             }  
  195.             if ( got_picture > 0 )  
  196.             {  
  197. #if 1  
  198.                 printf("index %3d\n", index);  
  199.                 printf("pts %5d\n", packet->pts);  
  200.                 printf("dts %5d\n", packet->dts);  
  201.                 printf("packet_size %5d\n", packet->size);  
  202.                   
  203.                 //printf("test %s\n", rtmp->m_inChunkSize);  
  204. #endif  
  205.                 //直接写入  
  206.                 //注意:数据是data【0】,长度是linesize【0】  
  207. #if 1  
  208.                 fwrite(pFrame->data[0], 1, pFrame->linesize[0], pFile);  
  209.                 //fwrite(pFrame, 1, got_picture, pFile);  
  210.                 //len+=got_picture;  
  211.                 index++;  
  212.                 //fprintf(pTSFile, "%4d,%5d,%8d\n", index, decompressed_audio_buf_size, packet.pts);  
  213. #endif  
  214.             }  
  215. #if 1  
  216.             //---------------------------------------  
  217.             //printf("begin....\n");   
  218.             //设置音频数据缓冲,PCM数据  
  219.             audio_chunk = (Uint8*) pFrame->data[0];   
  220.             //设置音频数据长度  
  221.             audio_len = pFrame->linesize[0];  
  222.             //audio_len = 4096;  
  223.             //播放mp3的时候改为audio_len = 4096  
  224.             //则会比较流畅,但是声音会变调!MP3一帧长度4608  
  225.             //使用一次回调函数(4096字节缓冲)播放不完,所以还要使用一次回调函数,导致播放缓慢。。。  
  226.             //设置初始播放位置  
  227.             audio_pos = audio_chunk;  
  228.             //回放音频数据   
  229.             SDL_PauseAudio(0);  
  230.             //printf("don't close, audio playing...\n");   
  231.             while(audio_len>0)//等待直到音频数据播放完毕!   
  232.                 SDL_Delay(1);   
  233.             //---------------------------------------  
  234. #endif  
  235.         }  
  236.         // Free the packet that was allocated by av_read_frame  
  237.         //已改  
  238.         av_free_packet(packet);  
  239.     }  
  240.     //printf("The length of PCM data is %d bytes.\n", len);  
  241.   
  242. #ifdef _WAVE_  
  243.     fseek(pFile, 0, SEEK_SET);  
  244.     struct WAVE_HEADER wh;  
  245.   
  246.     memcpy(wh.header.RiffID, "RIFF", 4);  
  247.     wh.header.RiffSize = 36 + len;  
  248.     memcpy(wh.header.RiffFormat, "WAVE", 4);  
  249.   
  250.     memcpy(wh.format.FmtID, "fmt ", 4);  
  251.     wh.format.FmtSize = 16;  
  252.     wh.format.wavFormat.FormatTag = 1;  
  253.     wh.format.wavFormat.Channels = pCodecCtx->channels;  
  254.     wh.format.wavFormat.SamplesRate = pCodecCtx->sample_rate;  
  255.     wh.format.wavFormat.BitsPerSample = 16;  
  256.     calformat(wh.format.wavFormat); //Calculate AvgBytesRate and BlockAlign  
  257.   
  258.     memcpy(wh.data.DataID, "data", 4);  
  259.     wh.data.DataSize = len;  
  260.   
  261.     fwrite(&wh, 1, sizeof(wh), pFile);  
  262. #endif  
  263.     SDL_CloseAudio();//关闭音频设备   
  264.     // Close file  
  265.     fclose(pFile);  
  266.     // Close the codec  
  267.     avcodec_close(pCodecCtx);  
  268.     // Close the video file  
  269.     av_close_input_file(pFormatCtx);  
  270.   
  271.     return 0;  
  272. }  

程序会打印每一帧的信息

运行截图:



完整工程文件路径:

http://download.csdn.net/detail/leixiaohua1020/6033893


FFMPEG相关学习资料:

SDL GUIDE 中文译本

ffdoc (FFMPEG的最完整教程)
如何用FFmpeg编写一个简单播放器

====================

在SourceForge上传了一个版本:

https://sourceforge.net/projects/simplestffmpegaudioplayer/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值