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程序的结构来修改界面还是比较容易的.

tcpmp 编译 源代码分析相关推荐

  1. 区块链教程Fabric1.0源代码分析scc(系统链码)

    区块链教程Fabric1.0源代码分析scc(系统链码),2018年下半年,区块链行业正逐渐褪去发展之初的浮躁.回归理性,表面上看相关人才需求与身价似乎正在回落.但事实上,正是初期泡沫的渐退,让人们更 ...

  2. CASSINI源代码分析

    CASSINI源代码分析 2004-11-10 http://blog.csdn.net/shanhe/ 为什么要分析CASSINI? Cassini(卡西尼)是asp.net上的一个开源项目.主要给 ...

  3. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBatis data mapper framework makes it easier to use a re ...

  4. 56. Netty源代码分析-服务器初始化 NioEventLoopGroup实例化

    一. 代码下载 Netty代码下载和编译参考前一篇Netty文章 https://blog.51cto.com/483181/2112163 二. 服务器代码分析 2.1 服务器代码编写 一般Nett ...

  5. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...

  6. 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】

    转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...

  7. wireshark源代码分析

    各位亲,不是我不想回复你们的问题.是我也不了解.不能误导.希望大家相互帮助.看看能否帮那些提问的小盆友们回复一下呢? 这些都是转载的,如果实在没有办法,可以打开链接到原作者哪里去提问试试看... 经过 ...

  8. Linux内核源代码分析-目录

    第一部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 ...

  9. 编译时如何看到每个文件的编译选项_导出 Clang 可视化编译耗时分析报告 —— ftimetrace 的使用...

    前言 笔者最近加入了新的团队,开始负责编译打包相关工作,因而开始学习优化编译时间相关技术.讲真,蛮开心的,每天都有挑战,同时每天都有收获,天天都在涨姿势,所以想记录下来并分享出来,也方便以后自己需要时 ...

最新文章

  1. js实现创建二叉树+先序遍历
  2. CPropertyPage::OnSetActive()和OnKillActive()函数:属性页切换时的处理函数
  3. Python编码错误的解决办法SyntaxError: Non-ASCII character ‘\xe5‘ in file
  4. 牛客多校3 - Sort the Strings Revision(笛卡尔树+分治)
  5. 分享Web应用运行的细节问题:预编译提高网站性能、跟踪用户习惯和解决线程同步...
  6. 第一个python小游戏
  7. android 通讯录 备份,安卓Android手机通讯录怎么备份 卓联系人备份 卓手机联系人导出...
  8. axure web组件下载_Element - 饿了么团队出品的神级桌面 UI 组件库
  9. Ruby on Rails Exception:Routing Error
  10. linux修正磁盘错误,找到了linux分区顺序错乱修复方法
  11. 解决Jquery Kendo.xxx is not a function 的方法
  12. lisp princ详解_LISP-输入和输出
  13. 创新Or模仿?浅谈“山寨”圈里的云计算
  14. 【工作技巧】WinRAR去除广告
  15. linux tomcat war解压吗,Tomcat 何时解压war包
  16. VUE 组件 有数据不渲染 v-for
  17. c语言编写排列组合程序,C语言实现排列组合
  18. 大三学生HTML期末作业,网页制作作业——HTML+CSS+JavaScript饮品饮料茶(7页)
  19. jmu-python-涨工资
  20. Mysql从入门到入魔——8. 视图、存储过程、事务处理

热门文章

  1. 38备忘录模式(Memento Pattern)
  2. QSplitter设置宽高比例的正确方法
  3. Win10上VMware的问题汇总
  4. mybatis配置文件加注释报错怎么办?改一笔就能帮你解决
  5. 关于报错:'nvidia-smi' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  6. 【牛客 - 318G】LLLYYY的数字思维 与【牛客 - 289J】这是一个沙雕题II(贪心构造)
  7. 【HDU - 1069】Monkey and Banana (最长下降子序列 + 贪心,最长上升子序列类问题)
  8. 【HDU - 2087】 剪花布条(直接模拟 or KMP)
  9. php 解析mib文件,Mib库解析
  10. java mac 转换 整形_JAVA的整型与字符串相互转换