多媒体技术
题 目:基于MFC设计多媒体播放器
姓 名: 喻 定
班 级: 1201班
指导教师: 王勇智
完成时间:2013年6月13日
摘要
利用MFC在VC++或其它语言环境下设计一个多媒体播放器,进一步熟悉与掌握多媒体编程的基本技术与方法。我的课题是基于MFC应用程序的数据类型转换软件,个人觉得这个小程序很实用,可以实现人们对视听的享受,程序主要是根据编码进行统计,这在以后的程序开发中经常要用到。对于我们日常的生活学习有很大的作用。 随着人们对视听效果的不断提高,多媒体技术已经成为计算机领域发展的重要部分。而且,随着编程平台的不断发展,开发多媒体程序不再是一件繁琐、艰难的工作了,无需具备太多的专业知识就可以编写多媒体程序。本案例就介绍用VC6.0自带的一个ActiveX控件----ActiveMovieControl Object,来建立自己的多媒体播放器。使用VC++6.0的AppWizard、ClassWizard和其中的各种控件可以方便地建立各种应用程序。但是想要实现更高级更复杂的功能,就要借助丰富的ActiveX控件资源。本实验计划使用VC++6.0自带的一个ActiveX控件——ActiveMovieControl Object,来设计多媒体播放器。此多媒体具有一般的播放功能,能播放:*.mp3,*.wma,*.mdi,*.wav,*.avi,*.dat等文件,还有Repeat功能。
关键词:程序设计;媒体播放器软件;MFC;按钮控件
一.开发工具及相关理论
Microsoft公司1998年推出了Visual C++6.0,它是支持Win32平台应用程序(application)、服务(service)和控件(control)开发的可视化编程的集成环境。与VC++5.0的最大不同之处是它的帮助功能更强大,MSDN(Microsoft Developer Networking)为包括VC++6.0在内的所有微软的程序产品提供在线帮助;另外,类的对象的可用成员函数、成员变量及函数的参数类型与个数都能动态显示在屏幕上,用户无须记住那些复杂而又枯燥乏味的函数名及复杂的参数,这无疑使得用VC++编程更加容易。所以VC++6.0可谓是Microsoft公司的王牌产品,编程功能强大而赢得广大程序的偏爱。多媒体技术的概念和应用出现于20世纪80年代初期,经过十余年的发展,随着计算机科学网络的普及和多媒体技术的发展,已成为计算机领域发展的热点技术,针对目前各种媒体格式,如何简单方便的播放各类媒体已成为人们普遍关注的问题,而媒体播放器的开发也变得十分重要。
二.总体设计框架
多媒体播放器播放控制播放模式打开文件播放列表播放暂停停止退出全屏顺序播放随机播放循环播放开始 添加文件 N 播放 播放控制(暂停、循环等) 添加文件 退出 N 退出
三.设计步骤与代码
1、注册控件:在windows“运行”里写入:regsvr32 msdxm.ocx 单击确定。
2、打开VC6.0,在Projects下选择MFC AppWizard(exe),并取名VedioPlayer,然后建立基于对话框的应用程序。最后删除“确定”按钮,保留“取消”按钮。
图3
图4
图5
图6
图7 MFC AppWizard生成的对话框
3、打开Resource View,选择其中的对话框,打开其中的主对话框,去掉对话框上的“确定”按钮,保留“取消”,将Caption改为“退出”。然后再在上面加上几个按钮,ID和Caption分别为 IDC_OPEN,打开; IDC_PLAY,播放; IDC_PAUSE,暂停; IDC_STOP,停止; IDC_CLOSE,关闭; IDC_LOWER,-; IDC_UPPER,+;
IDC_FULLSCREEN,全屏;
。。。。。。
最终如图8所示。
图8 播放器功能界面
4、加入ActiveMovieControl控件。打开Projects->Add to Project->Components and Controls->Registered ActiveX Controls对话框,选择其中的ActiveMovieControl Object, Insert,OK之后,会发现控件面板上多了一项ActiveMovieControl Object,
将它选中,直接放在对话框上,并拖成合适大小。最终效果如图11所示。
图9
图10
图11 最终界面效果
5、为ActiveMovieControl控件设置变量m_ActiveMovie。点中它,按Ctrl+W打开
ClassWizard为它添加变量m_ActiveMovie。
图12为ActiveMovieControl控件设置变量m_ActiveMovie
6、为程序添加消息处理函数。打开ClassWizard,为各个按钮加入消息处理函数。在
VedioPlayerDlg.cpp文件里为各消息处理函数添加代码,部分代码如下: void CVediorDlg::OnClose() {
m_ActiveMovie.CloseWindow();//关闭窗口 }
void CVedioPlayerDlg::OnOpen() { char szFileFilter[]= \"Mp3 File(*.mp3)|*.mp3|\" \"Wma File(*.wma)|*.wma|\" \"Video File(*.dat)|*.dat|\" \"Wave File(*.wav)|*.wav|\" \"AVI File(*.avi)|*.avi|\" \"Movie File(*.mov)|*.mov|\" \"Media File(*.mmm)|*.mmm|\" \"Mid File(*.mid;*,rmi)|*.mid;*.rmi|\" \"MPEG File(*.mpeg)|*.mpeg|\" \"All File(*.*)|*.*||\";//文件类型过滤 CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,szFileFilter); if(dlg.DoModal()==IDOK){ CString PathName=dlg.GetPathName(); PathName.MakeUpper();//这个函数可以将CString字符转化为一个大写的字符串 m_ActiveMovie.SetFileName(PathName); } }
void CVedioPlayerDlg::OnPlay() { m_ActiveMovie.Run();//播放文件
SetTimer(0,20,NULL);//设置定时器
//0:计时器的名称;20:时间间隔,单位是毫秒;NULL:使用OnTimer函数。 }
void CVedioPlayerDlg::OnStop() { m_ActiveMovie.Stop();//停止播放文件 KillTimer(0);//关掉定时器 }
void CVedioPlayerDlg::OnPause() { m_ActiveMovie.Pause();//暂停播放 }
void CVedioPlayerDlg::OnUpper()//增加音量 { long Volume=m_ActiveMovie.GetVolume(); m_ActiveMovie.Pause(); m_ActiveMovie.SetVolume(Volume+100); m_ActiveMovie.Run(); }
void CVedioPlayerDlg::OnLower()//减少音量 { long Volume=m_ActiveMovie.GetVolume(); m_ActiveMovie.Pause(); m_ActiveMovie.SetVolume(Volume-100); m_ActiveMovie.Run(); }
void CVedioPlayerDlg::OnFulscreeen()//全屏播放 {
m_ActiveMovie.Pause();
m_ActiveMovie.SetFullScreenMode(true);
m_ActiveMovie.SetMovieWindowSize(SW_SHOWMAXIMIZED); m_ActiveMovie.Run(); }
这里需要注意的是,下面的函数OnTimer()需通过ClassWizard(Ctrl+W)来添加,不能直接复制:
图13
void CVedioPlayerDlg::OnTimer(UINT nIDEvent) { double CurrentPos=m_ActiveMovie.GetCurrentPosition(); if(CurrentPos==0&&isRepeat)//如果当前是文件的起始位置而且为重复播放状态 m_ActiveMovie.Run(); CDialog::OnTimer(nIDEvent); }
7、为使播放器具有重复播放功能,需在头文件VedioPlayerDlg.h 加入控制变量BOOL isRepeat;类型可为Private。
同时修改OnInitDialog()函数:
BOOL CMediaPlayerDlg::OnInitDialog() {
CDialog::OnInitDialog(); isRepeat=FALSE; …… }
1)打开文件,添加列表
打开文件有两种方法,一是通过主面板上打开按纽,二是通过菜单中文件的
子菜单――打开文件。
添加文件:CButtonST,IDC_ADD,m_add 2)播放控制
播放控制包括:播放(暂停),停止,上一首,下一首,循环,播放进程控制。播放控制也可在菜单中进行控制。 1. 播放:CButtonST, IDC_PLAY, m_play 2. 暂停:CButtonST, IDC_PAUSE, m_pause 3. 停止:CButtonST, IDC_STOP, m_stop
4. 上一首:CButtonST, IDC_PREVIOUS, m_previous 5. 下一首:CButtonST, IDC_NEXT, m_next 6. 循环:CButtonST, IDC_REPEAT, m_repeat
7. 播放进程控制CMySliderControl, IDC_SLIDER, m_slider 3) 对列表的控制
列表的控制包括:添加文件,删除当前所选项,删除全部,在列表中双击播放,列表的隐藏弹出。设置如下:
1. 添加文件:CButtonST, IDC_ADD, m_add
2. 删除当前所选项:CButtonST, IDC_DEL, m_del 3. 删除全部:CButtonST, IDC_DELALL,m_delall
4. 列表中的双击播放:LIST BOX本身的LBN_DBLCLK消息函数
OnDblclkList()
5. 列表的隐藏弹出:CButtonST, IDC_HIDLIST, m_hid 4) 菜单设置
除去上述菜单控制之外,菜单中还包括音量控制,窗口总在最上。
1. 音量控制:IDC_ONVOICE, m_voice 2. 窗口总在最上:ID_W_TOP 最终效果:
四.结 语
王老师授人以鱼不如授人以渔,置身其间,耳濡目染,潜移默化,使我不仅接受了全新的思想观念,树立了宏伟的学术目标,领会了基本的思考方式,他严肃的科学态度,严谨的治学精神,精益求精的工作作风,深深地感染和激励着我,在此谨向王老师致以诚挚的谢意和崇高的敬意。
通过对整个媒体播放器软件的设计,我不仅对播放器的开发了有一定的了解,也认识了平常不常接触到的媒体类型及其发展,使我的编程能力了有了很大的提高。本软件实现在大多数的媒体格式的播放,基本上Windows Media Player能播放的格式,本软件都支持,还有其他一些格式。虽然看起来成果不错,但是一路走过来,也发现了自己还有很多不足。做毕业设计时让我对VC++的运用更是熟练了很多,而且有了系统地设计软件的概念,这对我以后的软件开发打下了很好的基础。但是毕竟只是一个人开发,且技术知识不够,当然不能跟专业的媒
体播放器(比如winamp,windows media player等)相比,本软件还可以继续开发,比如实现CD抓轨,全屏视频播放等。
“书到用时方恨少”,只是到要用的时候,才会觉得这句话的有理。在设计的过程中,时常会碰到问题,往往都是解决完一个,同时又会冒出很多,单单只靠书上查上没有用的,而且有时候,虽然网络资源丰富,但是搜索水平不够,也不能找到有效的解决办法,这种时候,向同学老师请教,就会让自己受益很多。
很遗憾,当时选该课题时,有些想实现的功能,由于知识水平有限,最后都只是放了一个模块而已,都没有具体的扩展。有些更只是想想,但是没有实现。总的说来,此次软件设计,让我深刻认识的不足,以后会更加努力。
五.参考文献
[1] 史元春 多媒体技术教程 , 机械工业出版社
[2] 刘雅琴,夏玉杰. 基于MFC的多媒体播放器的实现[J]. 信息技术, 2009,(03) .
[3] 龚月琴. 多功能媒体播放器的设计与开发[J]. 福建电脑, 2004,(07) .
//===================================================================
DWORD FCLzw::LZW_GIF_Encode (BYTE * DibBuffer, BYTE * OutBuffer, DWORD dwDibWidth, DWORD dwDibHeight,
WORD
wColorBit)
{ //
编码阶段不需要String Table,只需要
m_CurrTableIndex来获知写位数
m_pHash
=
new
WORD
[LZW_MAX_HASH_SIZE] ;
if ((m_pHash == NULL) || (DibBuffer == NULL) || (OutBuffer == NULL))
return 0 ; //
初
始
化
设
置
-----------------------------------------+
m_byMinCode = (wColorBit == 1) ? 2 : wColorBit ; // 1, 4, 8位色(最小码长度),1位色为2
m_LZW_CLEAR = 1 << m_byMinCode ; m_LZW_END = m_LZW_CLEAR + 1 ; m_pOrigin = m_pCurIn = DibBuffer ; m_pCurOut = OutBuffer ; m_byInBit = 8 ;
m_byOutBit = 0 ; // 输出从0开始
m_dwDibWidth = dwDibWidth ;
// DIB
宽 m_dwDibHeight = dwDibHeight ; // DIB高 m_dwCurrPixel = 0 ;
m_dwCurrHeight = 0 ; // 从最顶行开始 m_dwPitch = 4 * ((dwDibWidth * m_byMinCode + 31) / 32) ; //
初
始
化
设
置
完
成
-------------------------------------+
WORD Old ; // 保留字串 BYTE
Pixel ;
// 当前读入字符
this->Encode_InitStringTable () ;
this->Encode_WriteIndex (m_LZW_CLEAR) ; // 首先写clear
Old = this->Encode_GetNextPixel () ; // 编码
while (m_dwCurrHeight < m_dwDibHeight) { Pixel = this->Encode_GetNextPixel () ; if (this->Encode_IsInTable (Old, Pixel))
Old = m_pHash[(Old << 8) | Pixel] ;
// 已在表中, 取出索引, Hash Table中存放的是String Table的Index else { //
不在表中, 把Old + Pixel添
加到String Table中
this->Encode_WriteIndex (Old) ; this->Encode_AddStringToTable (Old, Pixel) ;
Old = Pixel ; if (m_CurrTableIndex
==
LZW_MAX_TABLE_SIZE) // 表填满
{ this->Encode_WriteIndex (Pixel) ;
this->Encode_WriteIndex (m_LZW_CLEAR) ;
this->Encode_InitStringTable () ; Old
=
this->Encode_GetNextPixel () ;
}
}
}
this->Encode_WriteIndex (Old) ;
this->Encode_WriteIndex (m_LZW_END) ; delete[] m_pHash ;
return (m_pCurOut - OutBuffer + 1) ;
}
//=================================================================== 编码:
void CLZW_DemoDlg::OnEncode() { TCHAR
SrcName[MAX_PATH] ; // 原文件
名 TCHAR DestName[MAX_PATH] ; // 压缩文件的name
TCHAR drive[_MAX_DRIVE] ; TCHAR dir[_MAX_DIR] ; TCHAR fname[_MAX_FNAME] ; TCHAR
ext[_MAX_EXT] ;
m_SelFile.GetWindowText (SrcName,
MAX_PATH) ;
::_tsplitpath (SrcName, drive, dir, fname, ext) ; //
压缩文件名
::_tmakepath (DestName, drive, dir, fname,
TEXT(\".foo\")) ; //
文件是否存在
WIN32_FIND_DATA tempFileInfo ; HANDLE hFind
=
::FindFirstFile
(DestName, &tempFileInfo) ;
bool bExist
=
(::GetLastError()
!=
ERROR_FILE_NOT_FOUND) ;
::FindClose (hFind) ; if (bExist) if (::MessageBox (NULL, TEXT(\"目标文件已经存在,是否覆盖?\"),
TEXT(\"文件存
在\"), MB_YESNO) == IDYES)
{ ::SetFileAttributes (DestName,
FILE_ATTRIBUTE_ARCHIVE) ;
::DeleteFile (DestName) ;
} else
return ;
// 获得原文件信息
hFind
=
::FindFirstFile
(SrcName,
&tempFileInfo) ;
::FindClose (hFind) ; FCMemMapFile flSrc, flDest ;
BYTE
*
pDest
=
(BYTE
*)
flDest.CreateFile
(DestName,
tempFileInfo.nFileSizeLow * 2 + 2048) ; ZeroMemory (pDest,
tempFileInfo.nFileSizeLow * 2 + 2048) ;
BYTE * pSrc = (BYTE *) flSrc.ReadFile (SrcName) ;
BYTE * pCurr = pDest ;
if ((pDest == NULL) || (pSrc == NULL))
{ ::MessageBox (NULL, TEXT(\"打开文件失败\"), TEXT(\"错误\"), MB_OK|MB_ICONSTOP) ;
return ;
}
//
写头 ::lstrcpyA ((char*)pDest, \"foo2002a\") ; pCurr += 8 ;
* (WORD *) pCurr = 16 ; pCurr += 2 ;
* (WORD *) pCurr = m_iMethod ; pCurr += 2 ; *
(DWORD
*)
pCurr
=
tempFileInfo.nFileSizeLow ;
pCurr += 4 ;
pCurr += 8 ; // 文件名
::lstrcat (fname, ext) ;
int ii=::lstrlen (fname); * (WORD *) pCurr = ::lstrlen (fname) ; ::lstrcpy ((char *)pCurr+2, fname) ; pCurr += * (WORD *) pCurr + 2 ;
//
压缩 DWORD dwCounter = ::GetTickCount
() ;
if (m_iMethod == 0) { FCLzw
fLzw ;
pCurr += fLzw.LZW_Encode (pSrc,
tempFileInfo.nFileSizeLow, pCurr) ;
} else { pCurr = ::RLE_PCX_EncodeLine (pSrc, 8, tempFileInfo.nFileSizeLow, pCurr) ;
}
dwCounter = ::GetTickCount () - dwCounter ; * (DWORD *) &pDest[16] = dwCounter ; flDest.SetSize (pCurr - pDest) ; //
设置
TCHAR
out[50] ;
::fooFormatCommaNumber (dwCounter, out, 50) ; this->GetDlgItem
(IDS_ENCODETIME)->SetWindowText (out) ;
::fooFormatCommaNumber (pCurr - pDest, out, 50) ; this->GetDlgItem
(IDS_NEWSIZE)->SetWindowText (out) ; ::fooFormatCommaNumber ((pCurr
-
pDest)*100/tempFileInfo.nFileSizeLow, out, 50) ; this->GetDlgItem
(IDS_RATE)->SetWindowText (out) ; }
解码:
void CLZW_DemoDlg::OnDecode() { TCHAR
SrcName[MAX_PATH] ; // 压缩文
件名 m_SelFile.GetWindowText
(SrcName,
MAX_PATH) ;
FCMemMapFile flCurr ; BYTE
* pSrc = (BYTE *) flCurr.ReadFile
(SrcName) ;
if (pSrc == NULL) {
::MessageBox (NULL, TEXT(\"打开文件
失败\"), TEXT(\"错误\"), MB_OK|MB_ICONSTOP) ; return ;
} //
获得原文件名
TCHAR
szOldName[MAX_PATH] ;
ZeroMemory (szOldName, sizeof(TCHAR) * MAX_PATH) ; CopyMemory (szOldName, &pSrc[26], *
(WORD *) &pSrc[24]) ;
//
文件是否存在
WIN32_FIND_DATA tempFileInfo ; HANDLE hFind
=
::FindFirstFile
(szOldName, &tempFileInfo) ;
bool bExist
=
(::GetLastError()
!=
ERROR_FILE_NOT_FOUND) ; ::FindClose (hFind) ; if (bExist)
if (::MessageBox (NULL, TEXT(\"目标文件已经存在,是否覆盖?\"),
TEXT(\"文件存
在\"), MB_YESNO) == IDYES)
{ ::SetFileAttributes
(szOldName,
FILE_ATTRIBUTE_ARCHIVE) ;
::DeleteFile (szOldName) ;
} else
return ;
//
创建文件
FCMemMapFile flOld ;
DWORD
dwOldSize
=
* (DWORD *) &pSrc[12] ; BYTE
*
pDest
=
(BYTE
*)
flOld.CreateFile (szOldName, dwOldSize) ;
ZeroMemory (pDest, dwOldSize) ; if (pDest == NULL) { ::MessageBox (NULL, TEXT(\"打开文件失败\"), TEXT(\"错误\"), MB_OK|MB_ICONSTOP) ;
return ;
}
// 解码
DWORD dwCounter = ::GetTickCount
() ; if (* (WORD *) &pSrc[10] == 0) { pSrc += 26 + (* (WORD *) &pSrc[24]) ; FCLzw
lzw ;
lzw.LZW_Decode (pSrc, pDest) ;
} else {
pSrc += 26 + (* (WORD *) &pSrc[24]) ;
::RLE_PCX_DecodeLine (pSrc, 8,
dwOldSize, pDest) ;
}
dwCounter = ::GetTickCount () - dwCounter ; flOld.SetSize (dwOldSize) ; TCHAR
out[50] ;
::fooFormatCommaNumber (dwCounter, out,
50) ; this->GetDlgItem
(IDS_DECODETIME)->SetWindowText (out) ;
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- yrrf.cn 版权所有 赣ICP备2024042794号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务