Буферизация в jPlayer - PullRequest
       23

Буферизация в jPlayer

0 голосов
/ 04 февраля 2019

Мы используем потоковый сервер 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, связанный с моей веб-разработкой, я, конечно, новичок, когда дело доходит до jPlayer или даже потокового аудио.Я надеялся, что кто-то может заметить что-то в приведенном выше коде, что может способствовать возникновению проблемы, которая возникает у нас при добавлении вступления в наш поток Icecast 2.4.4?

Наши потоки доступны по указанному ниже URL-адресу, у меня есть вступлениена нашем HD4-потоке в данный момент.

потоковый проигрыватель

Проблема легко дублируется, если запустить поток и прослушать немного, пока песня не изменится, позвонив по телефону, чтобыпрервите поток, затем повесьте трубку.Это приведет к тому, что первая прослушиваемая песня будет воспроизводиться снова после вступления.

Я считаю, что кодек совпадает, у меня действительно была проблема с тем, чтобы заставить вступление работать, пока я не отформатировал MP3 128 Кбит / с с частотой дискретизации 44,1 КГци 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 голосов
/ 04 февраля 2019

Звучит так, как будто кеш браузера начинает работать и заставляет воспроизводить что-то, хранящееся в памяти.Браузеры такие «крутые» в некоторых обстоятельствах, и затем стараются игнорировать директивы без кэширования и другие вещи.

Один из способов убедиться, что браузер не пытается воспроизвести кэш-махинации, - это добавить «кэш-бастер».По сути, строка запроса (/ stream? Foo = bar), которая заставляет движок браузера думать, что это динамически генерируемый контент, и отбрасывать его кеш;срhttps://www.keycdn.com/support/what-is-cache-busting.

В настоящее время ваш сервер Icecast, похоже, не отвечает ни на какие запросы.Поэтому я не могу разобраться в деталях с вашей стороны.

...