用Delphi在局域网中实现网上影院
陈经韬
相信用过<<超级解霸>>的人都知道里面有个"接收网络DVB广播",可以在局部网中实现一台计算机播放视频文件而另一台接收.因为DVB设计的知识面太广,下面我们用另外一个巧妙的思路来实现局部网中的"网络播放".因为涉及到多媒体文件的播放,所以我们在程序中使用了一个 Mediaplay控件,但是很多使用过这个控件做播放器的人都遇到过这样一个问题:就是在播放MPG等扩展名的文件时出现"MCI初始化错误",如果安装了WindvdPlayer后又没有问题了,怎么回事呢?现在市面上所有的有关DELPHI多媒体编程的书都没有讲到这个问题和如何解决.原来,我们要自己修改win.ini里面[mci extensions]小节,另外还要修改一下system.ini里面的[mci]小节.举例说,你想让Mediaplay可以播放MPG为扩展名的文件时,可以按照如下格式修改:
[mci extensions]
mpeg=MPEGVideo
mpg=MPEGVideo
...
在这里,我们写了个自定义函数WriteMciConfig来达到这一目的,详细代码请看源程序.
解霸的左右声道设置好像也没有什么书籍介绍如何实现,实际上我们可以调用底层API函数mciSendString,不过好像只对Wav,Mid等音频文件有效.比如说左声道设置:
mciSendString('set all audio all off',nil,0,Handle);
mciSendString('set all audio left on',nil,0,Handle);
先把所有声道关闭,然后打开左声道.如果你设计过防火墙,会发现有些防火墙也是这样的:先关上所有端口,然后打开常用的端口.注意:因为这里调用了多媒体方面的API,所以程序必须加上MMsystem这个单元,否则将无法编译.
还有一个难点就是如何实现全屏幕播放.可能读者会说:先把Form的BorderStyle动态设置为BsNone,再把Form的WindowState:=wsMaximized不就行了吗?呵呵,是吗?你试试看.结果播放屏幕的视频不见了.只看到一个光秃秃的面版.解决的方法是用API函数:
SetWindowLong(Handle,GWL_STYLE,GetWindowLong(Handle,GWL_STYLE) and (not WS_CAPTION));
动态把窗口标题栏隐藏起来,再最大化.另外,为了保持窗口最大化后播放的尺寸仍然保持跟窗口大小融合,必须在窗口的OnResize事件中写上MediaPlayer1.DisplayRect:=Panel1.ClientRect;另外,程序中还有"弹出CD-ROM",文件拖放,音量控制等功能,读者可自行参考代码,均附了注释.
实际上,我们上面的程序已经是一个功能比较完善的"多媒体播放器"了,下面我们就来添加我们的"网络播放"功能. 先来热热身.
大家知道,局部网中大家互相访问对方共享的文件夹时,可以直接在IE的地址栏输入如"//机器名/共享名"形式来打开.比如说电脑Boy的共享文件夹mpgfile下有一个MPG文件Andy.mpg,则平时我们就可以用"//Boy/mpegfile/Andy.mpg"来打开它.聪明的读者可能已经想到,把这个名称传给播放器不就可以播放了吗?对!我们的程序原理是这样的:甲机播放视频文件时,动态把文件所在目录"隐形"共享,并打开一个Socket服务进行监听,当客户Socket连接时把文件完整的路径和名称发送过去,客户机取得名称后赋给Mediaplay控件的Filename即可进行播放. 但是现在问题又来了:
一:如何实现"动态共享"?你总不能叫用户自己去击鼠标共享吧? 二:如何实现"隐形共享"?这里的"隐形"包括两方面:比如说Boy共享了一个文件夹,那么必须做到同一个局部网的用户打开"网上邻居"时看不见该文件夹.另外,用户在Boy上共享的文件夹击鼠标右键的"共享"一项里看不到该文件夹共享.
共享文件夹可以通过修改注册表,而且在DELPHI中操作注册表也很简单.但是通过修改注册表实现共享必须在电脑重新启动后才生效,即使你用API向系统广播注册表内容已经改变的消息也无效.但是为什么Windows系统本身可以实现动态共享?它肯定调用了什么东西.是什么呢?原来在Win9X下它调用了SVRAPI.DLL中的一个函数:NetShareAdd,在NT/2000下则是NETAPI32.DLL.其实有很多所谓的"系统漏洞",比如说在Win9x下动态隐藏自己的功能就是系统自己本身用的用来运行某些系统程序的功能,而不是什么漏洞.只不过是微软不公布,后来有人发现了才叫"漏洞".跟踪系统和分析系统可以让你跟系统更加靠近.因为网络共享这个不是我们的主题,所以下面只给出实现的单元My_Share(见源代码),感兴趣的朋友可自行翻阅相关资料.调用规则如下: 删除一个共享
eleteShare(nil,
Pchar(共享名称));
添加一个只读共享:
ShareResource(nil, pchar(路径), Pchar(共享名称), Pchar(共享说明),STYPE_DISKTREE,SHI50F_RDONLY,'','');
添加一个完全共享:
ShareResource(nil, pchar(路径),Pchar(共享名称), Pchar(共享说明),STYPE_DISKTREE,SSHI50F_FULL,'','');
好,第一个问题解决了,下面来解决第二个.我也不想浪费纳税人的金钱了.具体方法如下:
一:添加一个参数SHI50F_SYSTEM,这样一来在共享的文件夹击右键的"共享"里面就看不到自己的文件夹共享了. 如:ShareResource(nil, pchar(路径),Pchar(共享名称), Pchar(共享说明),STYPE_DISKTREE,SHI50F_RDONLY or SHI50F_SYSTEM,'', '');
二:共享文件夹时在共享名称后面加上一个$符号,这样一来打开"网上邻居"就看不见共享的文件夹了,但是还是可以访问.
如://Boy/Myfile$ 好,到这里我们的具有"网络播放"功能的播放器就完成了.程序代码很粗糙,没有作过多容错检查,请读者自行完善.另外,在程序中文件进度的拖放我们用的是Delphi自身带的TrackBar控件,所以在播放时有时会出现稍微短暂的停顿,建议读者改用其它控件比如说Trackbar95.为了功能的完整性和照顾没有第三方控件的读者,我们这里保留了该控件. 补充: 1:本程序在Pwin98第二版+100M局部网上调试通过. 2:本程序可以改进的地方:不用Socket控件改用UDP来广播,实现不用输入对方IP地址完全"傻瓜式"网络播放.或者用一台电脑做服务器,共享多个文件供客户端有选择的播放.实验证明:在100M局部网上一个文件可以同时供7-8个用户同时播放而速度无停滞.