整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

安卓webview html5 自动播放本地视频,网上视频,可以循环播放

们做安卓开发时,webview中发现html5 视频的autoplay根本没实现,查看了下android 源码HTMLVideoElement.cpp也未见相应处理,网上有资料显示,这可能关乎UI线程与后台线程的协调性,我看,以后的升级版本总会解决这个问题的。

html5标签属性不支持,那只有通过javascript来处理了

演示apk下载地址:http://download.csdn.net/detail/qiushi_1990/9513038

github源码下载:https://github.com/qiushi123/H5Demo

下面是实现步骤和原理

一,我们在设置的WebViewClient中覆盖方法:

不要忘记了webview.getSettings().settings.setJavaScriptEnabled(true)启用javascript,AndroidManifest加入INTERNET权限

@Override

public void onPageFinished( WebView view, String url) {

view.loadUrl("javascript:try{autoplay();}catch(e){}");

}

实例代码

 public class Html5VideoAutoPlay extends Activity {
		WebView webview = null;
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.html5video);
			webview = (WebView)findViewById(R.id.webview);
			webview.getSettings().setJavaScriptEnabled(true);
			webview.setWebViewClient(new WebViewClient(){
				/**
				 * 当前网页的链接仍在webView中跳转
				 */
				@Override
				public boolean shouldOverrideUrlLoading(WebView view, String url) {
					view.loadUrl(url);
					return true;
				}
				/**
				 * 处理ssl请求
				 */
				@Override
				public void onReceivedSslError(WebView view,
						SslErrorHandler handler, SslError error) {
					handler.proceed();
				}
				/**
				 * 页面载入完成回调
				 */
				@Override
				public void onPageFinished(WebView view, String url) {
					super.onPageFinished(view, url);
					view.loadUrl("javascript:try{autoplay();}catch(e){}");
				}
			});
			webview.setWebChromeClient(new WebChromeClient() {
				/**
				 * 显示自定义视图,无此方法视频不能播放
				 */
				@Override
				public void onShowCustomView(View view, CustomViewCallback callback) {
					super.onShowCustomView(view, callback);
				}
			});
			webview.loadUrl("file:///sdcard/html/video.html");
		}
		@Override
		protected void onPause() {
			if(null != webview) {
				webview.onPause();
			}
			super.onPause();
		}
 }

二,布局文件

html5video.xml

	<?xml version="1.0" encoding="utf-8"?>
		<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
			android:layout_width="match_parent"
					android:layout_height="match_parent"
					android:orientation="vertical" >
		<WebView android:id="@+id/webview"
			android:layout_width="match_parent"
			android:layout_height="match_parent"/>
		</LinearLayout>

三,html文件(这里用的是html5的video标签来设置自动播放和循环播放)

video.html

		<body>
		<video id="video" src="b.mp4" width="480" height="320" controls loop>
			don't support html5
		</video>
		</body>
		<script type="text/javascript">
			var video = document.getElementById("video");
			video.play();
		</script>

上面的src可以引入本地视频b.mp4,

也可以引入网上视频:http://2449.vod.myqcloud.com/2449_43b6f696980311e59ed467f22794e792.f20.mp4

个人觉得html5结合Android会让未来各种技术实现越来越简单。到我的个人博客查看更多h5与安卓的结合案例。

也可以关注我github里的其他开源项目。

github:https://github.com/qiushi123?tab=repositories

实例图片

效果图

有任何关于编程的问题都可以私信我,我看到后会及时解答。

编程小石头,为分享干货而生!据说,每个年轻上进,颜值又高的互联网人都关注了编程小石头。​

所周知,16年无疑是直播行业的春天,同时也是H5的一次高潮。

so,到现在用H5技术在移动端做网页直播也是见怪不怪了,但是!!!

今天我们的主角是webApp下播放视频

参考文献:

1)HTML5+CSS3+JQuery打造自定义视频播放器

简介

HTML5的<video>标签已经被目前大多数主流浏览器所支持,包括还未正式发布的IE9也声明将支持<video>标签,利用浏览器原生特性嵌入视频有很多好处,所以很多开发者想尽快用上,但是真正使用前还有些问题要考虑,尤其是 Opera/Firefox 和IE/Safari浏览器所支持的视频编码不同的问题,Google几个月前发布的开源视频编码VP8有望能解决这一问题,另外Google还发布了开放网络媒体项目WebM,旨在帮助开发者为开放网络制作出世界级媒体格式,Opera, Firefox, Chrome和IE9都将支持VP8,而且Flash Player也将可以播放VP8,这就意味着我们很快就可以只制作一个版本的视频然后在所有主流浏览器上播放了。另外一个主要的问题就是如何构建自定义的HTML5<video>播放器,这是目前Flash Player的优势所在,利用Flash的IDE所提供的接口可以很方便的构建一个个性化的视频播放器,那HTML5的<video>标签要怎样才能实现呢?这个问题就是本文所要解决的!我们将开发一个HTML5<video>视频播放器的jQuery插件,并且可以很方便的进行自定义,将分为以下几个部分:

1.视频控制工具条

2.视频控制按钮

3.打包成jQuery插件

4.外观和体验

5.自定义皮肤

视频控制工具条

做为一个专业的web开发人员,我们创建一个视频播放器时一定希望它的外观在各个浏览器中看起来一致(consistent),但是通过下面的图可以看到目前各个浏览器提供的视频控制工具条外观各不相同:

那就没办法了,我们得自己从头来创建这个控制工具条,利用HTML和CSS再加上一些图片实现起来并不算很难,另外通过HTML5多媒体元素提供的API我们可以很方便将创建的任何按钮与播放/暂停等事件进行绑定。

视频控制按钮

基本的视频控制工具条要包含一个播放/暂停按钮,一个进度条,一个计时器和一个音量控制按钮,我们将这些按钮放在<video>元素下面,并用一个div作为父容器:

Java代码

  1. <div class="ghinda-video-controls">

  2. <a class="ghinda-video-play" title="Play/Pause"></a>

  3. <div class="ghinda-video-seek"></div>

  4. <div class="ghinda-video-timer">00:00</div>

  5. <div class="ghinda-volume-box">

  6. <div class="ghinda-volume-slider"></div>

  7. <a class="ghinda-volume-button" title="Mute/Unmute"></a>

  8. </div>

  9. </div>

复制代码

注意,我们使用元素的class属性来代替ID属性是为了方便在一个页面上使用多个播放器。

打包成jQuery插件

创建好控制按钮后我们需要配合多媒体元素的API来实现视频控制的目的,正如前面提到的一样我们将我们的播放器打包成jQuery插件,这样可以很好的实现复用,代码如下:

Java代码

  1. $.fn.gVideo = function(options) {

  2. // build main options before element iteration

  3. var defaults = {

  4. theme: 'simpledark',

  5. childtheme: ''

  6. };

  7. var options = $.extend(defaults, options);

  8. // iterate and reformat each matched element

  9. return this.each(function() {

  10. var $gVideo = $(this);

  11. //create html structure

  12. //main wrapper

  13. var $video_wrap = $('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme);

  14. //controls wraper

  15. var $video_controls = $('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>');

  16. $gVideo.wrap($video_wrap);

  17. $gVideo.after($video_controls);

这里先假设您了解jQuery并知道如何创建一个jQuery插件,因为这个不在本文的讨论范围之内,在上面这段脚本中我们使用jQuery动态创建视频控制工具条的元素,接下来为了绑定事件我们需要获取对应的元素:

Java代码

  1. //get newly created elements

  2. var $video_container = $gVideo.parent('.ghinda-video-player');

  3. var $video_controls = $('.ghinda-video-controls', $video_container);

  4. var $ghinda_play_btn = $('.ghinda-video-play', $video_container);

  5. var $ghinda_video_seek = $('.ghinda-video-seek', $video_container);

  6. var $ghinda_video_timer = $('.ghinda-video-timer', $video_container);

  7. var $ghinda_volume = $('.ghinda-volume-slider', $video_container);

  8. var $ghinda_volume_btn = $('.ghinda-volume-button', $video_container);

  9. $video_controls.hide(); // keep the controls hidden

这里我们通过className方式获取,先让工具条隐藏直到所有资源加载完成,现在来实现播放/暂停按钮:

Java代码

  1. var gPlay = function() {

  2. if($gVideo.attr('paused') == false) {

  3. $gVideo[0].pause();

  4. } else {

  5. $gVideo[0].play();

  6. }

  7. };

  8. $ghinda_play_btn.click(gPlay);

  9. $gVideo.click(gPlay);

  10. $gVideo.bind('play', function() {

  11. $ghinda_play_btn.addClass('ghinda-paused-button');

  12. });

  13. $gVideo.bind('pause', function() {

  14. $ghinda_play_btn.removeClass('ghinda-paused-button');

  15. });

  16. $gVideo.bind('ended', function() {

  17. $ghinda_play_btn.removeClass('ghinda-paused-button');

  18. });

  19. 大多数浏览器在右键点击视频时会提供一个独立的菜单,它也提供了视频控制功能,如果用户通过这个右键菜单控制视频那就会跟我们的自定义控件冲突,所以为了避免这一点我们需要绑定视频播放器自身的“播放”,“暂停”和“结束”事件,在事件处理函数中处理播放/暂停按钮,控制按钮的样式。

    为了创建进度条的拖动块,我们使用了jQuery UI的Slider组件:

    Java代码

    1. var createSeek = function() {

    2. if($gVideo.attr('readyState')) {

    3. var video_duration = $gVideo.attr('duration');

    4. $ghinda_video_seek.slider({

    5. value: 0,

    6. step: 0.01,

    7. orientation: "horizontal",

    8. range: "min",

    9. max: video_duration,

    10. animate: true,

    11. slide: function(){

    12. seeksliding = true;

    13. },

    14. stop:function(e,ui){

    15. seeksliding = false;

    16. $gVideo.attr("currentTime",ui.value);

    17. }

    18. });

    19. $video_controls.show();

    20. } else {

    21. setTimeout(createSeek, 150);

    22. }

    23. };

    24. createSeek();

    正如你所看到的,这里我们写了一个递归函数,通过循环比较video的readyState属性来判断视频是否已经准备好,否则我们就不能获得视频的时长也无法创建滑动块,当视频准备好后我们初始化滑动块并显示控制工具条,下一步我们通过绑定video元素的timeupdate事件实现计时器功能:

    Java代码

    1. var gTimeFormat=function(seconds){

    2. var m=Math.floor(seconds/60)<10?"0"+Math.floor(seconds/60):Math.floor(seconds/60);

    3. var s=Math.floor(seconds-(m*60))<10?"0"+Math.floor(seconds-(m*60)):Math.floor(seconds-(m*60));

    4. return m+":"+s;

    5. };

    6. var seekUpdate = function() {

    7. var currenttime = $gVideo.attr('currentTime');

    8. if(!seeksliding) $ghinda_video_seek.slider('value', currenttime);

    9. $ghinda_video_timer.text(gTimeFormat(currenttime));

    10. };

    11. $gVideo.bind('timeupdate', seekUpdate);

    这里我们用seekUpdate函数获取video的currentTime属性值然后调用gTimeFormat函数进行格式化后得到当前播放的时间点。

    至于音量控制控件我们还是利用jQuery UI的Slider组件然后利用自定义函数实现静音和取消静音的功能:

    Java代码

    1. $ghinda_volume.slider({

    2. value: 1,

    3. orientation: "vertical",

    4. range: "min",

    5. max: 1,

    6. step: 0.05,

    7. animate: true,

    8. slide:function(e,ui){

    9. $gVideo.attr('muted',false);

    10. video_volume = ui.value;

    11. $gVideo.attr('volume',ui.value);

    12. }

    13. });

    14. var muteVolume = function() {

    15. if($gVideo.attr('muted')==true) {

    16. $gVideo.attr('muted', false);

    17. $ghinda_volume.slider('value', video_volume);

    18. $ghinda_volume_btn.removeClass('ghinda-volume-mute');

    19. } else {

    20. $gVideo.attr('muted', true);

    21. $ghinda_volume.slider('value', '0');

    22. $ghinda_volume_btn.addClass('ghinda-volume-mute');

    23. };

    24. };

    25. $ghinda_volume_btn.click(muteVolume);

    26. 最后当我们自己的自定义视频控制工具条构造完成后需要移除<video>标签的controls属性,这样浏览器默认的工具条就被去掉了。

      好了,我们的插件功能已经全部完成了,调用方法:

      Java代码

      1. $('video').gVideo();

      这会将我们的插件应用到页面上每一个video元素上。

      外观和体验

      好的,现在到了比较有意思的部分,也就是播放器的外观和体验了。当插件功能已经完成后利用一点CSS就可以很容易地自定义样式了,我们将全部使用CSS3来实现。

      首先,我们给播放器主容器加一些样式:

      Java代码

      1. .ghinda-video-player {

      2. float: left;

      3. padding: 10px;

      4. border: 5px solid #61625d;

      5. -moz-border-radius: 5px; /* FF1+ */

      6. -ms-border-radius: 5px; /* IE future proofing */

      7. -webkit-border-radius: 5px; /* Saf3+, Chrome */

      8. border-radius: 5px; /* Opera 10.5, IE 9 */

      9. background: #000000;

      10. background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */

      11. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */

      12. box-shadow: inset 0 15px 35px #535353;

      13. }

      14. 下一步,我们设置视频控制工具条左边浮动使它们水平对齐,利用CSS3的opacity和transitions我们给播放/暂停和静音/取消静音按钮添加了非常不错的悬浮效果:

        Java代码

        1. .ghinda-video-play {

        2. display: block;

        3. width: 22px;

        4. height: 22px;

        5. margin-right: 15px;

        6. background: url(../images/play-icon.png) no-repeat;

        7. opacity: 0.7;

        8. -moz-transition: all 0.2s ease-in-out; /* Firefox */

        9. -ms-transition: all 0.2s ease-in-out; /* IE future proofing */

        10. -o-transition: all 0.2s ease-in-out; /* Opera */

        11. -webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */

        12. transition: all 0.2s ease-in-out;

        13. }

        14. .ghinda-paused-button {

        15. background: url(../images/pause-icon.png) no-repeat;

        16. }

        17. .ghinda-video-play:hover {

        18. opacity: 1;

        19. }

        20. 如果您仔细看了前面那段根据视频播放状态(Playing/Paused)添加和移除播放/暂停按钮样式的JavaScript代码,就会明白为什么.ghinda-paused-button为什么要重写.ghinda-video-play的背景属性了。

          现在轮到滑动块了,我们进度条和音量控制的滑动块的实现都是利用了jQuery UI的Slider组件,这个组件它本身自带了样式,定义在jQuery UI对应的css文件中,但是为了使滑动块和播放器其他控件外观保持一致我们全部重写了它的样式:

          Java代码

          1. .ghinda-video-seek .ui-slider-handle {

          2. width: 15px;

          3. height: 15px;

          4. border: 1px solid #333;

          5. top: -4px;

          6. -moz-border-radius:10px;

          7. -ms-border-radius:10px;

          8. -webkit-border-radius:10px;

          9. border-radius:10px;

          10. background: #e6e6e6;

          11. background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5);

          12. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5));

          13. box-shadow: inset 0 -3px 3px #d5d5d5;

          14. }

          15. .ghinda-video-seek .ui-slider-handle.ui-state-hover {

          16. background: #fff;

          17. }

          18. .ghinda-video-seek .ui-slider-range {

          19. -moz-border-radius:15px;

          20. -ms-border-radius:15px;

          21. -webkit-border-radius:15px;

          22. border-radius:15px;

          23. background: #4cbae8;

          24. background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce);

          25. background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce));

          26. box-shadow: inset 0 -3px 3px #39a2ce;

          27. }

          28. 这时候音量控制的滑动块一直显示在音量按钮旁边,我们需要将它改成默认隐藏,当鼠标悬浮在音量按钮上再动态显示出来,使用transitions来实现这个效果会是个不错的的选择:

            Java代码

            1. .ghinda-volume-box {

            2. height: 30px;

            3. -moz-transition: all 0.1s ease-in-out; /* Firefox */

            4. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

            5. -o-transition: all 0.2s ease-in-out; /* Opera */

            6. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

            7. transition: all 0.1s ease-in-out;

            8. }

            9. .ghinda-volume-box:hover {

            10. height: 135px;

            11. padding-top: 5px;

            12. }

            13. .ghinda-volume-slider {

            14. visibility: hidden;

            15. opacity: 0;

            16. -moz-transition: all 0.1s ease-in-out; /* Firefox */

            17. -ms-transition: all 0.1s ease-in-out; /* IE future proofing */

            18. -o-transition: all 0.1s ease-in-out; /* Opera */

            19. -webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */

            20. transition: all 0.1s ease-in-out;

            21. }

            22. .ghinda-volume-box:hover .ghinda-volume-slider {

            23. position: relative;

            24. visibility: visible;

            25. opacity: 1;

            26. }

            27. 利用一些基础的CSS属性以及CSS3提供的新属性我们打造了一个全新的播放器外观,它看起来是这个样子:

              自定义皮肤

              可能您已经注意到,我们在编写插件的时候已经定义了一些默认选项,它们是theme和childtheme,可以在调用插件的时候根据需要方便的应用自定义皮肤。

              这里解释下theme就是所有控件的一整套样式定义,childtheme就是在theme基础上重写某些样式,我们在调用插件的时候可以同时指定这两个选项或者其中的一个:

              Java代码

              1. $('video').gVideo({

              2. childtheme:'smalldark'

              3. });

              我们写了一个示例的皮肤smalldark,它只重写了部分的样式,显示效果是这样的:

              总结

              利用HTML5 video,JavaScript和CSS3打造自定义的视频播放器真的非常容易,t实现工具条功能用JavaScrip,外观和体验交给CSS3,我们得到了一个功能强大并且易于定制的解决方案!

              enjoy!

              2)mui Html5 Video 实现方案

              前言: 最近项目中需要用到html5 视频播放功能,于是稍微研究了解了下,遇到了很多坑,特此记录下.

              一、 Html5 Video

              参考来源: http://www.xuanfengge.com/html5-video-play.html

              (这篇博文确实帮助很大)

              1.1、 目的

              将Html5 Video功能应用到实际项目中,针对不同的平台和环境,进行个性化处理。

              基本只考虑webkit浏览器兼容问题

              1.2、 Html5 Video支持格式

              只支持: .mp4后缀(.h264编码格式),和.webm后缀(专用web视频格式),以及.ogg后缀(音频文件)

              注意: Html5 Video 可以添加多个source源来进行兼容适配,这样,当第一个源读取出问题时会自动读取下一个源. 比如可以同时在前面加上.webm和.mp4源,这样一个出错时会自动读取另一个可用源(因为不同浏览器,支持的格式是不一样的)

              但是,Hybird模式的 Android 下,有些机型只能读取第一个source来源(测试华为和联想都是),所以也就是说在这种情况下,要确保第一个source源是正确的

              各大浏览器兼容如图所示:

              见图1

              1.3、 不同平台环境和对应实现方案

              说明: 这里分为两大块,普通浏览器环境(pc和手机,主要是移动端,pc不做特别处理)和Hybird模式的APP环境(Android和iOS).

              注: Html5 video可以播放本地视频或者网络视频

              1.3.1、 普通浏览器环境

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              注: 播放效果则由各大浏览器自行实现

              手机端浏览器实现的不同效果,比如:

              QQ浏览器(包括QQ客户端内置的浏览器):播放时会自动进入全屏

              华为自带浏览器: 正常小窗口播放

              1.3.2、 Hybird App环境

              说明: 内联播放是指直接在video标签中播放视频,没有必要进入全屏

              1.3.2.1、 Android内联播放

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              *Android内联播放需要注意,必须开启硬件加速,由于有些Android手机 webview是默认关闭硬件加速的,所以必须在创建这个带视频播放的webview时手动添加 硬件加速属性才行.(详情见plus创建webview的style)

              style.hardwareAccelerated = true;

              1.3.2.2、 iOS内联播放

              *用Html5 Video 自带的播放栏控件

              *用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.

              *内联播放注意要点,由于iOS下默认是全屏播放的,所以需要经过设置才能正常内联播放

              第一步:在项目的manifest里面配置允许webview内联播放

              "plus": {
               "splashscreen": {
               "autoclose": true,/*是否自动关闭程序启动界面,true表示应用加载应用入口页面后自动关闭;false则需调plus.navigator.closeSplashscreen()关闭*/
               "waiting": true/*是否在程序启动界面显示等待雪花,true表示显示,false表示不显示。*/
               },
               "allowsInlineMediaPlayback": true,/*设置ios下允许内联播放*/
               "popGesture": "close"

              第二步: 创建video标签时,手动加上内联播放的属性(iOS不支持preload)

              <!--
               让ios支持内联播放,必须添加 webkit-playsinline 标签
               -->
               <video webkit-playsinline id="videoMedia" controls="controls" preload>

              这样iOS手机在播放的时候才会采用内联播放

              1.3.2.3、 Android非内联播放

              *通过NJS使用原生播放器来播放视频,传入的url可以是本地的或网络的地址

              *用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接获取视频的资源地址,传给原生播放器播放

              注: 这种模式下,性能要比直接html5自带播放器播放高

              1.3.2.4、 iOS非内联播放

              *用Html5 Video 自带的播放栏控件(非内联播放需要去除特定内联属性”webkit-playsinline”,这样才能全屏播放)

              if(!isInlinePlay){
               //如果是非内敛,ios需要去除内联样式
               mediaTarget.removeAttribute('webkit-playsinline');
               }

              *用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接调用video.play()播放视频(这时候会用一个全屏播放器来播放视频)

              1.3.3、 注意要点

              如果采用NJS通过Android原生播放器播放视频,目前无法监听到视频的一些自定义事件.(如下载中,播放完毕,暂停,播放时间等)

              而如果采用Html5 Video自带播放,这些是可以通过脚本控制的.

              所以选定方案时需要进行衡量

              *另外,在Html5 Video播放时,无法监听到规定的结束事件seeked,只能在timeUpdate里面判断,如果ended为true就代表结束了.

              *在NJS通过Android原生播放器播放时,可以通过document监听resume和pause事件判断是否进入播放和退出播放

              1.4、 Tips

              1.4.1、 关于Video 视频统一处理的方案

              说明: 由于将一个<Video>直接显示在页面中,会有各种五花八门的播放器效果,如图:

              (这里引用了参考来源的图)

              见图2

              显然,体验效果并不好,所以现在的做法是用一张模拟播放的图片来替代<Video>所在的地方,而将Video元素设置为1*1像素大小.然后给图片设置点击监听,监听到点击时,播放视频.

              注意:

              *这里不要用{display: none}或者{width:0;height:0;}的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦.

              *这里没有考虑ios<6和一些低版本的Android的兼容性问题了(这些版本里,无法直接通过video.play()来播放),因为项目环境基本上要求Android>4.0 iOS 7.0以上的.

              *关于点击图片播放视频后,如果是内联播放模式下(或者是普通浏览器),就应该将图片隐藏,然后将视频大小设置为本来的大小(一般为图片大小);如果是非内联播放模式(全屏模式),就没有必要隐藏图片了,因为iOS下会自动打开一个全屏播放器来播放视频,Android下考虑到Html5 video较卡,所以也会通过NJS使用原生播放器来全屏播放视频.

              1.4.2、 Android NJS播放视屏的实现代码

              说明: 这个是Dcloud论坛中有人分享的

              //非内联模式下的plus下的android才用到
               var Intent = plus.android.importClass("android.content.Intent");
               var Uri = plus.android.importClass("android.net.Uri");
               var main = plus.android.runtimeMainActivity();
               var intent = new Intent(Intent.ACTION_VIEW);
               var uri = Uri.parse(url);
               intent.setDataAndType(uri, "video/*");
               main.startActivity(intent);

              1.4.3、 如何读取元素的宽高

              *在获取视频的宽度时,发现用 video.style.width无法获取到宽度.

              后来查了资料,发现dom.style.width(height)只能获取在stye直接赋予的值.而如果是通过css样式表赋予的值是无法直接获取的,只能通过dom.offsetWidth(offsetHeight)获取.

              *设置元素宽和高是不要直接在style里设置,而是最好通过css样式表赋予

              *读取元素宽和高时,用offsetWidth(offsetHeight)

              1.4.4、 关于全屏播放的问题

              在PC端webkit浏览器下,全屏代码如下:

              进入全屏: videoContainer(对应的dom).webkitRequestFullscreen();

              退出全屏: document.webkitCancelFullScreen();

              *但是经测试,在手机浏览器和Hybird模式下的手机环境中都无法使用,

              应该是手机浏览器中video 播放器的全屏模式和pc端的有区别,已经脱离了webkit的全屏组件,而是用原生自己实现的.

              1.5、 遇到问题及解决方法

              1.5.1、 Video.currentTime 设置值时设置无效,或者变为0

              原因分析:

              与测试的服务器和端口有关,测试环境是放在hbuild本地浏览器的,没有处理好视频快进问题,所以会导致每次快进后,视频都会重置-在某些测试服务器上,则出现快进无效,但不会重置

              解决方法:

              将网页用其它正式服务器打开均可正常,如tomcat,wampserver,甚至直接在本地打开也行.

              1.5.2、 无法通过代码Video.src获取资源路径

              原因分析:

              本实例中,Video是通过source添加src的,无法直接读取video的src

              解决方法:

              可以通过读取到第一个source的标签,再获取source的src

              注:本来这个方法有一个缺点就是有可能第一个source的src不可用.但是由于Android中第一个source必须有用才行.否则无法正常播放.所以在确保第一个source正确的情况下能这样用.

              1.5.3、 部分Android机型无法退出全屏

              描述:

              在使用Html5 Video自带播放器播放时,部分Android机型(如联想K860点击全屏按钮进入全屏后,无法退出全屏-因为进入全屏后,全屏按钮不见了)

              原因分析: 可能是手机厂商擅自劫持了浏览器或者篡改了浏览器实现方式

              解决方法:

              目前无法解决,在这类机型中,建议直接采用非内联模式播放或者是尽量不要手动进入全屏

              3)移动端HTML5<video>视频播放优化实践

              遇到的挑战

              移动端HTML5使用原生<video>标签播放视频,要做到两个基本原则,速度快和体验佳,先来分析一下这两个问题。

              下载速度

              以一个8s短视频为例,wifi环境下提供的高清视频达到1000kbps,文件大小大约1MB;非wifi环境下提供的低码率视频是500kbps左右,文件大小大约500KB;参考QzoneTouch多普勒测速,2g网络的平均速度是14KB/s,那么下载一个低码率视频耗时35s;那么要想流畅播放视频,就需要一个加载等待的过程,这个过程要有明确的反馈,不能让用户有“坏掉了”的感觉。

              多普勒测速数据参考

              #dns(s)conn(s)rtt(s)tran(kb/s)
              2g3.857852.334822.5747814.0374
              3g1.606430.7431090.60804760.1967
              wifi0.9869210.5502080.44433270.8728

              用户体验

              视频是否可以自动播放,是否能循环播放,是否能显示下载进度,播放的时候如何隐藏控制条,暂停的时候又能显示出来呢。这些问题看上去貌似简单,但是由于PC/iOS/Android这些不同平台、不同的浏览器内核、甚至相同内核的不同版本,所实现的<video>属性、方法和事件差异较大,解决兼容性问题又给开发造成了很大困扰。

              分析原因

              事件差异

              下面是播放一个短视频,在不同平台触发事件和获取属性的差异表现。

              PC

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2suspendNOTHING0
              3playNOTHING0
              4waitingNOTHING0
              5durationchangeMETADATA05.357.91获取到视频长度
              6loadedmetadataMETADATA00.667.91获取到元数据
              7loadeddataENOUGHDATA00.667.91
              8canplayENOUGH_DATA00.667.91
              9playingENOUGH_DATA00.667.91开始播放
              10canplaythroughENOUGH_DATA00.667.91可以流畅播放
              11progressENOUGH_DATA0.113.687.91持续下载
              12timeupdateENOUGH_DATA0.144.447.91播放进度变化
              23progressENOUGH_DATA1.777.917.91下载完毕
              24suspendENOUGH_DATA1.777.917.91
              25timeupdateENOUGH_DATA1.97.917.91继续播放中
              48timeupdateENOUGH_DATA7.77.917.91
              49timeupdateENOUGH_DATA07.917.91
              50seekingMETADATA07.917.91
              51waitingMETADATA07.917.91
              52timeupdateENOUGH_DATA07.917.91
              53seekedENOUGH_DATA07.917.91播放完毕进度回到起点
              54canplayENOUGH_DATA07.917.91
              55playingENOUGH_DATA07.917.91循环播放
              56canplaythroughENOUGH_DATA07.917.91
              57timeupdateENOUGH_DATA0.197.917.91

              iOS

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2playNOTHING0
              3waitingNOTHING0
              4durationchangeMETADATA07.91获取到视频长度
              5loadedmetadataMETADATA07.91获取到元数据
              6loadeddataENOUGHDATA07.91
              7canplayENOUGH_DATA07.917.91
              8canplaythroughENOUGH_DATA07.917.91可以流畅播放
              9playingENOUGH_DATA07.917.91开始播放
              10progressENOUGH_DATA07.917.91下载完毕
              11suspendENOUGH_DATA07.917.91
              12timeupdateENOUGH_DATA0.027.917.91播放进度变化
              43timeupdateENOUGH_DATA7.87.917.91
              44timeupdateENOUGH_DATA07.917.91
              45seekedENOUGH_DATA07.917.91播放完毕进度回到起点
              46timeupdateENOUGH_DATA0.227.917.91循环播放

              Android

              #eventreadyStatecurrentTime (s)buffered (s)duration (s)视频状态
              1loadstartNOTHING0
              2playNOTHING0
              3waitingNOTHING00
              4durationchangeENOUGH_DATA000
              5durationchangeENOUGH_DATA007.91获取到视频长度
              6loadedmetadataENOUGH_DATA007.91获取到元数据
              7loadeddataENOUGHDATA007.91
              8canplayENOUGH_DATA007.91
              9canplaythroughENOUGH_DATA007.91
              10playingENOUGH_DATA007.91
              11timeupdateENOUGH_DATA007.91
              12progressENOUGH_DATA03.577.91下载中
              13timeupdateENOUGH_DATA0.26.897.91开始播放
              14progressENOUGH_DATA07.917.91下载完毕
              49timeupdateENOUGH_DATA7.797.917.91
              50progressENOUGH_DATA7.877.917.91
              51timeupdateENOUGH_DATA07.917.91
              52seekingENOUGH_DATA07.917.91播放完毕进度回到起点
              53timeupdateENOUGH_DATA07.917.91
              54seekedENOUGH_DATA07.917.91循环播放失败卡住了
              55progressENOUGH_DATA07.917.91
              56stalledENOUGH_DATA07.917.91

              一些常用且需要重点关注的<video>事件

              eventiOSAndroid
              ****************************************************************************************************************
              play只是要播放视频,响应的是video.play()方法,并不代表已经开始播放和iOS一样,仅是响应video.play()方法
              durationchange会执行一次,一定会获取到视频的duration可能会执行多次,只有最后一次才能获取到真实的duration,前面的duration都是0;但低版本Android可能获取到的duration是0或1;(本文提到的低版本Android大部分是4.1以下)
              canplay可以认为是视频元素没有问题,可以运行,没有更多含义了,基本用不上同iOS
              canplaythrough会有明确的缓冲,表示可以流畅播放了;没有什么用,视频仍然会卡住,数据可能还没有开始加载;
              playing明确表示播放开始了;依然没有用,视频可能并没有开始播放;
              progress有明确的下载,可以获取到当前的buffer,并且全部下载完毕后不在触发;不一定有明确的数据下载,并且全部下载完毕后依然继续触发;
              timeupdate会有明确的进度变化,可以获取到currentTime;进度不一定变化,currentTime可能总是0,但是第一次有currentTime变化的timeupdate事件一定代表了视频开始播放了;
              erroriOS中会有明确的错误抛出;Android中某些浏览器会莫名其妙的抛出error;
              stalled网络状况不佳,导致视频下载中断;在没有play之前,也可能会抛出该事件。

              属性差异

              attributesiOSandroid
              ****************************************************************************************************************
              poster

              封面图片

              支持,但是加载速度明显比在<img>中要慢;不一定支持(浏览器厂商的实现标准不统一);
              preload

              预加载

              iPhone不支持;可能支持;
              autoplay

              自动播放

              iPhone Safari中不支持,但在webview中可能被开启;iOS开发文档明确说明蜂窝网络下不允许autoplay;可能支持;
              loop

              循环播放

              支持可能支持;
              controls

              控制条

              支持,但是需要开始播放了才显示基本都支持显示或者不显示
              width和height一定给出明确的属性设置,切不能为0;如果不设置,仅仅通过CSS样式去控制视频大小,可能会导致标签失效。

              其他怪异bug和不友好表现

              iOSandroid
              ******************************************************************************************************************
              物理位置覆盖在<video>区域上的元素,click和touch等事件会失效,比如一个<a>链接如果覆盖在<video>上,那么点击后没有任何效果。
              iOS8.0+中,单页面播放视频超过16个,再播放的视频全部MediaError解码异常无法播放。
              iPhone的Safari会弹出一个全屏的播放器来播放视频,iPad则支持内联播放。iOS7+ 如果webview(比如微信)开启了webview.allowsInlineMediaPlayback = YES;,可以通过设置webkit-playsinline属性支持内联播放;支持内联播放,但某些厂商会用自己的播放器劫持原生的视频播放;
              下载视频时,会先发送一个2字节的请求来获取视频元数据(比如时长),然后再不断的发送分包续传(206)请求来下载视频,抓包显示请求数和请求量至少有一倍的冗余(x2),这个严重的bug在iOS8中有明显的修复,但是分包的206请求仍然会有冗余数据的下载,浪费了流量。比iOS的处理方式好,没有第一个2字节请求,没有流量损耗;
              低版本Android(<=4.0.4)中,<video>如果在有相对和决定定位的层中,可能会导致整个页面错位。
              某些浏览器厂商会劫持<video>,用其“自己”的播放器来播放视频,“破坏”了产品本身的播放体验,那么只能case by case的解决了。
              加载视频时没有进度提示,视觉上看不出是播放完了还是卡住了;加载视频时,大都会显示一个自带的loading UI(菊花)。

              最佳实践

              视频初始化

              如果将一个<video>直接显示在页面中,那么就会看到各种五花八门的播放器初始效果;

              这显然不是一个好的视觉体验,那么通常的做法是制作一个模拟的视频播放视图,比如一个封面加一个播放按钮。

              而真实的<video>视频元素要隐藏起来,如何隐藏呢?最好不要用{display: none}或者{width:0;height:0;}的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦。最佳的方式是将视频设置成1×1像素大小,放在视觉边缘的位置。

              1

              2

              3

              4

              5

              <!--iOS-->

              <video webkit-playsinline width="1"height="1"class="vplayinside notaplink"x-webkit-airplay controls loop="loop"src="<%=src%>"></video>

              <!--Android-->

              <video width="1"height="1"controls loop="loop"src="<%=src%>"></video>

              自动播放

              autoplay的支持依赖内核和网络状况,比如iPhone在蜂窝网络下明确禁用了autoplay;

              经过试验,在没有明确的用户操作的情况下,直接通过video.play()也是无法激活播放的;

              并且在产品设计上,自动播放也不是一个舒服的用户体验,所以产品设计上尽量避免使用自动播放。

              点击播放

              之前提到,视频最好通过1px大小隐藏起来,那么这时如何触发播放呢?

              经过试验,当在明确的用户操作(touch、click)时,通过这些用户行为事件的回调函数,用video.play()是可以触发视频播放的,那么能否在用户操作后,再去同步的创建和播放视频呢?答案是肯定的,这无疑是一个视频元素初始化的最佳实践,但是有些差异需要注意。

              iOS6+

              可以在用户的touch时间中动态创建并播放视频。

              iOS < 6

              可以在用户的touch时间中动态创建视频,但不能播放;要再追加一个click事件来启动播放;也就是说,给伪造的视频播放按钮同时绑定tap和click事件,在tap的时候创建,在之后300毫秒的click中去播放。

              Android

              大部分高版本Android可以像iOS6+那样去处理,但是低版本的不行,必须要通过click事件去传递video.play(),为了保持兼容,最好是用帮tap和click两个事件来分别完成视频的初始化和播放。

              我们还发现,有些低版本Android中,无法通过video.play()来播放视频,必须有真实的用户点击视频元素才能播放;这种情况,有一个技巧就是在tap的时候初始化并放大视频覆盖在播放视图中,让300毫秒后的真实点击行为穿透点击在视频元素上来实现播放。

              循环播放

              如果视频需要循环播放,那么就增加loop属性,是否能循环播放就看浏览器是否支持了,因为还没有找到hack技巧来强制循环播放;

              即使,在不支持循环播放的Android中,通过监听seeked事件知道了播放进度到了终点或起点暂停了,此时也无法通过video.play()来让视频重新播放。

              监控下载进度

              如何获取视频时长和已经下载的时长?

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              // 视频时长

              varduration=video.duration

              // 获取视频已经下载的时长

              functiongetEnd(video){

              varend=0

              try{

              end=video.buffered.end(0)||0

              end=parseInt(end*1000+1)/1000

              }catch(e){

              }

              returnend

              }

              progress事件表示视频在加载,但是它的触发频率和时机并不规律,最佳做法是通过一个定时器去实时获取end,当end >= duration时,表示已经下载完毕,再终止定时器。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              vartimer=setInterval(function(){

              varend=getEnd(video),

              duration=video.duration

              if(end<duration){

              return

              }

              clearInterval(timer)

              },1000)

              全部下载后再播放

              假设播放短视频,如果网络不佳,会造成播放断断续续,在iOS中这种停顿还没有一个明确的等待提示,这不是一个好的体验,那么是否可以将视频全部下载完毕再播放呢?

              在iOS中,可以在视频刚开始下载的时候马上暂停,此时下载还将继续,可以做一个loading的菊花告知视频正在加载,然后等到视频全部下载完再开始播放。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              14

              15

              16

              17

              18

              19

              20

              21

              22

              23

              24

              25

              $(video).one('loadeddata',function(){

              // 暂停,但下载还在继续

              video.pause()

              // 启动定时器检测视频下载进度

              vartimer=setInterval(function(){

              varend=getEnd(video),

              duration=video.duration

              if(end<duration){

              return

              }

              varwidth=$(video).parent().width()

              // 下载完了,开始播放吧

              $(video).attr{

              width:width,

              height:width

              }

              video.play()

              clearInterval(timer)

              },1000)

              })

              缓冲播放——边下边播时,选择开始播放的最佳时间点

              当视频越来越长或者网络慢时,等待视频全部下载完再播放也不是好的体验,最好能边下边播,缓冲到流畅状态就开始播放,那什么时候播放才是最佳时间点呢?

              在iOS中,canplaythrough事件就是这个最佳时间点,它是通过动态计算缓冲量和下载速度得出的视频可以流畅播放的状态反馈。

              canplaythrough event: The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.

              注意:下载完再播放和缓冲播放只适用于iOS。

              统计播放时间和播放次数

              要统计实际的播放时间,要累加timeupdate事件变化的时间,再减去中间可能暂停的时间。

              1

              2

              3

              4

              5

              6

              7

              8

              9

              10

              11

              12

              13

              14

              15

              16

              17

              18

              19

              20

              21

              22

              23

              24

              25

              26

              27

              28

              29

              30

              $video.on('playing',function(){

              // 开始播放是打点

              $video.attr('data-updateTime',+newDate())

              })

              $video.on('pause',function(){

              // 暂停播放时清除打点

              $video.removeAttr('data-updateTime')

              })

              // 累加播放时间

              $video.on('timeupdate',function(event){

              var$video=$(event.target),

              updateTime=parseInt($video.attr('data-updateTime')||0),

              playingTime=parseInt($video.attr('data-playingTime')||0),

              times=parseInt($video.attr('data-times')||0),

              newtimes=0,

              video=$video.get(0),

              duration=parseFloat($video.attr('data-duration')||0),

              now=+newDate()

              // 播放时间

              playingTime=playingTime+now-updateTime

              // 播放次数

              newtimes=Math.ceil(playingTime/1000/duration)

              $video.attr('data-playingTime',playingTime)

              $video.attr('data-updateTime',now)

              })

              异常处理

              对error事件做详细的上报;

              对stalled事件做统计上报,并提示用户网络慢等。

              参考数据

              微视触屏版iOS视频测速

              网络环境视频码率获取到视频时长

              时间点(s)

              开始流畅播放

              时间点(s)

              全部下载完毕

              时间点(s)

              视频长度(s)
              wifi1000kbps2.863.975.858.69
              非wifi500kbps4.56810.628.67

              搬好凳子看HTML

              首先我们在HB下创建一个新的app项目,名称为 欠债

              新建一个video.html

              webkit-playsinline : 在ios中,加入此属性,可以关闭自动全屏播放

              object-fit:fill : 视频充满video容器的大小

              详细理由请看参考文献2or3

              在此我们向项目里放置一个mp4格式的视频,视频内容不限,可以是小动画,也可以是

              ps:要在meta中加上,否则视频会扩充变形哦

              <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />

              OK,现在布局已经完成,一个视频已经在页面中了

              旁白:尼玛,点了没反应,那这怎么播放?

              楼主:你们这群家伙看别的小视频等个1小时都行。。。

              旁白:一个简单的播放器,至少要有 暂停/播放,进度条,视频时长,全屏等控件吧

              楼主:来来来,不要急,先来个播放按钮写在video标签后面

               <div class="bad-video">
               <video class="" webkit-playsinline style="object-fit:fill;">
               <source src='xx.mp4' type="video/mp4"></source>
               <p>设备不支持</p>
               <video>
               <img src="img/play.png"/>
               </div>

              写好样式、

               .bad-video { position: relative; overflow: hidden; background-color: #CCCCCC;
               } 
               .bad-video .vplay{ position: absolute; width: 15%; z-index: 99; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);
               }

              楼主:当当当

              再在后面加一个控制条

               <img src="img/play.png" class="vplay" /> <div class="controls">
               <div>
               <div class="progressBar">
               <div class="timeBar"></div>
               </div>
               </div>
               <div><span class="current">00:00</span>/<span class="duration">00:00</span></div>
               <div><span class="fill">全屏</span></div>
               </div>
              .bad-video .controls { width: 100%; height: 2rem; line-height: 2rem; font-size: 0.8rem; color: white; display: block; position: absolute; bottom: 0; background-color: rgba(0, 0, 0, .55); display: -webkit-flex; display: flex;
              }.bad-video .controls>* { flex: 1;
              }.bad-video .controls>*:nth-child(1) { flex: 6;
              }.bad-video .controls>*:nth-child(2) { flex: 2; text-align: center;
              }.bad-video .controls .progressBar { margin: .75rem 5%; position: relative; width: 90%; height: .5rem; background-color: rgba(200, 200, 200, .55); border-radius: 10px;
              }.bad-video .controls .timeBar { position: absolute; top: 0; left: 0; width: 0; height: 100%; background-color: rgba(99, 110, 225, .85); border-radius: 10px;
              }

              总算有个看起来像样的了

              旁白:楼主,可是还是不能播放啊

              楼主:叫你别急,要不你先去撸一把,我写好了文字@你

              旁白:好啊,早说嘛,我先走了,记得@我

              楼主:你走,省的我精神分裂码两个人的字

              好,现在Html元素已经基本上弄好啦,看起来不是那么low了

前不久抽空对目前比较火的视频直播,做了下研究与探索,了解其整体实现流程,以及探讨移动端HTML5直播可行性方案。

发现目前 WEB 上主流的视频直播方案有 HLS 和 RTMP,移动 WEB 端目前以 HLS 为主(HLS存在延迟性问题,也可以借助 video.js 采用RTMP),PC端则以 RTMP 为主实时性较好,接下来将围绕这两种视频流协议来展开H5直播主题分享。

一、视频流协议HLS与RTMP

1. HTTP Live Streaming

HTTP Live Streaming(简称 HLS)是一个基于 HTTP 的视频流协议,由 Apple 公司实现,Mac OS 上的 QuickTime、Safari 以及 iOS 上的 Safari 都能很好的支持 HLS,高版本 Android 也增加了对 HLS 的支持。一些常见的客户端如:MPlayerX、VLC 也都支持 HLS 协议。

HLS 协议基于 HTTP,而一个提供 HLS 的服务器需要做两件事:

编码:以 H.263 格式对图像进行编码,以 MP3 或者 HE-AAC 对声音进行编码,最终打包到 MPEG-2 TS(Transport Stream)容器之中;分割:把编码好的 TS 文件等长切分成后缀为 ts 的小文件,并生成一个 .m3u8 的纯文本索引文件;浏览器使用的是 m3u8 文件。m3u8 跟音频列表格式 m3u 很像,可以简单的认为 m3u8 就是包含多个 ts 文件的播放列表。播放器按顺序逐个播放,全部放完再请求一下 m3u8 文件,获得包含最新 ts 文件的播放列表继续播,周而复始。整个直播过程就是依靠一个不断更新的 m3u8 和一堆小的 ts 文件组成,m3u8 必须动态更新,ts 可以走 CDN。一个典型的 m3u8 文件格式如下:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000
gear1/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=311111
gear2/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=484444
gear3/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=737777
gear4/prog_index.m3u8

可以看到 HLS 协议本质还是一个个的 HTTP 请求 / 响应,所以适应性很好,不会受到防火墙影响。但它也有一个致命的弱点:延迟现象非常明显。如果每个 ts 按照 5 秒来切分,一个 m3u8 放 6 个 ts 索引,那么至少就会带来 30 秒的延迟。如果减少每个 ts 的长度,减少 m3u8 中的索引数,延时确实会减少,但会带来更频繁的缓冲,对服务端的请求压力也会成倍增加。所以只能根据实际情况找到一个折中的点。

对于支持 HLS 的浏览器来说,直接这样写就能播放了:

<video src=”./bipbopall.m3u8″ height=”300″ width=”400″  preload=”auto” autoplay=”autoplay” loop=”loop” webkit-playsinline=”true”></video>

注意:HLS 在 PC 端仅支持safari浏览器,类似chrome浏览器使用HTML5 video

标签无法播放 m3u8 格式,可直接采用网上一些比较成熟的方案,如:sewise-player、MediaElement、videojs-contrib-hls、jwplayer。

程序猿的生活:web前端全栈资料粉丝福利(面试题、视频、资料笔记,进阶路线)zhuanlan.zhihu.com/p/136454207

2. Real Time Messaging Protocol

Real Time Messaging Protocol(简称 RTMP)是 Macromedia 开发的一套视频直播协议,现在属于 Adobe。这套方案需要搭建专门的 RTMP 流媒体服务如 Adobe Media Server,并且在浏览器中只能使用 Flash 实现播放器。它的实时性非常好,延迟很小,但无法支持移动端 WEB 播放是它的硬伤。

虽然无法在iOS的H5页面播放,但是对于iOS原生应用是可以自己写解码去解析的, RTMP 延迟低、实时性较好。浏览器端,HTML5 video

标签无法播放 RTMP 协议的视频,可以通过 video.js 来实现。

<link href=“http://vjs.zencdn.net/5.8.8/video-js.css” rel=“stylesheet”>
<video id=“example_video_1″ class=“video-js vjs-default-skin” controls preload=“auto” width=“640” height=“264” loop=“loop” webkit-playsinline>
<source src=“rtmp://10.14.221.17:1935/rtmplive/home” type=‘rtmp/flv’>
</video>
<script src=“http://vjs.zencdn.net/5.8.8/video.js”></script>
<script>
videojs.options.flash.swf = ‘video.swf’;
videojs(‘example_video_1′).ready(function() {
this.play();
});
</script>


3. 视频流协议HLS与RTMP对比


二、直播形式

目前直播展示形式,通常以YY直播、映客直播这种页面居多,可以看到其结构可以分成三层:

① 背景视频层

② 关注、评论模块

③ 点赞动画

而现行H5类似直播页面,实现技术难点不大,其可以通过实现方式分为:

① 底部视频背景使用video视频标签实现播放

② 关注、评论模块利用 WebScoket 来实时发送和接收新的消息通过DOM 和 CSS3 实现

③ 点赞利用 CSS3 动画

了解完直播形式之后,接下来整体了解直播流程。

相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】

音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

【免费分享】音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费领取~

三、直播整体流程

直播整体流程大致可分为:

视频采集端:可以是电脑上的音视频输入设备、或手机端的摄像头、或麦克风,目前以移动端手机视频为主。

直播流视频服务端:一台Nginx服务器,采集视频录制端传输的视频流(H264/ACC编码),由服务器端进行解析编码,推送RTMP/HLS格式视频流至视频播放端。

视频播放端:可以是电脑上的播放器(QuickTime Player、VLC),手机端的native播放器,还有就是 H5 的video标签等,目前还是以手机端的native播放器为主。

(web前端学习交流群:328058344 禁止闲聊,非喜勿进!)

四、H5 录制视频

对于H5视频录制,可以使用强大的 webRTC (Web Real-Time Communication)是一个支持网页浏览器进行实时语音对话或视频对话的技术,缺点是只在 PC 的 Chrome 上支持较好,移动端支持不太理想。

使用 webRTC 录制视频基本流程

① 调用 window.navigator.webkitGetUserMedia()

获取用户的PC摄像头视频数据。

② 将获取到视频流数据转换成 window.webkitRTCPeerConnection

(一种视频流数据格式)。

③ 利用 WebScoket

将视频流数据传输到服务端。

注意:

虽然Google一直在推WebRTC,目前已有不少成型的产品出现,但是大部分移动端的浏览器还不支持 webRTC(最新iOS 10.0也不支持),所以真正的视频录制还是要靠客户端(iOS,Android)来实现,效果会好一些。


WebRTC支持度

WebRTC支持度

iOS原生应用调用摄像头录制视频流程

① 音视频的采集,利用AVCaptureSession和AVCaptureDevice可以采集到原始的音视频数据流。

② 对视频进行H264编码,对音频进行AAC编码,在iOS中分别有已经封装好的编码库(x264编码、faac编码、ffmpeg编码)来实现对音视频的编码。

③ 对编码后的音、视频数据进行组装封包。

④ 建立RTMP连接并上推到服务端。


五、搭建Nginx+Rtmp直播流服务

安装nginx、nginx-rtmp-module

① 先clone nginx项目到本地:

brew tap homebrew/nginx

② 执行安装nginx-rtmp-module

brew install nginx-full –with-rtmp-module

2. nginx.conf配置文件,配置RTMP、HLS

查找到nginx.conf配置文件(路径/usr/local/etc/nginx/nginx.conf),配置RTMP、HLS。

① 在http节点之前添加 rtmp 的配置内容:

② 在http中添加 hls 的配置

3. 重启nginx服务

重启nginx服务,浏览器中输入 http://localhost:8080,是否出现欢迎界面确定nginx重启成功。

nginx -s reload

六、直播流转换格式、编码推流

当服务器端接收到采集视频录制端传输过来的视频流时,需要对其进行解析编码,推送RTMP/HLS格式视频流至视频播放端。通常使用的常见编码库方案,如x264编码、faac编码、ffmpeg编码等。鉴于 FFmpeg 工具集合了多种音频、视频格式编码,我们可以优先选用FFmpeg进行转换格式、编码推流。

1.安装 FFmpeg 工具

brew install ffmpeg

2.推流MP4文件

视频文件地址:/Users/gao/Desktop/video/test.mp4

推流拉流地址:rtmp://localhost:1935/rtmplive/home,rtmp://localhost:1935/rtmplive/home

//RTMP 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://10.14.221.17:1935/rtmplive/home
//HLS 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test


注意:

当我们进行推流之后,可以安装VLC、ffplay(支持rtmp协议的视频播放器)本地拉流进行演示

3.FFmpeg推流命令

① 视频文件进行直播

ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://192.168.1.101:1935/hls/test
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test


② 推流摄像头+桌面+麦克风录制进行直播

ffmpeg -f avfoundation -framerate 30 -i “1:0″ \-f avfoundation -framerate 30 -video_size 640x480 -i “0” \-c:v libx264 -preset ultrafast \-filter_complex ‘overlay=main_w-overlay_w-10:main_h-overlay_h-10′ -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://192.168.1.101:1935/hls/test


更多命令,请参考:

FFmpeg处理RTMP流媒体的命令大全

FFmpeg常用推流命令

七、H5 直播视频播放

移动端iOS和 Android 都天然支持HLS协议,做好视频采集端、视频流推流服务之后,便可以直接在H5页面配置 video 标签播放直播视频。

<video controls preload=“auto” autoplay=“autoplay” loop=“loop” webkit-playsinline>
<source src=“http://10.14.221.8/hls/test.m3u8″ type=“application/vnd.apple.mpegurl” />
<p class=“warning”>Your browser does not support HTML5 video.</p>
</video>

八、总结

本文从视频采集上传,服务器处理视频推流,以及H5页面播放直播视频一整套流程,具体阐述了直播实现原理,实现过程中会遇到很多性能优化问题。

① H5 HLS 限制必须是H264+AAC编码。

② H5 HLS 播放卡顿问题,server 端可以做好分片策略,将 ts 文件放在 CDN 上,前端可尽量做到 DNS 缓存等。

③ H5 直播为了达到更好的实时互动,也可以采用RTMP协议,通过video.js 实现播放。

原文 https://zhuanlan.zhihu.com/p/146323842