您的位置:首页 > 理论基础 > 计算机网络

TCPMP界面插件模块分析

2010-11-03 16:06 218 查看

TCPMP界面插件模块分析

一:界面模块主要节点结构

NODE (根节点)
├─WIN_CLASS[WIN_] (界面抽象模块,负责全局窗口类注册)
│ ├─QUERYKEY_ID[QKEY] (未知模块)
│ ├─OPENFILE_ID [OPEN] (打开文件窗口的全局模块)
│ ├─INTERFACE_ID [INTF] (主播放窗口的全局设置模块,负责播控快捷键)
│ ├─PLAYLIST_ID [PLST] (播放列表窗口的全局模块)
│ ├─ABOUT_ID [ABOU] (关于窗口模块)
│ ├─BENCHRESULT_ID [BENC] (平台测试结果窗口模块)
│ ├─MEDIAINFO_ID [MEDI] (媒体信息窗口模块)
│ ├─SETTINGS_ID [SETU] (设置窗口模块)

二:界面模块消息分发流程

WinMain[player.exe]
├─Main [interface.plg]
│ ├─Context_Init
│ ├─WinPopupClass(INTERFACE_ID)
│ │ ├─Popup
│ │ │ ├─CreateWindowEx
│ │ │ ├─while (GetMessage(&Msg, NULL, 0, 0)) HandleMessage(p,&Msg);
│ │ │ │ ├─优先处理自定义Application Window消息以及按键消息
│ │ │ │ ├─调用DispatchMessage分发消息给窗口注册处理函数Proc
│ │ │ │ │ ├─Proc[win_win32.c]
│ │ │ │ │ │ ├─处理常见窗口消息
│ │ │ │ │ │ ├─调用窗口节点实例的处理函数Proc
│ │ │ │ │ │ │ ├─Proc[interface.c]
│ ├─Context_Done

三:界面模块创建流程分析

1. TCPMP创建主界面的入口函数位于interface.plg插件的stdafx.c源文件中,具体为Main函数调用的WinPopupClass函数。在正确调用Context_Init之后,TCPMP节点树结构已经创建出来,其中界面部分的节点结构是通过调用Plugins_Init()进而调用interface.plg的插件注册函数DLLRegister()完成。
DLLEXPORT void Main(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine)
{
SAFE_BEGIN
if (Context_Init(Name,Version,Id,CmdLine,NULL))
{
WaitEnd();
WinPopupClass(INTERFACE_ID,NULL);
Context_Done();
}
SAFE_END
}

2. WinPopupClass函数调用Popup函数,在Popup函数中将调用CreateWindowEx来创建具体的主窗口,然后进入消息处理循环体。
Wnd=CreateWindowEx(ExStyle,WinClass.lpszClassName,LangStr(p->Node.Class,NODE_NAME),Style,x,y,Width,Height, Parent?Parent->Wnd:NULL,NULL,WinClass.hInstance,p);
……
while (p->Wnd && GetMessage(&Msg, NULL, 0, 0)) HandleMessage(p,&Msg);

3. 主窗口收到的第一个消息是WM_CREATE,这将进入到主窗口的注册处理函数Proc[win_win32.c]。
//触发MSG_PREPARE消息处理流程。主播放窗口的MSG_PREPARE流程为空。
if (p->Proc) p->Proc(p,MSG_PREPARE,0,0,&Result);
//创建窗口底部的菜单栏,调用WinCE API函数SHCreateMenuBar
CreateToolBar(p);
//调用interface_id的Proc过程。
if (p->Proc && p->Proc(p,Msg,wParam,lParam,&Result)) return Result;

4. 在interface.c文件的Proc函数WM_CREATE中,将完成一系列主要的初始化操作。
//设置窗口标题为程序名称
WinTitle(&p->Win,Context()->ProgramName);
//记录当前的播放控制模块指针
p->Player = (player*)Context()->Player;
//设置播放过程中的播放消息回调函数
Notify.This = p;
Notify.Func = (notifyfunc)PlayerNotify;
if (p->Player) p->Player->Set(p->Player,PLAYER_NOTIFY,&Notify,sizeof(Notify));
//设置播放过程中的播放出错回调函数
Context()->Error.This = p;
Context()->Error.Func = ErrorNotify;
//换肤功能,目前未实现
if (IsAutoRun(p,Context()->CmdLine))
DefaultSkin(p);
SkinLoad(p->Skin,p->Win.Wnd,p->SkinPath);
//创建播放操作按钮(播放/暂停,停止,全屏,静音)
if (!p->Skin[0].Valid)
CreateButtons(p);
UpdateSkin(p,0);
//创建播放进度条
UpdateTrackBar(p,0);
//创建播放标题栏
UpdateTitleBar(p,0);
//创建音量进度条
CreateVolumeTrack(p);
CreateDeviceMenu(p);
//主窗口绘制布局的最重要函数
Resize(p);

//发送MSG_INIT消息给自身,以进行后续的初始化工作。
// first finish window creation and postpone loading playlist and etc...
PostMessage(p->Win.Wnd,MSG_INIT,0,0);

5. 在interface.c文件的Proc函数MSG_INIT中,将完成一系列后续的初始化操作。
//设置主播放窗口为前置窗口
SetForegroundWindow(p->Win.Wnd);
//检查程序注册表项HKEY_LOCAL_MACHINE/Software/TCPMP/9216是否存在。如果存在,//则可认为上次程序为非正常退出。注:REG_INITING值为0x2400,十进制值即为9216。
if (Context()->CmdLine[0] || NodeRegLoadValue(0,REG_INITING,&i,sizeof(i),TYPE_INT))
{
b = 1; // last time crashed -> discard saved playlist
p->Player->Set(p->Player,PLAYER_DISCARDLIST,&b,sizeof(b));
}
//写入程序注册表项HKEY_LOCAL_MACHINE/Software/TCPMP/9216,标识程序正在启动中。//然后启动定时器三秒后删除此项,在程序退出前调用BeforeExit()函数也会负责删除此项。
#ifdef NDEBUG
i = 1;
NodeRegSaveValue(0,REG_INITING,&i,sizeof(int),TYPE_INT);
SetTimer(p->Win.Wnd,TIMER_INITING,INITING_CYCLE,NULL);
#endif

//设置视频渲染窗口为当前窗口
Context_Wnd(p->Win.Wnd);
//检查程序是否携带参数运行,并且检查PLAYER_PLAYATOPEN_FULL参数。
//如果为0则表示不启用全屏播放。然后调用ProcessCmdLine进行后续参数处理。
if (Context()->CmdLine[0])
{
p->Player->Get(p->Player,PLAYER_PLAYATOPEN_FULL,&b,sizeof(b));
if (!b)
{
UpdateWindow(p->Win.Wnd); //http connection may take a while
if (p->Win.WndTB) UpdateWindow(p->Win.WndTB);
}
ProcessCmdLine(p,Context()->CmdLine);
}

6. 在interface.c文件的ProcessCmdLine函数中,将完成程序参数的解析流程。
//检查参数是否为”-autorun”,如果为自动运行,则会枚举用户手机SD卡中的所有可识别的音视频介质,加入到播放列表中。否则将把参数作为普通的播放URL。
if (IsAutoRun(p,CmdLine))
//把参数作为普通的播放URL,加入到播放列表中
n = 1;
p->Player->Set(p->Player,PLAYER_LIST_COUNT,&n,sizeof(n));
PlayerAdd(p->Player,0,URL,NULL);
//指定开始播放列表中的第1个介质文件。
n = 0;
p->Player->Set(p->Player,PLAYER_LIST_CURRIDX,&n,sizeof(n));

四:界面布局分析

界面布局的主要函数为interface.c文件中的Resize函数。
static void Resize(intface* p)
{
//检查屏幕显示模式是否变化(横屏/竖屏)
bool_t Rotated = IsOrientationChanged();
if (Rotated)
{
//设置开始旋转
p->Player->Set(p->Player,PLAYER_ROTATEBEGIN,NULL,0);
//重置视频输出
p->Player->Set(p->Player,PLAYER_RESETVIDEO,NULL,0);
if (!p->Win.ToolBarHeight)
{
p->VolResizeNeeded2 = 1; // toolbar position may change later
if (p->WndVolBack && IsWindowVisible(p->WndVolBack))
{
ShowVol(p,0);
p->VolResizeNeeded2 = 2;
}
}
else
p->VolResizeNeeded = 1;

if (p->Win.FullScreen)
PostMessage(p->Win.Wnd,MSG_PLAYER,PLAYER_FULLSCREEN,0);
}

p->Offset.x = 0;
p->Offset.y = 0;
ClientToScreen(p->Win.Wnd,&p->Offset);

if (!p->Win.FullScreen)
{
#if !defined(TARGET_WINCE) && defined(MAXIMIZE_FULLSCREEN)
WINDOWPLACEMENT Place;
Place.length = sizeof(Place);
GetWindowPlacement(p->Win.Wnd,&Place);
if (Place.showCmd != SW_MAXIMIZE)
#endif
{
GetClientRect(p->Win.Wnd,&r);

if (r.right != p->ClientRect.right || r.bottom != p->ClientRect.bottom)
{
bool_t Skin = p->Skin[p->SkinNo].Valid;
int TrackThumb;
int TrackHeight = 0;
p->TitleHeight = 0;
p->ClientRect = r;

r.top += p->Win.ToolBarHeight;

if (p->WndTrack)
{
TrackHeight = WinUnitToPixelY(&p->Win,TRACKHEIGHT);
r.bottom -= TrackHeight;
MoveWindow(p->WndTrack,r.left,r.bottom,r.right,TrackHeight,TRUE);

TrackThumb = WinUnitToPixelY(&p->Win,TRACKTHUMB);
if (p->TrackThumb != TrackThumb)
{
p->TrackThumb = TrackThumb; // avoid calling this regulary because it shows the trackbar
SendMessage(p->WndTrack, TBM_SETTHUMBLENGTH,TrackThumb,0);
}
}

if (p->WndTitle)
{
p->TitleTimeWidth = 0;
p->TitleFontSize = TITLEFONT;
p->TitleFont = WinFont(&p->Win,&p->TitleFontSize,0);
p->TitleHeight = WinUnitToPixelY(&p->Win,TITLEHEIGHT);
p->TitleBorder = WinUnitToPixelX(&p->Win,3);
p->TitleWidth = r.right - r.left;

if (Skin)
{
skinitem* i = &p->Skin[p->SkinNo].Item[SKIN_TITLE];
p->TitleWidth = i->Rect.Width;
p->TitleHeight = i->Rect.Height;
MoveWindow(p->WndTitle,r.left + i->Rect.x,r.top + i->Rect.y,p->TitleWidth,p->TitleHeight,TRUE);
}
else
{
if (p->Win.ToolBarHeight)
{
r.bottom -= p->TitleHeight;
MoveWindow(p->WndTitle,r.left,r.bottom,p->TitleWidth,p->TitleHeight,TRUE);
}
else
{
MoveWindow(p->WndTitle,r.left,r.top,p->TitleWidth,p->TitleHeight,TRUE);
r.top += p->TitleHeight;
}
}

p->TitleTop = (p->TitleHeight-WinUnitToPixelY(&p->Win,p->TitleFontSize))/2;
}

p->SkinArea.x = r.left;
p->SkinArea.y = r.top;
p->SkinArea.Width = r.right - r.left;
p->SkinArea.Height = r.bottom - r.top;/

if (!Skin)
p->SkinViewport = p->SkinArea;
else
{
p->SkinViewport = p->Skin[p->SkinNo].Item[SKIN_VIEWPORT].Rect;
p->SkinViewport.x += p->SkinArea.x;
p->SkinViewport.y += p->SkinArea.y;
}

if (p->Win.ToolBarHeight && !p->VolResizeNeeded)
ResizeVolume(p);
}

p->Viewport = p->SkinViewport;
p->Viewport.x += p->Offset.x;
p->Viewport.y += p->Offset.y;
p->Player->Set(p->Player,PLAYER_SKIN_VIEWPORT,&p->Viewport,sizeof(rect));
p->Player->Set(p->Player,PLAYER_UPDATEVIDEO,NULL,0);

if (p->VolResizeNeeded)
{
ResizeVolume(p);
p->VolResizeNeeded = 0;
}
}
}
else
{
GetClientRect(p->Win.Wnd,&r);
p->Viewport.x = r.left + p->Offset.x;
p->Viewport.y = r.top + p->Offset.y;
p->Viewport.Width = r.right - r.left;
p->Viewport.Height = r.bottom - r.top;
}

if (Rotated)
p->Player->Set(p->Player,PLAYER_ROTATEEND,NULL,0);

DEBUG_MSG4(DEBUG_VIDEO,T("Resize end %d,%d,%d,%d"),p->Viewport.x,p->Viewport.y,p->Viewport.Width,p->Viewport.Height);
}

int GetOrientation()
{
#if defined(TARGET_WINCE)
if (Orientation < 0)
{
HKEY Key;
context* p;
char Buffer[256];
DEVMODE* Mode = (DEVMODE*)Buffer;

Mode->dmSize = 192;
Mode->dmFields = DM_DISPLAYQUERYORIENTATION;

if (QueryPlatform(PLATFORM_VER) >= 421 && // we don't trust this method on pre wm2003se systems
FuncChangeDisplaySettingsEx &&
FuncChangeDisplaySettingsEx(NULL, Mode, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL)
{
Mode->dmFields = DM_DISPLAYORIENTATION;
FuncChangeDisplaySettingsEx(NULL, Mode, NULL, CDS_TEST, NULL);

switch ((&Mode->dmDisplayFrequency)[1]) //(Mode->dmDisplayOrientation)
{
case DMDO_0: Orientation = 0; break;
case DMDO_90: Orientation = DIR_SWAPXY | DIR_MIRRORUPDOWN; break;
case DMDO_270: Orientation = DIR_SWAPXY | DIR_MIRRORLEFTRIGHT; break;
case DMDO_180: Orientation = DIR_MIRRORUPDOWN | DIR_MIRRORLEFTRIGHT; break;
}
}

p = Context();
if (Orientation < 0 && p->HwOrientation)
Orientation = p->HwOrientation(p->HwOrientationContext);

if (Orientation < 0 && RegOpenKeyEx(HKEY_LOCAL_MACHINE, T("System//GDI//ROTATION"), 0, KEY_READ, &Key) == ERROR_SUCCESS)
{
DWORD Value;
DWORD RegSize = sizeof(Value);
DWORD RegType;

if (RegQueryValueEx(Key, T("Angle"), 0, &RegType, (LPBYTE) &Value, &RegSize) == ERROR_SUCCESS)
switch (Value)
{
case 0: Orientation = 0; break;
case 90: Orientation = DIR_SWAPXY | DIR_MIRRORUPDOWN; break;
case 270: Orientation = DIR_SWAPXY | DIR_MIRRORLEFTRIGHT; break;
case 180: Orientation = DIR_MIRRORUPDOWN | DIR_MIRRORLEFTRIGHT; break;
}

RegCloseKey(Key);
}

if (Orientation < 0)
Orientation = 0;
}
#else
Orientation = 0;
#endif
return Orientation;
}

五:界面模块接口交互分析

1. 控制模块通知界面模块
控制模块通知界面模块的回调函数为interface.c文件中的PlayerNotify函数。该函数将通过自定义Windows消息MSG_PLAYER将控制处理权交给主界面窗口。
PostMessage(p->Win.Wnd,MSG_PLAYER,PLAYER_PERCENT,Value);

case MSG_PLAYER:
switch (wParam)
{
case PLAYER_EXIT_AT_END:
if (p->CmdLineMode)
PostMessage(p->Win.Wnd,WM_COMMAND,IF_FILE_EXIT,0);
break;
case PLAYER_PERCENT:
UpdatePosition(p);
break;
case PLAYER_LOADMODE:
if (p->Wait != (bool_t)lParam)
{
p->Wait = lParam;
if (p->Wait)
{
if (WaitBegin())
UpdateClipping(p,1,0);
}
else
WaitEnd();
}
break;
case PLAYER_FULLSCREEN:
p->ForceFullScreen = lParam;
if (GetForegroundWindow() == p->Win.Wnd || !lParam)
{
ToggleFullScreen(p,lParam,1);
p->ForceFullScreen = 0;
}
break;
case PLAYER_PLAY:
RefreshButton(p,PLAYER_PLAY,&p->Play,IF_PLAY,0,1);
RefreshButton(p,PLAYER_FFWD,&p->FFwd,IF_FASTFORWARD,4,1);
UpdateSleepTimer(p);
break;
case PLAYER_BENCHMARK:
if (p->Bench && p->Player->Get(p->Player,PLAYER_BENCHMARK,&t,sizeof(tick_t))==ERR_NONE)
{
p->Bench = 0;
ToggleFullScreen(p,0,0);
WaitEnd();
if (p->WndTrack)
SetTrackThumb(p->WndTrack,1);
UpdateWindow(p->Win.Wnd);
WinPopupClass(BENCHRESULT_ID,&p->Win);
}
break;
}

2. 界面模块发送指令给控制模块
界面模块主要通过使用interface的Player指针进行操作。具体操作主要参考interface.c中的Command函数。
static int Command(intface* p,int Cmd)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: