Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions src/app/elements/replay/guacamole/guacamole.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,15 @@ export class ElementReplayGuacamoleComponent implements OnInit, OnChanges, After
if (this.isMobile()) {
this.initTouchEvents();
}

} catch (error) {
throw new Error(error);
}
}

initRecording() {
this.recording.connect('');

// 先注册事件,避免在 connect 之后事件被瞬间触发而丢失
this.recording.onload = () => {
this.applyScaleWithRetry(200);
this.recording.play();
};

this.recording.onplay = () => {
Expand All @@ -161,7 +158,6 @@ export class ElementReplayGuacamoleComponent implements OnInit, OnChanges, After
}
};

// If paused, the play/pause button should read "Play"
this.recording.onpause = () => {
this.isPlaying = false;
};
Expand All @@ -185,6 +181,34 @@ export class ElementReplayGuacamoleComponent implements OnInit, OnChanges, After
this.lastDuration = this.max;
}
}, 1000);

// 连接录制流
this.recording.connect('');

// 立即尝试播放,不等待 onload,增强大文件/慢速网络下的起播速度
this.safeAutoplayWithRetry();
}

private safeAutoplayWithRetry(maxRetry: number = 5, delayMs: number = 500) {
let attempts = 0;
const tryPlay = () => {
if (!this.recording) {
return;
}
try {
if (!this.recording.isPlaying()) {
this.recording.play();
}
} catch (e) {
console.error(e)
}

if (this.recording && !this.recording.isPlaying() && attempts < maxRetry) {
attempts++;
setTimeout(tryPlay, delayMs);
}
};
tryPlay();
}

private applyScaleWithRetry(delay: number = 100, maxRetries: number = 5) {
Expand Down
30 changes: 13 additions & 17 deletions src/app/elements/replay/parts/parts.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,8 @@ export class ElementsPartsComponent implements OnInit {
// 3.5 的 TS 版本无法使用 ?.
// @ts-ignore
const data = res.type ? res : res.resp ? res.resp.data : undefined;
// 某些后端会把状态放在 data 中,兜底读取
// @ts-ignore
const status = (res && res.status) || (data && data.status);

// 只要拿到 src 就先构建分片并播放,后续是否 still running 由后台继续处理
if (data && data.src) {
if (data && data.src && res.status !== 'running') {
section = {
id: this.id,
account: data.account,
Expand Down Expand Up @@ -180,7 +176,7 @@ export class ElementsPartsComponent implements OnInit {

return false;
}
} else if (status === 'running') {
} else if (res && res.status === 'running') {
this.alertShown = true;
await this.delay(3000);
return await this.fetchSection(item, sessionId, isFirstPush);
Expand All @@ -202,19 +198,19 @@ export class ElementsPartsComponent implements OnInit {
async handlePartFileReplay(file: IFile[], sessionId: string) {
let isFirstPush = true;

if (!file || file.length === 0) {
return;
}
for (const item of file) {
// 先拿第一个保证有东西可以播出来
isFirstPush = await this.fetchSection(file[0], sessionId, isFirstPush);

// 先请求第一个,保证尽快有可播放的内容
isFirstPush = await this.fetchSection(file[0], sessionId, isFirstPush);
// 其他的放到异步任务中
Promise.resolve().then(async() => {
for(const item of file.slice(1)) {
await this.fetchSection(item, sessionId, isFirstPush);
}
})

// 其余分片放到后台依次请求,避免与首个分片并发拥塞
Promise.resolve().then(async () => {
for (const rest of file.slice(1)) {
await this.fetchSection(rest, sessionId, false);
}
});
isFirstPush = await this.fetchSection(item, sessionId, isFirstPush);
}
}

/**
Expand Down