小巧的http live streaming m3u8播放器


轉載請注明: TheViper http://www.cnblogs.com/TheViper 

原來發表過一篇分段播放的flash播放器。這個播放器其實就沒有神馬原理,就是把一個視頻分成好幾個視頻,點播的時候通過關鍵幀(keyframe)索引找到離點擊點最近的關鍵幀播放。如果當前快要播放完,就去加載下一段。當前播放完,就播放下一段。

原理很簡單,實現起來卻很是糾結,痛苦。因為本質上是一開始就創建了幾個video,播放,點擊,緩沖等時候就需要不停的計算下一個要播放的第幾個video,然后像放幻燈片那樣切換,雖然不明顯。然后計算的時候兼容flv和mp4也很無語,就像搞瀏覽器兼容一樣。另外,點播的時候會多一次請求,用於獲取關鍵幀信息。如果這個關鍵幀信息很大很長的話,體驗是非常不好的。

這么多不足,就嘗試下另一種方案hls.

hls m3u8的優點在於不錯的兼容性。

可以看到m3u8通殺移動設備,而不支持的設備都是瀏覽器,可以通過flash播放器解決。事實上現在有長視頻的視頻網站在pc端基本上都用的m3u8.短視頻的話用視頻偽流技術(pseudo streaming)就已經可以滿足要求了。

具體怎么寫呢?通常都是用的osmf和HLSProvider,比如百度雲網盤里面的播放器,

HLSProvider(org.denivip.osmf包)已經封裝好了一切,com.baidu.player.DiskPlayer里面是很簡單的調用。

然而我卻發現了更好的東西flashls。 這個東西好在體積小,它例子里的flashlsChromeless.swf只有36kb.剛開始我不敢相信,因為那些視頻網站的swf(皮膚swf除外)都是150kb以上,這讓我覺得好像寫一個flash hls寫完真的就要好幾百kb,另外,它沒有其他依賴,比如加密類,osmf....

事實上,真正對hls m3u8的解析要不了那么多.

這類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地址就可以使用了。

hls_example

播放器源碼

只有44kb!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM