mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
HLS player updates
This commit is contained in:
parent
575fa5264d
commit
5959eb29d3
File diff suppressed because one or more lines are too long
@ -57,6 +57,7 @@ export class SourceBufferStub extends EventTarget {
|
|||||||
window.bridgeObjectMap[this.bridgeId] = this;
|
window.bridgeObjectMap[this.bridgeId] = this;
|
||||||
|
|
||||||
window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "constructor", {
|
window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "constructor", {
|
||||||
|
"mediaSourceId": this.mediaSource.bridgeId,
|
||||||
"mimeType": mimeType
|
"mimeType": mimeType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -146,6 +147,7 @@ export class MediaSourceStub extends EventTarget {
|
|||||||
this._duration = NaN;
|
this._duration = NaN;
|
||||||
|
|
||||||
window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "constructor", {
|
window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "constructor", {
|
||||||
|
"id": this.internalId
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simulate asynchronous opening of MediaSource
|
// Simulate asynchronous opening of MediaSource
|
||||||
@ -167,6 +169,12 @@ export class MediaSourceStub extends EventTarget {
|
|||||||
const sourceBuffer = new SourceBufferStub(this, mimeType);
|
const sourceBuffer = new SourceBufferStub(this, mimeType);
|
||||||
this.sourceBuffers._add(sourceBuffer);
|
this.sourceBuffers._add(sourceBuffer);
|
||||||
this.activeSourceBuffers._add(sourceBuffer);
|
this.activeSourceBuffers._add(sourceBuffer);
|
||||||
|
|
||||||
|
window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "updateSourceBuffers", {
|
||||||
|
"ids": this.sourceBuffers._buffers.map((sb) => sb.bridgeId)
|
||||||
|
}).then((result) => {
|
||||||
|
})
|
||||||
|
|
||||||
return sourceBuffer;
|
return sourceBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +183,11 @@ export class MediaSourceStub extends EventTarget {
|
|||||||
throw new DOMException('SourceBuffer not found', 'NotFoundError');
|
throw new DOMException('SourceBuffer not found', 'NotFoundError');
|
||||||
}
|
}
|
||||||
this.activeSourceBuffers._remove(sourceBuffer);
|
this.activeSourceBuffers._remove(sourceBuffer);
|
||||||
|
|
||||||
|
window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "updateSourceBuffers", {
|
||||||
|
"ids": this.sourceBuffers._buffers.map((sb) => sb.bridgeId)
|
||||||
|
}).then((result) => {
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
endOfStream(error) {
|
endOfStream(error) {
|
||||||
|
@ -2,9 +2,11 @@ import { TimeRangesStub } from "./TimeRangesStub.js"
|
|||||||
import { TextTrackStub, TextTrackListStub } from "./TextTrackStub.js"
|
import { TextTrackStub, TextTrackListStub } from "./TextTrackStub.js"
|
||||||
|
|
||||||
export class VideoElementStub extends EventTarget {
|
export class VideoElementStub extends EventTarget {
|
||||||
constructor() {
|
constructor(id) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.instanceId = id;
|
||||||
|
|
||||||
this.bridgeId = window.nextInternalId;
|
this.bridgeId = window.nextInternalId;
|
||||||
window.nextInternalId += 1;
|
window.nextInternalId += 1;
|
||||||
window.bridgeObjectMap[this.bridgeId] = this;
|
window.bridgeObjectMap[this.bridgeId] = this;
|
||||||
@ -23,13 +25,14 @@ export class VideoElementStub extends EventTarget {
|
|||||||
this.autoplay = false;
|
this.autoplay = false;
|
||||||
this.controls = false;
|
this.controls = false;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.src = '';
|
this._src = '';
|
||||||
this.videoWidth = 0;
|
this.videoWidth = 0;
|
||||||
this.videoHeight = 0;
|
this.videoHeight = 0;
|
||||||
this.textTracks = new TextTrackListStub();
|
this.textTracks = new TextTrackListStub();
|
||||||
this.isWaiting = false;
|
this.isWaiting = false;
|
||||||
|
|
||||||
window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "constructor", {
|
window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "constructor", {
|
||||||
|
"instanceId": this.instanceId
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -52,6 +55,7 @@ export class VideoElementStub extends EventTarget {
|
|||||||
this.dispatchEvent(new Event('seeking'));
|
this.dispatchEvent(new Event('seeking'));
|
||||||
|
|
||||||
window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "setCurrentTime", {
|
window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "setCurrentTime", {
|
||||||
|
"instanceId": this.instanceId,
|
||||||
"currentTime": value
|
"currentTime": value
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
this.dispatchEvent(new Event('seeked'));
|
this.dispatchEvent(new Event('seeked'));
|
||||||
@ -59,6 +63,33 @@ export class VideoElementStub extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get src() {
|
||||||
|
return this._src;
|
||||||
|
}
|
||||||
|
|
||||||
|
set src(value) {
|
||||||
|
this._src = value;
|
||||||
|
var media = window.mediaSourceMap[this._src];
|
||||||
|
if (media) {
|
||||||
|
window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "setMediaSource", {
|
||||||
|
"instanceId": this.instanceId,
|
||||||
|
"mediaSourceId": media.bridgeId
|
||||||
|
}).then((result) => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAttribute(name) {
|
||||||
|
if (name === "src") {
|
||||||
|
this._src = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
querySelectorAll(name) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
return fragment.querySelectorAll('*');
|
||||||
|
}
|
||||||
|
|
||||||
bridgeUpdateBuffered(value) {
|
bridgeUpdateBuffered(value) {
|
||||||
const updatedRanges = value;
|
const updatedRanges = value;
|
||||||
var ranges = [];
|
var ranges = [];
|
||||||
@ -103,6 +134,7 @@ export class VideoElementStub extends EventTarget {
|
|||||||
play() {
|
play() {
|
||||||
if (this.paused) {
|
if (this.paused) {
|
||||||
return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "play", {
|
return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "play", {
|
||||||
|
"instanceId": this.instanceId,
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
this.dispatchEvent(new Event('play'));
|
this.dispatchEvent(new Event('play'));
|
||||||
this.dispatchEvent(new Event('playing'));
|
this.dispatchEvent(new Event('playing'));
|
||||||
@ -118,6 +150,7 @@ export class VideoElementStub extends EventTarget {
|
|||||||
this.dispatchEvent(new Event('pause'));
|
this.dispatchEvent(new Event('pause'));
|
||||||
|
|
||||||
return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "pause", {
|
return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "pause", {
|
||||||
|
"instanceId": this.instanceId,
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -131,43 +164,12 @@ export class VideoElementStub extends EventTarget {
|
|||||||
return window.mediaSourceMap[this.src];
|
return window.mediaSourceMap[this.src];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*_simulateTimeUpdate() {
|
|
||||||
if (this._isPlaying) {
|
|
||||||
// Simulate time progression
|
|
||||||
setTimeout(() => {
|
|
||||||
var bufferedEnd = 0.0;
|
|
||||||
|
|
||||||
const media = this._getMedia();
|
|
||||||
if (media) {
|
|
||||||
if (media.sourceBuffers.length != 0) {
|
|
||||||
this.buffered = media.sourceBuffers._buffers[0].buffered;
|
|
||||||
bufferedEnd = this.buffered.length == 0 ? 0 : this.buffered.end(this.buffered.length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume buffered data
|
|
||||||
if (this.currentTime < bufferedEnd) {
|
|
||||||
// Advance currentTime
|
|
||||||
this._currentTime += 0.1 * this.playbackRate; // Increment currentTime
|
|
||||||
this.dispatchEvent(new Event('timeupdate'));
|
|
||||||
|
|
||||||
// Continue simulation
|
|
||||||
this._simulateTimeUpdate();
|
|
||||||
} else {
|
|
||||||
console.log("Buffer underrun");
|
|
||||||
// Buffer underrun
|
|
||||||
this._isPlaying = false;
|
|
||||||
this.paused = true;
|
|
||||||
this.dispatchEvent(new Event('waiting'));
|
|
||||||
// The player should react by buffering more data
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
addTextTrack(kind, label, language) {
|
addTextTrack(kind, label, language) {
|
||||||
const textTrack = new TextTrackStub(kind, label, language);
|
const textTrack = new TextTrackStub(kind, label, language);
|
||||||
this.textTracks._add(textTrack);
|
this.textTracks._add(textTrack);
|
||||||
return textTrack;
|
return textTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,11 @@ export function bridgeInvokeCallback(callbackId, result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var useStubs = true;
|
|
||||||
|
|
||||||
window.nextInternalId = 0;
|
window.nextInternalId = 0;
|
||||||
window.mediaSourceMap = {};
|
window.mediaSourceMap = {};
|
||||||
|
|
||||||
// Replace the global MediaSource with our stub
|
// Replace the global MediaSource with our stub
|
||||||
if (useStubs && typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.MediaSource = MediaSourceStub;
|
window.MediaSource = MediaSourceStub;
|
||||||
window.ManagedMediaSource = MediaSourceStub;
|
window.ManagedMediaSource = MediaSourceStub;
|
||||||
window.SourceBuffer = SourceBufferStub;
|
window.SourceBuffer = SourceBufferStub;
|
||||||
@ -57,44 +55,35 @@ if (useStubs && typeof window !== 'undefined') {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function postPlayerEvent(id, eventName, eventData) {
|
||||||
function postPlayerEvent(eventName, eventData) {
|
|
||||||
if (window.webkit && window.webkit.messageHandlers) {
|
if (window.webkit && window.webkit.messageHandlers) {
|
||||||
window.webkit.messageHandlers.performAction.postMessage({'event': eventName, 'data': eventData});
|
window.webkit.messageHandlers.performAction.postMessage({'instanceId': id, 'event': eventName, 'data': eventData});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
var video;
|
export class HlsPlayerInstance {
|
||||||
var hls;
|
constructor(id) {
|
||||||
|
this.id = id;
|
||||||
var isManifestParsed = false;
|
this.isManifestParsed = false;
|
||||||
var isFirstFrameReady = false;
|
this.currentTimeUpdateTimeout = null;
|
||||||
|
this.video = new VideoElementStub(this.id);
|
||||||
var currentTimeUpdateTimeout = null;
|
|
||||||
|
|
||||||
export function playerInitialize(params) {
|
|
||||||
video.muted = false;
|
|
||||||
|
|
||||||
video.addEventListener('loadeddata', (event) => {
|
|
||||||
if (!isFirstFrameReady) {
|
|
||||||
isFirstFrameReady = true;
|
|
||||||
refreshPlayerStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playerInitialize(params) {
|
||||||
|
this.video.addEventListener("playing", () => {
|
||||||
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
video.addEventListener("playing", function() {
|
this.video.addEventListener("pause", () => {
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
video.addEventListener("pause", function() {
|
this.video.addEventListener("seeking", () => {
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
video.addEventListener("seeking", function() {
|
this.video.addEventListener("waiting", () => {
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
|
||||||
video.addEventListener("waiting", function() {
|
|
||||||
refreshPlayerStatus();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hls = new Hls({
|
this.hls = new Hls({
|
||||||
startLevel: 0,
|
startLevel: 0,
|
||||||
testBandwidth: false,
|
testBandwidth: false,
|
||||||
debug: params['debug'] || true,
|
debug: params['debug'] || true,
|
||||||
@ -105,59 +94,59 @@ export function playerInitialize(params) {
|
|||||||
maxFragLookUpTolerance: 0.001,
|
maxFragLookUpTolerance: 0.001,
|
||||||
nudgeMaxRetry: 10000
|
nudgeMaxRetry: 10000
|
||||||
});
|
});
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, function() {
|
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
isManifestParsed = true;
|
this.isManifestParsed = true;
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
hls.on(Hls.Events.LEVEL_SWITCHED, function() {
|
this.hls.on(Hls.Events.LEVEL_SWITCHED, () => {
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
hls.on(Hls.Events.LEVELS_UPDATED, function() {
|
this.hls.on(Hls.Events.LEVELS_UPDATED, () => {
|
||||||
refreshPlayerStatus();
|
this.refreshPlayerStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
hls.loadSource('master.m3u8');
|
this.hls.loadSource(params["urlPrefix"] + "master.m3u8");
|
||||||
hls.attachMedia(video);
|
this.hls.attachMedia(this.video);
|
||||||
}
|
|
||||||
|
|
||||||
export function playerLoad(initialLevelIndex) {
|
|
||||||
hls.startLevel = initialLevelIndex;
|
|
||||||
hls.startLoad(-1, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function playerPlay() {
|
|
||||||
video.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function playerPause() {
|
|
||||||
video.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function playerSetBaseRate(value) {
|
|
||||||
video.playbackRate = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function playerSetLevel(level) {
|
|
||||||
if (level >= 0) {
|
|
||||||
hls.currentLevel = level;
|
|
||||||
} else {
|
|
||||||
hls.currentLevel = -1;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function playerSeek(value) {
|
playerLoad(initialLevelIndex) {
|
||||||
video.currentTime = value;
|
this.hls.startLevel = initialLevelIndex;
|
||||||
}
|
this.hls.startLoad(-1, false);
|
||||||
|
}
|
||||||
|
|
||||||
export function playerSetIsMuted(value) {
|
playerPlay() {
|
||||||
video.muted = value;
|
this.video.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLevels() {
|
playerPause() {
|
||||||
|
this.video.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
playerSetBaseRate(value) {
|
||||||
|
this.video.playbackRate = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerSetLevel(level) {
|
||||||
|
if (level >= 0) {
|
||||||
|
this.hls.currentLevel = level;
|
||||||
|
} else {
|
||||||
|
this.hls.currentLevel = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playerSeek(value) {
|
||||||
|
this.video.currentTime = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerSetIsMuted(value) {
|
||||||
|
this.video.muted = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevels() {
|
||||||
var levels = [];
|
var levels = [];
|
||||||
for (var i = 0; i < hls.levels.length; i++) {
|
for (var i = 0; i < this.hls.levels.length; i++) {
|
||||||
var level = hls.levels[i];
|
var level = this.hls.levels[i];
|
||||||
levels.push({
|
levels.push({
|
||||||
'index': i,
|
'index': i,
|
||||||
'bitrate': level.bitrate || 0,
|
'bitrate': level.bitrate || 0,
|
||||||
@ -166,69 +155,71 @@ function getLevels() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return levels;
|
return levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshPlayerStatus() {
|
refreshPlayerStatus() {
|
||||||
var isPlaying = false;
|
var isPlaying = false;
|
||||||
if (!video.paused && !video.ended && video.readyState > 2) {
|
if (!this.video.paused && !this.video.ended && this.video.readyState > 2) {
|
||||||
isPlaying = true;
|
isPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
postPlayerEvent('playerStatus', {
|
postPlayerEvent(this.id, 'playerStatus', {
|
||||||
'isReady': isManifestParsed,
|
'isReady': this.isManifestParsed,
|
||||||
'isFirstFrameReady': isFirstFrameReady,
|
'isPlaying': !this.video.paused,
|
||||||
'isPlaying': !video.paused,
|
'rate': isPlaying ? this.video.playbackRate : 0.0,
|
||||||
'rate': isPlaying ? video.playbackRate : 0.0,
|
'defaultRate': this.video.playbackRate,
|
||||||
'defaultRate': video.playbackRate,
|
'levels': this.getLevels(),
|
||||||
'levels': getLevels(),
|
'currentLevel': this.hls.currentLevel
|
||||||
'currentLevel': hls.currentLevel
|
|
||||||
});
|
});
|
||||||
|
|
||||||
refreshPlayerCurrentTime();
|
this.refreshPlayerCurrentTime();
|
||||||
|
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
if (currentTimeUpdateTimeout == null) {
|
if (this.currentTimeUpdateTimeout == null) {
|
||||||
currentTimeUpdateTimeout = setTimeout(() => {
|
this.currentTimeUpdateTimeout = setTimeout(() => {
|
||||||
refreshPlayerCurrentTime();
|
this.refreshPlayerCurrentTime();
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(currentTimeUpdateTimeout != null){
|
if(this.currentTimeUpdateTimeout != null){
|
||||||
clearTimeout(currentTimeUpdateTimeout);
|
clearTimeout(this.currentTimeUpdateTimeout);
|
||||||
currentTimeUpdateTimeout = null;
|
this.currentTimeUpdateTimeout = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshPlayerCurrentTime() {
|
||||||
|
postPlayerEvent(this.id, 'playerCurrentTime', {
|
||||||
|
'value': this.video.currentTime
|
||||||
|
});
|
||||||
|
this.currentTimeUpdateTimeout = setTimeout(() => {
|
||||||
|
this.refreshPlayerCurrentTime()
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshPlayerCurrentTime() {
|
window.invokeOnLoad = function() {
|
||||||
postPlayerEvent('playerCurrentTime', {
|
postPlayerEvent(this.id, 'windowOnLoad', {
|
||||||
'value': video.currentTime
|
|
||||||
});
|
});
|
||||||
currentTimeUpdateTimeout = setTimeout(() => {
|
|
||||||
refreshPlayerCurrentTime()
|
|
||||||
}, 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
if (useStubs) {
|
window.invokeOnLoad();
|
||||||
video = new VideoElementStub();
|
|
||||||
} else {
|
|
||||||
video = document.createElement('video');
|
|
||||||
video.playsInline = true;
|
|
||||||
video.controls = true;
|
|
||||||
document.body.appendChild(video);
|
|
||||||
}
|
|
||||||
|
|
||||||
postPlayerEvent('windowOnLoad', {
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window.playerInitialize = playerInitialize;
|
window.hlsPlayer_instances = {};
|
||||||
window.playerLoad = playerLoad;
|
|
||||||
window.playerPlay = playerPlay;
|
window.hlsPlayer_makeInstance = function(id) {
|
||||||
window.playerPause = playerPause;
|
window.hlsPlayer_instances[id] = new HlsPlayerInstance(id);
|
||||||
window.playerSetBaseRate = playerSetBaseRate;
|
}
|
||||||
window.playerSetLevel = playerSetLevel;
|
|
||||||
window.playerSeek = playerSeek;
|
window.hlsPlayer_destroyInstance = function(id) {
|
||||||
window.playerSetIsMuted = playerSetIsMuted;
|
const instance = window.hlsPlayer_instances[id];
|
||||||
|
if (instance) {
|
||||||
|
delete window.hlsPlayer_instances[id];
|
||||||
|
instance.video.pause();
|
||||||
|
instance.hls.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.bridgeInvokeCallback = bridgeInvokeCallback;
|
window.bridgeInvokeCallback = bridgeInvokeCallback;
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user