從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
};
原型
typedef struct MPContext {
int osd_show_percentage;
int osd_function;
ao_functions_t *audio_out;
play_tree_t *playtree;
play_tree_iter_t *playtree_iter;
int eof;
int play_tree_step;
int loop_times;
stream_t *stream;
demuxer_t *demuxer;
sh_audio_t *sh_audio;
sh_video_t *sh_video;
demux_stream_t *d_audio;
demux_stream_t *d_video;
demux_stream_t *d_sub;
mixer_t mixer;
vo_functions_t *video_out;
// Frames buffered in the vo ready to flip. Currently always 0 or 1.
// This is really a vo variable but currently there’s no suitable vo
// struct.
int num_buffered_frames;
// AV sync: the next frame should be shown when the audio out has this
// much (in seconds) buffered data left. Increased when more data is
// written to the ao, decreased when moving to the next frame.
// In the audio-only case used as a timer since the last seek
// by the audio CPU usage meter.
double delay;
float begin_skip; ///< start time of the current skip while on edlout mode
// audio is muted if either EDL or user activates mute short edl_muted;
///< Stores whether EDL is currently in muted mode. short user_muted;
///< Stores whether user wanted muted mode. int global_sub_size;
// this encompasses all subtitle sources int global_sub_pos;
// this encompasses all subtitle sources int set_of_sub_pos;
int set_of_sub_size;
int global_sub_indices[SUB_SOURCES]; 一些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);
Demux 過程
demux_open
get_demuxer_type_from_name
……
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裡把圖像輸出到顯示器。
這個時候需要考慮的是圖像存儲的方法即用哪種色彩空間。
留言列表