TCPMP源代碼分析
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;???
??????? int Format1;
??????? int???? Format2;
} 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,
??????? 第三個參數是媒體文件的目錄和名稱,
??????? 第四個參數為媒體文件標題,該參數可以忽略。
TCPMP編譯方法
?本文主要講解TCPMP播放器到WINDOWS CE平臺的移植編譯過程,硬件平臺以ARMV4為主,結束部分會講解到ARMV4I編譯中需要注意的問題?
????這幾天為公司一個項目做準備,準備編譯移植來自linux系統的超級開源媒體播放器TCPMP(商業版名稱:The Core Player)到Windwos CE平臺,tcpmp是目前支持最多可以用在嵌入式設備中多媒體播放器.除了rm,rmvb等少數幾種格式不支持外,其它常見視、音頻格式幾乎都支持。好東西是好定西,可是編譯起來十分麻煩,網上關于這方面編譯的文章很少,多半針對自己的環境粗略的講一下,系統性不強。今天TCPMP媒體播放器正式編譯成功,將經驗總結一下:
?
開發環境:windows xp sp2 + EVC4.0(SP4)
目標平臺:Windows CE5.0(ARMV4)
?
1.下載源碼,可以去http://picard.exceed.hu/tcpmp/下載TCPMP源代碼。我下載的源碼版本是0.72RC1。
?
2.編譯環境.我安裝的是evc4.2(SP4)+standard sdk+Win32(WCE ARMV4) Release.如果是編譯x86或者Emulator版本的.要下載一個nasm匯編工具.這點在readme.txt里面提到.nasm的下載地址http://nasm.sourceforge.net/.否則的話會因為缺少匯編器而報大量的錯誤。
?
3.下載下來的源碼包中不包含ARM的解碼器源碼,可以從下面2個網址下載AMR的解碼器的源代碼:http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-610.zip
http://www.3gpp.org/ftp/Specs/archive/26_series/26.204/26204-600.zip
并且分別拷貝到AMR目錄下的26104和26204中。同樣,這個信息在readme.txt中提到。????????????
?
4.準備ARM的匯編器,根據實踐經驗,從ARM官網上面下載下來的匯編器編譯時會報錯,不適合使用,建議使用VS2005的ARM匯編器ARMASM.EXE,將其拷貝到C:/Microsoft eMbedded C++ 4.0/EVC/WCE400/BIN下面。
?
5.編譯的時候切勿rebuild all,否則會報大量的錯誤,從Project—-Dependencies下來框里選擇player_ce3(主項目),可以看到子項目間的相互依賴關系,所以player_ce3是最后一個編譯的項目。隨便在下拉框中選擇一個子項目,在依賴項中都會發現common項目,說明該項目應該是首先需要進行編譯的,下面我們首先從common項目入手。
?
6.將player_ce3項目set as active project,編譯版本直接選擇Win32 (WCE ARMV4) Release。這個項目將最后一個進行編譯,最終生成一個player_ce3.exe的應用程序,那我們的目標也就達成了。但編譯這個程序依賴許多的庫。這些庫就是其他的project編譯后提供的。
?
7.在EVC左邊的文件查看模式里首先選擇common files,右鍵菜單Build(selection only)進行common項目的編譯,會很順利的過去。接下來由上而下順序為子項目進行編譯,當然,在Project—-Dependencies中沒有關聯的項目不需要進行編譯(總共6項:player_ce2、sample_ce3、setup_ce2、setup_ce3、template、vorbislq),其它的把asap、flac、player_ce3三項放下來最后處理,這三項需要對編譯器進行額外配置,否則會報大量錯誤。不出意外的話,都會順利編譯過去。接下來我們需要處理剩下來的3個項目。
?
8.編譯asap項目。右鍵點asap files –>settings–>c/c++–>Category–>Preprocessor在Additional include directories:中增加項目路徑(注意這個是相對路徑,以下所有需添加內容都不包括引號)”.,asap,atari800/src”.不然一堆頭文件會找不到.在Preprocessor definitions:中增加一個宏定義”,ASAP”當然這2步動作也可以在源代碼中修改.如此設置完畢后,asap files project就可以正確編譯了.
?
9.編譯flac項目。同8,打開flac的settings到相同界面.在Additional include directories:中增加路徑”flac/include,flac/src/libFLAC/include”,不然一堆頭文件找不到。然后,在Preprocessor definitions添加”,FLAC__NO_DLL”。增加這個定義避免使用_declspec(dllexport)定義函數造成的一大堆c2491錯誤.如此設置后,flac project應該可以正確編譯.
?
10.同理修改player_ce3項目,在Additional include directories中增加路徑 ”../asap/asap,../asap,../asap/atari800/src,flac/include,flac/src/libFLAC/include”。然后在Preprocessor difinitions:增加”,ASAP”。這是最后一個項目文件,也是主項目文件,成功編譯player_ce3.exe。
?
11. 拷貝包含player_ce3.exe在內所有的生成文件到目標板上(所有文件必須放在同一個目錄中),可以運行!但是菜單沒有顯示正確。主要原因是現實語言配置文件沒有加載上去,可以將源碼lang目錄下面的多國語言支持文件拷貝到目標板同一個目錄下面。如果只需要簡體中文和英文的,只要拷貝lang_std.txt lang_en.txt,lang_chs.txt,lang_ca.def四個文件(四個語言配置文件一定要和應用程序放在同一個目錄)就可以了,打開后默認顯示是英語,你可以更改到簡體中文,前提是你的CE平臺支持簡體中文。
?
我是在VS2005中用開發平臺的模擬器跑的,一切正常,就是播放不流暢,后期需要對這塊進行優化。
?
我也嘗試將其編譯到ARMV4I平臺上,結果也是可行的,不過由于的平臺的特殊性,有部分配置需要改動,也有部分插件不被支持,不過不影響使用,大體總結如下:
?
1.經驗總結表明在ARMV4I平臺的編譯工作中,TCPMP有部分模塊不被支持,編譯提示缺少相應文件。由于該部分不被支持的模塊不影響播放器的正常使用,可以在Project–Dependenties中下拉框中選擇player_ce3,然后將以下幾項前面的勾拿掉:ffmpeg、mpc、speex 這三項可以不必編譯。
?
2.Win32 (WCE ARMV4I) Debug及Release版本需要自己手動創建。在Build–Configurations里為每個子項目選擇ADD(上面提到的3項,和依賴項中不需要編譯的6項不必添加),CPU選擇Win32 (WCE ARMV4I),在Copy settings from里選擇Win32 (WCE ARMV4) Release,然后選擇OK,你就為該子項目添加了相應編譯版本。
?
3.右擊需要編譯的子項目,選擇Settings–Link,在Category中選擇General,然后再下面Project Options里將最后一行語句:/MACHINE:ARM 改成 /MACHINE:THUMB (每個項目都必須要改)
?
4.其它步驟按照按照上文ARMV4的過程來就可以了,相應的修改也是需要的,先從common開始,以player_ce3結束。
?
經過以上過程,你就可以定制自己專用的TCPMP播放器了,可以在interface項目中更改TCPMP的外觀,當然,重頭工作還在于對于特定平臺的一些優化工作:)
?
注:如果上面給出網址的解碼器包下載不了,可以在本站軟件專區的windows ce工具中下載ARM匯編器(VS2005提取出)和解碼器包
使用TCPMP解碼顯示JPEG圖片WINCE5.0
張挺哥哥今天給兄弟姐妹們帶來的是使用TCPMP0.72RC1版本的插件解碼JPEG/JPG大圖片的東東.在WINCE下解碼大圖片是很多人都遇到的難題,做起來也不是很順利.所以呢,我就把使用TCPMP解大JPEG圖片的方法告訴兄弟們,兄弟們如果因此而在經濟危機中保住飯碗的話,嘿嘿,有機會請哥哥出去弄幾個妞來整整就非常感謝了(開玩笑哈).
一、大致思路說明:其實要解碼JPEG圖片,只需要用到TCPMP中的兩個插件:COMMON和CAMERA兩個鳥插件.CAMERA插件負責解碼JPEG,COMMON負責顯示圖片.各位兄弟,為了讓解出來的圖象顯示在我們指定的DC中,我們是需要修改COMMON插件的.為什么要把圖象顯示在我們指定的DC中呢?因為我們如果要做顯示的特技的話,就是需要這樣做.比如所謂的"百葉窗"特技等等,那些鳥特級都是使用了雙緩沖的喲.(張挺eMail:zt00@tom.com)
二、步驟
1、先按照我的另一文章《TCPMP0.72RC1的編譯與移植以及自己另外做UI完整方法》把COMMON修改編譯出來。文章地址:http://blog.csdn.net/zhangting00_2000/archive/2009/04/20/4095272.aspx
2、設置COMMON為當前工程,然后把Context_Wnd全部搜出來。修改后的代碼如下:
(1)
void Context_Wnd(void* Wnd,void* hMemDC)
{
?context* p = Context();
?p->hMemDC = hMemDC;//zt00
?if (p)
?{
#ifdef REGISTRY_GLOBAL
? if (Wnd)
?? NodeRegLoadGlobal();
? else
?? NodeRegSaveGlobal();
#endif
? p->Wnd = Wnd; // only set after globals are loaded
? NodeSettingsChanged();
?}
}
(2)
DLL void Context_Wnd(void* Wnd,void* hMemDC);
(3)把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;
?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;
?void * hMemDC;//這個東西用來繪制內存圖片的。
} context;
(張挺eMail:zt00@tom.com)
(4)進入COMMON下的Overlay,然后打開overlay_gdi.c,找到Blit函數,修改如下:
static int Blit(gdi* p, const constplanes Data, const constplanes DataLast )
{
?HDC DC;
?if (!p->Planes[0] && !AllocBitmap(p))
? return ERR_OUT_OF_MEMORY;
#ifdef BLITTEST
?BlitImage(p->Soft2,p->Planes2,Data,DataLast,-1,-1);
?BlitImage(p->p.Soft,p->Planes,p->Planes2,NULL,-1,-1);
#else
?BlitImage(p->p.Soft,p->Planes,Data,DataLast,-1,-1);
#endif
//
?if (!p->DIBSection)
?{
? if (p->Bitmap)
? {
?? SelectObject(p->DC2,p->Bitmap0);
?? DeleteObject(p->Bitmap);
? }
? p->Bitmap = CreateBitmap( p->Overlay.Width, p->Overlay.Height, 1,
?? p->Overlay.Pixel.BitCount, (char*)p->Planes[0]);
? if (!p->Bitmap)
?? return ERR_OUT_OF_MEMORY;
? p->Bitmap0 = SelectObject(p->DC2,p->Bitmap);
?}
?//MessageBox(NULL,_T("sdhfjshdfk"),_T("fg"),MB_OK);
?DC = GetDC(Context()->Wnd);//zt00
?BitBlt(Context()->hMemDC,p->p.GUIAlignedRect.x,p->p.GUIAlignedRect.y,
???? p->OverlayRect.Width,p->OverlayRect.Height,p->DC2,p->OverlayRect.x,p->OverlayRect.y,SRCCOPY);
?ReleaseDC(Context()->Wnd,DC);//zt00
?PostMessage(Context()->Wnd,WM_USER+1988,100,100);//這里是發個消息出去,表示已經解碼完畢可以顯示了.
?return ERR_NONE;
}
呵呵,修改完了這些后,把COMMON編譯,編譯出來的插件就是我們需要的插件.
最后呢,自己寫一個程序調用這兩個插件(COMMON和CAMERA)就可以了.我給大家寫了一個這樣的程序,代碼下載地址如下:http://download.csdn.net/source/1235426
呵呵,但愿大家玩的開心.
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zhangting00_2000/archive/2009/04/22/4099607.aspx
TCPMP播放器UI的修改方法
TCPMP播放器UI的修改方法??? 前段時間對TCPMP程序進行了研究,花了點時間把TCPMP程序的UI修改成了自已想要的樣子,現對UI的修改方法簡單介紹下:
??? 網上有文章對于TCPMP程序在ARMV4下的編譯方法和程序的結構介紹比較詳細,但對于inteface方面的修改方法講得并不多.
??? 修改TCPMP界面基本上有兩種方法:
1)???? 建立自已的工程,把TCPMP下的lib移植到這個工程里,這樣界面的修改比較靈活,但是工作量比較大;
2)???? 直接在TCPMP工程里修改interface,這樣工作量相對比較小,但是UI的修改受了定的約束, 沒有前種方法靈活.
??? 本人所修改的UI是按照第二種方法來做的.主要是對win_win32.c文件和inteface.c 文件進行修改.
?? 首先,我們來看看win_win32.c文件里的Win_Init()函數,這個函數可以看作是TCPMP UI部分的一個入口,在這個函數里注冊了兩個窗口類,WinClass和DialogClass.另外還調用了其它控件的初始化函數,下面是這個函數的代碼.
view plaincopy to clipboardprint?
void Win_Init()??
{??
??? HMODULE Module = Context()->LoadModule;??
??? if (!Module) Module = GetModuleHandle(NULL);??
??? InitCommonControls();??
??? WidcommAudio_Init();??
??? stprintf_s(WinClassName,TSIZEOF(WinClassName),T("%s_Win"),Context()->ProgramName);??
?
??? memset(&WinClass,0,sizeof(WinClass));??
??? WinClass.style? = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;??
??? WinClass.lpfnWndProc??? = Proc;??
??? WinClass.cbClsExtra???? = 0;??
??? WinClass.cbWndExtra???? = 0;??
??? WinClass.hInstance????? = Module;??
??? WinClass.hIcon? = LoadIcon?? (GetModuleHandle(NULL), MAKEINTRESOURCE(WIN_ICON));??
??? WinClass.hCursor??????? = WinCursorArrow();??
??? WinClass.hbrBackground? =NULL;??
??? WinClass.lpszMenuName?? = 0;??
??? WinClass.lpszClassName? = WinClassName;??
??? RegisterClass(&WinClass);??
#if 1??
??? memset(&DialogClass,0,sizeof(DialogClass));??
??? DialogClass.style?? = CS_HREDRAW | CS_VREDRAW;??
??? DialogClass.lpfnWndProc???? = DialogProc;??
??? DialogClass.cbClsExtra????? = 0;??
??? DialogClass.cbWndExtra????? = 0;??
??? DialogClass.hInstance?????? = Module;??
??? DialogClass.hCursor???? =? WinCursorArrow();??
#if defined(TARGET_WINCE)??
??? DialogClass.hbrBackground?? = GetSysColorBrush(COLOR_STATIC);//???
#else?????????????
??? DialogClass.hbrBackground?? = GetSysColorBrush(COLOR_BTNFACE);??
#endif??
??? DialogClass.lpszMenuName??? = 0;??
??? DialogClass.lpszClassName?? = T("DialogBase");??
??? RegisterClass(&DialogClass);??
#endif??
??????
??? memset(&FontCache,0,sizeof(FontCache));??
?
#if defined(TARGET_WINCE)??
??? if (Context()->ProgramId >= 3 && !QueryAdvanced(ADVANCED_OLDSHELL))??
??? {??
??? AygShell = LoadLibrary(T("aygshell.dll"));??
??? *(FARPROC*)&FuncSHCreateMenuBar = GetProcAddress(AygShell,T("SHCreateMenuBar"));??
??? *(FARPROC*)&FuncSHInitDialog = GetProcAddress(AygShell,T("SHInitDialog"));??
??? *(FARPROC*)&FuncSHFullScreen = GetProcAddress(AygShell,T("SHFullScreen"));??
??? *(FARPROC*)&FuncSHHandleWMActivate = GetProcAddress(AygShell,MAKEINTRESOURCE(84));??
??? *(FARPROC*)&FuncSHHandleWMSettingChange = GetProcAddress(AygShell,MAKEINTRESOURCE(83));??
??? *(FARPROC*)&FuncSHSendBackToFocusWindow = GetProcAddress(AygShell,MAKEINTRESOURCE(97));??
??? }??
??? CoreDLL = LoadLibrary(T("coredll.dll"));??
??? *(FARPROC*)&FuncUnregisterFunc1 = GetProcAddress(CoreDLL,T("UnregisterFunc1"));??
??? *(FARPROC*)&FuncAllKeys = GetProcAddress(CoreDLL,T("AllKeys"));??
??? *(FARPROC*)&FuncSipShowIM = GetProcAddress(CoreDLL,T("SipShowIM"));??
??? *(FARPROC*)&FuncSipGetInfo = GetProcAddress(CoreDLL,T("SipGetInfo"));??
#endif??
??? NodeRegisterClass(&Win);??
??? QueryKey_Init();??
??? OpenFile_Init();??
??? Interface_Init();??
??? PlaylistWin_Init();??
??? PlaylistNewWin_Init();????
}?
void Win_Init()
{
??? HMODULE Module = Context()->LoadModule;
??? if (!Module) Module = GetModuleHandle(NULL);
??? InitCommonControls();
??? WidcommAudio_Init();
??? stprintf_s(WinClassName,TSIZEOF(WinClassName),T("%s_Win"),Context()->ProgramName);
??? memset(&WinClass,0,sizeof(WinClass));
??? WinClass.style?= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
??? WinClass.lpfnWndProc?= Proc;
??? WinClass.cbClsExtra??= 0;
??? WinClass.cbWndExtra??= 0;
??? WinClass.hInstance??= Module;
??? WinClass.hIcon?= LoadIcon?? (GetModuleHandle(NULL), MAKEINTRESOURCE(WIN_ICON));
??? WinClass.hCursor??= WinCursorArrow();
??? WinClass.hbrBackground?=NULL;
??? WinClass.lpszMenuName?= 0;
??? WinClass.lpszClassName?= WinClassName;
??? RegisterClass(&WinClass);
#if 1
??? memset(&DialogClass,0,sizeof(DialogClass));
??? DialogClass.style?= CS_HREDRAW | CS_VREDRAW;
??? DialogClass.lpfnWndProc??= DialogProc;
??? DialogClass.cbClsExtra??= 0;
??? DialogClass.cbWndExtra??= 0;
??? DialogClass.hInstance??= Module;
??? DialogClass.hCursor??=? WinCursorArrow();
#if defined(TARGET_WINCE)
??? DialogClass.hbrBackground?= GetSysColorBrush(COLOR_STATIC);//
#else???
??? DialogClass.hbrBackground?= GetSysColorBrush(COLOR_BTNFACE);
#endif
??? DialogClass.lpszMenuName?= 0;
??? DialogClass.lpszClassName?= T("DialogBase");
??? RegisterClass(&DialogClass);
#endif
?
??? memset(&FontCache,0,sizeof(FontCache));
#if defined(TARGET_WINCE)
??? if (Context()->ProgramId >= 3 && !QueryAdvanced(ADVANCED_OLDSHELL))
??? {
?AygShell = LoadLibrary(T("aygshell.dll"));
?*(FARPROC*)&FuncSHCreateMenuBar = GetProcAddress(AygShell,T("SHCreateMenuBar"));
?*(FARPROC*)&FuncSHInitDialog = GetProcAddress(AygShell,T("SHInitDialog"));
?*(FARPROC*)&FuncSHFullScreen = GetProcAddress(AygShell,T("SHFullScreen"));
?*(FARPROC*)&FuncSHHandleWMActivate = GetProcAddress(AygShell,MAKEINTRESOURCE(84));
?*(FARPROC*)&FuncSHHandleWMSettingChange = GetProcAddress(AygShell,MAKEINTRESOURCE(83));
?*(FARPROC*)&FuncSHSendBackToFocusWindow = GetProcAddress(AygShell,MAKEINTRESOURCE(97));
??? }
??? CoreDLL = LoadLibrary(T("coredll.dll"));
??? *(FARPROC*)&FuncUnregisterFunc1 = GetProcAddress(CoreDLL,T("UnregisterFunc1"));
??? *(FARPROC*)&FuncAllKeys = GetProcAddress(CoreDLL,T("AllKeys"));
??? *(FARPROC*)&FuncSipShowIM = GetProcAddress(CoreDLL,T("SipShowIM"));
??? *(FARPROC*)&FuncSipGetInfo = GetProcAddress(CoreDLL,T("SipGetInfo"));
#endif
??? NodeRegisterClass(&Win);
??? QueryKey_Init();
??? OpenFile_Init();
??? Interface_Init();
??? PlaylistWin_Init();
??? PlaylistNewWin_Init();?
}
?
??? 在此函數中我們需要注意??? WinClass.lpfnWndProc= Proc;?? Proc是消息處理函數名.實際上所有的消息處理都是在static LRESULT CALLBACK Proc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)函數里完成的.
下面對此函數中比較重點的消息作下說明:
??? WM_CREATE消息里主要是建立播放的窗口;
?? WM_PAINT消息里可以貼上UI的背景圖片,這樣使UI看上去比較美觀.貼圖部分程序如下:
?view plaincopy to clipboardprint?
case WM_PAINT:??
??? {??
??? hdc = BeginPaint(Wnd,&Paint);??
?
??? MainBkGnd = LoadBitmap(p->Module,MAKEINTRESOURCE(IDB_MAIN_BKG));??
??? MemDC = CreateCompatibleDC(hdc);??
??? bmpOld = (HBITMAP)SelectObject(MemDC,MainBkGnd);??
??? BitBlt(hdc,0,0,LCD_XSIZE,LCD_YSIZE,MemDC,0,0,SRCCOPY);??
??? SelectObject(MemDC,bmpOld);??
??? DeleteObject(bmpOld);?????
??? DeleteDC(MemDC);??
?
??? EndPaint(Wnd,&Paint);??
?? }??
?? break;?
case WM_PAINT:
??? {
??? hdc = BeginPaint(Wnd,&Paint);
??? MainBkGnd = LoadBitmap(p->Module,MAKEINTRESOURCE(IDB_MAIN_BKG));
??? MemDC = CreateCompatibleDC(hdc);
??? bmpOld = (HBITMAP)SelectObject(MemDC,MainBkGnd);
??? BitBlt(hdc,0,0,LCD_XSIZE,LCD_YSIZE,MemDC,0,0,SRCCOPY);
??? SelectObject(MemDC,bmpOld);
??? DeleteObject(bmpOld);?
??? DeleteDC(MemDC);
??? EndPaint(Wnd,&Paint);
?? }
?? break;
?
???????? WM_COMMAND消息是所有菜單 ,按鈕等點擊后處理的入口點,具體的實現在interface.c里的static int Command(intface* p,int Cmd)函數里來做處理。如播放,暫停,前一首,下一首等.還有其它的一些消息處理在此不做介紹了.
??????? Interface.c的程序結構和win_win32.c? 基本差不多,其中最主要的也是static bool_t Proc(intface* p, int Msg, uint32_t wParam, uint32_t lParam, int* Result)函數對消息的處理.在此函數的WM_CREATE消息里可以建立起各種控件的消息處理.然后分別在各控件的消息處理函數里通過上面貼bitmap圖片的方式來改變控件的外觀.
???????? 其實對于TCPMP程序UI部分的修改,最主要的還是要讀懂源代碼.在讀懂源代碼的基礎上再結合Win32程序的結構來修改界面還是比較容易的.