TCPMP源代碼分析
播放器主要由核心架構子產品(common工程)和解碼器、分離器插件組成。TCPMP的插件非常多,其中主要的插件有:interface插件實作了TCPMP的界面,ffmpeg是系統主要的音視訊解碼子產品,splitter是媒體檔案分離器。
由于ffmpeg的解碼效率不高,系統僅使用了ffmpeg的部分功能。并且未使用其中的libavformat子產品,而使用splitter子產品進行。其他插件暫時沒有研究。本周主要分析的是common工程。
common工程是核心子產品,是一個開放的集資料輸入、轉換、音/視訊解碼、信号輸出等功能為一體的完整的多媒體播放架構。這個架構自身不包含任何的Decode和Split功能,這些功能由插件實作,核心子產品以一個樹狀結構管理所有的功能子產品和插件子產品,實作資料Render功能,對輸入、轉換、輸出流程的控制,接受播放過程中的操作和對事件進行處理,同時也實作系統運作中經常使用的一些共用函數,比如解碼過程中經常使用的逆離散餘弦變換,記憶體操作,界面中需要使用的多語言字元處理等。
common工程的主目錄下主要有:blit、dyncode、overlay、pcm、softidct、win32、zlib等子目錄。其中blit和overlay存放是視訊信号渲染子產品,pcm存放PCM音頻信号轉換子產品,softidct存放逆離散餘弦變換函數,win32存放記憶體操作等常用子產品,dyncode這個目錄的代碼比較晦澀,存放的是程式運作時動态生成代碼子產品,針對不同的CPU指令集,PCM資料聲道和采樣率不同,視訊渲染資料格式和色深等不同情況動态生成不同的優化代碼,zlib則提供了記憶體中壓縮和解壓縮的函數,包括未壓縮資料的完整性檢查。
以下是common工程核心子產品中幾個重要的概念:
(1)上下文對象context
該對象在初始化函數bool_t Context_Init中建立了一個該對象執行個體(context.h)。該對象執行個體記錄管理各個功能子產品,使用者界面可以通過該對象和核心子產品互動,管理控制播放過程。
(2)功能子產品
功能子產品包括定義對象nodedef和資料對象node,定義對象描述功能子產品互相間的邏輯結構,資料對象記錄子產品屬性和方法。所有的功能子產品結構按一個樹狀結構來組織,結構關系如下,NODE是整個結構的根結點,其下為子節點,節點按類型可分為實節點,全局節點,設定節點,抽象節點。
抽象節點沒有對應的對象執行個體,類似C++的抽象基類,為了按照邏輯關系組織系統結構而存在,例如NODE就是抽象節點。全局節點隻有一個對象的執行個體,如播放控制子產品PLAYER_ID。設定節點表示和系統播放設定相關,比如聲音均衡器子產品EQUALIZER_ID,顔色控制子產品COLOR_ID。實節點與抽象節點不同,指可以生成對象執行個體的節點,實節點沒有特殊辨別,一般以資料對象占用記憶體大小表示是否是一個實節點,建立節點時要根據該資訊配置設定記憶體單元,實節點也可以有子節點,例如:MMS_ID的父節點是HTTP_ID。全局節點,設定節點和實節點可以互相組合,比如播放控制節點同時是全局節點,設定節點和實節點。
下面是主要的節點樹狀分布圖:
NODE (根節點)
├─FLOW (流控制子產品)
│ ├─CODEC (解碼子產品)
│ │ ├─EQUALIZER_ID (聲音均衡器子產品)
│ │ ├─VBUFFER_ID (視訊緩沖子產品)
│ │ ├─DMO (DirectX Media Object)
│ │ │ ├─WMV_ID
│ │ │ ├─WMS_ID
│ │ │ ├─WMVA_ID
│ │ │ ├─WMA_ID
│ │ │ └─WMAV_ID
│ │ ├─FFMPEG VIDEO (FFMpeg 解碼子產品)
│ │ └─LIBMAD_ID (Libmad Mp3解碼子產品)
│ ├─OUT (信号渲染子產品)
│ │ ├─AOUT (音頻信号渲染)
│ │ │ ├─NULLAUDIO_ID
│ │ │ └─WAVEOUT_ID
│ │ └─VOUT (視訊信号渲染)
│ │ ├─NULLVIDEO_ID
│ │ └─OVERLAY
│ ├─IDCT (離散餘弦解碼子產品)
│ │ └─SOFTIDCT_ID
│ └─CODECIDCT(離散餘弦解碼子產品,函數比IDCT要少)
│ └─MPEG1_ID
├─MEDIA (媒體檔案格式編碼解析子產品)
│ ├─FORMAT (格式解析子產品)
│ │ └─FORMATBASE
│ │ ├─RAWAUDIO
│ │ │ └─MP3_ID
│ │ ├─RAWIMAGE
│ │ ├─ASF_ID
│ │ ├─AVI_ID
│ │ ├─MP4_ID
│ │ ├─MPG_ID
│ │ ├─NSV_ID
│ │ └─WAV_ID
│ ├─PLAYLIST (播放清單子產品)
│ │ ├─ASX_ID
│ │ ├─M3U_ID
│ │ └─PLS_ID
│ └─STREAMPROCESS (資料流處理子產品)
├─STREAM (資料輸入子產品)
│ ├─MEMSTREAM_ID (記憶體資料流子產品)
│ ├─FILE_ID (檔案IO子產品)
│ └─HTTP_ID (網絡資料擷取子產品)
├─TIMER (定時器子產品)
│ └─SYSTIMER_ID
├─ASSOCIATION_ID (檔案擴充名自動關聯子產品)
├─ADVANCED_ID (進階設定子產品)
├─COLOR_ID (顔色控制子產品)
├─PLATFORM_ID (平台資訊子產品)
├─XSCALEDRIVER_ID
├─PLAYER_ID (播放控制子產品)
└─PLAYER_BUFFER_ID (播放緩沖子產品)
以下是common工程核心子產品的幾個重要資料結構:
(1)context 上下文對象
typedef struct context
{
int Version;//版本資訊
uint32_t ProgramId;//應用程式句柄
const tchar_t* ProgramName;//應用程式名稱
const tchar_t* ProgramVersion;//程式版本号,字元串
const tchar_t* CmdLine;//程式指令行資訊
void* Wnd;//視訊渲染視窗句柄
void* NodeLock;//功能子產品通路臨界區互斥變量
array Node; //功能子產品資料對象數組
array NodeClass; // ordered by id功能子產品定義對象數組,按照系統邏輯關系組織
array NodeClassPri; // ordered by priority|id功能子產品定義對象數組,按照系統邏輯關系和優先級排列
array NodeModule;//外部插件子產品數組
int LoadModuleNo;//目前正在加載的外部插件序号
void* LoadModule;//目前正在加載的外部插件
array StrTable[2];//字元串資源數組,字元串分為:給底層使用的标準字元串資源、給界面使用的顯示字元串資源,兩個資源用兩個數組表示
array StrBuffer;
array StrModule;//未使用
void* StrLock;//字元串數組通路臨界區互斥變量
uint32_t Lang;//目前使用語言标志
int CodePage;//目前使用代碼頁标志
struct pcm_soft* PCM;//PCM音頻信号轉換子產品
struct blitpack* Blit;//視訊信号渲染子產品
struct node* Platform;//得到平台相關資訊
struct node* Advanced;//得到播放子產品進階資訊
struct node* Player;//播放控制子產品
notify Error;//資訊錯誤回調函數
int (*HwOrientation)(void*);
void *HwOrientationContext;
bool_t TryDynamic;//未使用
int SettingsPage;//未使用
size_t StartUpMemory;//可以使用的有效記憶體數
bool_t InHibernate;//是否進入休眠狀态
bool_t WaitDisable;//未使用
int FtrId;//未使用
bool_t LowMemory;//可以使用的有效記憶體數是否小于系統要求的最低要求
//動态代碼生成中間狀态及資料
bool_t CodeFailed;
bool_t CodeMoveBack;
bool_t CodeDelaySlot;
void* CodeLock;
void* CodeInstBegin;
void* CodeInstEnd;
int NextCond;
bool_t NextSet;
bool_t NextByte;
bool_t NextHalf;
bool_t NextSign;
uint32_t* FlushCache;//未使用
void* CharConvertUTF8;//未使用
void* CharConvertCustom;//未使用
int CustomCodePage;//未使用
void* CharConvertAscii;//未使用
void* Application;
void* Logger;//未使用
bool_t KeepDisplay;//是否保持背光長亮
int DisableOutOfMemory;//未使用
} context;
(2)nodedef 功能子產品定義對象
功能子產品樹狀結構通常由若幹個靜态定義對象(nodedef)執行個體實作,
typedef struct nodedef
int Flags;//功能子產品節點的類型:抽象、實節點、全局、設定。
int Class;//功能子產品節點的辨別,如MEDIA_CLASS或ASF_ID等等。
int ParentClass;//功能子產品父節點的辨別,如SYSTIMER_ID對象的父節點是TIMER_CLASS。
int Priority;//表示功能子產品節點優先級。
nodecreate Create;//建立功能子產品定義對象的函數指針
nodedelete Delete;//銷毀功能子產品定義對象的函數指針
} nodedef;//功能子產品定義對象
如解碼器功能子產品靜态定義對象:
static const nodedef Codec =
sizeof(codec)|CF_ABSTRACT,
CODEC_CLASS,
FLOW_CLASS,
PRI_DEFAULT,
(nodecreate)Create,
(nodedelete)Delete,
};
(3)nodeclass 功能子產品定義對象連結清單結構
用連結清單的方式實作了功能子產品樹狀結構,每個連結清單代表樹狀結構的一個分支。
typedef struct nodeclass
nodedef Def;//功能子產品定義對象
bool_t Registered;//是否注冊
int ModuleNo;//子產品辨別
struct nodeclass* Parent;//功能子產品定義對象的父對象
} nodeclass;//功能子產品定義節點對象連結清單結構
(4)node 功能子產品資料對象
typedef struct node
int Class;//功能子產品節點的類型,如MEDIA_CLASS等等,與nodedef相同。
nodeenum Enum;//枚舉節點屬性函數指針
nodeget Get;//擷取節點屬性的函數指針
nodeset Set;//設定節點屬性的函數指針
} node;//功能子產品資料對象
上述幾個資料對象的互相關系:
在系統上下文對象context中有兩個元素記錄功能子產品資訊array Node和array NodeClass,array是數組資料類型(在buffer.h/c中定義和實作),Node是功能子產品資料對象的數組,NodeClass功能子產品定義對象的數組,按照系統邏輯關系組織。
建立功能子產品時傳入nodedef對象到功能子產品建立函數,函數會根據nodedef資訊生成對應nodeclass對象添加到NodeClass數組,同時根據nodedef資訊配置設定資料對象的記憶體空間。在該節點的Create函數裡面再初始化該功能子產品的資料對象node。
(5)datadef 功能子產品屬性
typedef struct datadef
int No;//屬性的辨別,如播放控制子產品的#define PLAYER_PLAY 0x32 就表示控制播放器播放或暫停。
int Type;//屬性的資料類型,在node.h中定義,如TYPE_BOOL
int Flags;//屬性資料的标志,是屬性資料的标志,表示該資料是不是隻讀資料,是否有最大最小值等等,node.h中定義,如DF_RDONLY
int Format1;
int Format2;
const tchar_t* Name;
int Class;
int Size;
} datadef;//屬性對象定義
其中Format1和Format2是可選标志與Flags配合使用,比如如果Flags表示該屬性存在最大最小值,Format1就是最大值,Format2則是最小值;
另外,如果(!(Flags & DF_NOSAVE) && !(Flags & DF_RDONLY))即屬性辨別為儲存且可讀寫,則會被記錄到系統資料庫中,下次啟動時用系統資料庫的資料初始化該屬性表。
(6)datatable 功能子產品屬性清單
typedef struct datatable
int No;
int Type;
int Flags;
} datatable;//功能子產品屬性清單
各功能子產品的屬性通常以數組的形式定義和存儲,如格式解析子產品屬性清單
static const datatable Params[] =
{ FORMAT_INPUT, TYPE_NODE, DF_INPUT|DF_HIDDEN, STREAM_CLASS },
{ FORMAT_OUTPUT, TYPE_NODE, DF_HIDDEN, STREAM_CLASS },
{ FORMAT_DURATION, TYPE_TICK },
{ FORMAT_FILEPOS, TYPE_INT, DF_HIDDEN },
{ FORMAT_FILESIZE, TYPE_INT, DF_KBYTE },
{ FORMAT_AUTO_READSIZE, TYPE_BOOL, DF_HIDDEN },
{ FORMAT_GLOBAL_COMMENT,TYPE_COMMENT, DF_OUTPUT },
{ FORMAT_FIND_SUBTITLES,TYPE_BOOL, DF_HIDDEN },
{ FORMAT_STREAM_COUNT, TYPE_INT, DF_HIDDEN },
DATATABLE_END(FORMAT_CLASS)
(7)nodemodule 外部插件功能子產品
typedef struct nodemodule
int Id;//插件辨別
int ObjectCount;//該插件的執行個體個數(引用計數)
bool_t Tmp;//是否是臨時節點
int64_t Date;//設定時間
int KeepAlive;//保持時間
void* Module;//外部插件子產品
void* Db;
void* Func;
uint8_t* Min;
uint8_t* Max;
} nodemodule;//外部插件子產品節點
核心子產品的初始化流程及相應代碼對應關系(參考context.c中的Context_Init函數)
Mem_Init();
//記憶體等資源初始化(Win32/mem_win32.c)
DynCode_Init();
//程式運作動态生成代碼子產品,優化PCM,視訊渲染子產品等(DynCode/DynCode.c)
String_Init();
//系統使用字元串初始化(str.c,Win32/str_win32.c)
PCM_Init();
//音頻信号轉換子產品初始化(PCM/pcm_soft.c)
Blit_Init();
//視訊信号渲染子產品初始化(Blit/blit_soft.c)
Node_Init();
//根節點子產品初始化(node.c,Win32/node_win32.c)
Platform_Init();
//平台資訊子產品初始化(platform.c,Win32/platform_win32.c)
Stream_Init();
//輸入資料流子產品初始化(streams.c)
Advanced_Init();
//進階設定子產品初始化(advance.c)
Flow_Init();
//流控制子產品初始化(flow.c)
Codec_Init();
//解碼子產品初始化(codec.c)
Audio_Init();
//音頻信号處理子產品初始化(audio.c)
Video_Init();
//視訊信号處理子產品初始化(video.c)
Format_Init();
//格式解析子產品初始化(format.c)
Playlist_Init();
//播放清單子產品初始化(playlist.c)
FormatBase_Init();
//基本格式解析子產品初始化(format_base.c,format_subtitle.c)
NullOutput_Init();
//無輸出裝置子產品初始化(nulloutput.c)
RawAudio_Init();
//RawAudio子產品初始化(rawaudio.c)
RawImage_Init();
//RawImage子產品初始化(rawimage.c)
Timer_Init();
//定時器子產品初始化(timer.c)
IDCT_Init();
//離散餘弦解碼子產品初始化(idct.c)
Overlay_Init();
//視訊疊加子產品初始化(overlay.c)
M3U_Init();
//M3U格式播放清單子產品初始化(PlayList/m3u.c)
PLS_Init();
//PLS格式播放清單子產品初始化(PlayList/pls.c)
ASX_Init();
//ASX格式播放清單子產品初始化(PlayList/asx.c)
WaveOut_Init();
//波形輸出子產品初始化(waveout.c,Win32/waveout_win32.c)
SoftIDCT_Init();
//soft離散餘弦解碼子產品初始化(SoftIDCT/softidct.c)
Plugins_Init();
//外部插件子產品初始化(Win32/node_win32.c)
另外還有檔案擴充名自動關聯子產品Association_Init (參考檔案Win32/ association_win32.c);顔色控制子產品Color_Init(參考color.c);聲音均衡器子產品Equalizer_Init(參考equalizer.c);播放控制子產品初始化(參考player.c )。
向系統中載入外部插件子產品(參考node.c以及node_win32.c)
node.c中的LoadModule函數,可以在系統中載入外部插件子產品,
static NOINLINE nodemodule* LoadModule(context* p,int No),
第一個參數是上下文對象,
第二個參數是外部插件子產品辨別
node_win32.c定義了dll的載入與解除安裝函數以及相應的系統資料庫操作,如
在功能子產品節點載入外部插件子產品
void* NodeLoadModule(const tchar_t* Path,int* Id,void** AnyFunc,void** Db)
與界面互相動的播放控制子產品(player.c)
在所有功能子產品中和界面加互動的主要就是播放控制子產品struct node* Player;使用方法如下:
context* p = Context();
player* myplayer = NULL;
if(p) myplayer = (player*)(p->Player);
控制播放使用
Set(void* This,int No,const void* Data,int Size)
第一個參數是播放子產品指針,
第二個參數是控制代碼,即要進行什麼操作,
第三個參數是需要指派給控制代碼的數值,
最後一個參數是所賦數值的占用記憶體的大小。
myplayer->Set(myplayer,PLAYER_PLAY,1,sizeof(int));
PLAYER_PLAY為控制代碼,表示目前控制的是播放暫停功能,數值為1表 示播放為0表示暫停。
得到某一控制屬性使用Get(void* This,int No,void* Data,int Size);函數,參數含義和Set函數相同。
控制代碼是一組宏,定義在player.h檔案中。比較重要的控制參數有播放控制子產品所有可用參數見static const datatable PlayerParams[]結構。
添加一個媒體檔案到播放子產品使用
int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title);
第一個參數為播放子產品指針,
第二個參數是添加到播放子產品檔案隊列的序号,如果是使檔案成為第一個文 件該參數設為0,
第三個參數是媒體檔案的目錄和名稱,
第四個參數為媒體檔案标題,該參數可以忽略。