ckplayer-plus 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ckplayer.js CHANGED
@@ -141,7 +141,7 @@
141
141
  cdnFallback:true// getPath 失败时是否回退到 jsDelivr(内网可设 false,并配置 pluginPath)
142
142
  };
143
143
  // 打包时替换为真实版本;用于 bundler 下 getPath 失效时的 CDN 回退
144
- var ckplayerPlusDefaultCdn='https://cdn.jsdelivr.net/npm/ckplayer-plus@1.1.1/dist/';
144
+ var ckplayerPlusDefaultCdn='https://cdn.jsdelivr.net/npm/ckplayer-plus@1.1.2/dist/';
145
145
  function ckplayerEmbed(videoObj){
146
146
  /*
147
147
  * rightMenu
@@ -446,7 +446,7 @@
446
446
  }
447
447
  var allowCdn=!vars || vars['cdnFallback']!==false;
448
448
  var cdn=(typeof window!=='undefined' && window.ckplayerPlusCdn) ? window.ckplayerPlusCdn : ckplayerPlusDefaultCdn;
449
- if(!allowCdn || !cdn || cdn.indexOf('1.1.1')>-1){
449
+ if(!allowCdn || !cdn || cdn.indexOf('1.1.2')>-1){
450
450
  // 避免占位版本或显式关闭 CDN 时请求坏地址;交给相对路径或业务 pluginPath
451
451
  return folder+'/'+file;
452
452
  }
@@ -990,10 +990,8 @@
990
990
  if(!resumeAt){
991
991
  resumeAt=parseResumeSeekFromVars();
992
992
  }
993
- // 部分旧 HLS 的分片不从关键帧开始。直接以续播点 startPosition / startLoad(target)
994
- // 起播会触发 Chrome PIPELINE_ERROR_DECODE。策略:
995
- // 1) 从目标前约一个分片开始加载,让前序关键帧进入缓冲
996
- // 2) 等 hlsCanSafelySeekTo 后再写 currentTime
993
+ // HLS 续播:不要用精确秒数做 startPosition(易在分片中部起播导致解码失败/永久 loading)。
994
+ // 对齐目标分片起点加载,等分片从起点缓冲到目标后再一次 currentTime,并强制 play。
997
995
  if(resumeAt && resumeAt>0 && (isUndefined(vars['live']) || !vars['live'])){
998
996
  maxSeeTime=Math.max(maxSeeTime,resumeAt);
999
997
  if(firstSeekTime<0){
@@ -1002,16 +1000,28 @@
1002
1000
  seekTime=resumeAt;
1003
1001
  hlsResumeTarget=resumeAt;
1004
1002
  hlsResumeWarmup=true;
1005
- hlsConf.startPosition=hlsPriorLoadTime(resumeAt);
1006
1003
  }
1007
1004
  hls = new Hls(hlsConf);
1008
1005
  hls.loadSource(url);
1009
1006
  hls.attachMedia(video);
1010
1007
  hls.on(Hls.Events.MANIFEST_PARSED, function(){
1011
1008
  if(hlsResumeWarmup && seekTime>0){
1009
+ var loadAt=hlsPriorLoadTime(seekTime);
1012
1010
  try{
1013
- hls.startLoad(hlsPriorLoadTime(seekTime));
1011
+ hls.startLoad(loadAt);
1014
1012
  }catch(event){}
1013
+ // 超时兜底:仍未定位则对齐分片起点起播,避免一直转圈
1014
+ setTimeout(function(){
1015
+ if(!hlsResumeWarmup || !seekTime){return;}
1016
+ var target=seekTime;
1017
+ if(hlsCanSafelySeekTo(target)){
1018
+ finishResumeSeek(target);
1019
+ return;
1020
+ }
1021
+ var snap=hlsPriorLoadTime(target);
1022
+ hlsResumeWarmup=false;
1023
+ finishResumeSeek(snap);
1024
+ },5000);
1015
1025
  return;
1016
1026
  }
1017
1027
  if(!hlsStartPositionUsed){
@@ -1020,10 +1030,8 @@
1020
1030
  });
1021
1031
  hls.on(Hls.Events.FRAG_BUFFERED, function(event, data){
1022
1032
  if(hlsResumeWarmup && seekTime>0){
1023
- // 必须等「目标 + 前序分片」都在同一连续缓冲里,再定位
1024
1033
  if(hlsCanSafelySeekTo(seekTime)){
1025
- hlsResumeWarmup=false;
1026
- applySeekToMedia(seekTime);
1034
+ finishResumeSeek(seekTime);
1027
1035
  }
1028
1036
  return;
1029
1037
  }
@@ -1033,7 +1041,6 @@
1033
1041
  var fragStart=data.frag.start;
1034
1042
  var fragEnd=fragStart+data.frag.duration;
1035
1043
  if(hlsResumeTarget>=fragStart-0.5 && hlsResumeTarget<=fragEnd+0.5){
1036
- // 续播分片已进入缓冲,解除 waiting
1037
1044
  if(!waited){
1038
1045
  waited=true;
1039
1046
  if(C['buffer']){
@@ -1833,38 +1840,109 @@
1833
1840
  }catch(event){}
1834
1841
  return false;
1835
1842
  },
1843
+ /*
1844
+ * hlsFragAround
1845
+ * 功能:查找覆盖指定时间的 HLS 分片(用于对齐关键帧起点)
1846
+ */
1847
+ hlsFragAround=function(t){
1848
+ if(!hls || isNaN(t) || t<0){
1849
+ return null;
1850
+ }
1851
+ try{
1852
+ var level=-1;
1853
+ if(typeof hls.currentLevel==='number' && hls.currentLevel>=0){
1854
+ level=hls.currentLevel;
1855
+ }
1856
+ else if(typeof hls.firstLevel==='number' && hls.firstLevel>=0){
1857
+ level=hls.firstLevel;
1858
+ }
1859
+ else{
1860
+ level=0;
1861
+ }
1862
+ var details=hls.levels && hls.levels[level] && hls.levels[level].details;
1863
+ var frags=details && details.fragments;
1864
+ if(!frags || !frags.length){
1865
+ return null;
1866
+ }
1867
+ for(var i=0;i<frags.length;i++){
1868
+ var f=frags[i];
1869
+ if(!f){continue;}
1870
+ var start=f.start||0;
1871
+ var end=start+(f.duration||0);
1872
+ if(t>=start-0.05 && t<end+0.05){
1873
+ return f;
1874
+ }
1875
+ }
1876
+ // 超出末片时回退最后一片
1877
+ return frags[frags.length-1];
1878
+ }catch(event){}
1879
+ return null;
1880
+ },
1836
1881
  /*
1837
1882
  * hlsPriorLoadTime
1838
- * 功能:续播加载起点——至少回退约一个分片,保证目标分片前仍有关键帧可参考
1883
+ * 功能:续播加载起点——优先对齐目标分片起点(该流分片开头通常有 IDR)
1839
1884
  */
1840
1885
  hlsPriorLoadTime=function(t){
1841
1886
  if(isNaN(t) || t<=0){
1842
1887
  return 0;
1843
1888
  }
1889
+ var frag=hlsFragAround(t);
1890
+ if(frag && !isUndefined(frag.start)){
1891
+ return Math.max(0,frag.start);
1892
+ }
1844
1893
  return Math.max(0,t-12);
1845
1894
  },
1846
1895
  /*
1847
1896
  * hlsCanSafelySeekTo
1848
- * 功能:目标已缓冲,且同一连续缓冲区间覆盖目标前约一个分片
1849
- * 部分旧 TS 分片首帧不是关键帧;若只缓冲目标分片就 seek,Chrome 会报 PIPELINE_ERROR_DECODE
1897
+ * 功能:目标分片已从分片起点连续缓冲到目标点,才允许 currentTime
1898
+ * 避免分片未完整进入 MSE 时中途 seek 触发 PIPELINE_ERROR_DECODE / 永久 loading
1850
1899
  */
1851
1900
  hlsCanSafelySeekTo=function(t){
1852
1901
  if(!video || isUndefined(video.buffered) || isNaN(t) || t<0){
1853
1902
  return false;
1854
1903
  }
1904
+ if(!timeInBuffered(t)){
1905
+ return false;
1906
+ }
1907
+ var fragStart=hlsPriorLoadTime(t);
1908
+ // 同一连续区间需覆盖分片起点→目标
1855
1909
  try{
1856
1910
  var buf=video.buffered;
1857
- var needFrom=hlsPriorLoadTime(t);
1858
1911
  for(var i=0;i<buf.length;i++){
1859
1912
  var start=buf.start(i);
1860
1913
  var end=buf.end(i);
1861
- if(t<=end+0.15 && needFrom>=start-0.35){
1914
+ if(fragStart>=start-0.35 && t<=end+0.15){
1862
1915
  return true;
1863
1916
  }
1864
1917
  }
1865
1918
  }catch(event){}
1866
1919
  return false;
1867
1920
  },
1921
+ /*
1922
+ * finishResumeSeek
1923
+ * 功能:完成续播定位并恢复播放(清除 loading 卡住)
1924
+ */
1925
+ finishResumeSeek=function(t){
1926
+ hlsResumeWarmup=false;
1927
+ applySeekToMedia(t);
1928
+ // seeked 可能因解码抖动延迟;主动再拉一次播放,避免永久转圈
1929
+ setTimeout(function(){
1930
+ if(!video){return;}
1931
+ if(C['buffer']){
1932
+ C['buffer'].hide();
1933
+ }
1934
+ waited=true;
1935
+ if(vars['autoplay'] || autoplayPending){
1936
+ try{
1937
+ var p=video.play();
1938
+ if(p && typeof p.then==='function'){
1939
+ p.catch(function(){});
1940
+ }
1941
+ }catch(event){}
1942
+ autoplayPending=false;
1943
+ }
1944
+ },120);
1945
+ },
1868
1946
  /*
1869
1947
  * clearSeekRetry
1870
1948
  * 功能:清理续播等待计时器
@@ -1933,17 +2011,14 @@
1933
2011
  firstSeekTime=t;
1934
2012
  }
1935
2013
  }
1936
- // HLS 首分片尚未完成预热时不能直接 startLoad(target),否则某些
1937
- // 非关键帧分片会让浏览器解码器永久报错。
2014
+ // HLS 首分片尚未完成预热时不能直接 startLoad(target)
1938
2015
  if(hlsResumeWarmup){
1939
2016
  if(hlsCanSafelySeekTo(t)){
1940
- hlsResumeWarmup=false;
1941
- applySeekToMedia(t);
2017
+ finishResumeSeek(t);
1942
2018
  }
1943
2019
  return;
1944
2020
  }
1945
- // HLS:用 hls.startLoad 从目标分片加载(去重,避免取消进行中的 .ts)
1946
- // 若目标附近尚无前序缓冲,改为预热模式从更早处分片加载
2021
+ // HLS:用 hls.startLoad 从目标分片起点加载(去重,避免取消进行中的 .ts)
1947
2022
  if(hls && typeof hls.startLoad==='function'){
1948
2023
  if(!hlsCanSafelySeekTo(t) && t>1){
1949
2024
  hlsResumeWarmup=true;
@@ -1998,8 +2073,11 @@
1998
2073
  if(isNaN(resumeAt) || resumeAt<0){
1999
2074
  resumeAt=0;
2000
2075
  }
2001
- // 每次失败再往前多退一个分片,避免反复落到同一坏点
2002
- var loadFrom=Math.max(0,resumeAt-(12*decodeRecoverCount));
2076
+ // 优先对齐分片起点(有 IDR);多次失败再往前一片
2077
+ var loadFrom=hlsPriorLoadTime(resumeAt);
2078
+ if(decodeRecoverCount>=2){
2079
+ loadFrom=Math.max(0,loadFrom-10);
2080
+ }
2003
2081
  seekApplyLock=false;
2004
2082
  clearSeekRetry();
2005
2083
  try{
@@ -2008,7 +2086,9 @@
2008
2086
  }
2009
2087
  }catch(event){}
2010
2088
  if(resumeAt>0 && hls && typeof hls.startLoad==='function'){
2011
- seekTime=resumeAt;
2089
+ // 第 3 次直接落到分片起点起播,保证能出画面
2090
+ var target=(decodeRecoverCount>=3)?loadFrom:resumeAt;
2091
+ seekTime=target;
2012
2092
  hlsResumeTarget=resumeAt;
2013
2093
  hlsResumeWarmup=true;
2014
2094
  hlsLoadAt=-1;
@@ -2017,31 +2097,20 @@
2017
2097
  hls.startLoad(loadFrom);
2018
2098
  }catch(event){}
2019
2099
  setTimeout(function(){
2020
- if(hlsCanSafelySeekTo(resumeAt)){
2021
- hlsResumeWarmup=false;
2022
- applySeekToMedia(resumeAt);
2023
- if(video && video.paused && vars['autoplay']){
2024
- try{video.play();}catch(e2){}
2025
- }
2100
+ if(hlsCanSafelySeekTo(target)){
2101
+ finishResumeSeek(target);
2102
+ }
2103
+ else if(decodeRecoverCount>=2){
2104
+ finishResumeSeek(loadFrom);
2026
2105
  }
2027
2106
  else{
2028
2107
  applyPendingSeek(false);
2029
2108
  }
2030
- },500);
2109
+ },600);
2031
2110
  return true;
2032
2111
  }
2033
2112
  try{
2034
- var soft=Math.max(0,resumeAt-1.5);
2035
- seekTime=resumeAt;
2036
- if(video){
2037
- video.currentTime=soft;
2038
- }
2039
- setTimeout(function(){
2040
- applyPendingSeek(true);
2041
- if(video && video.paused && vars['autoplay']){
2042
- try{video.play();}catch(e2){}
2043
- }
2044
- },400);
2113
+ finishResumeSeek(Math.max(0,resumeAt-1.5));
2045
2114
  return true;
2046
2115
  }catch(event){}
2047
2116
  return false;
@@ -2303,7 +2372,8 @@
2303
2372
  eventTarget('seeked');
2304
2373
  },
2305
2374
  seeking:function(){
2306
- if(paused){
2375
+ // 续播预热/定位过程中不要抢先 play,否则会打断 play Promise 导致永久 loading
2376
+ if(paused && !seekApplyLock && !hlsResumeWarmup && !seekTime){
2307
2377
  player.play();
2308
2378
  }
2309
2379
  var seekingTime=this.currentTime;
@@ -7194,7 +7264,7 @@
7194
7264
  if(!isRetry && (!vars || vars['cdnFallback']!==false)){
7195
7265
  var cdn=(typeof window!=='undefined' && window.ckplayerPlusCdn) ? window.ckplayerPlusCdn : ckplayerPlusDefaultCdn;
7196
7266
  var m=String(file).match(/\/(hls\.js|flv\.js|mpegts\.js|language)\/([^/?#]+)(?:[?#].*)?$/i);
7197
- if(cdn && cdn.indexOf('1.1.1')===-1 && m){
7267
+ if(cdn && cdn.indexOf('1.1.2')===-1 && m){
7198
7268
  if(cdn.slice(-1)!=='/'){
7199
7269
  cdn+='/';
7200
7270
  }