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