最简单的基于FFMPEG+SDL的音频播放器
=====================================================
最簡單的基于FFmpeg的音頻播放器系列文章列表:
《最簡單的基于FFMPEG+SDL的音頻播放器》
《最簡單的基于FFMPEG+SDL的音頻播放器 ver2 (采用SDL2.0)》
《最簡單的基于FFMPEG+SDL的音頻播放器:拆分-解碼器和播放器》
=====================================================
簡介
FFMPEG工程浩大,可以參考的書籍又不是很多,因此很多剛學習FFMPEG的人常常感覺到無從下手。
在此我把自己做項目過程中實現的一個非常簡單的音頻播放器(大約200行代碼)源代碼傳上來,以作備忘,同時方便新手學習FFMPEG。
該播放器雖然簡單,但是幾乎包含了使用FFMPEG播放一個音頻所有必備的API,并且使用SDL輸出解碼出來的音頻。
并且支持流媒體等多種音頻輸入。程序使用了新的FFMPEG類庫,和早期版本的FFMPEG類庫的API函數略有不同。平臺使用VC2010。SourceForge項目主頁
https://sourceforge.net/projects/simplestffmpegaudioplayer/
注:本版本的SDL采用了SDL1.2,采用SDL2.0的播放器可以參考:
最簡單的基于FFMPEG+SDL的音頻播放器 ver2 (采用SDL2.0)
注意:
1.程序輸出的解碼后PCM音頻數據可以使用Audition打開播放
2.m4a,aac文件可以直接播放。mp3文件需要調整SDL音頻幀大小為4608(默認是4096),否則播放會不流暢
3.也可以播放視頻中的音頻
源代碼
/*** 最簡單的基于FFmpeg的音頻播放器 1.2* Simplest FFmpeg Audio Player 1.2** 雷霄驊 Lei Xiaohua* leixiaohua1020@126.com* 中國傳媒大學/數字電視技術* Communication University of China / Digital TV Technology* http://blog.csdn.net/leixiaohua1020** 本程序實現了音頻的解碼和播放。** This software decode and play audio streams.*/#include "stdafx.h" #include <stdlib.h> #include <string.h> extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswresample/swresample.h" //SDL #include "sdl/SDL.h" };#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio//Output PCM #define OUTPUT_PCM 1 //Use SDL #define USE_SDL 1//Buffer: //|-----------|-------------| //chunk-------pos---len-----| static Uint8 *audio_chunk; static Uint32 audio_len; static Uint8 *audio_pos; /* The audio function callback takes the following parameters: * stream: A pointer to the audio buffer to be filled * len: The length (in bytes) of the audio buffer * 回調函數 */ void fill_audio(void *udata,Uint8 *stream,int len){ if(audio_len==0) /* Only play if we have data left */ return; len=(len>audio_len?audio_len:len); /* Mix as much data as possible */ SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);audio_pos += len; audio_len -= len; } //-----------------int main(int argc, char* argv[]) {AVFormatContext *pFormatCtx;int i, audioStream;AVCodecContext *pCodecCtx;AVCodec *pCodec;char url[]="WavinFlag.aac";//char url[]="WavinFlag.mp3";//char url[]="72bian.wma";av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();//Openif(avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0){printf("Couldn't open input stream.\n");return -1;}// Retrieve stream informationif(av_find_stream_info(pFormatCtx)<0){printf("Couldn't find stream information.\n");return -1;}// Dump valid information onto standard errorav_dump_format(pFormatCtx, 0, url, false);// Find the first audio streamaudioStream=-1;for(i=0; i < pFormatCtx->nb_streams; i++)if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){audioStream=i;break;}if(audioStream==-1){printf("Didn't find a audio stream.\n");return -1;}// Get a pointer to the codec context for the audio streampCodecCtx=pFormatCtx->streams[audioStream]->codec;// Find the decoder for the audio streampCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){printf("Codec not found.\n");return -1;}// Open codecif(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Could not open codec.\n");return -1;}FILE *pFile=NULL; #if OUTPUT_PCMpFile=fopen("output.pcm", "wb"); #endifAVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));av_init_packet(packet);//Out Audio Paramuint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;//AAC:1024 MP3:1152int out_nb_samples=pCodecCtx->frame_size;AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;int out_sample_rate=44100;int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);//Out Buffer Sizeint out_buffer_size=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);uint8_t *out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);AVFrame *pFrame;pFrame=avcodec_alloc_frame(); //SDL------------------ #if USE_SDL//Initif(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1;}//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = out_sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = out_channels; wanted_spec.silence = 0; wanted_spec.samples = out_nb_samples; wanted_spec.callback = fill_audio; wanted_spec.userdata = pCodecCtx; if (SDL_OpenAudio(&wanted_spec, NULL)<0){ printf("can't open audio.\n"); return -1; } #endifprintf("Bitrate:\t %3d\n", pFormatCtx->bit_rate);printf("Decoder Name:\t %s\n", pCodecCtx->codec->long_name);printf("Channels:\t %d\n", pCodecCtx->channels);printf("Sample per Second\t %d \n", pCodecCtx->sample_rate);uint32_t ret,len = 0;int got_picture;int index = 0;//FIX:Some Codec's Context Information is missingint64_t in_channel_layout=av_get_default_channel_layout(pCodecCtx->channels);//Swrstruct SwrContext *au_convert_ctx;au_convert_ctx = swr_alloc();au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,out_channel_layout, out_sample_fmt, out_sample_rate,in_channel_layout,pCodecCtx->sample_fmt , pCodecCtx->sample_rate,0, NULL);swr_init(au_convert_ctx);//PlaySDL_PauseAudio(0);while(av_read_frame(pFormatCtx, packet)>=0){if(packet->stream_index==audioStream){ret = avcodec_decode_audio4( pCodecCtx, pFrame,&got_picture, packet);if ( ret < 0 ) {printf("Error in decoding audio frame.\n");return -1;}if ( got_picture > 0 ){swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);printf("index:%5d\t pts:%lld\t packet size:%d\n",index,packet->pts,packet->size);#if OUTPUT_PCM//Write PCMfwrite(out_buffer, 1, out_buffer_size, pFile); #endifindex++;} //SDL------------------ #if USE_SDL//Set audio buffer (PCM data)audio_chunk = (Uint8 *) out_buffer; //Audio buffer lengthaudio_len =out_buffer_size;audio_pos = audio_chunk;while(audio_len>0)//Wait until finishSDL_Delay(1); #endif}av_free_packet(packet);}swr_free(&au_convert_ctx);#if USE_SDLSDL_CloseAudio();//Close SDLSDL_Quit(); #endif#if OUTPUT_PCMfclose(pFile); #endifav_free(out_buffer);avcodec_close(pCodecCtx);av_close_input_file(pFormatCtx);return 0; }結果
程序會打印每一幀的信息,同時將音頻輸出到音頻輸出設備。運行截圖如下所示。
完整工程下載地址:
http://download.csdn.net/detail/leixiaohua1020/6033893
更新列表
更新(2014.5.8)===============================================
simplest ffmpeg audio player
完整工程(更新版)下載地址:
http://download.csdn.net/detail/leixiaohua1020/7319225
新版本中使用了最新版本的FFMPEG類庫(2014.5.7)。FFMPEG在新版本中的音頻解碼方面發生了比較大的變化。如果將舊版的主程序和新版的類庫組合使用的話,會出現聽到的都是雜音這一現象。經過研究發現,新版中avcodec_decode_audio4()解碼后輸出的音頻采樣數據格式為AV_SAMPLE_FMT_FLTP(float, planar)而不再是AV_SAMPLE_FMT_S16(signed 16 bits)。因此無法直接使用SDL進行播放。
最后的解決方法是使用SwrContext對音頻采樣數據進行轉換之后,再進行輸出播放,問題就可以得到解決了。轉換方面的代碼如下示例:
//輸出音頻數據大小,一定小于輸出內存。 int out_linesize; //輸出內存大小 int out_buffer_size=av_samples_get_buffer_size(&out_linesize, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1); uint8_t *out_buffer=new uint8_t[out_buffer_size]; ... au_convert_ctx = swr_alloc(); au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,pCodecCtx->channel_layout,pCodecCtx->sample_fmt , pCodecCtx->sample_rate,0, NULL); swr_init(au_convert_ctx);while(av_read_frame(pFormatCtx, packet)>=0){......swr_convert(au_convert_ctx,&out_buffer, out_linesize,(const uint8_t **)pFrame->data , pFrame->nb_samples);...... }更新(2014.9.1)===============================================
simplest ffmpeg audio player classic
完整工程(classic)下載地址:
http://download.csdn.net/detail/leixiaohua1020/7849625
本版本使用的類庫編譯時間為2012年的,無需經過swr_convert()即可播放,代碼簡潔。
重建了工程,刪掉了不必要的代碼,把代碼修改得更規范更易懂。
可以通過宏控制是否使用SDL,以及是否輸出PCM。
//Output PCM #define OUTPUT_PCM 0 //Use SDL #define USE_SDL 1更新(2014.9.2)===============================================
simplest ffmpeg audio player 1.2
完整工程下載地址:
http://download.csdn.net/detail/leixiaohua1020/7853199
本版本使用新的類庫(2014.5.6),解碼后的音頻需要經過swr_convert()轉換后方可播放。
重建了工程,刪掉了不必要的代碼,把代碼修改得更規范更易懂。
可以通過宏控制是否使用SDL,以及是否輸出PCM。
此外修改了部分地方,在原先版本的基礎上,支持更多種的音頻格式:AAC,MP3...
這一版本后不再修正這個音頻播放器,以后改為修正基于SDL2.0的音頻播放器
=============================================================
FFMPEG相關學習資料:
SDL GUIDE 中文譯本
http://download.csdn.net/detail/leixiaohua1020/6389841ffdoc (FFMPEG的最完整教程)
http://download.csdn.net/detail/leixiaohua1020/6377803
如何用FFmpeg編寫一個簡單播放器
http://download.csdn.net/detail/leixiaohua1020/6373783
總結
以上是生活随笔為你收集整理的最简单的基于FFMPEG+SDL的音频播放器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL事务嵌套
- 下一篇: Linux 下wifi 驱动开发(一)—