利用FLEX開發(fā)視頻播放器可以利用自帶的組件VideoDisplay,也可以不用,最終兩種方式開發(fā)出來的效果是可以一樣的。
視頻播放器這個(gè)做開發(fā)的兄弟們應(yīng)該都熟悉,現(xiàn)在的視聽網(wǎng)站這么火熱,流媒體技術(shù)也相當(dāng)成熟,網(wǎng)上的介紹也很多。不過基本上流媒體播放器都是flash,大多是寫AactionScript腳本開發(fā),利用FLEX開發(fā)也比較方便,和VS.NET的開發(fā)環(huán)境一樣,看起來也熟悉,開發(fā)起來也有感覺些,我想至少應(yīng)該比寫AS腳本要有感覺些,當(dāng)然FLEX開發(fā)的應(yīng)用程序最終也會(huì)被解析成AS腳本,生成SWF文件,供WEB頁面嵌入調(diào)用。
最近稍微空閑那么一點(diǎn),從同事那拷了個(gè)FLEX4.0,裝上感覺下,之前也有意想做個(gè)流媒體播放器,正好這段時(shí)間有研究下。嚴(yán)格來說單純的播放器不會(huì)難做,網(wǎng)上也有很多例子,只是流媒體播放器只支持流格式的媒體文件,所有這里還有個(gè)媒體格式轉(zhuǎn)換的問題,就是要把不同類型的視頻格式轉(zhuǎn)換成流格式的文件,即轉(zhuǎn)換成視頻流格式。我寫的這篇暫時(shí)只說說流媒體播放器的開發(fā),不涉及轉(zhuǎn)換的問題,這個(gè)有時(shí)間研究下再說說。 我下面說的主要是不利用組件的開發(fā)方式,利用組件開發(fā)的后面我也貼下代碼片段和截圖。主要以開發(fā)完后的開發(fā)文檔內(nèi)容來說下,寫的內(nèi)容不多,基本上要點(diǎn)應(yīng)該還是寫清楚了,先看下目錄,按目錄順序講解:
1 流媒體視頻簡(jiǎn)介
1.1 什么是流媒體
所謂流媒體是指采用流式傳輸方式在Internet上播放的媒體格式(擴(kuò)展名一般為.flv,目前 Adobe公司為迎接高清時(shí)代又推出了.f4v格式)。流媒體又稱流式媒體,是指用一個(gè)視頻傳輸服務(wù)器把把節(jié)目當(dāng)成數(shù)據(jù)包發(fā)出,傳送到網(wǎng)絡(luò)上,同過流媒體播放器進(jìn)行畫面還原顯示給用戶觀看。
1.2 什么是視頻流
視頻流(Video Streaming)是指視頻數(shù)據(jù)的傳輸,例如,它能夠被作為一個(gè)穩(wěn)定的和連續(xù)的流通過網(wǎng)絡(luò)處理。因?yàn)榱鲃?dòng),客戶機(jī)瀏覽器或插件能夠在整個(gè)文件被傳輸完成前顯示多媒體數(shù)據(jù)。視頻流技術(shù)基于 2 密鑰技術(shù),視頻譯碼技術(shù)和可升級(jí)的視頻分發(fā)技術(shù)發(fā)展。
1.3 流媒體視頻的優(yōu)點(diǎn)
流媒體視頻是邊下載邊播放邊緩沖的,用戶體驗(yàn)相比傳統(tǒng)的下載播放好得多,傳統(tǒng)的下載播放是用戶等視頻文件全部下載到緩存后再進(jìn)行播放,用戶等待時(shí)間比較長,因此與單純的下載播放方式相比,這種對(duì)多媒體文件邊下載邊播放的流式傳輸方式不僅使啟動(dòng)延時(shí)大幅度地縮短,而且對(duì)系統(tǒng)緩存容量的需求也大大降低。
1.4 流媒體視頻應(yīng)用
由于流媒體格式的數(shù)據(jù)傳輸速度快,因此被廣泛應(yīng)用于互聯(lián)網(wǎng)上的大型視頻點(diǎn)播網(wǎng)站,比較典型的視頻點(diǎn)播網(wǎng)站又56視頻網(wǎng),優(yōu)酷視頻網(wǎng)、土豆視頻網(wǎng)等。同時(shí)這種技術(shù)也可以用于在線視頻教學(xué)系統(tǒng)進(jìn)行點(diǎn)播學(xué)習(xí)或公司內(nèi)部會(huì)議視頻在線提供觀看等。
補(bǔ)充說明下那個(gè)高清格式的.f4v,因?yàn)闇y(cè)試的時(shí)候從土豆上拉了個(gè)高清格式的視頻文件下來,發(fā)現(xiàn)后綴是.f4v的,利用開發(fā)的這個(gè)播放器播放不了,后綴名改成flv是可以播放的,覺得奇怪,都是流格式應(yīng)該是可以播放的才對(duì),原來忘了在網(wǎng)站的http頭文件的MIME類型添加擴(kuò)展名,添加下.f4v的擴(kuò)展名就OK了,可以正常播放了,后來也看了下百度百科對(duì)這種高清格式是這么說的:
作為一種更小更清晰,更利于在網(wǎng)絡(luò)傳播的格式,F(xiàn)4V已經(jīng)逐漸取代了傳統(tǒng)FLV,也已經(jīng)被大多數(shù)主流播放器兼容播放,而不需要通過轉(zhuǎn)換等復(fù)雜的方式。
F4V是Adobe公司為了迎接高清時(shí)代而推出繼FLV格式后的支持H.264的F4V流媒體格式。
它和FLV主要的區(qū)別在于,F(xiàn)LV格式采用的是H263編碼,而F4V則支持H.264編碼的高清晰視頻,碼率最高可達(dá)50Mbps。
2 功能概述
2.1 設(shè)計(jì)目的
作為系統(tǒng)平臺(tái)功能的擴(kuò)充,開發(fā)此模塊,現(xiàn)實(shí)一個(gè)功能相對(duì)全面的流媒體播放器,主要用于播放流媒體格式的視頻,提供給特殊要求的客戶用于外網(wǎng)在線播放新聞視頻。
2.2 功能說明
1) 視頻的暫停、播放
2) 視頻拖動(dòng)播放和定點(diǎn)播放
3) 音量的禁音和開啟
4) 拖動(dòng)滑塊控制音量
5) 視頻緩沖進(jìn)度高亮顯示
6) 視頻全屏處理,點(diǎn)擊按鈕或點(diǎn)擊視頻畫面實(shí)現(xiàn)全屏
2.3 運(yùn)行環(huán)境
此模塊采用Flex4.0開發(fā),要求客戶端安裝flash 10.0.0 (含以上版本)
3 功能設(shè)計(jì)
3.1 相關(guān)變量屬性
private var isPause:Boolean = false; //暫停狀態(tài)
private var isSound:Boolean = true; //聲音狀態(tài)(是否禁音)
private var _volume :Number = 0.6; //默認(rèn)音量大小(最大值為1)
private var isFullScreen:Boolean = false; //是否是全屏
private var totalTime:Number; //播放總時(shí)間
private var playPosition:Number; //剪輯位置
private var videoUrl:String; //視頻文件地址
private var videoWidth:Number; //視頻寬度
private var videoHeight:Number; //視頻高度
private var isAutoPlay:Boolean = true; //是否自動(dòng)播放
3.2 初始化視頻畫布
點(diǎn)開視頻播放頁面后首先初始化視頻播放的畫面,根據(jù)接收的用戶參數(shù)初始化視頻畫面的大小。
對(duì)象定義:
import mx.events.SliderEvent; //滑塊事件命名空間引用
private var nc:NetConnection; //媒體連接對(duì)象
private var ns:NetStream; //網(wǎng)絡(luò)流對(duì)象
private var metaDataObj:Object = {}; //媒體的元數(shù)據(jù)信息
private var video:Video ; //視頻對(duì)象
初始化方法如下:
private function init():void
{
videoUrl = parameters.videoUrl;
videoWidth = parameters.videoWidth;
videoHeight = parameters.videoHeight
video = new Video(videoWidth,videoHeight);
video.smoothing = true;//畫面平滑處理,去掉全屏后的水紋以提高畫面清晰度
uic.addChild(video); //將視頻對(duì)象添加到頁面
}
3.3 加載視頻流并播放
當(dāng)視頻初始化完成后調(diào)用視頻播放方法,將方法置于應(yīng)用程序事件頭里面。
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" initialize="init()" creationComplete="startVideo()" >
private function startVideo():void
{
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler); //添加播放連接監(jiān)聽事件
nc.connect(null);
}
當(dāng)連接對(duì)象成功連接后,播放視頻,上面的nc.connect(null);表示如果未使用 Flash Media Server,可使用null作為參數(shù)以便從本地文件系統(tǒng)或 Web 服務(wù)器中播放視頻和 MP3 文件。
private function netStatusHandler(e:NetStatusEvent):void
{
ns = new NetStream(nc);
metaDataObj.onMetaData = this.onMetaData;
ns.client = metaDataObj;
video.attachNetStream(ns);
//ns.bufferTime = 5;
ns.play(videoUrl);
soundProcess.value = _volume;
soundTrans.volume = _volume
ns.soundTransform = soundTrans;
this.addEventListener(Event.ENTER_FRAME,EnterFrameHandler); //添加播放過程中的監(jiān)聽事件
ns.addEventListener(NetStatusEvent.NET_STATUS,NetStreamStatusHandler);//添加播放完畢(或其它狀態(tài))后的監(jiān)聽事件
if(!isAutoPlay) //客戶端沒有設(shè)置為自動(dòng)播放時(shí)的處理
{
ns.pause();
btnPlay.source = playClass;
isPause = true;
}
else
{
btnPlay.source = pauseClass;
isPause = false;
}
}
//獲取視頻的元數(shù)據(jù)信息,這里的元數(shù)據(jù)信息包括視頻編碼,視頻碼率,音頻編碼,音頻碼率,音視頻文件大小,流文件總大小,播放總時(shí)間等
private function onMetaData(obj:Object):void
{
totalTime = obj.duration;
fileSize = (obj.filesize/(1024*1024)).toFixed(2).toString()+"MB";//換算成兆字節(jié)并保留兩位小數(shù)
}
說明:
this.addEventListener(Event.ENTER_FRAME,EnterFrameHandler);用于監(jiān)聽播放過程中的事件處理,由于在播放過程中,播放進(jìn)度和緩沖進(jìn)度要實(shí)時(shí)顯示以及播放到哪一個(gè)時(shí)間點(diǎn)了也都需要?jiǎng)討B(tài)實(shí)時(shí)呈現(xiàn)給用戶,因此視頻進(jìn)入加載畫面時(shí)就需要實(shí)時(shí)不斷監(jiān)控該事件。
ns.addEventListener(NetStatusEvent.NET_STATUS,NetStreamStatusHandler);用于監(jiān)聽在視頻流播放完畢后的事件處理。
onMetaData是一個(gè)回調(diào)方法,當(dāng)客戶端加載到視頻流時(shí),通過異步獲取該媒體的元數(shù)據(jù)信息,如媒體總大小、總的播放時(shí)間、采樣率等等。
3.4 播放進(jìn)度及緩沖進(jìn)度高亮顯示
//播放進(jìn)度和緩沖進(jìn)度處理
private function EnterFrameHandler(e:Event):void
{
if (totalTime>0)
{
playTime = ns.time;// ns.time為流媒體實(shí)時(shí)播放的時(shí)間
}
if (ns.bytesLoaded>0)
{
bufferRect.width = ns.bytesLoaded / ns.bytesTotal*(playProcess.width-10);//計(jì)算緩沖方框的寬度(滑塊本身也有一定的寬度,減去約10個(gè)像素寬度)
}
}
說明:
playTime作為播放進(jìn)度條當(dāng)前實(shí)時(shí)播放的時(shí)間點(diǎn),視頻的總時(shí)間作為播放顯示進(jìn)度條的最大值。
<mx:HSlider id="playProcess"minimum="0" value="{playTime}" maximum="{totalTime}"
ns.bytesLoaded為已緩沖好的流媒體字節(jié)大小(單位為byte),ns.bytesTotal為流媒體的總大小,通過比例計(jì)算(如上)可以得出緩沖進(jìn)度在播放進(jìn)度條上的位置,緩沖進(jìn)度條其實(shí)是一個(gè)長方形框,以層的形式位于播放進(jìn)度條下放,初始寬度為0,當(dāng)緩沖達(dá)到100%時(shí),即緩沖完畢時(shí),緩沖條的長度和播放進(jìn)度條等長(除去滑塊寬度)。緩沖方框可以是一個(gè)BorderContainer,如下:
<s:BorderContainer x="14" y="411" width="0" height="4" id="bufferRect" buttonMode="true" borderColor="#70b2ee" backgroundColor="#70b2ee">
</s:BorderContainer>
頁面所有的控件和標(biāo)簽如下:
代碼
3.5 視頻的播放與暫停
視頻的暫停與播放調(diào)用視頻流的pause()方法和resume()方法,通過是否暫停的狀態(tài)變量判斷控制,代碼片段如下:
//播放、暫停設(shè)置
private function play():void
{
if(isPause)
{
ns.resume();
btnPlay.source = pauseClass; //設(shè)置按鈕圖標(biāo)為點(diǎn)擊暫停圖標(biāo)
isPause = false;
}
else
{
ns.pause();
btnPlay.source = playClass; //設(shè)置按鈕圖標(biāo)為點(diǎn)擊播放圖標(biāo)
isPause = true;
}
}
3.6 拖動(dòng)滑塊播放視頻
拖動(dòng)滑塊播放視頻文件,主要是判斷和記錄流的剪輯位置,找到最終的剪輯位置后可以調(diào)用視頻流的seek(參數(shù))方法,參數(shù)為當(dāng)前的剪輯位置,如果不拖動(dòng)直接點(diǎn)擊任意剪輯位置定點(diǎn)播放,那么最終的剪輯位置應(yīng)該是鼠標(biāo)彈起的位置,這里定點(diǎn)點(diǎn)擊實(shí)際上還是相當(dāng)于觸發(fā)了滑塊移動(dòng)的事件,只不過是滑塊快速移動(dòng)到你點(diǎn)擊的位置而已,相關(guān)代碼片段如下:
//拖動(dòng)進(jìn)度條時(shí)改變播放位置
private function play_onchange(event:SliderEvent):void
{
if(ns.time == 0)
{
playProcess.value = 0;
return;
}
playPosition = playProcess.value; //保正播放進(jìn)度統(tǒng)一
ns.seek(playPosition);
}
//進(jìn)度條鼠標(biāo)按下
private function thumbPress():void
{
ns.pause();
}
//進(jìn)度條鼠標(biāo)彈起,指拖動(dòng)時(shí)滑塊時(shí)鼠標(biāo)彈起
private function thumbRelease():void
{
//ns.seek(playPosition);
btnPlay.source = pauseClass;
isPause = false;
ns.resume();
}
3.7 播放結(jié)束處理
一般視頻播放完畢后,播放的指針頭歸零,即播放進(jìn)度條上的滑塊指向起始位置,同時(shí)播放按鈕狀態(tài)為準(zhǔn)備就緒狀態(tài),視頻流處于暫停狀態(tài)?梢酝ㄟ^視頻流的當(dāng)前狀態(tài)信息進(jìn)行判斷,如下面e.info.code狀態(tài)值可以獲得各種不同的狀態(tài),這里只取播放完畢停止后的狀態(tài)值,代碼片段如下:
//播放完畢處理
private function NetStreamStatusHandler(e:NetStatusEvent):void
{
if(e.info.code == "NetStream.Play.Stop")
{
ns.seek(0);
btnPlay.source = playClass;
isPause = true;
ns.pause();
}
}
3.8 音量大小控制
視頻聲音控制通過SoundTransform 類操作,該類包含音量和平移的屬性。如果禁音后運(yùn)行調(diào)節(jié)滑塊的話,需要再定義一個(gè)臨時(shí)變量tmpSound,以便開啟聲音時(shí)為最終設(shè)置的音量。
//靜音、開音控制
private function closeSound():void
{
if(isSound)
{
soundImg.source = sound;
tmpSound= ns.soundTransform;
soundTrans.volume = 0;
ns.soundTransform = soundTrans; // 這里禁音直接ns.soundTransform.volume = 0 這樣不行,需要用對(duì)象賦值
isSound = false;
}else
{
soundImg.source = sound1;
ns.soundTransform = tmpSound;
isSound = true;
}
}
//通過滑塊調(diào)整聲音
private function sound_thumbChanges(event:SliderEvent):void
{
tmpSound.volume = soundProcess.value;
ns.soundTransform = tmpSound;
}
3.9 全屏控制
全屏可以用flash畫布舞臺(tái)的stage屬性值設(shè)置,不過要考慮下普通屏幕和寬屏的處理, 常見的顯示器分辨率按其長寬比可分為為:4:3(1024×768)、5:4(1280×1024)、16:9、16:10 (這里暫以寬屏測(cè)試的,需要后續(xù)處理),點(diǎn)擊全屏按鈕或點(diǎn)擊視頻畫面都可以全屏,調(diào)用display()方法即可,代碼片段如下:
//切換全屏顯示
private function display():void
{
if(!isFullScreen)
{
stage.fullScreenSourceRect = new Rectangle(video.x, video.y,video.width,video.height);
stage.displayState =StageDisplayState.FULL_SCREEN;
isFullScreen = true;
}else
{
stage.displayState = StageDisplayState.NORMAL;
isFullScreen = false;
}
}
3.10 流數(shù)據(jù)字符格式化
視頻播放時(shí)當(dāng)前時(shí)間和總時(shí)間是以秒為單位的,比如180s的文件,當(dāng)前播放到一半顯示90s,需要按時(shí)間格式來顯示才友好,另外還有音量的值是介于0到1之間的某個(gè)值,也需要按百分比來顯示。代碼片段如下:
//格式化時(shí)間
private function formatTime(time:Number):String
{
var min:Number = Math.floor(time/60);
var sec:Number = Math.floor(time%60);
var timeResult:String = (min < 10 ? "0"+min.toString() : min.toString()) + ":" + (sec == 10 ? "0"+sec.toString() : sec.toString());
return timeResult;
}
//聲音slider格式化
private function SoundTipFormat(data:Number):String
{
return (data*100).toFixed(0).toString()+"%"; //拖動(dòng)聲音進(jìn)度條時(shí)顯示百分比音量
}
3.11 視頻畫面平滑優(yōu)化處理
一般視頻畫布全屏后,會(huì)產(chǎn)生文字圖像有些失真的感覺,會(huì)產(chǎn)生水紋,對(duì)于這點(diǎn)的處理,F(xiàn)lex封裝了一個(gè)簡(jiǎn)單有效的方法,只需設(shè)置一個(gè)屬性即可,即video對(duì)象中有一個(gè)屬性設(shè)置。如下:
video.smoothing = true;
把該屬性設(shè)置為true,表是啟用畫面優(yōu)化處理,加上這個(gè)設(shè)置能大大提高畫面質(zhì)量 。
3.12 播放接口調(diào)用
采用FLEX開放的播放器編譯后最終生成的是一個(gè).swf文件,需要通過頁面去加載調(diào)用,可以是靜態(tài)的html頁面,也可以是動(dòng)態(tài)的aspx頁面,調(diào)用過程中引用Flex內(nèi)置的一個(gè)JS文件——swfobject.js,
其中flashContent標(biāo)簽作為嵌入的swf播放器文件(FLVPlayer.swf)的容器,同時(shí)網(wǎng)站的http頭進(jìn)行如下設(shè)置(添加.flv和.f4v擴(kuò)展名):
下面貼幾張播放的效果圖:
讓美工美化下,做一個(gè)像目前視頻網(wǎng)類似效果的播放器還是相當(dāng)簡(jiǎn)單的。
利用VideoDisplay組件開發(fā)的我貼下完整的代碼片段,這里和上面對(duì)比少了緩沖進(jìn)度的效果,另外沒有畫面優(yōu)化的效果出來,如果要加這兩個(gè)效果的話,要向上面那樣定義連接,輸入流對(duì)象,然后讓這個(gè)組件加載實(shí)例化的video對(duì)象就可以了,代碼如下:
效果圖如下:
可以對(duì)比下平滑處理過的效果,明顯文字更清晰些,尤其在全屏后效果更明顯,平滑處理后基本不會(huì)有畫面失真的現(xiàn)象。
說明:采用的是FLEX4.0開發(fā)的,其實(shí)里面的標(biāo)簽有還是3.0的,直接參考了網(wǎng)上的一些例子,也都是兼容的,就沒改了。
原本我也想放個(gè)可以運(yùn)行的demo上來,發(fā)現(xiàn)編譯發(fā)布后還帶有好幾個(gè)swf的類庫文件,沒有的話播放不了,都是flex4.0的發(fā)布文件必須要的,想發(fā)布后打包成一個(gè)單獨(dú)的swf文件,也沒有找到合適的方法,也就沒有進(jìn)一步處理了,如果大家有興趣想玩下或研究下,可以裝個(gè)FLEX4.0版本的,新建一個(gè)應(yīng)用程序,安照上面的步驟做個(gè)可以播放流媒體視頻的播放器是沒問題的。