mplayer源代码解析(资料汇总)-

MPlayer流程

int c_mplay_main(int argc,char* argv[])
**調用AddExcept()注冊異常處理函數
** initmplayer();//初始化,創建快進和暫停的信號量
**InitTimer();初始化計時器
**mp_msg_init();初始化消息係統
**set_path_env();設置路徑、環境
**ipu_image_start();ipu初始化
**mplayer_showmode(1);設置顯示模式
**parse_codec_cfg(NULL);解析codec配置寄存器
**打開數據流
**分析播放樹
**添加播放樹列表
**初始化預填充緩存
**打開播放的文件
**創建buffer
**打開數據流
**檢測數據流類型(音頻格式和視頻格式)
**分析音頻流視頻流的信息(原始視頻尺寸、分辨率、幀頻率、碼流大小)
**啟動相應的分離器
**分析剪輯信息
**初始化codec(多媒體數字信號編解碼器)
**選擇打開相應的視頻解碼器
**初始化視頻解碼器,分析視頻流信息
**選擇打開相應的音頻解碼器
**初始化音頻解碼器、PCM,分析音頻信息
**同步音頻視頻輸出
**開始播放
main函數中的入口如下~

if (mpctx->sh_audio)
if (!fill_audio_out_buffers())
// at eof, all audio at least written to ao
//由mpctx->sh_audio引出,我想重點強調一下sh_audio_t結構,這是音頻流頭部結構~
// Stream headers:
typedef struct {
intaid;
demux_stream_t*ds;
struct codecs_st*codec;
unsigned intformat;
intinitialized;
float stream_delay; // numberof seconds stream should bedelayed(according
to dwStart or similar)
// outputformat:
intsample_format;
intsamplerate;
intsamplesize;
intchannels;
int o_bps; // ==samplerate*samplesize*channels(uncompr.bytes/sec)
int i_bps; // ==bitrate (compressedbytes/sec)
// inbuffers:
intaudio_in_minsize;// max. compressed packet size (== min. inbuffersize
)
char*a_in_buffer;
inta_in_buffer_len;
inta_in_buffer_size;
// decoderbuffers:
int audio_out_minsize; // max.uncompressed packet size (==min.outbuffsize
)
char*a_buffer;
inta_buffer_len;
inta_buffer_size;
// outputbuffers:
char*a_out_buffer;
inta_out_buffer_len;
inta_out_buffer_size;
struct af_stream_s*afilter;// the audio filter stream
struct ad_functions_s*ad_driver;
#ifdef DYNAMIC_PLUGINS
void*dec_handle;
#endif
// win32-compatible codecparameters:
AVIStreamHeaderaudio;
WAVEFORMATEX*wf;
//codec-specific:
void* context; //codec-specific stuff (usually HANDLE orstructpointer)
unsigned char* codecdata; //extra header data passed from demuxertocodec
intcodecdata_len;
doublepts; // last known pts value inoutput from decoder
int pts_bytes; // bytes outputby decoder after last known pts
char* lang; // tracklanguage
intdefault_track;
} sh_audio_t;
三.step by step
1.下麵我們來分析fill_audio_out_buffers()函數
static intfill_audio_out_buffers(void)
1.1查看音頻驅動有多大的空間可以填充解碼後的音頻數據
bytes_to_write=mpctx->audio_out->get_space();
這裏是查看音頻驅動還有多少空閑空間(即pcm緩衝區隊列的可用空間),如果有空閑的空間,就調用ao->play()
函數來填充這個buffer,並播放音頻數據。(pcm緩衝區的數據會被dma自動送去播放,我們不能讓pcm緩衝區為空,否則聲音就沒了!)這個音頻驅動的buffer的大小要設置合適,太小會導致一幀圖像還沒播放完,buffer就已經空了,會導致音視頻嚴重不同步;太大會導致Mplayer需要不停地解析媒體文件來填充這個音頻buffer,而視頻可能還來不及播放。。。
1.2 對音頻流進行解碼
if (decode_audio(sh_audio, playsize) <0)
注:這裏的decode_audio隻是一個接口,並不是具體的解碼api。這個函數從sh_audio->a_out_buffer獲得至少playsizebytes的解碼或過濾後的音頻數據,成功返回0,失敗返回-1

1.3 將解碼後的音頻數據進行播放
playsize=mpctx->audio_out->play(sh_audio->a_out_buffer,playsize,playflags);
注: 從sh_audio->a_out_buffer中copyplaysizebytes數據,注意這裏一定要copy出來,因為當play調用後,這部分數據就會被覆蓋了。這些數據不一定要用完,返回的值是用掉的數據長度。另外當flags|AOPLAY_FINAL_CHUNK的值是真時,說明音頻文件快結束了。(play()函數把音頻數據寫到pcm緩衝區。)
1.4 if (playsize > 0) {
sh_audio->a_out_buffer_len -=playsize;
memmove(sh_audio->a_out_buffer,&sh_audio->a_out_buffer[playsize],
sh_audio->a_out_buffer_len);
mpctx->delay+=playback_speed*playsize/(double)ao_data.bps;
}
播放成功後就從sh_audio->a_out_buffer中移出這段數據,同時減去sh_audio->a_out_buffer_len的長度.
2.剛才說了,decode_audio()函數並不是真正的解碼函數,它隻是提供一個接口,下麵我們就來看看這個函數到底做了哪些事情.
int decode_audio(sh_audio_t *sh_audio, intminlen)
2.1 解碼後的視頻數據都被cut成大小相同的一個個區間~
int unitsize = sh_audio->channels*sh_audio->samplesize * 16;
2.2 如果解碼器設置了audio_out_minsize,解碼可以等價於
while (output_len < target_len) output_len+=audio_out_minsize;
因此我們必需保證a_buffer_size大於我們需要的解碼數據長度加上audio_out_minsize,所以我們最大解碼長度也就有了如下的限製:(是不是有點繞口?~~)
int max_decode_len = sh_audio->a_buffer_size-sh_audio->audio_out_minsize;
2.3 如果a_out_buffer中沒有我們需要的足夠多的解碼後數據,我們當然要繼續解碼撒~隻不過我們要注意,
a_out_buffer中的數據是經過filter過濾了的(長度發生了變化),這裏會有一個過濾係數,我們反方向求過濾前的數據長度,所以要除以這個係數。
while (sh_audio->a_out_buffer_len<minlen) {
int declen = (minlen - sh_audio->a_out_buffer_len)/filter_multiplier
+ (unitsize << 5); // some extraforpossible filter buffering
declen -= declen % unitsize;
if (filter_n_bytes(sh_audio, declen) <0)
return -1;
}
3.我們來看看這個filter_n_bytes函數
static intfilter_n_bytes(sh_audio_t *sh, int len)
3.1 還記得我們剛才說的那個最大解碼長度嗎? 這裏是要確保不會發生解碼後數據溢出。
assert(len-1 +sh->audio_out_minsize<=sh->a_buffer_size);
3.2 按需要解碼更多的數據
while (sh->a_buffer_len < len){
unsigned char *buf = sh->a_buffer+sh->a_buffer_len;
int minlen = len -sh->a_buffer_len;
int maxlen = sh->a_buffer_size-sh->a_buffer_len;
//這裏才是調用之前確定的音頻解碼器進行真正音頻解碼
int ret=sh->ad_driver->decode_audio(sh,buf,minlen, maxlen);
if (ret <= 0) {
error = -1;
len = sh->a_buffer_len;
break;
}
//解碼之後a_buffer_len增加
sh->a_buffer_len += ret;
}
3.3 在前麵我們提到過過濾這個步驟,下麵的圖描述了整個過程
filter_output =af_play(sh->afilter,&filter_input);
注:這裏實際上做的是過濾這部分的工作
----------------------------------
|| 解碼||過濾|| 播放
| 未解碼數據 | ----->|解碼後數據| ------>|播放的數據| ------>
|a_in_buffer|| a_buffer||a_out_buffer|
----------------------------------
3.4 if (sh->a_out_buffer_size<sh->a_out_buffer_len+filter_output->len)
注:如果過濾後的數據長度太長,需要擴展a_out_buffer_size
3.5 將過濾後的數據從decoder buffer移除:
sh->a_buffer_len -= len;
memmove(sh->a_buffer, sh->a_buffer+len, sh->a_buffer_len);
四.結語
回顧一下今天的內容,我們從sh_audio_t結構體出發,依次分析了fill_audio_out_buffers()函數,
decode_audio()函數,filter_n_bytes()函數,但是並沒有分析具體的decode和play函數。
順便說點題外話,看Mplayer的代碼,結構化模塊化的特點很突出,通常是先定義一組
接口,然後具體的程序去實現這些接口,這樣子對代碼的維護和擴展都很有好處~

這裏再補充一下Mplayer的目錄結構和子文件夾說明:


libavcodec libavformat libavutil三個文件夾來自ffmpeg的庫 ;
libfaad2 libao2 liba52 libmpg2 mp3lib vidix幾個文件夾是其它的三方庫 ;
libmpcodecs libmpdemux 文件夾中為mplayer 的 demux 和codecs。 ;
其中demux_XXX.c為處理各種不同的container.
vd_XXX.c為mplayer的內置視頻解碼器,ad_xxx.c是音頻解碼器。
vf_XXX.c 和af_XXX.c 為視,音頻的各種filter。
vo_XXX.c 為解碼後視頻輸出。
libvo 視頻輸出模塊的 Make 文件配置部分頭
libao2 音頻輸出模塊的 Make 文件配置部分頭

drivers使用VIDIX 技術用到的直接硬件訪問驅動程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值