前段时间做了个 Flex 的小项目,项目中需要流媒体播放器,参考了一些资料,自己做了一个,现发布出来与大家分享
直接上代码:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="710" height="445"
backgroundColor="#000000" creationComplete="completeHandler()"
horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import com.doc.view.docmanager.player.helper.PlayItem;
import com.doc.view.docmanager.player.helper.PlayList;
import mx.events.FlexEvent;
import mx.events.MetadataEvent;
import mx.events.SliderEvent;
import mx.core.UIComponent;
import mx.controls.Alert;
import mx.utils.StringUtil;
private var _source:String;
private var _autoPlay:Boolean;
public var videoWidth:Number = 0;
public var videoHeight:Number = 0;
private var nc:NetConnection;
[Bindable]
private var ns:NetStream;
private var videoContainer:UIComponent;
private var video:Video;
private var rtmpUrl:String;
private var fileName:String;
private var timer:Timer;
private var totalTime:Number;
//播放列表
private var _playList:PlayList;
//当前播放视频的索引
private var _currentIndex:int = 0;
//用来控制音量的临时变量
private var volumn:SoundTransform = new SoundTransform();
//NetStream 对象所引用的client的对象
private var nsClient:Object;
//是否已经初始化
private var isInit:Boolean = false;
//是否在暂停状态
private var isPause:Boolean = false;
//是否在播放状态
private var isPlay:Boolean = false;
//是否静音
private var isMute:Boolean = false;
//临时保存音量
private var tmpVolumn:Number = 5;
/*图标定义*/
//播放
[Embed(source="assets/player/play.jpg")]
[Bindable]
private var playImage:Class;
[Embed(source="assets/player/play_over.jpg")]
[Bindable]
private var playOverImage:Class;
//暂停
[Embed(source="assets/player/pause.jpg")]
[Bindable]
private var pauseImage:Class;
[Embed(source="assets/player/pause_over.jpg")]
[Bindable]
private var pauseOverImage:Class;
//停止
[Embed(source="assets/player/stop.jpg")]
[Bindable]
private var stopImage:Class;
[Embed(source="assets/player/stop_over.jpg")]
[Bindable]
private var stopOverImage:Class;
//上一个
[Embed(source="assets/player/previous.jpg")]
[Bindable]
private var previousImage:Class;
[Embed(source="assets/player/previous_over.jpg")]
[Bindable]
private var previousOverImage:Class;
//下一个
[Embed(source="assets/player/next.jpg")]
[Bindable]
private var nextImage:Class;
[Embed(source="assets/player/next_over.jpg")]
[Bindable]
private var nextOverImage:Class;
//音量
[Embed(source="assets/player/sound.jpg")]
[Bindable]
private var soundImage:Class;
[Embed(source="assets/player/sound_over.jpg")]
[Bindable]
private var soundOverImage:Class;
//静音
[Embed(source="assets/player/mute.jpg")]
[Bindable]
private var muteImage:Class;
[Embed(source="assets/player/mute_over.jpg")]
[Bindable]
private var muteOverImage:Class;
// 设置flv地址,rtmp和http都支持,绝对路径
public function set playList(playList:PlayList):void{
this._playList = playList;
var data:ArrayCollection = new ArrayCollection();
for(var i:int = 0; i < playList.size(); i++)
{
data.addItem(this._playList.getItem(i).name);
}
this.videoList.dataProvider = data;
setCurrentIndex(0);
}
//设置当前播放的索引
private function setCurrentIndex(index:int):void
{
if(this._playList.validIndex(index))
{
this._currentIndex = index;
}
else
{
if(index >= _playList.size())
{
this._currentIndex = 0;
}
else
{
this._currentIndex = _playList.size() - 1;
}
}
this.videoList.selectedIndex = _currentIndex;
setSource(this._playList.getItem(this._currentIndex));
}
//设置当前的播放文件
private function setSource(playItem:PlayItem):void
{
var beginIndex:int;
var endIndex:int;
this._source = playItem.url;
if(playItem.url.indexOf("rtmp") == 0){
endIndex = this._source.lastIndexOf("/");
this.rtmpUrl = this._source.substring(0, endIndex);
beginIndex = endIndex;
endIndex = this._source.lastIndexOf(".");
this.fileName = this._source.substring(beginIndex + 1, endIndex);
}
else{
this.rtmpUrl = null;
this.fileName = playItem.url;
}
if(this._autoPlay){
this.play();
}
}
public function get source():String{
return this._source;
}
public function set autoPlay(auto:Boolean):void{
this._autoPlay = auto;
}
public function get autoPlay():Boolean{
return this._autoPlay;
}
//初始化完毕后设置video对象的大小,要把video对象加入到Flex的UI控件中,必须先用UIComponent或其子类包装。
private function completeHandler():void{
this.video = new Video();
this.video.width = this.width * 0.7;
this.video.height = this.height - this.controlBar.height - this.sliderBar.height;
this.videoContainer = new UIComponent();
this.videoContainer.addChild(this.video);
this.videoBox.addChild(this.videoContainer);
this.videoContainer.addEventListener(MouseEvent.CLICK, videoClickHandler);
this.isPause = false;
this.addEventListener(FlexEvent.EXIT_STATE, function():void{
close();
});
if(this._autoPlay){
this.isPlay = true;
changeImage(1);
showTip0("正在播放");
}
else{
this.isPlay = false;
changeImage(1);
showTip0("可以开始播放");
}
}
private function init():void{
if(StringUtil.trim(this._source) != ""){
this.nc = new NetConnection();
this.nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
this.nc.connect(this.rtmpUrl);
connectStream();
this.isInit = true;
}
}
private function connectStream():void{
this.nsClient = new Object();
this.nsClient.onMetaData = metaDataHandler;
this.nsClient.onCuePoint = cuePointHandler;
this.ns = new NetStream(this.nc);
this.ns.bufferTime = 15;
this.ns.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
this.ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
this.ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
showTip(this.fileName);
this.ns.play(this.fileName);
this.ns.client = this.nsClient;
//设置声音
resetVolumn();
this.video.attachNetStream(this.ns);
}
private function netStatusHandler(event:NetStatusEvent):void{
switch(event.info.code){
case "NetConnection.Connect.Success":
showTip0("连接成功");
break;
case "NetConnection.Connect.Rejected":
showTip0("没有访问权限");
this.slider.setThumbValueAt(0, 0);
this.isPause = false;
this.isPlay = false;
changeImage(1);
break;
case "NetStream.Play.StreamNotFound":
showTip0("找不到文件");
this.slider.setThumbValueAt(0, 0);
this.isPause = false;
this.isPlay = false;
changeImage(1);
break;
case "NetStream.Play.Stop":
this.isPause = false;
this.isPlay = false;
changeImage(1);
// showTip0("已停止");
next();
break;
case "NetStream.Buffer.Empty":
showTip0("正在缓冲");
break;
case "NetStream.Buffer.Full":
if(isPlay)
{
showTip0("正在播放");
}
break;
}
}
//IO 错误
private function ioErrorHandler(event:IOErrorEvent):void{
}
//安全错误
private function securityErrorHandler(event:SecurityErrorEvent):void{
}
//同步错误
private function asyncErrorHandler(event:AsyncErrorEvent):void{
}
//当获得媒体数据时,开始监听enterFrame事件,该事件是不断发生的,在该事件的handler中更新进度条
private function metaDataHandler(info:Object):void{
this.slider.enabled = true;
this.totalTime = info.duration;
this.slider.maximum = this.totalTime;
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
this.volumnSlider.setThumbValueAt(0, this.ns.soundTransform.volume * 10);
}
private function enterFrameHandler(event:Event):void{ //在该事件中不断更新进度条的位置
this.slider.setThumbValueAt(0, this.ns.time);
showTip(formatTime(this.ns.time) + "/" + formatTime(this.totalTime));
}
private function cuePointHandler(info:Object):void{
showTip0("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
}
//播放当前文件
public function play():void{
this.init();
try{
this.ns.close();
this.ns.play(this.fileName);
}
catch(error:Error){
showTip0("发生错误:" + error.message);
this.isPause = false;
this.isPlay = false;
changeImage(1);
}
this.isPause = false;
this.isPlay = true;
changeImage(1);
showTip0("正在播放");
}
//播放上一个
public function previous():void
{
setCurrentIndex(this._currentIndex - 1);
}
//播放下一个
public function next():void
{
setCurrentIndex(this._currentIndex + 1);
}
//单击视频,实现播放暂停
private function videoClickHandler(event:MouseEvent):void{
this.playPause();
}
//播放 or 暂停
private function playPause():void{
if(isPlay)
{
if(isPause){
this.isPause = false;
changeImage(1);
this.ns.resume();
showTip0("正在播放");
}
else{
this.ns.pause();
this.isPause = true;
changeImage(1);
showTip0("已暂停");
}
}
else
{
this.play();
}
}
//停止播放
private function stop():void{
this.ns.seek(0);
this.ns.pause();
this.slider.setThumbValueAt(0, 0);
this.isPause = false;
this.isPlay = false;
changeImage(1);
showTip0("已停止");
}
//拖拽进度滑块
private function thumbDragHandler():void{ //拖拽进度条时,删除对enterFrame事件的监听,以便使此时的进度条与播放头脱离
if(!this.ns) return;
this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
//释放进度滑块
private function thumbReleaseHandler():void{ //拖拽释放后,根据进度条该点的值让视频的播放头跳到该时间点
if(!this.ns) return;
this.ns.seek(slider.value);
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
//调整音量事件
private function volumnChangeHandler():void{ //视频音量控制
resetVolumn();
this.isMute = false;
this.mute.source = soundImage;
}
//重置NetStream的音量
private function resetVolumn():void
{
if(!this.ns) return;
volumn.volume = this.volumnSlider.value / 10;
this.ns.soundTransform = volumn;
}
//格式化音量进度条的提示
private function formatVolumnSliderTooltip(value:Number):String
{
return (int(value * 10)) + "%";
}
//格式化播放进度条的提示
private function formatTime(value:Number):String
{
var hour:int;
var minute:int;
var second:int;
hour = int(value / 3600);
minute = int(value / 60) - hour * 60;
second = int(value % 60);
if(hour > 0)
{
return (hour < 10 ? ("0" + hour) : hour) + ":" + (minute < 10 ? ("0" + minute) : minute) + ":" + (second < 10 ? ("0" + second) : second);
}
else
{
return (minute < 10 ? ("0" + minute) : minute) + ":" + (second < 10 ? ("0" + second) : second);
}
}
//关闭所有连接
public function close():void
{
if(this.ns) ns.close();
if(this.nc) nc.close();
}
//静音
private function resetMute():void
{
if(isMute)
{
this.volumnSlider.value = tmpVolumn;
mute.source = soundImage;
isMute = false;
}
else
{
this.tmpVolumn = this.volumnSlider.value;
this.volumnSlider.value = 0;
mute.source = muteImage;
isMute = true;
}
resetVolumn();
}
//全屏, 暂时不用
private function fullScreen():void
{
stage.displayState = (stage.displayState ==
StageDisplayState.NORMAL?StageDisplayState.FULL_SCREEN:StageDisplayState.NORMAL);
}
/*鼠标进入图标时,改变图标*/
private function changeOverImage(type:int):void
{
// 停止
if(type == 0)
{
this.stopBtn.source = stopOverImage;
}
//播放
else if(type == 1)
{
if(isPlay)
{
if(isPause)
{
this.playPauseBtn.source = playOverImage;
}
else
{
this.playPauseBtn.source = pauseOverImage;
}
}
else
{
this.playPauseBtn.source = playOverImage;
}
}
//音量
else if(type == 2)
{
if(isMute)
{
this.mute.source = muteOverImage;
}
else
{
this.mute.source = soundOverImage;
}
}
//上一个
else if(type == 3)
{
this.previousBtn.source = previousOverImage;
}
//下一个
else if(type == 4)
{
this.nextBtn.source = nextOverImage;
}
}
/*鼠标移出图标时,改变图标*/
private function changeImage(type:int):void
{
//停止
if(type == 0)
{
this.stopBtn.source = stopImage;
}
//播放
else if(type == 1)
{
if(isPlay)
{
if(isPause)
{
this.playPauseBtn.source = playImage;
}
else
{
this.playPauseBtn.source = pauseImage;
}
}
else
{
this.playPauseBtn.source = playImage;
}
}
//音量
else if(type == 2)
{
if(isMute)
{
this.mute.source = muteImage;
}
else
{
this.mute.source = soundImage;
}
}
//上一个
else if(type == 3)
{
this.previousBtn.source = previousImage;
}
//下一个
else if(type == 4)
{
this.nextBtn.source = nextImage;
}
}
//显示提示信息
private function showTip(msg:String):void
{
this.timpTip.text = msg;
}
//另一处提示信息
private function showTip0(msg:String):void
{
this.timpTip0.text = msg;
}
]]>
</mx:Script>
<mx:Canvas width="100%" height="100%" x="0" y="0">
<mx:Canvas id="playArea" width="500" height="100%" x="-1" y="-1" horizontalScrollPolicy="off" verticalScrollPolicy="off" borderColor="#FFFFFF" borderStyle="solid">
<mx:Canvas id="videoBox" width="100%" x="0" y="0" height="360"
horizontalScrollPolicy="off" verticalScrollPolicy="off">
</mx:Canvas>
<mx:VBox id="sliderBar" y="360" height="18" width="100%" backgroundColor="#000000"
horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:HSlider id="slider" width="498" minimum="0" snapInterval="1"
showTrackHighlight="true" liveDragging="true" thumbDrag="thumbDragHandler()"
thumbRelease="thumbReleaseHandler()" dataTipFormatFunction="formatTime"
allowTrackClick="false" y="420" trackColors="[0xEEEEEE,0x0000FF]" enabled="false">
</mx:HSlider>
</mx:VBox>
<mx:Canvas id="controlBar" width="100%" height="60" y="375" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image source="{stopImage}" id="stopBtn" click="stop()"
mouseOver="changeOverImage(0)" mouseOut="changeImage(0)"
width="32" height="32" x="7" y="14"/>
<mx:Image source="{previousImage}" id="previousBtn" click="previous()"
mouseOver="changeOverImage(3)" mouseOut="changeImage(3)"
width="32" height="32" x="51" y="14"/>
<mx:Image source="{playImage}" id="playPauseBtn" click="playPause()"
mouseOver="changeOverImage(1)" mouseOut="changeImage(1)"
width="64" height="64" x="85" y="-3"/>
<mx:Image source="{nextImage}" id="nextBtn" click="next()"
mouseOver="changeOverImage(4)" mouseOut="changeImage(4)"
width="32" height="32" x="152" y="14"/>
<mx:Label id="timpTip0" width="87" textAlign="center" x="188" y="22" color="#FFFFFF"/>
<mx:Label id="timpTip" width="115" textAlign="center" x="273" y="22" color="#FFFFFF"/>
<mx:Image id="mute" click="resetMute()" source="{soundImage}"
mouseOver="changeOverImage(2)" mouseOut="changeImage(2)"
width="32" height="32" x="392" y="14"/>
<mx:HSlider id="volumnSlider" width="75" value="2" minimum="0" maximum="10" snapInterval="0.1"
change="volumnChangeHandler()" liveDragging="true" x="421" y="18" dataTipFormatFunction="formatVolumnSliderTooltip"/>
</mx:Canvas>
</mx:Canvas>
<mx:Canvas width="210" height="100%" x="500" y="0">
<mx:VBox width="100%" height="20" y="5" horizontalAlign="center">
<mx:Label textAlign="center" text="播放列表" color="#FFFFFF" width="79" fontFamily="宋体" fontSize="15" fontWeight="bold"/>
</mx:VBox>
<mx:List x="-1" y="25" width="209" height="420" backgroundColor="#000000" color="#FFFFFF" id="videoList" itemClick="{setCurrentIndex(this.videoList.selectedIndex)}"></mx:List>
</mx:Canvas>
</mx:Canvas>
</mx:Canvas>
由于时间仓促,所以代码中还有很多可以优化的地方,现提供出来,仅供大家参考。