轉載請註明: TheViper http://www.cnblogs.com/TheViper html
原來發表過一篇分段播放的flash播放器。這個播放器其實就沒有神馬原理,就是把一個視頻分紅好幾個視頻,點播的時候經過關鍵幀(keyframe)索引找到離點擊點最近的關鍵幀播放。若是當前快要播放完,就去加載下一段。當前播放完,就播放下一段。git
原理很簡單,實現起來卻非常糾結,痛苦。由於本質上是一開始就建立了幾個video,播放,點擊,緩衝等時候就須要不停的計算下一個要播放的第幾個video,而後像放幻燈片那樣切換,雖然不明顯。而後計算的時候兼容flv和mp4也很無語,就像搞瀏覽器兼容同樣。另外,點播的時候會多一次請求,用於獲取關鍵幀信息。若是這個關鍵幀信息很大很長的話,體驗是很是很差的。github
這麼多不足,就嘗試下另外一種方案hls.chrome
hls m3u8的優勢在於不錯的兼容性。瀏覽器
能夠看到m3u8通殺移動設備,而不支持的設備都是瀏覽器,能夠經過flash播放器解決。事實上如今有長視頻的視頻網站在pc端基本上都用的m3u8.短視頻的話用視頻僞流技術(pseudo streaming)就已經能夠知足要求了。緩存
具體怎麼寫呢?一般都是用的osmf和HLSProvider,好比百度雲網盤裏面的播放器,less
HLSProvider(org.denivip.osmf包)已經封裝好了一切,com.baidu.player.DiskPlayer裏面是很簡單的調用。ide
然而我卻發現了更好的東西flashls。 這個東西好在體積小,它例子裏的flashlsChromeless.swf只有36kb.剛開始我不敢相信,由於那些視頻網站的swf(皮膚swf除外)都是150kb以上,這讓我以爲好像寫一個flash hls寫完真的就要好幾百kb,另外,它沒有其餘依賴,好比加密類,osmf....post
事實上,真正對hls m3u8的解析要不了那麼多.flex
這類hls plugin一般都被當作那些開源播放器(flowplayer,osmf,jwplayer)的hls插件,用以支持hls.好比上面的HLSProvider就只支持OSMF 2.0 based video players,而osmf即便是隻有最基本的功能,也有100多kb,再加上hurlant加密包,就更大了。
效果
下面開始進入正題,看看hls player是怎麼作的。
1.進入flashls文件下src文件夾,取出org包,放到項目文件夾。將裏面每一個文件形如CONFIG::LOGGING{}的代碼去除,由於沒有flex builder環境,flash沒法編譯。這個是去除後的flashls。
2.調用flashls封裝,具體的能夠看flashls examle目錄下chromeless的那個例子,很詳細。
hls事件
private function _init_events() { stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState); } private function _onStageVideoState(event : StageVideoAvailabilityEvent):void { var available : Boolean = (event.availability == StageVideoAvailability.AVAILABLE); _hls = new HLS(); HLSSettings.maxBufferLength = 10;//最大緩存長度 HLSSettings.seekMode = "ACCURATE";//查找模式 _hls.stage = stage; _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler); _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler);//當前播放回調 _hls.addEventListener(HLSEvent.FRAGMENT_PLAYING, _fragmentPlayingHandler);//片斷播放回調 _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);//當前狀態回調 if (available && stage.stageVideos.length > 0) { _stageVideo = stage.stageVideos[0]; _stageVideo.attachNetStream(_hls.stream); } else { _video = new Video(); addChild(_video); _video.smoothing = true; _video.attachNetStream(_hls.stream); } stage.removeEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState); }
兩個設置須要注意
maxBufferLength表示最大的緩衝長度,好比個人片斷是10秒一個,maxBufferLength設置成20秒,那麼當我播放的時候,播放器會加載後兩個片斷,若是暫停了,那播放器緩衝完後兩個片斷就再也不加載片斷了。這個作的真的很贊,既保證了用戶體驗,又節約了流量,另外若是在m3u8文件中配置了多碼率的話,會根據用戶當前的網速,匹配相應清晰度的片斷。
seekMode能夠爲ACCURATE,KEYFRAME,SEGMENT。
ACCURATE表示點播時會精確的跳到點播位置,這個乍看之下很好,實際用時會發現若是片斷稍微長一點,畫面會從點播的片斷開頭快進到點播位置。
KEYFRAME表示定位基於關鍵幀,這個也有問題,那就是通常的關鍵幀是幾秒一個,這樣點播的時候會有幾秒的偏差,由於只能用關鍵幀定位。
SEGMENT表示點播時會跳到包含點播位置的片斷的開頭。在直播的時候能夠用這個。
而後是回調
private function _manifestHandler(event : HLSEvent):void { _duration = event.levels[_hls.startlevel].duration; if (_autoLoad) { _play(-1); } } private function _mediaTimeHandler(event : HLSEvent):void { _duration = event.mediatime.duration; _media_position = event.mediatime.position; nav.progress_line.x = _media_position / _duration * W; nav.progressBar.width = (_media_position + event.mediatime.buffer) / _duration * W; nav.notify.text = formatTime(_media_position) + " / " + formatTime(_duration); } private function _fragmentPlayingHandler(event : HLSEvent):void { _video.width = _videoWidth = event.playMetrics.video_width; _video.height = _videoHeight = event.playMetrics.video_height; _video.x=(stage.stageWidth-_video.width)*0.5; _video.y=(stage.stageHeight-_video.height)*0.5; } private function _stateHandler(event : HLSEvent):void { if (event.state == 'PLAYING_BUFFERING') { buffering.visible = true; } else { buffering.visible = false; } }
_manifestHandler裏面能夠獲得視頻的總長度,注意這裏不是片斷長度。
_mediaTimeHandler也能夠獲得視頻總長度,還有視頻播放的當前位置,當前緩衝長度。所以裏面能夠更新播放條,緩衝條長度,和播放時間。
_stateHandler能夠獲得播放器視頻流當前的狀態,好比中止,暫停,緩衝,播放等狀態。
_fragmentPlayingHandler裏面能夠獲取視頻信息,好比寬度,高度
最後是hls對netstream封裝的方法
private function _load(url : String):void { _hls.load(url); } private function _play(position : Number = -1):void { _hls.stream.play(null, position); } private function _pause():void { _hls.stream.pause(); } protected function _resume():void//回放 { _hls.stream.resume(); } private function _seek(position : Number):void { _hls.stream.seek(position); } private function _stop():void { _hls.stream.close(); } private function _setVolume(percent : Number):void { st.volume = percent / 100; _hls.stream.soundTransform = st; } private function _getVolume():Number { return _hls.stream.soundTransform.volume; }
裏面須要注意的_load方法,傳入的是m3u8文件地址,不是視頻地址。播放器會自動解析m3u8文件,加載相應視頻片斷。
3.視頻分段,並生成m3u8文件
能夠看這一篇文章.
須要注意的是這裏的音頻編碼是aac,是ffmpeg實驗的東西,須要加上後面的-strict -2纔不會報錯。另外,-hls_list_size參數取的稍微大點,由於它表示寫入m3u8文件的片斷信息個數,好比取值爲3,那只有3個片斷信息被寫入m3u8。
默認獲得的片斷格式是ts,這個格式不像mp4,flv,f4v.ts是獨立編碼,不依賴頭信息的,在點播的時候不像前面說的那些格式,須要加載頭信息,獲取關鍵幀列表後,才能根據關鍵幀跳到點播位置。
最後說下怎麼使用這個播放器
flash_object.embedSWF('http://localhost/hls_example/youku_player.swf','wrap_player','youku_player','720','540',{ url:'http://localhost/hls_example/videos/video.m3u8' },{wmode:'transparent'});
想播放器傳入url參數,值爲m3u8地址就可使用了。
只有44kb!