在jPlayer中进行缓冲

时间:2019-02-04 17:02:59

标签: mobile jplayer icecast

我们使用Icecast流媒体服务器在我们的网站上将jPlayer用作移动应用程序。我试图将<intro>添加到Icecast配置中,但是当我这样做时,它在移动设备上出现了问题。每当电话中断导致临时断开时(例如打进来的电话),流都会重复您首次连接流时开始播放的内容,当然会再次播放介绍视频。例如,如果我开始收听一则节目或一首歌曲的流,然后有电话打入并结束,则介绍将在重新连接上播放,然后从我最初开始收听的位置开始播放流。

我玩过Icecast队列和突发设置,上下都没有,尝试了不同的格式,得到了相同的结果。我还与其他一些与流相关的帖子进行了交谈,并被告知似乎问题出在客户端缓冲区和播放器上,而我没有设置。我看了看我们的stream-player.js,它是jPlayer 2.9.2,在3507行的末尾加上了以下内容:

;(function() {
  var DOMParser, find, parse;

  DOMParser = (typeof window !== "undefined" && window !== null ? window.DOMParser : void 0) || (typeof require === "function" ? require('xmldom').DOMParser : void 0) || function() {};

  find = function(node, list) {
    var attributes, childNode, childNodeName, childNodes, i, match, x, _i, _j, _ref, _ref1;
    if (node.hasChildNodes()) {
      childNodes = node.childNodes;
      for (i = _i = 0, _ref = childNodes.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
        childNode = childNodes[i];
        childNodeName = childNode.nodeName;
        if (/REF/i.test(childNodeName)) {
          attributes = childNode.attributes;
          for (x = _j = 0, _ref1 = attributes.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; x = 0 <= _ref1 ? ++_j : --_j) {
            match = attributes[x].nodeName.match(/HREF/i);
            if (match) {
              list.push({
                file: childNode.getAttribute(match[0]).trim()
              });
              break;
            }
          }
        } else if (childNodeName !== '#text') {
          find(childNode, list);
        }
      }
    }
    return null;
  };

  parse = function(playlist) {
    var doc, ret;
    ret = [];
    doc = (new DOMParser()).parseFromString(playlist, 'text/xml').documentElement;
    if (!doc) {
      return ret;
    }
    find(doc, ret);
    return ret;
  };

  (typeof module !== "undefined" && module !== null ? module.exports : window).ASX = {
    name: 'asx',
    parse: parse
  };

}).call(this);

(function() {
  var COMMENT_RE, EXTENDED, comments, empty, extended, parse, simple;

  EXTENDED = '#EXTM3U';

  COMMENT_RE = /:(?:(-?\d+),(.+)\s*-\s*(.+)|(.+))\n(.+)/;

  extended = function(line) {
    var match;
    match = line.match(COMMENT_RE);
    if (match && match.length === 6) {
      return {
        length: match[1] || 0,
        artist: match[2] || '',
        title: match[4] || match[3],
        file: match[5].trim()
      };
    }
  };

  simple = function(string) {
    return {
      file: string.trim()
    };
  };

  empty = function(line) {
    return !!line.trim().length;
  };

  comments = function(line) {
    return line[0] !== '#';
  };

  parse = function(playlist) {
    var firstNewline;
    playlist = playlist.replace(/\r/g, '');
    firstNewline = playlist.search('\n');
    if (playlist.substr(0, firstNewline) === EXTENDED) {
      return playlist.substr(firstNewline).split('\n#').filter(empty).map(extended);
    } else {
      return playlist.split('\n').filter(empty).filter(comments).map(simple);
    }
  };

  (typeof module !== "undefined" && module !== null ? module.exports : window).M3U = {
    name: 'm3u',
    parse: parse
  };

}).call(this);

(function() {
  var LISTING_RE, parse;

  LISTING_RE = /(file|title|length)(\d+)=(.+)\r?/i;

  parse = function(playlist) {
    var index, key, line, match, tracks, value, _, _i, _len, _ref;
    tracks = [];
    _ref = playlist.trim().split('\n');
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      line = _ref[_i];
      match = line.match(LISTING_RE);
      if (match && match.length === 4) {
        _ = match[0], key = match[1], index = match[2], value = match[3];
        if (!tracks[index]) {
          tracks[index] = {};
        }
        tracks[index][key.toLowerCase()] = value;
      }
    }
    return tracks.filter(function(track) {
      return track != null;
    });
  };

  (typeof module !== "undefined" && module !== null ? module.exports : window).PLS = {
    name: 'pls',
    parse: parse
  };

}).call(this);
;(function() {
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  window.PlayerUI = (function() {
    function PlayerUI(container) {
      var _this = this;
      this.container = container;
      this.onStateButtonClicked = __bind(this.onStateButtonClicked, this);
      this.duration = null;
      this.state = 'loading';
      this.player = $('<div></div>');
      this.container.append(this.player);
      this.player.jPlayer({
        ready: function() {
          return _this.state = 'paused';
        }
      });
      this.volume = this.container.find('.volume-slider input').rangeslider({
        polyfill: false,
        onSlide: function(position, value) {
          return _this.player.jPlayer('volume', value / 100.0);
        },
        onSlideEnd: function(position, value) {
          return _this.player.jPlayer('volume', value / 100.0);
        }
      });
      this.hookEvents();
    }

    PlayerUI.prototype.hookEvents = function() {
      var _this = this;
      this.container.find('.state-button a').click(this.onStateButtonClicked);
      this.player.on($.jPlayer.event.play, function() {
        return _this.setState('playing');
      });
      this.player.on($.jPlayer.event.pause, function() {
        return _this.setState('paused');
      });
      this.player.on($.jPlayer.event.durationchange, function(e) {
        return _this.container.trigger('player.setProgressMax', {
          maxValue: e.jPlayer.status.duration
        });
      });
      this.player.on($.jPlayer.event.timeupdate, function(e) {
        return _this.container.trigger('player.updateProgress', {
          value: e.jPlayer.status.currentTime
        });
      });
      return this.player.on($.jPlayer.event.ended, function(e) {
        return _this.container.trigger('player.trackEnded');
      });
    };

    PlayerUI.prototype.setState = function(state) {
      this.state = state;
      return this.container.find('.state-button a').removeClass().addClass("state-" + state);
    };

    PlayerUI.prototype.onStateButtonClicked = function(event) {
      event.preventDefault();
      switch (this.state) {
        case 'playing':
          return this.pause();
        case 'paused':
          return this.play();
        default:
          return this.noop();
      }
    };

    PlayerUI.prototype.setMedia = function(media) {
      this.pause();
      return this.player.jPlayer('setMedia', media);
    };

    PlayerUI.prototype.setProgress = function(pct) {
      return this.player.jPlayer('playHead', pct);
    };

    PlayerUI.prototype.play = function() {
      this.setState('playing');
      return this.player.jPlayer('play');
    };

    PlayerUI.prototype.pause = function() {
      this.setState('paused');
      return this.player.jPlayer('pause');
    };

    PlayerUI.prototype.noop = function() {
      return null;
    };

    return PlayerUI;

  })();

}).call(this);
;(function() {
  window.PlaylistUI = (function() {
    function PlaylistUI(container) {
      var _this = this;
      this.container = container;
      this.container.hide();
      $(window).on('playlistloader.finished', function(evt, data) {
        return _this.setPlaylist(PlaylistLoader.coalescePlaylists(data.playlists));
      });
    }

    PlaylistUI.prototype.loadM3UList = function(m3uList) {
      return new PlaylistLoader(m3uList);
    };

    PlaylistUI.prototype.setPlaylist = function(playlistData) {
      if (typeof playlistData.data !== 'undefined') {
        this.name = playlistData.name;
        playlistData = playlistData.data;
      }
      this.playlist = playlistData;
      this.container.hide();
      this.unhookEvents();
      this.renderPlaylist();
      this.container.show();
      this.hookEvents();
      return this.container.trigger('playlistui.ready', {
        ui: this,
        autoplay: false //this.getAutoplay()
      });
    };

    PlaylistUI.prototype.unhookEvents = function() {
      return this.container.find('.playlist-item').off('click.playlistUI', 'a');
    };

    PlaylistUI.prototype.hookEvents = function() {
      var _this = this;
      return this.container.find('.playlist-item').on('click.playlistUI', 'a', function(evt) {
        var idx, item;
        evt.preventDefault();
        idx = $(evt.target).parent('.playlist-item').data('idx');
        item = _this.getItemByIdx(idx);
        return _this.select(item);
      });
    };

    PlaylistUI.prototype.renderPlaylist = function() {
      var idx, item, playlist, _i, _len, _ref, _results;
      playlist = this.container.find('.playlist');
      playlist.empty();
      _ref = this.playlist;
      _results = [];
      for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
        item = _ref[idx];
        _results.push(playlist.append(this.rowTemplate(item, idx)));
      }
      return _results;
    };

    PlaylistUI.prototype.rowTemplate = function(item, idx) {
      return $("<li class=\"playlist-item\" data-idx=\"" + idx + "\"><a href=\"" + item.file + "\">" + item.title + "</a></li>");
    };

    PlaylistUI.prototype.getAutoplay = function() {
      var item, _i, _len, _ref;
      _ref = this.playlist;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        item = _ref[_i];
        if (item.autoplay) {
          return item;
        }
      }
      return null;
    };

    PlaylistUI.prototype.getItemByIdx = function(idx) {
      return this.playlist[idx];
    };

    PlaylistUI.prototype.getRowForItem = function(item) {
      var compare, found, idx, _i, _len, _ref;
      _ref = this.playlist;
      for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
        compare = _ref[idx];
        if (compare === item) {
          found = this.container.find(".playlist-item[data-idx=" + idx + "]");
          return found;
        }
      }
      return null;
    };

    PlaylistUI.prototype.getIndexForItem = function(item) {
      var compare, idx, _i, _len, _ref;
      _ref = this.playlist;
      for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
        compare = _ref[idx];
        if (item === compare) {
          return idx;
        }
      }
      return null;
    };

    PlaylistUI.prototype.findNext = function() {
      var currentIndex, nextIndex;
      currentIndex = this.getIndexForItem(this.current);
      if (currentIndex === null) {
        return null;
      }
      nextIndex = currentIndex + 1;
      if (nextIndex >= this.playlist.length) {
        return null;
      }
      return this.playlist[nextIndex];
    };

    PlaylistUI.prototype.select = function(item) {
      if (item) {
          this.current = item;
          this.getRowForItem(item).addClass('selected').siblings().removeClass('selected');
          return this.container.trigger('playlistui.select', {
            ui: this,
            item: item
        });
      }
    };

    PlaylistUI.prototype.selectFirst = function() {
      return this.select(this.playlist[0]);
    };

    PlaylistUI.prototype.selectNext = function() {
      var nextItem;
      nextItem = this.findNext();
      if (nextItem === null) {
        return false;
      }
      this.select(nextItem);
      return true;
    };

    return PlaylistUI;

  })();

}).call(this);
;(function() {
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  window.PlaylistLoader = (function() {
    function PlaylistLoader(playlists) {
      this.playlists = playlists;
      this.loadedItem = __bind(this.loadedItem, this);
      this.loadPlaylists();
    }

    PlaylistLoader.prototype.loadPlaylists = function() {
      var idx, item, _i, _len, _ref, _results,
        _this = this;
      this.loadCount = 0;
      this.data = new Array(this.playlists.length);
      _ref = this.playlists;
      _results = [];
      for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
        item = _ref[idx];
        _results.push((function() {
          var tmp;
          tmp = idx;
          return jQuery.ajax({
            url: item
          }).done(function(data) {
            return _this.loadedItem(tmp, data);
          });
        })());
      }
      return _results;
    };

    PlaylistLoader.prototype.loadedItem = function(idx, data) {
      var playlist;
      playlist = M3U.parse(data);
      this.data[idx] = playlist;
      $(window).trigger('playlistloader.loadeditem', {
        index: idx,
        playlist: playlist
      });
      this.loadCount++;
      if (this.loadCount === this.playlists.length) {
        return this.finishedLoading();
      }
    };

    PlaylistLoader.prototype.finishedLoading = function() {
      return $(window).trigger('playlistloader.finished', {
        playlists: this.data
      });
    };

    PlaylistLoader.coalescePlaylists = function(playlistsLoaded) {
      var fileEntry, output, playlist, _i, _j, _len, _len1;
      output = [];
      for (_i = 0, _len = playlistsLoaded.length; _i < _len; _i++) {
        playlist = playlistsLoaded[_i];
        for (_j = 0, _len1 = playlist.length; _j < _len1; _j++) {
          fileEntry = playlist[_j];
          output.push(fileEntry);
        }
      }
      return output;
    };

    return PlaylistLoader;

  })();

}).call(this);
;(function() {
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  window.StreamUI = (function() {
    function StreamUI(selector, streamPlaylists) {
      this.selector = selector;
      this.streamPlaylists = streamPlaylists;
      this.playlistSelect = __bind(this.playlistSelect, this);
      this.playlistReady = __bind(this.playlistReady, this);
      this.container = jQuery(this.selector);
      this.playlist = new PlaylistUI(this.container.find('.playlist-ui'));
      this.player = new PlayerUI(this.container.find('.player-ui'));
      this.hookEvents();
      this.playlist.loadM3UList(this.streamPlaylists);
    }

    StreamUI.prototype.hookEvents = function() {
      var playlistUI;
      playlistUI = this.container.find('.playlist-ui');
      playlistUI.on('playlistui.ready', this.playlistReady);
      return playlistUI.on('playlistui.select', this.playlistSelect);
    };

    StreamUI.prototype.playlistReady = function(evt, eventinfo) {
      if (eventinfo.autoplay !== null) {
        return eventinfo.ui.select(eventinfo.autoplay);
      } else {
        return eventinfo.ui.selectFirst();
      }
    };

    StreamUI.prototype.playlistSelect = function(evt, eventinfo) {
      this.player.setMedia({
        mp3: eventinfo.item.file
      });
      return this.player.play();
    };

    return StreamUI;

  })();

}).call(this);

尽管我主要是Linux开发人员,我在Perl和PHP中具有大部分编程经验,并且确实非常了解jQuery与我的Web开发打交道,但是对于jPlayer甚至音频流,我无疑是一个新手。我希望有人能在上面的代码中发现某些东西,这可能会导致在向Icecast 2.4.4流添加简介时遇到的问题?

我们的流可在下面的URL上找到,目前我对HD4流进行了介绍。

streaming player

可以通过以下方法轻松地复制该问题:启动流并稍稍听一听,直到歌曲改变为止,然后拨打电话使其中断流,然后挂断电话。在介绍之后,这将导致第一首收听的歌曲再次播放。

我相信编解码器是匹配的,在将前奏格式设置为MP3 128Kbps比特率44.1KHz采样和2声道立体声之前,确实存在问题。这是介绍文件信息:

user@stream:~$ mediainfo /usr/share/icecast2/web/high_quality.mp3
General
Complete name                            : /usr/share/icecast2/web/high_quality.mp3
Format                                   : MPEG Audio
File size                                : 138 KiB
Duration                                 : 8s 777ms
Overall bit rate mode                    : Constant
Overall bit rate                         : 128 Kbps
Writing library                          : LAME3.99r

Audio
Format                                   : MPEG Audio
Format version                           : Version 1
Format profile                           : Layer 3
Mode                                     : Joint stereo
Mode extension                           : MS Stereo
Duration                                 : 8s 803ms
Bit rate mode                            : Constant
Bit rate                                 : 128 Kbps
Channel(s)                               : 2 channels
Sampling rate                            : 44.1 KHz
Compression mode                         : Lossy
Stream size                              : 137 KiB (100%)
Writing library                          : LAME3.99r
Encoding settings                        : -m j -V 4 -q 3 -lowpass 17 -b 128

1 个答案:

答案 0 :(得分:0)

诸如底层浏览器缓存之类的声音会踢进来,并强制重放内存中保存的内容。在某些情况下,浏览器“很棒”,然后会竭尽全力忽略无缓存指令和其他内容。

确保浏览器不尝试播放高速缓存恶作剧的一种方法是添加“高速缓存破坏者”。本质上是查询字符串(/ stream?foo = bar),它使浏览器引擎认为它是动态生成的内容并丢弃其缓存; cf. https://www.keycdn.com/support/what-is-cache-busting

目前,您的Icecast服务器似乎未回答任何请求。因此,我无法调查您这一方面的细节。