mplayer安装记录 源码分析
mplayer源碼下載地址:
http://www.mplayerhq.hu/MPlayer/releases/
下載最新的MPlayer-1.0rc4
#mkdir /usr/local/mplayer
#mkdir /usr/local/codecs
#cd?MPlayer-1.0rc4
#./configure --prefix=/usr/local/mplayer --codecsdir=/usr/local/ ? ?codecs --language=zh_CN
這里有的網上加了參數:--enable-gui (開啟圖形界面),但是我編譯的時候會出現如下錯誤:mplayer dereferencing pointer to incomplete type
據網上說是結構體沒有定義出現的錯誤。
我們不需要gui的界面所以去掉。
#make
#make install
#/usr/local/mplayer/bin/mplayer ? ? /media/hp-v245/nobody.avi
發現沒有聲音,提示:
audio_setup: Can't open audio device /dev/dsp:?No such file or directory
#ls /dev/dsp
發現果然沒有此文件
#sudo su
#modprobe snd_pcm_oss
創建dsp文件
再播放就可正常播放,
#ls mplayer
發現有10M,這是沒有裁剪的mplayer很大,下面做的工作就是要裁剪mplayer。
?
交叉編譯MPlayer-1.0rc2,到3210板子上運行,交叉編譯工具鏈:gcc-3.4.6
./configure --enable-cross-compile --cc=mipsel-linux-gcc --target=mips-linux --as=mipsel-linux-as --ar=mipsel-linux-ar --host-cc=gcc --disable-gui --disable-ivtv
沒有加-disable-ivtv出現如下錯誤: vo_ivtv.c:79: error: storage size of 'sd' isn't knownvo_ivtv.c:80: error: storage size of 'sd1' isn't known
。。。。。。。
這幾天在學習mplayer以下是在網上搜集到的關于mplayer的文章,主要是源碼分析這塊。首先感謝這些文章的作者,有些沒有標明原文出處,實在表示抱歉。
?
從Mplayer.c的main開始?
//處理參數?
mconfig = m_config_new();?
m_config_register_options(mconfig,mplayer_opts);?
// TODO : add something to let modules register their options?
mp_input_register_options(mconfig);?
parse_cfgfiles(mconfig);?
//初始化mpctx結構體,mpctx應該是mplayer context的意思,顧名思義是一個統籌全局的變量。?
static MPContext *mpctx = &mpctx_s;?
// Not all functions in mplayer.c take the context as an argument yet?
static MPContext mpctx_s = {?
.osd_function = OSD_PLAY,?
.begin_skip = MP_NOPTS_VALUE,?
.play_tree_step = 1,?
.global_sub_pos = -1,?
.set_of_sub_pos = -1,?
.file_format = DEMUXER_TYPE_UNKNOWN,?
.loop_times = -1,?
#ifdef HAS_DVBIN_SUPPORT?
.last_dvb_step = 1,?
#endif?
};?
一些GUI相關的操作?
打開字幕流?
打開音視頻流?
mpctx->stream=open_stream(filename,0,&mpctx->file_format);?
//fileformat文件還是TV流DEMUXER_TYPE_PLAYLIST或DEMUXER_TYPE_UNKNOWN DEMUXER_TYPE_TV?
//current_module記錄狀態vobsub?? open_stream handle_playlist dumpstream?
stream_reset(mpctx->stream);?
stream_seek(mpctx->stream,mpctx->stream->start_pos);?
f=fopen(stream_dump_name,”wb”); dump文件流?
stream->type==STREAMTYPE_DVD?
//============ Open DEMUXERS — DETECT file type ======================?
//Demux:分離視頻流和音頻流?
mpctx->demuxer=demux_open(mpctx->stream,mpctx->file_format,audio_id,video_id,dvdsub_id,filename);?
mpctx->d_audio=mpctx->demuxer->audio;?
mpctx->d_video=mpctx->demuxer->video;?
mpctx->d_sub=mpctx->demuxer->sub;?
mpctx->sh_audio=mpctx->d_audio->sh;?
mpctx->sh_video=mpctx->d_video->sh;?
分離了之后就開始分別Play audio和video?
這里只關心play video?
/*========================== PLAY VIDEO ============================*/?
vo_pts=mpctx->sh_video->timer*90000.0;?
vo_fps=mpctx->sh_video->fps;?
if (!mpctx->num_buffered_frames) {?
?? double frame_time = update_video(&blit_frame);?
?? mp_dbg(MSGT_AVSYNC,MSGL_DBG2,”*** ftime=%5.3f ***/n”,frame_time);?
?? if (mpctx->sh_video->vf_inited < 0) {?
???? mp_msg(MSGT_CPLAYER,MSGL_FATAL, MSGTR_NotInitializeVOPorVO);?
???? mpctx->eof = 1; goto goto_next_file;?
?? }?
?? if (frame_time < 0)?
?? mpctx->eof = 1;?
?? else {?
???? // might return with !eof && !blit_frame if !correct_pts?
???? mpctx->num_buffered_frames += blit_frame;?
???? time_frame += frame_time / playback_speed;?? // for nosound?
?? }?
}?
//關鍵的函數是update_video?
根據pts是否正確調整一下同步并在必要的時候丟幀處理。?
最終調用decode_video開始解碼(包括generate_video_frame里)。?
mpi = mpvdec->decode(sh_video, start, in_size, drop_frame);?
mpvdec是在main里通過reinit_video_chain的一系列調用動態選定的解碼程序。?
其實就一結構體。它的原型是?
typedef struct vd_functions_s?
{?
vd_info_t *info;?
int (*init)(sh_video_t *sh);?
void (*uninit)(sh_video_t *sh);?
int (*control)(sh_video_t *sh,int cmd,void* arg, …);?
mp_image_t* (*decode)(sh_video_t *sh,void* data,int len,int flags);?
} vd_functions_t;?
這是所有解碼器必須實現的接口。?
int (*init)(sh_video_t *sh);是一個名為init的指針,指向一個接受sh_video_t *類型參數,并返回int類型值的函數地址。?
那些vd_開頭的文件都是解碼相關的。隨便打開一個vd文件以上幾個函數和info變量肯定都包含了。?
mpi被mplayer用來存儲解碼后的圖像。在mp_image.h里定義。?
typedef struct mp_image_s {?
unsigned short flags;?
unsigned char type;?
unsigned char bpp;?? // bits/pixel. NOT depth! for RGB it will be n*8?
unsigned int imgfmt;?
int width,height;?? // stored dimensions?
int x,y,w,h;?? // visible dimensions?
unsigned char* planes[MP_MAX_PLANES];?
int stride[MP_MAX_PLANES];?
char * qscale;?
int qstride;?
int pict_type; // 0->unknown, 1->I, 2->P, 3->B?
int fields;?
int qscale_type; // 0->mpeg1/4/h263, 1->mpeg2?
int num_planes;?
/* these are only used by planar formats Y,U(Cb),V(Cr) */?
int chroma_width;?
int chroma_height;?
int chroma_x_shift; // horizontal?
int chroma_y_shift; // vertical?
/* for private use by filter or vo driver (to store buffer id or dmpi) */?
void* priv;?
} mp_image_t;?
圖像在解碼以后會輸出到顯示器,mplayer本來就是一個視頻播放器么。但也有可能作為輸入提供給編碼器進行二次編碼,MP附帶的mencoder.exe就是專門用來編碼的。在這之前可以定義filter對圖像進行處理,以實現各種效果。所有以vf_開頭的文件,都是這樣的filter。?
圖像的顯示是通過vo,即video out來實現的。解碼器只負責把解碼完成的幀傳給vo,怎樣顯示就不用管了。這也是平臺相關性最大的部分,單獨分出來的好處是不言而喻的,像在Windows下有通過direcx實現的vo,Linux下有輸出到X的vo。vo_*文件是各種不同的vo實現,只是他們不都是以顯示為目的,像vo_md5sum.c只是計算一下圖像的md5值。?
在解碼完成以后,即得到mpi以后,filter_video被調用,其結果是整個filter鏈上的所有filter都被調用了一遍,包括最后的VO,在vo的put_image里把圖像輸出到顯示器。這個時候需要考慮的是圖像存儲的方法即用哪種色彩空間。?
[MPlayer core]?
| (1)?
_____V______?? (2)?? /~~~~~~~~~~/???? (3,4)?? |~~~~~~|?
|?????????? | —–> | vd_XXX.c |?? ——-> | vd.c |?
| decvideo |???????? /__________/?? <-(3a)– |______|?
|?????????? | —–,?? ,………….(3a,4a)…..:?
~~~~~~~~~~~~?? (6) V?? V?
/~~~~~~~~/???? /~~~~~~~~/?? (8)?
| vf_X.c | –> | vf_Y.c | —->?? vf_vo.c / ve_XXX.c?
/________/???? /________/?
|?????????????? ^?
(7) |?? |~~~~~~|?? : (7a)?
`-> | vf.c |…:?
|______|?
感覺Mplayer的開發人員們都是無比的牛,硬是用原始的C實現了很多OO語言才支持的特性,帶來不好的結果是代碼看起來比較費勁.
?
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函數中的入口如下~?
/*========================== PLAY AUDIO ============================*/?
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 {?
?? int aid;?
?? demux_stream_t *ds;?
?? struct codecs_st *codec;?
?? unsigned int format;?
?? int initialized;?
?? float stream_delay; // number of seconds stream should be delayed (according?
to dwStart or similar)?
?? // output format:?
?? int sample_format;?
?? int samplerate;?
?? int samplesize;?
?? int channels;?
?? int o_bps; // == samplerate*samplesize*channels?? (uncompr. bytes/sec)?
?? int i_bps; // == bitrate?? (compressed bytes/sec)?
?? // in buffers:?
?? int audio_in_minsize;???????? // max. compressed packet size (== min. in buffer size?
)?
?? char* a_in_buffer;?
?? int a_in_buffer_len;?
?? int a_in_buffer_size;?
?? // decoder buffers:?
?? int audio_out_minsize; // max. uncompressed packet size (==min. out buffsize?
)?
?? char* a_buffer;?
?? int a_buffer_len;?
?? int a_buffer_size;?
?? // output buffers:?
?? char* a_out_buffer;?
?? int a_out_buffer_len;?
?? int a_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 codec parameters:?
?? AVIStreamHeader audio;?
?? WAVEFORMATEX* wf;?
?? // codec-specific:?
?? void* context; // codec-specific stuff (usually HANDLE or struct pointer)?
?? unsigned char* codecdata; // extra header data passed from demuxer to codec?
?? int codecdata_len;?
?? double pts;?? // last known pts value in output from decoder?
?? int pts_bytes; // bytes output by decoder after last known pts?
?? char* lang; // track language?
?? int default_track;?
} sh_audio_t;?
三.step by step?
?? 1.下面我們來分析fill_audio_out_buffers()函數?
?? static int fill_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獲得至少playsize bytes的解碼或過濾后的音頻數據,成功返回0,失敗返回-1?
???? 1.3 將解碼后的音頻數據進行播放?
???? playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags);?
???? 注:從sh_audio->a_out_buffer中copy playsize bytes數據,注意這里一定要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, int minlen)?
???? 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 extra for possible filter buffering?
???????? declen -= declen % unitsize;?
???????? if (filter_n_bytes(sh_audio, declen) < 0)?
???????????? return -1;?
???? }?
???? 3.我們來看看這個filter_n_bytes函數?
?? static int filter_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的目錄結構和子文件夾說明:
原文地址:http://qzone.qq.com/blog/81182980-1235106011
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 技術用到的直接硬件訪問驅動程序
?
怎么樣,還是很注重條理的吧?不過,vo_XXX.c是在libvo下。
總結
以上是生活随笔為你收集整理的mplayer安装记录 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汇编语言中MOV和OFFSET指令的两个
- 下一篇: 比较全的C语言面试题