[+] 视频添加
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
center
|
||||
width="30%"
|
||||
title="房间设置"
|
||||
@open="init"
|
||||
@open="loadList"
|
||||
:show-close="false"
|
||||
v-model="roomVisible"
|
||||
>
|
||||
@@ -80,22 +80,27 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 菜单 -->
|
||||
<div class="menu">
|
||||
<div class="menus">
|
||||
<el-button type="primary" @click="signalVisible = true">连接信令</el-button>
|
||||
<el-button type="primary" @click="roomActive = 'enter'; roomVisible = true;">选择房间</el-button>
|
||||
<el-button type="primary" @click="roomActive = 'create';roomVisible = true;">创建房间</el-button>
|
||||
<el-button>邀请终端</el-button>
|
||||
<el-button>退出房间</el-button>
|
||||
<el-button type="danger">关闭房间</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 终端 -->
|
||||
<div class="client">
|
||||
<div class="clients">
|
||||
<LocalClient ref="local"></LocalClient>
|
||||
<RemoteClient :ref="'remote-' + kv[0]" v-for="(kv, index) in remoteClients" :key="index"></RemoteClient>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Taoyao } from "./components/Taoyao.js";
|
||||
import LocalClient from './components/LocalClient.vue';
|
||||
import RemoteClient from './components/RemoteClient.vue';
|
||||
|
||||
export default {
|
||||
name: "Taoyao",
|
||||
@@ -114,7 +119,8 @@ export default {
|
||||
taoyao: {},
|
||||
roomActive: "enter",
|
||||
roomVisible: false,
|
||||
signalVisible: true,
|
||||
signalVisible: false,
|
||||
remoteClients: new Map(),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -124,7 +130,14 @@ export default {
|
||||
`);
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
async connectSignal() {
|
||||
let self = this;
|
||||
self.taoyao = new Taoyao({ ...this.config });
|
||||
self.remoteClients = self.taoyao.remoteClients;
|
||||
await self.taoyao.connectSignal(self.callback, self.callbackMedia);
|
||||
self.signalVisible = false;
|
||||
},
|
||||
async loadList() {
|
||||
this.rooms = await this.taoyao.roomList();
|
||||
this.medias = await this.taoyao.mediaList();
|
||||
},
|
||||
@@ -138,12 +151,6 @@ export default {
|
||||
this.room = room;
|
||||
await this.enterRoom(room.roomId);
|
||||
},
|
||||
async connectSignal() {
|
||||
let self = this;
|
||||
self.taoyao = new Taoyao({ ...this.config });
|
||||
await self.taoyao.connectSignal(self.callback);
|
||||
self.signalVisible = false;
|
||||
},
|
||||
/**
|
||||
* 信令回调
|
||||
*
|
||||
@@ -172,10 +179,37 @@ export default {
|
||||
message: data.message,
|
||||
type: "error",
|
||||
});
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* 媒体回调
|
||||
*/
|
||||
callbackMedia(type, track, consumer) {
|
||||
const self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
if(type === 'local') {
|
||||
self.$refs.local.media(track);
|
||||
} else {
|
||||
this.$refs['remote-' + consumer.sourceId][0].media(track, consumer);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
},
|
||||
components: {
|
||||
LocalClient,
|
||||
RemoteClient
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.menus{width:100%;top:1rem;left:0;text-align:center;position:fixed;z-index:1;}
|
||||
.clients{width:100%;height:100%;top:0;left:0;position:fixed;}
|
||||
.client{float:left;width:50vw;height:50vh;border:1px solid #eee;}
|
||||
.client .buttons{width:100%;bottom:1rem;left:0;text-align:center;position:absolute;padding:0.8rem 0;background: rgba(0,0,0,0.6);text-align:center;}
|
||||
.client audio{display:none;}
|
||||
.client video{width:100%;height:100%;}
|
||||
</style>
|
||||
@@ -1,24 +1,113 @@
|
||||
<!-- 本地终端 -->
|
||||
<template></template>
|
||||
<template>
|
||||
<div class="client">
|
||||
<audio ref="audio"></audio>
|
||||
<video ref="video"></video>
|
||||
<div class="buttons">
|
||||
<el-button type="danger" title="打开麦克风" :icon="Mute" circle />
|
||||
<el-button type="primary" title="关闭麦克风" :icon="Microphone" circle />
|
||||
<el-button type="danger" title="打开摄像头" :icon="VideoPause" circle />
|
||||
<el-button type="primary" title="关闭摄像头" :icon="VideoPlay" circle />
|
||||
<el-button title="交换媒体" :icon="Refresh" circle />
|
||||
<el-button title="拍照" :icon="Camera" circle />
|
||||
<el-button title="录像" :icon="VideoCamera" circle />
|
||||
<el-button title="媒体信息" :icon="InfoFilled" circle />
|
||||
<el-select placeholder="视频质量">
|
||||
<el-option
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@vue/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
import {
|
||||
Mute,
|
||||
Camera,
|
||||
Refresh,
|
||||
VideoPlay,
|
||||
VideoPause,
|
||||
InfoFilled,
|
||||
Microphone,
|
||||
VideoCamera,
|
||||
CircleClose,
|
||||
} from "@element-plus/icons";
|
||||
export default {
|
||||
name: "LocalClient",
|
||||
setup() {
|
||||
// 本地视频
|
||||
this._externalVideo = document.createElement("video");
|
||||
|
||||
this._externalVideo.controls = true;
|
||||
this._externalVideo.muted = true;
|
||||
this._externalVideo.loop = true;
|
||||
this._externalVideo.setAttribute("playsinline", "");
|
||||
this._externalVideo.src = EXTERNAL_VIDEO_SRC;
|
||||
|
||||
// TODO:关闭摄像头、视频、音频
|
||||
this._externalVideo
|
||||
.play()
|
||||
.catch((error) => console.warn("externalVideo.play() failed:%o", error));
|
||||
return {
|
||||
Mute,
|
||||
Camera,
|
||||
Refresh,
|
||||
VideoPlay,
|
||||
VideoPause,
|
||||
InfoFilled,
|
||||
Microphone,
|
||||
VideoCamera,
|
||||
CircleClose,
|
||||
};
|
||||
},
|
||||
});
|
||||
data() {
|
||||
return {
|
||||
taoyao: null,
|
||||
audio: null,
|
||||
video: null,
|
||||
audioStream: null,
|
||||
videoStream: null,
|
||||
dataProducer: null,
|
||||
audioProducer: null,
|
||||
videoProducer: null,
|
||||
options: [
|
||||
{
|
||||
value: "HD",
|
||||
label: "高清",
|
||||
},
|
||||
{
|
||||
value: "SD",
|
||||
label: "标签",
|
||||
},
|
||||
{
|
||||
value: "FD",
|
||||
label: "超清",
|
||||
},
|
||||
{
|
||||
value: "BD",
|
||||
label: "蓝光",
|
||||
},
|
||||
{
|
||||
value: "QD",
|
||||
label: "2K",
|
||||
},
|
||||
{
|
||||
value: "UD",
|
||||
label: "4K",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.audio = this.$refs.audio;
|
||||
this.video = this.$refs.video;
|
||||
},
|
||||
methods: {
|
||||
media(track) {
|
||||
if (track.kind === "video") {
|
||||
if (this.videoStream) {
|
||||
// TODO:资源释放
|
||||
} else {
|
||||
this.videoStream = new MediaStream();
|
||||
this.videoStream.addTrack(track);
|
||||
this.video.srcObject = this.videoStream;
|
||||
}
|
||||
this.video.play().catch((error) => console.warn("视频播放失败", error));
|
||||
} else {
|
||||
console.debug("本地不支持的媒体类型:", track);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,88 @@
|
||||
<!-- 远程终端 -->
|
||||
<template>
|
||||
<video></video>
|
||||
<audio></audio>
|
||||
<div>
|
||||
<el-button type="primary" title="关闭麦克风" :icon="Edit" circle />
|
||||
<div class="client">
|
||||
<audio ref="audio"></audio>
|
||||
<video ref="video"></video>
|
||||
<div class="buttons">
|
||||
<el-button type="danger" title="打开麦克风" :icon="Mute" circle />
|
||||
<el-button type="primary" title="关闭麦克风" :icon="Microphone" circle />
|
||||
<el-button type="danger" title="打开摄像头" :icon="VideoPause" circle />
|
||||
<el-button type="primary" title="关闭摄像头" :icon="VideoPlay" circle />
|
||||
<el-button title="拍照" :icon="Camera" circle />
|
||||
<el-button title="录像" :icon="VideoCamera" circle />
|
||||
<el-button title="媒体信息" :icon="InfoFilled" circle />
|
||||
<el-button title="踢出" :icon="CircleClose" circle />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
Mute,
|
||||
Camera,
|
||||
Refresh,
|
||||
VideoPlay,
|
||||
VideoPause,
|
||||
InfoFilled,
|
||||
Microphone,
|
||||
VideoCamera,
|
||||
CircleClose,
|
||||
} from "@element-plus/icons";
|
||||
export default {
|
||||
name: "RemoteClient",
|
||||
}
|
||||
</script>
|
||||
setup() {
|
||||
return {
|
||||
Mute,
|
||||
Camera,
|
||||
Refresh,
|
||||
VideoPlay,
|
||||
VideoPause,
|
||||
InfoFilled,
|
||||
Microphone,
|
||||
VideoCamera,
|
||||
CircleClose,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
taoyao: null,
|
||||
audio: null,
|
||||
video: null,
|
||||
audioStream: null,
|
||||
videoStream: null,
|
||||
dataConsumer: null,
|
||||
audioConsumer: null,
|
||||
videoConsumer: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.audio = this.$refs.audio;
|
||||
this.video = this.$refs.video;
|
||||
},
|
||||
methods: {
|
||||
media(track, consumer) {
|
||||
if(track.kind === 'audio') {
|
||||
if (this.audioStream) {
|
||||
// TODO:资源释放
|
||||
} else {
|
||||
this.audioStream = new MediaStream();
|
||||
this.audioStream.addTrack(track);
|
||||
this.audio.srcObject = this.audioStream;
|
||||
}
|
||||
this.audio.play().catch((error) => console.warn("视频播放失败", error));
|
||||
} else if(track.kind === 'video') {
|
||||
if (this.videoStream) {
|
||||
// TODO:资源释放
|
||||
} else {
|
||||
this.videoStream = new MediaStream();
|
||||
this.videoStream.addTrack(track);
|
||||
this.video.srcObject = this.videoStream;
|
||||
}
|
||||
this.video.play().catch((error) => console.warn("视频播放失败", error));
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -11,18 +11,14 @@ import {
|
||||
} from "./Config.js";
|
||||
|
||||
// Used for simulcast webcam video.
|
||||
const WEBCAM_SIMULCAST_ENCODINGS =
|
||||
[
|
||||
{ scaleResolutionDownBy: 4, maxBitrate: 500000, scalabilityMode: 'S1T2' },
|
||||
{ scaleResolutionDownBy: 2, maxBitrate: 1000000, scalabilityMode: 'S1T2' },
|
||||
{ scaleResolutionDownBy: 1, maxBitrate: 5000000, scalabilityMode: 'S1T2' }
|
||||
const WEBCAM_SIMULCAST_ENCODINGS = [
|
||||
{ scaleResolutionDownBy: 4, maxBitrate: 500000, scalabilityMode: "S1T2" },
|
||||
{ scaleResolutionDownBy: 2, maxBitrate: 1000000, scalabilityMode: "S1T2" },
|
||||
{ scaleResolutionDownBy: 1, maxBitrate: 5000000, scalabilityMode: "S1T2" },
|
||||
];
|
||||
|
||||
// Used for VP9 webcam video.
|
||||
const WEBCAM_KSVC_ENCODINGS =
|
||||
[
|
||||
{ scalabilityMode: 'S3T3_KEY' }
|
||||
];
|
||||
const WEBCAM_KSVC_ENCODINGS = [{ scalabilityMode: "S3T3_KEY" }];
|
||||
|
||||
/**
|
||||
* 信令通道
|
||||
@@ -291,8 +287,14 @@ const signalChannel = {
|
||||
case "client::reboot":
|
||||
self.defaultClientReboot(message);
|
||||
break;
|
||||
case "client::shutdown":
|
||||
self.defaultClientShutdown(message);
|
||||
case "client::shutdown":
|
||||
self.defaultClientShutdown(message);
|
||||
break;
|
||||
case "room::enter":
|
||||
self.defaultRoomEnter(message);
|
||||
break;
|
||||
case "room::client::list":
|
||||
self.defaultRoomClientList(message);
|
||||
break;
|
||||
case "platform::error":
|
||||
self.callbackError(message);
|
||||
@@ -334,6 +336,24 @@ const signalChannel = {
|
||||
console.info("关闭终端");
|
||||
window.close();
|
||||
},
|
||||
defaultRoomEnter(message) {
|
||||
const { roomId, clientId } = message.body;
|
||||
if(clientId === this.taoyao.clientId) {
|
||||
// 忽略自己
|
||||
} else {
|
||||
this.taoyao.remoteClients.set(clientId, roomId);
|
||||
}
|
||||
},
|
||||
defaultRoomClientList(message) {
|
||||
const self = this;
|
||||
message.body.forEach(v => {
|
||||
if(v.clientId === self.taoyao.clientId) {
|
||||
// 忽略自己
|
||||
} else {
|
||||
self.taoyao.remoteClients.set(v.clientId, self.taoyao.roomId);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -354,6 +374,8 @@ class Taoyao {
|
||||
password;
|
||||
// 回调事件
|
||||
callback;
|
||||
// 媒体回调
|
||||
callbackMedia;
|
||||
// 音频媒体配置
|
||||
audio;
|
||||
// 视频媒体配置
|
||||
@@ -364,10 +386,6 @@ class Taoyao {
|
||||
push;
|
||||
// 请求信令
|
||||
request;
|
||||
// 本地终端
|
||||
localClient;
|
||||
// 远程终端
|
||||
remoteClients = new Map();
|
||||
// 信令通道
|
||||
signalChannel;
|
||||
// 发送媒体通道
|
||||
@@ -381,30 +399,31 @@ class Taoyao {
|
||||
// 是否生产
|
||||
produce;
|
||||
// 视频来源:file | camera | screen
|
||||
videoSource = "screen";
|
||||
videoSource = "camera";
|
||||
// 强制使用TCP
|
||||
forceTcp;
|
||||
// 强制使用VP9
|
||||
forceVP9;
|
||||
// 强制使用H264
|
||||
forceH264;
|
||||
//
|
||||
useSimulcast;
|
||||
// 是否生产数据
|
||||
dataProduce;
|
||||
// 是否生产音频
|
||||
audioProduce;
|
||||
// 是否生成视频
|
||||
videoProduce;
|
||||
// 强制使用TCP
|
||||
forceTcp;
|
||||
// 使用数据通道
|
||||
useDataChannel;
|
||||
// 数据生产者
|
||||
dataProducer;
|
||||
// 音频生产者
|
||||
audioProducer;
|
||||
// 视频生产者
|
||||
videoProducer;
|
||||
// 数据生产者
|
||||
dataChannnelProducer;
|
||||
// 媒体消费者
|
||||
// 消费者:音频、视频、数据
|
||||
consumers = new Map();
|
||||
// 数据消费者
|
||||
dataConsumers = new Map();
|
||||
// 远程终端
|
||||
remoteClients = new Map();
|
||||
|
||||
constructor({
|
||||
roomId,
|
||||
@@ -418,7 +437,7 @@ class Taoyao {
|
||||
audioProduce = true,
|
||||
videoProduce = true,
|
||||
forceTcp = false,
|
||||
useDataChannel = true,
|
||||
dataProduce = true,
|
||||
}) {
|
||||
this.roomId = roomId;
|
||||
this.clientId = clientId;
|
||||
@@ -428,22 +447,24 @@ class Taoyao {
|
||||
this.password = password;
|
||||
this.consume = consume;
|
||||
this.produce = produce;
|
||||
this.dataProduce = produce && dataProduce;
|
||||
this.audioProduce = produce && audioProduce;
|
||||
this.videoProduce = produce && videoProduce;
|
||||
this.forceTcp = forceTcp;
|
||||
this.useDataChannel = useDataChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接信令
|
||||
*
|
||||
* @param {*} callback
|
||||
* @param {*} callback 信令回调
|
||||
* @param {*} callbackMedia 媒体回调
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
async connectSignal(callback) {
|
||||
async connectSignal(callback, callbackMedia) {
|
||||
const self = this;
|
||||
self.callback = callback;
|
||||
self.callbackMedia = callbackMedia;
|
||||
self.signalChannel = signalChannel;
|
||||
signalChannel.taoyao = self;
|
||||
// 不能直接this.push = this.signalChannel.push这样导致this对象错误
|
||||
@@ -469,6 +490,7 @@ class Taoyao {
|
||||
} else {
|
||||
console.warn("没有注册回调:", message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 错误回调
|
||||
const errorMessage = protocol.buildMessage(
|
||||
@@ -490,6 +512,12 @@ class Taoyao {
|
||||
);
|
||||
return response.body;
|
||||
}
|
||||
async clientList() {
|
||||
const response = await this.request(
|
||||
protocol.buildMessage("client::list", { roomId: self.roomId })
|
||||
);
|
||||
return response.body;
|
||||
}
|
||||
/**
|
||||
* 创建房间
|
||||
*/
|
||||
@@ -526,7 +554,7 @@ class Taoyao {
|
||||
? self.mediasoupDevice.rtpCapabilities
|
||||
: undefined,
|
||||
sctpCapabilities:
|
||||
self.consume && self.useDataChannel
|
||||
self.consume && self.dataProduce
|
||||
? self.mediasoupDevice.sctpCapabilities
|
||||
: undefined,
|
||||
})
|
||||
@@ -564,7 +592,7 @@ class Taoyao {
|
||||
forceTcp: self.forceTcp,
|
||||
producing: true,
|
||||
consuming: false,
|
||||
sctpCapabilities: self.useDataChannel
|
||||
sctpCapabilities: self.dataProduce
|
||||
? self.mediasoupDevice.sctpCapabilities
|
||||
: undefined,
|
||||
})
|
||||
@@ -661,7 +689,7 @@ class Taoyao {
|
||||
forceTcp: self.forceTcp,
|
||||
producing: false,
|
||||
consuming: true,
|
||||
sctpCapabilities: self.useDataChannel
|
||||
sctpCapabilities: self.dataProduce
|
||||
? self.mediasoupDevice.sctpCapabilities
|
||||
: undefined,
|
||||
})
|
||||
@@ -843,20 +871,24 @@ class Taoyao {
|
||||
track = stream.getVideoTracks()[0];
|
||||
} else if (self.videoSource === "screen") {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia({
|
||||
// 如果需要共享声音
|
||||
audio: false,
|
||||
video: {
|
||||
displaySurface: "monitor",
|
||||
logicalSurface: true,
|
||||
cursor: true,
|
||||
width: { max: 1920 },
|
||||
height: { max: 1080 },
|
||||
frameRate: { max: 30 },
|
||||
logicalSurface: true,
|
||||
displaySurface: "monitor",
|
||||
},
|
||||
});
|
||||
track = stream.getVideoTracks()[0];
|
||||
} else {
|
||||
// TODO:异常
|
||||
}
|
||||
|
||||
self.callbackMedia("local", track);
|
||||
|
||||
let codec;
|
||||
let encodings;
|
||||
const codecOptions = {
|
||||
@@ -904,7 +936,7 @@ class Taoyao {
|
||||
// if (this._e2eKey && e2e.isSupported()) {
|
||||
// e2e.setupSenderTransform(this.videoProducer.rtpSender);
|
||||
// }
|
||||
|
||||
|
||||
this.videoProducer.on("transportclose", () => {
|
||||
this.videoProducer = null;
|
||||
});
|
||||
@@ -924,6 +956,69 @@ class Taoyao {
|
||||
}
|
||||
}
|
||||
|
||||
async closeVideoProducer() {
|
||||
console.debug("disableWebcam()");
|
||||
if (!this.videoProducer) {
|
||||
return;
|
||||
}
|
||||
this.videoProducer.close();
|
||||
try {
|
||||
await this.request(
|
||||
protocol.buildMessage("media::producer::close", {
|
||||
producerId: this.videoProducer.id,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
this._webcamProducer = null;
|
||||
}
|
||||
|
||||
async pauseVideoProducer() {
|
||||
console.debug("关闭摄像头");
|
||||
this.videoProducer.pause();
|
||||
try {
|
||||
await this.request(
|
||||
protocol.buildMessage("media::producer::pause", {
|
||||
producerId: this.videoProducer.id,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("关闭摄像头异常", error);
|
||||
// TODO:异常调用回调
|
||||
}
|
||||
}
|
||||
|
||||
async resumeVideoProducer() {
|
||||
console.debug("恢复摄像头");
|
||||
this.videoProducer.resume();
|
||||
try {
|
||||
await this.request(
|
||||
protocol.buildMessage("media::producer::resume", {
|
||||
producerId: this.videoProducer.id,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("恢复摄像头异常", error);
|
||||
}
|
||||
}
|
||||
|
||||
async updateVideoConfig(config) {
|
||||
console.debug("更新摄像头参数");
|
||||
try {
|
||||
this.videoProducer.track.stop();
|
||||
// TODO:screen、参数配置
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: true,
|
||||
});
|
||||
const track = stream.getVideoTracks()[0];
|
||||
await this.videoProducer.replaceTrack({ track });
|
||||
} catch (error) {
|
||||
console.error("changeWebcam() | failed: %o", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费媒体
|
||||
*
|
||||
@@ -941,6 +1036,7 @@ class Taoyao {
|
||||
type,
|
||||
roomId,
|
||||
clientId,
|
||||
sourceId,
|
||||
streamId,
|
||||
producerId,
|
||||
consumerId,
|
||||
@@ -962,6 +1058,7 @@ class Taoyao {
|
||||
appData, // Trick.
|
||||
});
|
||||
consumer.clientId = clientId;
|
||||
consumer.sourceId = sourceId;
|
||||
consumer.streamId = streamId;
|
||||
self.consumers.set(consumer.id, consumer);
|
||||
consumer.on("transportclose", () => {
|
||||
@@ -991,18 +1088,10 @@ class Taoyao {
|
||||
// )
|
||||
// );
|
||||
self.push(message);
|
||||
console.log(consumer);
|
||||
|
||||
const audioElem = document.createElement("video");
|
||||
document.getElementsByTagName("body")[0].appendChild(audioElem);
|
||||
|
||||
const stream = new MediaStream();
|
||||
stream.addTrack(consumer.track);
|
||||
audioElem.srcObject = stream;
|
||||
audioElem
|
||||
.play()
|
||||
.catch((error) => console.warn("audioElem.play() failed:%o", error));
|
||||
console.log("消费者", consumer);
|
||||
|
||||
self.callbackMedia("remote", consumer.track, consumer);
|
||||
|
||||
// If audio-only mode is enabled, pause it.
|
||||
if (consumer.kind === "video" && !self.videoProduce) {
|
||||
// this.pauseConsumer(consumer);
|
||||
@@ -1013,6 +1102,26 @@ class Taoyao {
|
||||
}
|
||||
}
|
||||
|
||||
async pauseConsumer(consumer) {
|
||||
if (consumer.paused) return;
|
||||
try {
|
||||
await this._protoo.request("pauseConsumer", { consumerId: consumer.id });
|
||||
consumer.pause();
|
||||
} catch (error) {
|
||||
logger.error("_pauseConsumer() | failed:%o", error);
|
||||
}
|
||||
}
|
||||
|
||||
async resumeConsumer(consumer) {
|
||||
if (!consumer.paused) return;
|
||||
try {
|
||||
await this._protoo.request("resumeConsumer", { consumerId: consumer.id });
|
||||
consumer.resume();
|
||||
} catch (error) {
|
||||
logger.error("_resumeConsumer() | failed:%o", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证设备
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user