作者:董维春
CSDN专栏作家(ch_builder),CB论坛ActiveX/COM/DCOM栏目斑主,有多篇文章发表在《程序员》杂志上。
BLOG:http://blog.csdn.net/ch_builder
研究方向:小型数据库开发、报表开发、Office办公系统应用
邮箱:ch_builder@163.com
QQ:116001522
注:本文已发表在《编程技巧与维护》2005年第一期中,应广大朋友的要求,发表于此!
你若想转载或摘抄,可与作者联系,谢谢!
用CB编写网络电视
作者:董维春
在网上收看电视、收听广播,其实并不是一件很难的事情,网上的视频或音频流多数是用Windows Media Player、RealPlayer,两者所占比例差不多。但这些节目也不是分布在一起的,要是一个一个的去找也不太方便,而随着在线流媒体数量的增多,各种版本的网络电视如雨后春笋,他们多数要注册,让不就是广告一堆。女朋友闹着也要拥有一个属于自己的类似软件,我就动手做了一个,觉得还不错,对于是否写出源码的分析,我也想过好久。其实写出来并不难,但人可能就是这样,当掌握了一点书本上根本就没有的东西或自己的心血就要成为别人的时候难免有些不安。这可能也是我们中国软件业的一个最大问题,人们总是很少交流,总让别人公开源码,而自己呢?那日与欧阳璄老弟说到此事时,他的想法与我惊人的相似,为什么不公开让大家互相学习,在学习和总结中共同前进呢?于是有了此文。
为了便于说明,先把程序中的两个窗体给出(还有一个关于窗体,这里就不给出了),当我们选择收音时,视频窗口是不会出现的,只有播放电视时才会出现J
(图1,控制面板frmContronl)
(图2,视频窗口frmWindow)
一、TTWindowsMediaPlayer与TRealAudio组件的基础知识
这个程序的核心中是对TWindowsMediaPlayer与TRealAudio这两个ActiveX组件的应用。我们有必要先了解一下这两个组件。
先看一下在你的ActiveX页中(当然也可能在你自定义的其它组件页中,系统默认的通常是ActiveX页)有没有TWindowsMediaPlayer与TRealAudio这两个ActiveX组件,如果没有,那你要自行安装。关于一些这方面的一些基础知识可参见我写的《在BCB中轻松使用ActiveX控件》,网址:
http://www.csdn.net/develop/Read_Article.asp?Id=19032
相信你看过后会对这个两个组件有所了解。这里我们再介绍一些程序中能用到的、鲜为人知的属性及方法。
1)TWindowsMediaPlayer组件的属性及方法:
TWindowsmediaplayer中的一些特殊属性我们可以调用Windows Media Player属性框来完成。只要我们双击TWindowsmediaplayer这个组件,就会弹出Windows Media Player属性框。若我们只想要一个显示窗口,那只在常规的控件布局中我们选择None,就可以了。选择自动起动时,只要给出URL,TWindowsmediaplayer就可以自动开始播放了。
这个对话框可以实现很多功能,我们一定应该花些时间熟悉她,这并不太难,只要用几次就应该差不多了。
URL属性:文件名或网上链接地址;
Status属性:状态栏信息;
Close():关闭正在播放的内容。
TWindowsmediaplayer状态表:
OpenState属性值 | PlayState属性值 | WMP当时状态 |
12 | 9 | 准备开始 |
10 | 9 | 正在连接 |
13 | 6 | 正在缓冲 |
13 | 3 | 正在播放 |
13 | 6 | 准备就绪(播放失败) |
13 | 1 | 停止播放 |
2)TRealAudio组件属性及方法:
Controls属性:IMAGEWINDOW,CONTROLPANEL,STATUSBAR(其中:IMAGEWINDOW、CONTROLPANEL、STATUSBAR分别表示显示视频播放窗口、控制条、状态条),在这个程序中我们只给出IMAGEWINDOW一个值,也就是我们只得到视频窗口;
Source属性:文件名或网上链接地址;
DoPlay():播放;
DoStop():停止播放;
GetLastMessage():建立连接的信息;
GetBandWidthAverage():当前播放的平均速率,单位比特/秒;
GetPlayState()属性值含义:
GetPlayState()属性值 | TRealAudio当时状态 |
0 | 停止 |
1 | 正在连接 |
2 | 正在缓冲 |
3 | 正在播出 |
4 | 暂停 |
二、程序的初步实现
1)从数据库中得到相关信息
程序的实现并不困难,首先从数据库中取得要播放的电视(或广播)的相关的信息,很显示我们要从库中得到足够的数据才能更有利我们下面的程序的设计。由于流媒体的格式不同,我们有必要在我们的库中写出一个流媒体格式的字段,这样更有利用程序判断是到底应该选用那个组件。同样,对于视频窗口,我们没有必要在听广播时或无播放时放在桌面上,只有在播放视频时才调用这来,至于什么时候调用我们也完全在数据库中给出是否调用视频窗口这个字段。当然在数据库中一些最基本的字段,如电视台或广播台的基本信息与流地址是必须给出的。当然这些内容都是从库中提取出来的,下面是有关数据库提取的代码:
void __fastcall TfrmControl::sbnNextClick(TObject *Sender)
{
if(sbnTV->Down==true||sbnRD->Down==true)//判断数据库是否连接,原程序中我用了两个数据联接组件,且 sbnTV与sbnRD是选择那一个连接的按钮,它们均是位图按钮,这里充分利用了他们的Down属性,至于联接程序你可以视具体情况自行编写,其中sbnTV控制电视节目的连接,sbnRD控制广播节目的连接
{
if(sbnTV->Down==true)//确定qryTV是否连接
{
if(qryTV->Eof!=true)//因为向下选,防止到最后一个还向后选
{
qryTV->Next();//选择下一条记录
}
else
{
qryTV->First();//回到第一条记录
}
//从数据库中取出相关数据,此处所用的变量均为全局变量,String型
lblPlaying->Caption=qryTV->FieldValues["Name"];//所选节目名称
chname=qryTV->FieldValues["Name"];//所选节目名称
chtype=qryTV->FieldValues["Type"];//所选节目,所用的播放格式
chaddress=qryTV->FieldValues["Address"];//所选节目的流地址
}
else
{
if(qryRD->Eof!=true)
{
qryRD->Next();
}
else
{
qryRD->First();
}
lblPlaying->Caption=qryRD->FieldValues["Name"];
chname=qryRD->FieldValues["Name"];
chtype=qryRD->FieldValues["Type"];
chaddress=qryRD->FieldValues["Address"];
}
sbnNext->Down=false;
}
else//若数据库没连接,则提示连接
{
ShowMessage("请先选择右侧的电视或收音键");
}
}
//---------------------------------------------------------------------------
2)播放功能的实现
TWindowsMediaPlayer与TRealAudio两个组件是应用是这个程序的核心部分,播放功能的实现就是充分利用他们的结果。这两个组件都放在视频窗口中,如图2,黑色的框体就是他们了,当然他们是完全重合在一起的,我想这并不难,而关于他们的基本设计前面已经说过,这里重点在代码的实现上:
void __fastcall TfrmControl::sbnPlayClick(TObject *Sender)
{
if(chtype=="rtsp")//判断播放格式
{
//若类型为rtsp,则调用realplayer播放器,mediaplayer则关闭、隐藏
frmWindow->wmpChtv->close();
frmWindow->wmpChtv->Hide();
frmWindow->raChtv->Show();//启用RealAudio
frmWindow->raChtv->Source=chaddress;//给定流地址
frmWindow->raChtv->DoPlay();//播放
lblStatus->Caption=frmWindow->raChtv->GetLastMessage();//获取连接信息
if(frmWindow->raChtv->GetPlayState()==3)//如果正在播放,则显示播放速率
{
lblStatus->Caption="当前播放为"+frmWindow->raChtv->GetBandwidthAverage()+"比特/秒";
}
lblPlay->Caption=lblPlaying->Caption;//在面板中显示播放的节目名
ra=true;//指出这里选用的是realplayer播放器,ra是全局变量,bool型,这里的目的是告知停止时知道当前播放的格式,采取相应的停止收段
}
else
{
frmWindow->raChtv->DoStop();//停止realplayer播放
frmWindow->raChtv->Hide();//把realplayer隐藏
frmWindow->wmpChtv->Show();//调用mediaplayer
frmWindow->wmpChtv->URL=chaddress;//给出流地址
lblPlay->Caption=lblPlaying->Caption;
ra=false;//指出这里选用的不是realplayer播放器
}
if(sbnTV->Down==true)//判断是否调出视窗窗体
{
frmWindow->Show();
}
else
{
frmWindow->Hide();
}
……//原程序此处是取得声音的波值
sbnPlay->Down=true;
sbnStop->Down=false;
}
//---------------------------------------------------------------------------
3)停止播放
TWindowsMediaPlayer与TRealAudio两个组件的停止方式不是相同的,不能用同一代码实现,我们得分别处理,最初我在设计时想了很多方案,最后还是选用现在的这个方案,就是在播放中放一个全局变量(布尔变量ra),利用他的状态确定出当前正在播放的是那个播放器,下面给出这段程序的代码:
void __fastcall TfrmControl::sbnStopClick(TObject *Sender)
{
if(ra==true)//若用的是realplayer播放器,则按realplayer关闭
{
frmWindow->raChtv->DoStop();
frmWindow->raChtv->Hide();
}
else//否则按 mediaplayer 关闭
{
frmWindow->wmpChtv->close();
frmWindow->wmpChtv->Hide();
}
lblPlay->Caption="停止播放";
frmWindow->Hide();
sbnPlay->Down=false;
sbnStop->Down=true;
}
//---------------------------------------------------------------------------
三、进一步完善
由于我们的TWindowsMediaPlayer与TRealAudio两个组件只取其视频窗口,其播放状态之类的信息都要在显示面板中显示出来,这些只能靠我们编程来实现。在前面已经对这两个组件这方面的内容做了介绍,实现起来并不难。
这些在显示面板中显示内容的实现(即图1中的出现的信息),我是用的TTimer定时循环组件来完成的,设定每秒更新一次内容,并在OnTimer事件中写出相应的处理语句:
void __fastcall TfrmControl::tmrControlTimer(TObject *Sender)
{
lblTime->Caption=Time();//取定系统时间
//循环显示作品信息
String s1="春辉(CH)软件 作者:董维春 王岩 CH工作室";
if(i<=54)
{
lblAbout->Caption=s1.SubString(i,18);
i+=2;
}
else
{
i=1;
}
if(sbnPlay->Down==false)//如果停止播放了,就提醒选择节目
{
lblStatus->Caption="选择你喜欢的电视或收音节目";
}
else
{
if(ra==true)//如果正在播放,则显示播放信息。先判断一下是用那个播放器,根据不同的播放器,选择不同的信息处理手段
{
lblStatus->Caption=frmWindow->raChtv->GetLastMessage();//取得联接信息
if(frmWindow->raChtv->GetPlayState()==3)//若联接成功,则显示播放速率
{
String chb="当前播放为"+IntToStr(frmWindow->raChtv->GetBandwidthAverage())+"比特/秒";
lblStatus->Caption=chb;
}
}
else
{
lblStatus->Caption=frmWindow->wmpChtv->status;//取得联接及播放信息
}
}
}
//---------------------------------------------------------------------------
至此,这个程序就基本完成了,其功能已经与专业的相差无几了。当然这只是源程序中的主要部分,但恰恰这些就是整个程序的核心技术,其它的内容,比如数据库的连接、无标题窗体的拖动、众多按钮功能的实现,程序界面与外观设计,我觉得不应该是本文的重点,因为每个人采用的方法可能有所不同,比如数据库,我选用的是Paradox,而你可能用Access,还有象流媒体的收集,这里限于篇幅就不在多说了。我相信按上面的作法,你也会得到一个不错的网络电视J
注:此程序已在XP系统、BCB6下编译通过,并由BCB5打包工具打包(关于用BCB5打包工具打包BCB6程序,请见作者的另文:http://www.csdn.net/develop/read_article.asp?id=23337),经多位好友帮忙测试,打包后的程序,安装后能顺利运行,观看效果不错。
另外要说的一点,因为多数的网络电视都要注册收费,我这里没有别的目的,只是想做一个技术方面的交流,还希望那些网络电视的作者原谅!其实真正的网络电视并不应该象本文所说的从网上收集流媒体来实现,而是应该有自己的专用的流播放服务器,只有那样才是真正的网络电视:)