html audio缓冲效果实现

网页自定义播放器控件时,需要解决比较棘手的问题一般来说就是媒体文件的缓冲效果数据的来源,和任意跳转播放的缓冲数据来源;

这里我只做音频播放控件,以audio为例:

对于媒体的加载可以js触发监控的事件有:

audio.addEventListener("loadstart", function() {
  // 开始加载
});
audio.addEventListener("durationchange", function() {
  // 已获得播放时长
});
audio.addEventListener("loadeddata", function() {
  // 已获得播放头文件
});
audio.addEventListener("progress", function() {
  // 缓冲下载中
});
audio.addEventListener("canplay", function() {
  // 可以播放
});
audio.addEventListener("timeupdate", function() {
  // 播放中
});
audio.addEventListener("canplaythrough", function() {
  // 可以不发生缓冲下载从头播放到结束,一般来说是加载完成过一次的判断
});

而常用的一般是:

audio.addEventListener("progress", function() {
  // 缓冲下载中
});

audio.addEventListener("timeupdate", function() {
  // 播放中
});

缓冲下载事件用来监控缓冲数据,目的是为了得到缓冲数据的播放时长,通过该时长再和总时长 duration 相比较就能得到缓冲比例,通过这个比例参数我们就可以根据播放器控件的宽度*比例参数得到缓冲数据需要的偏移量;

通过timeupdate对播放的时间的监控能够得到时时刻刻的currenttime,再比上duration总时长,就能得到时时刻刻的播放效果偏移量;

先说说timeupdate:

该事件是在播放中时时发生的,大约每秒输出一次,可以输出currenttime得到播放中的当前时间点;

比较绕脑的是progress,缓冲:

对于缓冲这个概念有多个角度可以去描述:

1、属性:buffered、seekable

2、事件:seeking、seeked

先说说事件:

当加载媒体文件的时候,一开始加载就会是seeking状态,如果这其中没有打断的情况,比如网络断开、手动点击播放到指定位置等,那么seeking会持续到文件加载结束,结束后触发seeked,而seeked则是seeking触发的false事件结束后的;

但是如果,在加载过程中,手动跳转到了任意一个位置,那么seeking就会被false掉,从而触发seeked;这个概念很重要,他和属性关联性强;

而我们的缓冲的时间数据来源就是buffered属性;

buffered属性有三个参数:

1、length、2、start() 3、end()

buffered.length表示加载的数据的时间范围数;这个时间范围数怎么理解?

举例我有一首3分钟的歌曲

如果数据加载没有任何打断,直接从0加载到了3分钟,那么整个数据的加载时间范围数就是1,可以看成index=0;

如果数据被在50秒处因为网络原因或其他原因打断了一次,表现形式为0-50秒加载完成后就暂时没加载了,那么0-50秒这个片段的时间范围数就是1,index=0,而后自动请求又开始加载从50秒开始加载到一份半产生一个新片段,那么这50-一分半的数据加载就是第二个时间范围数2,index=1;同样的之后又加载一次产生一个新片段3,index=2;

所以总结下来:

buffered的length,通俗的来讲可以说是描述了音频数据加载是否产生的打断事件,记录了有多少的缓冲片段,如果没有被打断过,一个完整的加载,默认的buffered.length = 1,index=0,如果有一次打断,缓冲两次产生2个片段,那么buffered.length=2, 包含index索引[0,1]的片段,三个片段就会有buffered.length = 3 包含index=2的 [0,1,2]片段,所以可以看出,length是加载片段个数,如果没有中断加载,默认是一次完整的加载,所以只会有1个缓冲片段,片段数量判断可位置查询可以通过buffer.length来判断和查询,如果是一次完整的加载,buffered.length = 1,那么加载的数据开始时间和结束时间就可以用start和end来得到;

buffered.start(0)代表第一个缓冲片段的已加载的起始时间,一般来说start(0)就是歌曲开始时间,

buffered.end(0)代表第一个缓冲片段的已加载的结束时间,表示该片段的结束时间,这里的0是个数索引index,和循环很相似;

 

如果是一次加载类型:start(0)就可以表示歌曲开始时间,end(0)就可以表示歌曲结束时间,注意如果没有打断,end()是持续缓冲变化的,因为缓冲是按时间进行的,不是一次完成,所以只要没有中断,后10秒的end()得到的缓冲结束时间是大于前10秒的end()借宿时间的,同样,因为是根据时间变化,监控buffered.end()需要放在timeupdate事件中,timeupdate监控是时时执行的,只要处于播放状态,大约每秒都会执行一次,所以在timeupdate事件中时时监控buffered.end()能时时得到最新的end()时间;

如果是两次加载类型,这会有两个片段[0,1],那么start(0),表示片段1的开始时间,end(0)表示片段1 的结束时间,同样的就可以获取buffered.start(1)和buffered.end(1),则表示第二个片段的起始时间和第二个片段的结束时间。

为什么会有多个片段?

对于视频文件很好理解,因为视频文件一般来说很大,一次是无法加载完成的,所以加载多次就被分成多个片段;

其实对于mp3这个小文件类型,同样有时候会因为网络波动问题一次网络加载缓冲一段,一般只到50秒左右的样子,之后会断开触发seeked事件,之后会再次触发seeking请求数据事件再加载几十秒的数据,所以如果用本地音频文件作为测试,会直接一次加载全部,所以网络文件可能audio.buffered.length有1/2/3/4/5.....多个buffered.length,而本地文件只有0,这就描述了网络加载产生了多个buffered.length数据时间段,而本地缓冲一次完成,就只有一个时间段;而且用户如果手动点击指定播放位置,肯定会中断;


到了这里,就可以理解为什么在MDN会有这样的代码描述:

  window.onload = function(){

    var myAudio = document.getElementById('my-audio');
    var myCanvas = document.getElementById('my-canvas');
    var context = myCanvas.getContext('2d');

    context.fillStyle = 'lightgray';
    context.fillRect(0, 0, myCanvas.width, myCanvas.height);
    context.fillStyle = 'red';
    context.strokeStyle = 'white';
    
    var inc = myCanvas.width / myAudio.duration;

    // display TimeRanges
    
    myAudio.addEventListener('seeked', function() {
      for (i = 0; i < myAudio.buffered.length; i++) {

        var startX = myAudio.buffered.start(i) * inc;
        var endX = myAudio.buffered.end(i) * inc;
        var width = endX - startX;

        context.fillRect(startX, 0, width, myCanvas.height);
        context.rect(startX, 0, width, myCanvas.height);
        context.stroke();
      }
    });
  }

这里监控seeked就表示监控是否产生打断事件,如果没有产生,就是一次加载,那么在加载完成后直接把整个进度条全部更新颜色,如果产生了打断,那么表示只加载了部分,那么就去遍历获取buffered.length,从第一个片段开始,获取到最新的一个片段,而start()和end()遍历buffered.length 的index;得到每个片段的时间点,在和总时间对比得到进度条需要改变的比例,再更新缓冲颜色实现部分缓冲的更新;由于网络加载一般来说都不会是一次加载完成的,所以一般都会有多次seeked;

如果buffered.length = 0 ,则无法输出start和end,所以可以加入一个if判断buffered.length 

 


上面的seeked事件监控已经可以得到数据从而写出缓冲的进度条了,但是个人建议通过progress缓冲事件再监控seeked事件,再定义默认页面不触发媒体加载,通过按钮触发媒体加载,可以更好的得到用户点击按钮再缓冲播放歌曲的效果;


本人测试结果:

html audio缓冲效果实现_第1张图片

输出结果 :

html audio缓冲效果实现_第2张图片

 

分析结果:

对照输出顺序和代码顺序,可以看出,当我点击播放的时候会先触发progress缓冲,然而触发progress缓冲并不是立即的到audio的buffered.length,需要等待到 durationchange事件,即得到时长数据后才会得到buffered.length,所以一开始progress事件中buffered.length = 0,这时候如果直接输出berffered.start(0)和buffered.end(0)会报错,因为都没有得到数据,避免这种报错只需要判断下buffered.length != 0即可;

你可能感兴趣的:(audio,buffered,JavaScript)