MSE的切片

借助MediaSource和SourceBuffer来实现webm格式视频的分片传输
<div class="article-info-box">
    <div class="article-bar-top d-flex">
                                            <span class="time">2014年08月30日 16:29:19</span>
        <div class="float-right">
            <span class="read-count">阅读数:4280</span>
                                        </div>
    </div>
</div>
<article>
    <div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
                <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/htmledit_views-0a60691e80.css">
        <div class="htmledit_views">

在CloudTV项目的初期曾实现了整段视频的对等传输和播放,但是当视频较大时会产生相当长时间的延迟(具体延迟时间由网络状况和视频文件大小决定);因此开始尝试视频的切片传输。

基于没有过多的音视频编码基础,所以尝试了对webm格式视频的强制型切片传输。

之所以选择webm格式的视频,一是webm格式被html5和目前大多数浏览器所支持,二是webm格式视频相对编码方式比较单一,所使用的codec比较单一,所以在添加sourcebuffer时直接使用addSourceBuffer(‘video/webm; codecs=”vorbis,vp8”’)即可;

其基本思想比较简单:

对于视频发送端,用fileReader读取文件后,使用slice()函数将文件切片,然后将每个segment逐一传输到对等接收端;

对于视频接收端,将接收到得每一个文件片append到<video>标签对应的sourcebuffer上(在上一篇文章中已经讲过如何将sourcebuffer对应到video标签上)

但是具体实现时使用了一下几个技巧来解决一些问题:

(1)在发送端开始发送视频数据之前,需要先向对等端发送初始化消息通知(video command: start)对等端准备接受视频数据

          对等端接收到初始化消息后,开始初始化工作:

[javascript] view plain copy
print ?
  1. if(data[‘type’]==“video_com”){  
  2.                         if(data[‘content’]==“start”){  
  3.                             var oSourceBuffer, oMediaSource = new MediaSource();  
  4.                             var url = window.URL.createObjectURL(oMediaSource);  
  5.                             video.src = url;  
  6.                             oMediaSource.addEventListener(prefix +’sourceopen’function () {  
  7.                                 oSourceBuffer = oMediaSource.addSourceBuffer(’video/webm; codecs=”vorbis,vp8”’);  
  8.                                 //oSourceBuffer = oMediaSource.addSourceBuffer(‘video/mp4; codecs=”avc1.4d401f,mp4a.40.2”’);  
  9.                                 mediaSource = oMediaSource;  
  10.                                 sourceBuffer = oSourceBuffer;  
  11.                                 console.debug(’MediaSource readyState: <’this.readyState, ‘>’);  
  12.                             }, false);  
  13.                             //video.src = window.URL.createObjectURL(oMediaSource);  
  14.                             seg_num=0;  
  15.                         }  
  16.                         if(data[‘content’]== “end”){  
  17.                             mediaSource.endOfStream();  
  18.                         }  
  19.                     }  
if(data['type']=="video_com"){
                        if(data['content']=="start"){
                            var oSourceBuffer, oMediaSource = new MediaSource();
                            var url = window.URL.createObjectURL(oMediaSource);
                            video.src = url;
                            oMediaSource.addEventListener(prefix +'sourceopen', function () {
                                oSourceBuffer = oMediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"');
                                //oSourceBuffer = oMediaSource.addSourceBuffer('video/mp4; codecs="avc1.4d401f,mp4a.40.2"');
                                mediaSource = oMediaSource;
                                sourceBuffer = oSourceBuffer;
                                console.debug('MediaSource readyState: <', this.readyState, '>');
                            }, false);
                            //video.src = window.URL.createObjectURL(oMediaSource);
                            seg_num=0;
                        }
                        if(data['content']== "end"){
                            mediaSource.endOfStream();
                        }
                    }
(2)对等端接收到分片的视频数据之后会将数据append到sourcebuffer上

[javascript] view plain copy
print ?
  1. if(data[‘type’] == “video_seg”){  
  2.                         console.debug(”get the ” + seg_num + “ segment”);  
  3.                         var uint8array = new window.Uint8Array(data[‘content’]);  
  4.                         sourceBuffer.appendBuffer(uint8array);  
  5.                         if(seg_num==20){  
  6.                             video.play();  
  7.                         }  
  8.                         seg_num++;  
  9.                     }  
if(data['type'] == "video_seg"){
                        console.debug("get the " + seg_num + " segment");
                        var uint8array = new window.Uint8Array(data['content']);
                        sourceBuffer.appendBuffer(uint8array);
                        if(seg_num==20){
                            video.play();
                        }
                        seg_num++;
                    }

         此处需注意;append()函数时需要一定的执行时间的,如果在很短的时间内接收到两片及以上的数据,就会引发两次append()函数,在前次append()函数未执行结束的情况下再次调用append()函数会产生错误从而导致该sourcebuffer被移除从而出现错误

         对于这个问题的解决方法有两种:

一是在接收端建立一个List或者Queue来按序存放接收到得视频数据片, 然后按顺序append到sourcebuffer上,此时可以通过检测sourcebuffer的appendingstate来检测当然的append函数是否执行完毕,当前一次执行完毕后再新append一个数据片

二是在发送端设置一定的发送间隔,为append()函数留出一定的执行时间,当然这个时间间隔的设置要合理。

实现代码为:

[javascript] view plain copy
print ?
  1. function inner_streamer() {  
  2.             reader = new window.FileReader();  
  3.             reader.onload = function (e) {  
  4.                 //self.push(new window.Uint8Array(e.target.result));  
  5.                 var send_data = new window.Uint8Array(e.target.result);  
  6.                 myconn.send({type:”video_seg”,content:send_data});  
  7.                 startIndex += plus;  
  8.                 if (startIndex <= size)  
  9.                 {  
  10.                     setTimeout(’inner_streamer()’,100);  
  11.                 }  
  12.                 else {  
  13.                     //self.push({end: true});  
  14.                     myconn.send({type:”video_com”,content:“end”});  
  15.                 }  
  16.             };  
  17.             reader.readAsArrayBuffer(send_blob.slice(startIndex, startIndex + plus));  
  18.         }  
function inner_streamer() {
            reader = new window.FileReader();
            reader.onload = function (e) {
                //self.push(new window.Uint8Array(e.target.result));
                var send_data = new window.Uint8Array(e.target.result);
                myconn.send({type:"video_seg",content:send_data});
                startIndex += plus;
                if (startIndex <= size)
                {
                    setTimeout('inner_streamer()',100);
                }
                else {
                    //self.push({end: true});
                    myconn.send({type:"video_com",content:"end"});
                }
            };
            reader.readAsArrayBuffer(send_blob.slice(startIndex, startIndex + plus));
        }



        </div>
            </div>
        </article>

    <div class="article-bar-bottom">
                    <div class="tags-box artic-tag-box">
        <span class="label">文章标签:</span>
                    <a class="tag-link" href="http://so.csdn.net/so/search/s.do?q=webm&amp;t=blog" target="_blank">webm                     </a><a class="tag-link" href="http://so.csdn.net/so/search/s.do?q=视频&amp;t=blog" target="_blank">视频                     </a><a class="tag-link" href="http://so.csdn.net/so/search/s.do?q=浏览器&amp;t=blog" target="_blank">浏览器                       </a><a class="tag-link" href="http://so.csdn.net/so/search/s.do?q=video&amp;t=blog" target="_blank">video                       </a><a class="tag-link" href="http://so.csdn.net/so/search/s.do?q=DASH&amp;t=blog" target="_blank">DASH                     </a>
    </div>
                    <div class="tags-box">
        <span class="label">个人分类:</span>
                    <a class="tag-link" href="https://blog.csdn.net/liulangdeyue/article/category/2377183" target="_blank">开源夏令营                        </a>
    </div>
                </div>

<!-- !empty($pre_next_article[0]) -->
    </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值