如何基于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 ,它提供了界面,應用處理功能,如播放列表管理,音頻和視頻解碼和輸出,線程系統。所有libvlc源文件設在的/src目錄及其子目錄:

# config/ :從命令行和配置文件加載配置,提供功能模塊的讀取和寫入配置
# control/: 提供動作控制功能,如播放/暫停,音量管理,全屏,日志等。
# extras/: 大多是平臺的特殊代碼
# modules/: 模塊管理
# network/: 提供網絡接口(socket管理,網絡錯誤等)
# osd/: 顯示屏幕上的操作
# test/: libvlc測試模塊
# text/: 字符集
# interface/ : 提供代碼中可以調用的接口中,如按鍵后硬件作出反應。
# playlist/: 管理播放功能,如停止,播放,下一首,隨機播放等
# input/: 建立并讀取一個輸入流,并且分離其中的音頻和視頻,然后把分離好的音頻流和視頻流發送給解碼器.
# video_output/ : 初始化視頻播放器,把從解碼器得到的視頻畫面轉化格式(從YUV 轉為 RGB)然后播放它們
# audio_output/ : 初始化音頻混合器,即設置正確的同步頻率,并對從解碼器傳來的音頻流重新取樣
# stream_output/: 輸出音頻流和視頻流到網絡
# misc/: libvlc使用的其他部分功能 ,如線程系統,消息隊列, CPU的檢測,對象查找系統,或平臺的特定代碼。

模塊部份:

VLC媒體播放器的模塊部份,在/modules的子目錄下(詳細說明可以參考其下的List文件),這些模塊只在程序載入它們時有效.每一個模塊,可提供不同的功能,它們會適合的特定文件或某一特定的環境.此外,audio_output/video_output/interface 模塊都寫成了可跨平臺的代碼,方便支持新的平臺(如beos或服務Mac OS X ) 。

插件模塊可以在 src/modules.c 和 include/vlc_modules*.h 提供函數中,動態加載和卸載

LibVLC可以將模塊直接插入到應用程序中,例如不支持動態加載代碼的操作系統.模塊靜態插入到應用程序叫內建.


VLC框架分析

1.vlc.c 只是入口程序

2.Libvlc.c 是各個模塊的結合點,這要是對接口編程

  • Vlc_Create(): 兩個重要的數據結構:libvlc_t & vlc_t , 所有的參數傳遞都在這里面
  • Vlc_Init(): 初始化參數, module_bank
  • Vlc_AddInf(): 添加module

3./src/misc/configure.c 命令行參數和參數文件分析
參數文件是~/.vnc/vlcrc。其中可以設置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
如果找到合適的,就調用AllocatePlugin()動態的分配一個。

7.how to link to different modules without OOP

?

?

vlc網絡數據流接收處理過程分析

?

網絡數據流接收處理分析

1、在input.c(src/input)文件中的主線程循環

????? 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、在下列函數中:

  1. 分離出access , demux , name字符串 ;
  2. 根據分離出的access 字符串通過module_Need函數找到acess 指針模塊;
  3. 根據分離出的demux 字符串通過module_Need函數找到demux 指針模塊;

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.通過消息映射宏賦值啟動函數Activate;

b.通過函數Activate賦值p_input->pf_demux = Demux;

c. 通過函數module_Need( p_input, "mpeg-system", NULL, 0 ) 激活p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data )函數(pf_read_ps);

d.在InitThread函數中激活;

?

??????? 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的成員函數;

???? 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;

}

并且申明函數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 );

}

進行讀取數據和分離工作;

6、在system.c (module/demux/mpeg)文件中

數據走向圖如下

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結構的pf_read函數成員如果是為udp.c(modules/access)的RTPChoose函數

則在開啟access(UDP 模塊)時通過module_need 激活;

激活網絡讀數據模塊 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_RunDecodersrc/input/input_dec.c->DecoderThread->DecoderDecode ->vout_DisplayPicture

VLC程序宏及線程分析

第一部分 變量及宏定義
1.消息映射宏
vlc_module_begin();
…………………..
vlc_module_end();
2.結構中包含函數
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
};//定義一個結構來使用宏定義的公共成員

4.定義導出函數
#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.定義回調函數

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.函數作為參數的定義方式
Int Fun(int n,int (*pf)(int ,int),char *pstr)
{ int j =10;
pf(n,j);
}

7.回調函數的聲明
必須聲明為global,或者static

Int vlc_callback_t (int ,int)
{。。。。。。。。。。。}

8.回調函數的使用
Fun(0, vlc_callback_t,”test”);

9.函數表達式
#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.定義函數
/* 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 )


應用為:
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改變了
}

第二部分 程序框架實現
1. 播放列表文件src/playlist/playlist.c的線程
playlist_t * __playlist_Create ( vlc_object_t *p_parent )函數中創建的線程,線程函數為
static void RunThread ( playlist_t *p_playlist )
線程思路分析:
在RunThread里面執行循環,如果沒有任務執行,則適當的延遲,如果接到p_playlist->i_status != PLAYLIST_STOPPED的條件,則調用PlayItem( p_playlist )函數,在PlayItem( p_playlist )函數中從新創建輸入線程。

通過void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,int i_arg )接收來自GUI界面的各種命令,然后設置p_playlist->i_status的狀態,由該狀態改變該播放列表文件主循環線程的執行。

2. 輸入文件SRC/INPUT/INPUT.C的輸入線程
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
input_item_t *p_item )函數中創建的線程,線程函數為
static int RunThread( input_thread_t *p_input )
線程思路分析:
由 input_thread_t結構的成員分析是接收文件流還是網絡流,如果是文件流,則調用file module 的讀函數(pf_read)和打開函數(--).如果是network 則打開network module 的打開函數和讀函數(pf_read)。
在 RunThread線程函數中接收數據和調用demux 或者decode etc處理。
一旦產生新的輸入,則在播放列表線程中會首先結束該輸入線程,然后從新創建新的輸入線程。

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 )函數中創建的線程,線程函數為
static void RunThread( vout_thread_t *p_vout)
線程思路分析:
在RunThread里面執行循環,任務是顯示視頻。

4. 在modules/gui/wxwindows/wxwindows.cpp中的GUI線程
static void Run( intf_thread_t *p_intf ) 函數中創建的線程,線程函數為
static void Init( intf_thread_t *p_intf )

線程思路分析:
在Init( intf_thread_t *p_intf )里面執行循環,創建新的GUI實例。Instance-》OnInit()(CreateDialogsProvider)-》DialogsProvider為運行的對話框。

接收網絡文件的步驟
OnOpenNet( wxCommandEvent& event )打開網絡文件的步驟。打開OpenDialog對話框,點擊Ok后調用OpenDialog::OnOk( wxCommandEvent& WXUNUSED(event) )函數,調用playlist_Command函數改變播放列表線程的狀態。

激活線程分析:
在wxwindow.cpp中的消息映射中 set_callbacks( OpenDialogs, Close ); 則設置了module_t->pf_activate= OpenDialogs函數,
在module.c 的__module_Need( vlc_object_t *p_this, const char *psz_capability,
const char *psz_name, vlc_bool_t b_strict )
函數中用到了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();
然后設置模塊結構的成員函數為:
#define set_callbacks( activate, deactivate ) /
p_submodule->pf_activate = activate; /
p_submodule->pf_deactivate = deactivate

在__module_Need函數中啟動pf_activate 激活相應的module。

?

?

對VLC源代碼閱讀的計劃是從其程序的框架開始,先對其主要的文件進行整理:
1.include/main.h 文件: access to all program variables,主要定義了2個結構體:libvlc_t,vlc_t。
???????? a. struct libvlc_t 根據程序注釋:該結構體只有一個實例,在main函數中被分配,而且只能在main中訪問。它用來存儲一些只能初始化一次的數據,比如說cpu容量或者global lock.
????????? b. struct vlc_t?? 注釋稱:This structure is a LibVLC instance
???????? libvlc_t,vlc_t在VLC_COMMON_MEMBERS宏中出現,分別定義了 libvlc_t *?? p_libvlc; vlc_t *?? p_vlc; 對象,注釋稱為 root of the evil,可見其結構體的重要性.所有的參數傳遞都在這里面(具體尚不清楚)。
2.include/Vlc_common.h 文件:common definitions,Collection of useful common types and macros definitions,通用類型和宏定義的集合
???????? 主要作用是為了將不同的操作系統中的變量定義統一起來,比如說根據將unit_8來統一代表unsiged char類型.
??????? 該文件中還定義了VLC_COMMON_MEMBERS宏,該宏中包括了所有VLC基本對象的通用成員變量:these members are common for all vlc objects。
??????? 定義導出函數
#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
定義回調函數
????? 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的定義和處理函數
?????? struct vlc_object_t
{
VLC_COMMON_MEMBERS
}; //定義一個結構來使用宏定義的公共成員

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455427.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455427.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455427.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flask 概述

什么是Web Framework? Web Application Framework(Web應用程序框架)或簡單的Web Framework(Web框架)表示一個庫和模塊的集合,使Web應用程序開發人員能夠編寫應用程序,而不必擔心協議&#xff0…

(五)Maven中的聚合和繼承

一、為什么要聚合? 定義:我們在開發過程中,創建了2個以上的模塊,每個模塊都是一個獨立的maven project,在開始的時候我們可以獨立的編譯和測試運行每個模塊,但是隨著項目的不斷變大和復雜化,我們…

python堆棧反向輸出列表_python - IPython:將Python腳本的輸出重定向到文件(如bash) - 堆棧內存溢出...

IPython有自己的上下文管理器來捕獲stdout / err ,但它沒有重定向到文件,它重定向到一個對象:from IPython.utils import iowith io.capture_output() as captured:%run my_script.pyprint captured.stdout # prints stdout from your script…

關于datagrid

基本在公司使用的datagrid不需要自己寫前臺代碼,只需要自己給grid明確id,url以及列屬性即可。 后臺需要返回一個數據類型:{recordsFiltered2, data[], drawnull, recordsTotal2},通常返回這個數據類型的話,只需要調用d…

M-JPEG、MPEG4、H.264都有何區別 依維安防論壇

壓縮方式是網絡視頻服務器和網絡攝像機的核心技術,壓縮方式很大程度上決定著圖像的質量、壓縮比、傳輸效率、傳輸速度等性能,它是評價網絡視頻服務器和網絡攝像機性能優劣的重要一環。 隨著多媒體技術的發展,相繼推出了許多壓縮編碼標準&…

Django/Flask/Tornado三大web框架性能分析

寫在前面:本文的數據涉及到之前遇到過的問題,大概一次 http 請求到收到響應需要多少時間。這個問題在實際工作中與框架有比較大的關系,因此特別就框架的性能做了一次分析。這里使用之前的一個報告數據: Pythons Web Framework Ben…

python urllib模塊學習筆記

這個模塊是最基本最常用的,以前看過,總結一下 #coding : utf-8import urlliburl http://cnblogs.com#代理服務器proxies {http:http://127.0.0.1:8087}#使用代理服務器打開r urllib.urlopen(url,proxies proxies)print r.info()print r.getcode()pri…

hibernate基礎工具findBySQL學習

public List<Map<String,Object>> findBySQL(String sql,Map<String,Object> param,int start,int max) {log.debug("finding List by hql");try {       //最后返回map map的key可為別名和數據庫字段SQLQuery querysessionFactory.getCurr…

python處理ini文件_python對ini配置文件處理

>>> cf.read("test.ini") #讀取配置文件[test.ini]>>> cf.sections() #片段名[base, callback]>>> cf.options("callback") #配置…

Python實現自動推本地github博客到遠程倉庫

Python實現自動推本地github博客到遠程倉庫 以前的簡單版本 通過python中的os模塊操作系統命令 詳情可參考:Python實現一行代碼推本地git到遠程倉庫 升級版本 本次加入了監聽文件修改功能 這樣腳本只需在后臺運行,即可檢測到對應的文件夾中的內容是否變化 如果變化,則調用…

H.264/MPEG-4 AVC

維基百科&#xff0c;自由的百科全書跳轉到&#xff1a; 導航, 搜索 跳過字詞轉換說明 漢漢▼▲為了閱讀方便&#xff0c;本文使用全文手工轉換。轉換內容&#xff1a;本文采用電腦和信息技術組全文轉換 [查看] ? [編輯] ? [強制刷新] 以下為本條目單獨的全文轉換&#xff0c…

JavaScript 專題之函數柯里化

JavaScript 專題系列第十三篇&#xff0c;講解函數柯里化以及如何實現一個 curry 函數 定義 維基百科中對柯里化 (Currying) 的定義為&#xff1a; In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes m…

機器學習模板

根據心情補充&#xff0c;語言都是Python hash&#xff0c;把所有的文本轉化成數字 from sklearn.preprocessing import LabelEncoder for c in train.columns:if train[c].dtype object:lbl LabelEncoder()lbl.fit(list(train[c].values) list(test[c].values))train[c] l…

漂亮特殊字體可復制_12個創意字體免費下載網站

今天為大家介紹12個創意字體的網站&#xff0c;這些網站都有提供免費下載的字體哦&#xff0c;希望對大家在創作上面有所幫助。FontSpace在Fontspace上有超過42000種免費字體。在這里字體被整齊的分門歸類&#xff0c;幫助你找到想要的字體。除了典型的“serif” “script”等&…

使用postman測試接口

Postman是一款功能強大的網頁調試與發送網頁HTTP請求的Chrome插件。在java web開發中使用非常多&#xff0c;經常用來測試接口。 使用postman模擬json數據的發送 第一步:在header里邊設置發送數據的類型 Paste_Image.png設置發送數據類型為json&#xff0c;也就是key為Content-…

刪除github上的commit歷史記錄

刪除github上的commit歷史記錄 起步 今天小編發現了git克隆下來的遠程庫特別大: 經過查詢之后發現是每次推送之后都會留下記錄緩存&#xff0c;這樣很多沒用的記錄就會占用多余的空間&#xff0c;別人克隆的時候也會多耗費時間&#xff0c;今天我查到了一個清除無用記錄的方…

DirectShow組件原理分析及應用

1 DirectX簡介  DirectX是Microsoft公司為游戲和其他高性能多媒體應用所提供的一套底層應用程序編程接口。這些接口包括對二維和三維圖形&#xff0c;聲效和音樂&#xff0c;輸入設備以及多玩家網絡游戲等的支持。目前DirectX的最高版本是DirectX 9.0。  1.1 DirectX的組成…

接口安全

老大發了篇文章&#xff0c;讓看如何寫出安全的接口。 如何寫出安全的API接口&#xff1f;接口參數加密簽名設計思路轉載于:https://www.cnblogs.com/Tpf386/p/7053795.html

python中xml模塊_python學習第十五天-2(XML模塊)

也是一種文本轉換形式。importxxxxxxxxxxxxxxxxxxx asxx,可以用xx代替xxxxxxxxxxxxxxxxxxx模塊xml文件的新增&#xff0c;修改&#xff0c;刪除&#xff0c;查詢。新增&#xff1a;?import xml.etree.ElementTreeas ET?new_xmlET.Element(nameList)#創建xml的根節點相當于na…

ubuntu系統下Java環境JDK的安裝

Debian Linux下安裝jdk 下載壓縮包 官網下載對應的.gz包 點擊下載 解壓文件 創建一個目錄用于存放解壓后的文件&#xff0c;并解壓縮到該目錄下 sudo mkdir /opt/java8 sudo tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt/java8修改環境變量 sudo vim ~/.bashrc 進入…