如何基于FFMPEG和SDL写一个少于1000行代码的视频播放器
| ? ? http://blog.csdn.net/eplaylity/archive/2008/12/05/3454431.aspx http://www.cnblogs.com/konyel/tag/SDL+Guide+%E4%B8%AD%E6%96%87%E8%AF%91%E7%89%88/ ffmpeg文檔http://blog.sina.com.cn/s/blog_46dc65a90100a91b.html http://dranger.com/ffmpeg/ffmpeg.html |
VLC核心
功能部份:
VLC媒體播放器的核心是libvlc ,它提供了界面,應(yīng)用處理功能,如播放列表管理,音頻和視頻解碼和輸出,線程系統(tǒng)。所有l(wèi)ibvlc源文件設(shè)在的/src目錄及其子目錄:
# config/ :從命令行和配置文件加載配置,提供功能模塊的讀取和寫入配置
# control/: 提供動作控制功能,如播放/暫停,音量管理,全屏,日志等。
# extras/: 大多是平臺的特殊代碼
# modules/: 模塊管理
# network/: 提供網(wǎng)絡(luò)接口(socket管理,網(wǎng)絡(luò)錯誤等)
# osd/: 顯示屏幕上的操作
# test/: libvlc測試模塊
# text/: 字符集
# interface/ : 提供代碼中可以調(diào)用的接口中,如按鍵后硬件作出反應(yīng)。
# playlist/: 管理播放功能,如停止,播放,下一首,隨機播放等
# input/: 建立并讀取一個輸入流,并且分離其中的音頻和視頻,然后把分離好的音頻流和視頻流發(fā)送給解碼器.
# video_output/ : 初始化視頻播放器,把從解碼器得到的視頻畫面轉(zhuǎn)化格式(從YUV 轉(zhuǎn)為 RGB)然后播放它們
# audio_output/ : 初始化音頻混合器,即設(shè)置正確的同步頻率,并對從解碼器傳來的音頻流重新取樣
# stream_output/: 輸出音頻流和視頻流到網(wǎng)絡(luò)
# misc/: libvlc使用的其他部分功能 ,如線程系統(tǒng),消息隊列, CPU的檢測,對象查找系統(tǒng),或平臺的特定代碼。
模塊部份:
VLC媒體播放器的模塊部份,在/modules的子目錄下(詳細說明可以參考其下的List文件),這些模塊只在程序載入它們時有效.每一個模塊,可提供不同的功能,它們會適合的特定文件或某一特定的環(huán)境.此外,audio_output/video_output/interface 模塊都寫成了可跨平臺的代碼,方便支持新的平臺(如beos或服務(wù)Mac OS X ) 。
插件模塊可以在 src/modules.c 和 include/vlc_modules*.h 提供函數(shù)中,動態(tài)加載和卸載
LibVLC可以將模塊直接插入到應(yīng)用程序中,例如不支持動態(tài)加載代碼的操作系統(tǒng).模塊靜態(tài)插入到應(yīng)用程序叫內(nèi)建.
VLC框架分析
1.vlc.c 只是入口程序
2.Libvlc.c 是各個模塊的結(jié)合點,這要是對接口編程
- Vlc_Create(): 兩個重要的數(shù)據(jù)結(jié)構(gòu):libvlc_t & vlc_t , 所有的參數(shù)傳遞都在這里面
- Vlc_Init(): 初始化參數(shù), module_bank
- Vlc_AddInf(): 添加module
3./src/misc/configure.c 命令行參數(shù)和參數(shù)文件分析
參數(shù)文件是~/.vnc/vlcrc。其中可以設(shè)置log文件的位置
4./include/ 所有頭文件的集合
5./src/interface/Interface.h 所有module的集合
6./src/misc/Modules.c
其中module_t * __module_Need( vlc_object_t *p_this, const char *psz_capability,
const char *psz_name, vlc_bool_t b_strict ) 方法是尋找合適的interface
如果找到合適的,就調(diào)用AllocatePlugin()動態(tài)的分配一個。
7.how to link to different modules without OOP
?
?
vlc網(wǎng)絡(luò)數(shù)據(jù)流接收處理過程分析
?
網(wǎng)絡(luò)數(shù)據(jù)流接收處理分析
1、在input.c(src/input)文件中的主線程循環(huán)
????? Thread in charge of processing the network packets and demultiplexing
RunThread( input_thread_t *p_input )
{
????????? InitThread( p_input ) ;
…………………………………………………….
???? input_SelectES( p_input, p_input->stream.p_newly_selected_es );
???????????? …………………………………………………….
??? ??/* Read and demultiplex some data. */
??? i_count = p_input->pf_demux( p_input );
?
}
2、在下列函數(shù)中:
static int InitThread( input_thread_t * p_input )
{
???? msg_Dbg( p_input, "access `%s', demux `%s', name `%s'",
???????????? p_input->psz_access, p_input->psz_demux, p_input->psz_name );
?
??? /* Find and open appropriate access module */
??? p_input->p_access = module_Need( p_input, "access",
???????????????????????????????????? p_input->psz_access, VLC_TRUE );
…………………………………………………….
while( !input_FillBuffer( p_input ) )
…………………………………………………….
??? /* Find and open appropriate demux module */
??? p_input->p_demux =
??????? module_Need( p_input, "demux",
???????????????????? (p_input->psz_demux && *p_input->psz_demux) ?
???????????????????? p_input->psz_demux : "$demux",
???????????????????? (p_input->psz_demux && *p_input->psz_demux) ?
???????????????????? VLC_TRUE : VLC_FALSE );
…………………………………………………….
}
3、在ps.c (module/demux/mpeg)文件中
a.通過消息映射宏賦值啟動函數(shù)Activate;
b.通過函數(shù)Activate賦值p_input->pf_demux = Demux;
c. 通過函數(shù)module_Need( p_input, "mpeg-system", NULL, 0 ) 激活p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data )函數(shù)(pf_read_ps);
d.在InitThread函數(shù)中激活;
?
??????? static int Activate( vlc_object_t * p_this )
{
????? /* Set the demux function */
p_input->pf_demux = Demux;
p_input->p_private = (void*)&p_demux->mpeg;
??? p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0 );
}
4、在system.c (module/demux/mpeg)文件中
???????? 賦值解碼模塊mpeg_demux_t的成員函數(shù);
???? static int Activate ( vlc_object_t *p_this )
{
??? static mpeg_demux_t mpeg_demux =
??????????????????? { NULL, ReadPS, ParsePS, DemuxPS, ReadTS, DemuxTS };
??? mpeg_demux.cur_scr_time = -1;
??? memcpy( p_this->p_private, &mpeg_demux, sizeof( mpeg_demux ) );
?
??? return VLC_SUCCESS;
}
并且申明函數(shù)static ssize_t ReadPS( input_thread_t * p_input, data_packet_t ** pp_data );
?
5、在ps.c (module/demux/mpeg)文件中
Demux( input_thread_t * p_input )
{
i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data );
??? ??p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );
}
進行讀取數(shù)據(jù)和分離工作;
6、在system.c (module/demux/mpeg)文件中
數(shù)據(jù)走向圖如下
ReadPS-> PEEK-> input_Peek(src/input/input_ext-plugins.c)-> input_FillBuffert 通過 i_ret = p_input->pf_read( p_input,
????????? ????????????????????(byte_t *)p_buf + sizeof(data_buffer_t)
?????????????????????????????? + i_remains,
????????????????????????????? p_input->i_bufsize );
input_thread_t結(jié)構(gòu)的pf_read函數(shù)成員如果是為udp.c(modules/access)的RTPChoose函數(shù)
則在開啟access(UDP 模塊)時通過module_need 激活;
激活網(wǎng)絡(luò)讀數(shù)據(jù)模塊 RTPChoose(modules/access/ udp.c)->Read->net_Read(src/misc/net.c);
?
7、在input_programs.c(src/input)文件中
???????? 運行解碼器對ES流解碼
?? int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
{
????? p_es->p_dec = input_RunDecoder( p_input, p_es );
??
}
input_SelectES(src/input/input_programs.c)->input_RunDecoder(src/input/input_dec.c)->DecoderThread->DecoderDecode ->vout_DisplayPicture
VLC程序宏及線程分析
第一部分 變量及宏定義
1.消息映射宏
vlc_module_begin();
…………………..
vlc_module_end();
2.結(jié)構(gòu)中包含函數(shù)
struct input_thread_t
{
VLC_COMMON_MEMBERS
/* Thread properties */
vlc_bool_t b_eof;
vlc_bool_t b_out_pace_control;
/* Access module */
module_t * p_access;
ssize_t (* pf_read ) ( input_thread_t *, byte_t *, size_t );
int (* pf_set_program )( input_thread_t *, pgrm_descriptor_t * );
int (* pf_set_area )( input_thread_t *, input_area_t * );
void (* pf_seek ) ( input_thread_t *, off_t );
}
3.宏與換行符妙用
#define VLC_COMMON_MEMBERS /
/** /name VLC_COMMON_MEMBERS /
* these members are common for all vlc objects /
*/ /
/**@{*/ /
int i_object_id; /
int i_object_type; /
char *psz_object_type; /
char *psz_object_name; /
/
/** Just a reminder so that people don't cast garbage */ /
int be_sure_to_add_VLC_COMMON_MEMBERS_to_struct; /
/**@}*/
#define VLC_OBJECT( x ) /
((vlc_object_t *)(x))+
0*(x)->be_sure_to_add_VLC_COMMON_MEMBERS_to_struct
struct vlc_object_t
{
VLC_COMMON_MEMBERS
};//定義一個結(jié)構(gòu)來使用宏定義的公共成員
4.定義導(dǎo)出函數(shù)
#ifndef __PLUGIN__
# define VLC_EXPORT( type, name, args ) type name args
#else
# define VLC_EXPORT( type, name, args ) struct _u_n_u_s_e_d_
extern module_symbols_t* p_symbols;
#endif
5.定義回調(diào)函數(shù)
typedef int ( * vlc_callback_t ) ( vlc_object_t *, /* variable's object */
char const *, /* variable name */
vlc_value_t, /* old value */
vlc_value_t, /* new value */
void * ); /* callback data */
6.函數(shù)作為參數(shù)的定義方式
Int Fun(int n,int (*pf)(int ,int),char *pstr)
{ int j =10;
pf(n,j);
}
7.回調(diào)函數(shù)的聲明
必須聲明為global,或者static
Int vlc_callback_t (int ,int)
{。。。。。。。。。。。}
8.回調(diào)函數(shù)的使用
Fun(0, vlc_callback_t,”test”);
9.函數(shù)表達式
#define input_BuffersInit(a) __input_BuffersInit(VLC_OBJECT(a))
void * __input_BuffersInit( vlc_object_t * );
#define module_Need(a,b,c,d) __module_Need(VLC_OBJECT(a),b,c,d)
VLC_EXPORT( module_t *, __module_Need, ( vlc_object_t *, const char *, const char *, vlc_bool_t ) );
10.定義函數(shù)
/* Dynamic array handling: realloc array, move data, increment position */
#define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem ) /
do /
{ /
if( i_oldsize ) /
{ /
(p_ar) = realloc( p_ar, ((i_oldsize) + 1) * sizeof( *(p_ar) ) ); /
} /
else /
{ /
(p_ar) = malloc( ((i_oldsize) + 1) * sizeof( *(p_ar) ) ); /
} /
if( (i_oldsize) - (i_pos) ) /
{ /
memmove( (p_ar) + (i_pos) + 1, /
(p_ar) + (i_pos), /
((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) ); /
} /
(p_ar)[i_pos] = elem; /
(i_oldsize)++; /
} /
while( 0 )
應(yīng)用為:
INSERT_ELEM( p_new->p_libvlc->pp_objects,
p_new->p_libvlc->i_objects,
p_new->p_libvlc->i_objects,
p_new );
11.改變地址的方式傳遞其值
stream_t *input_StreamNew( input_thread_t *p_input )
{ stream_t *s = vlc_object_create( p_input, sizeof( stream_t ) );
input_stream_sys_t *p_sys;
if( s )
{
s->p_sys = malloc( sizeof( input_stream_sys_t ) );
p_sys = (input_stream_sys_t*)s->p_sys;
p_sys->p_input = p_input;
}
return s;//注解:s->p_sys改變了
}
第二部分 程序框架實現(xiàn)
1. 播放列表文件src/playlist/playlist.c的線程
playlist_t * __playlist_Create ( vlc_object_t *p_parent )函數(shù)中創(chuàng)建的線程,線程函數(shù)為
static void RunThread ( playlist_t *p_playlist )
線程思路分析:
在RunThread里面執(zhí)行循環(huán),如果沒有任務(wù)執(zhí)行,則適當?shù)难舆t,如果接到p_playlist->i_status != PLAYLIST_STOPPED的條件,則調(diào)用PlayItem( p_playlist )函數(shù),在PlayItem( p_playlist )函數(shù)中從新創(chuàng)建輸入線程。
通過void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,int i_arg )接收來自GUI界面的各種命令,然后設(shè)置p_playlist->i_status的狀態(tài),由該狀態(tài)改變該播放列表文件主循環(huán)線程的執(zhí)行。
2. 輸入文件SRC/INPUT/INPUT.C的輸入線程
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
input_item_t *p_item )函數(shù)中創(chuàng)建的線程,線程函數(shù)為
static int RunThread( input_thread_t *p_input )
線程思路分析:
由 input_thread_t結(jié)構(gòu)的成員分析是接收文件流還是網(wǎng)絡(luò)流,如果是文件流,則調(diào)用file module 的讀函數(shù)(pf_read)和打開函數(shù)(--).如果是network 則打開network module 的打開函數(shù)和讀函數(shù)(pf_read)。
在 RunThread線程函數(shù)中接收數(shù)據(jù)和調(diào)用demux 或者decode etc處理。
一旦產(chǎn)生新的輸入,則在播放列表線程中會首先結(jié)束該輸入線程,然后從新創(chuàng)建新的輸入線程。
3. 視頻輸出文件src/video_output/ video_output.c的線程
vout_thread_t * __vout_Create( vlc_object_t *p_parent,
unsigned int i_width, unsigned int i_height,
vlc_fourcc_t i_chroma, unsigned int i_aspect )函數(shù)中創(chuàng)建的線程,線程函數(shù)為
static void RunThread( vout_thread_t *p_vout)
線程思路分析:
在RunThread里面執(zhí)行循環(huán),任務(wù)是顯示視頻。
4. 在modules/gui/wxwindows/wxwindows.cpp中的GUI線程
static void Run( intf_thread_t *p_intf ) 函數(shù)中創(chuàng)建的線程,線程函數(shù)為
static void Init( intf_thread_t *p_intf )
線程思路分析:
在Init( intf_thread_t *p_intf )里面執(zhí)行循環(huán),創(chuàng)建新的GUI實例。Instance-》OnInit()(CreateDialogsProvider)-》DialogsProvider為運行的對話框。
接收網(wǎng)絡(luò)文件的步驟
OnOpenNet( wxCommandEvent& event )打開網(wǎng)絡(luò)文件的步驟。打開OpenDialog對話框,點擊Ok后調(diào)用OpenDialog::OnOk( wxCommandEvent& WXUNUSED(event) )函數(shù),調(diào)用playlist_Command函數(shù)改變播放列表線程的狀態(tài)。
激活線程分析:
在wxwindow.cpp中的消息映射中 set_callbacks( OpenDialogs, Close ); 則設(shè)置了module_t->pf_activate= OpenDialogs函數(shù),
在module.c 的__module_Need( vlc_object_t *p_this, const char *psz_capability,
const char *psz_name, vlc_bool_t b_strict )
函數(shù)中用到了pf_activate激活GUI對話框;
在video_output.c 的static void RunThread( vout_thread_t *p_vout)線程中,也用到了pf_activate激活GUI對話框;
5. 開始所有module 的精髓
消息映射宏
vlc_module_begin();
set_callbacks( NetOpen, NULL );
vlc_module_end();
然后設(shè)置模塊結(jié)構(gòu)的成員函數(shù)為:
#define set_callbacks( activate, deactivate ) /
p_submodule->pf_activate = activate; /
p_submodule->pf_deactivate = deactivate
在__module_Need函數(shù)中啟動pf_activate 激活相應(yīng)的module。
?
?
對VLC源代碼閱讀的計劃是從其程序的框架開始,先對其主要的文件進行整理: 1.include/main.h 文件: access to all program variables,主要定義了2個結(jié)構(gòu)體:libvlc_t,vlc_t。 ???????? a. struct libvlc_t 根據(jù)程序注釋:該結(jié)構(gòu)體只有一個實例,在main函數(shù)中被分配,而且只能在main中訪問。它用來存儲一些只能初始化一次的數(shù)據(jù),比如說cpu容量或者global lock. ????????? b. struct vlc_t?? 注釋稱:This structure is a LibVLC instance ???????? libvlc_t,vlc_t在VLC_COMMON_MEMBERS宏中出現(xiàn),分別定義了 libvlc_t *?? p_libvlc; vlc_t *?? p_vlc; 對象,注釋稱為 root of the evil,可見其結(jié)構(gòu)體的重要性.所有的參數(shù)傳遞都在這里面(具體尚不清楚)。 2.include/Vlc_common.h 文件:common definitions,Collection of useful common types and macros definitions,通用類型和宏定義的集合 ???????? 主要作用是為了將不同的操作系統(tǒng)中的變量定義統(tǒng)一起來,比如說根據(jù)將unit_8來統(tǒng)一代表unsiged char類型. ??????? 該文件中還定義了VLC_COMMON_MEMBERS宏,該宏中包括了所有VLC基本對象的通用成員變量:these members are common for all vlc objects。 ??????? 定義導(dǎo)出函數(shù)#ifndef __PLUGIN__
#?? define VLC_EXPORT( type, name, args ) type name args
#else
#?? define VLC_EXPORT( type, name, args ) struct _u_n_u_s_e_d_
extern module_symbols_t* p_symbols;
#endif
定義回調(diào)函數(shù) ????? typedef int ( * vlc_callback_t ) ( vlc_object_t *,????? /* variable's object */
char const *,??????????? /* variable name */
vlc_value_t,???????????? /* old value */
vlc_value_t,??????????? /* new value */ ????????????????????????????????????????????????? void * ); ??????????????? /* callback data */?????????? 3.include/vlc_objects.h 文件:vlc_object_t definition and manipulation methods,vlc_object_t的定義和處理函數(shù) ?????? struct vlc_object_t
{
VLC_COMMON_MEMBERS
}; //定義一個結(jié)構(gòu)來使用宏定義的公共成員
總結(jié)
以上是生活随笔為你收集整理的如何基于FFMPEG和SDL写一个少于1000行代码的视频播放器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MFC中将CBitmap画到cdc上
- 下一篇: MFC中获取任务栏大小