[+] 音频监控

This commit is contained in:
acgist
2023-03-05 11:56:31 +08:00
parent 3bc39b4d48
commit c49c3b19d8
15 changed files with 527 additions and 332 deletions

View File

@@ -1,102 +1,104 @@
<!-- 桃夭 -->
<template>
<!-- 信令 -->
<el-dialog
center
width="30%"
title="终端设置"
:show-close="false"
v-if="taoyao === null"
v-model="signalVisible"
>
<el-form ref="SignalSetting">
<el-form-item label="终端标识">
<el-input v-model="config.clientId" placeholder="终端标识" />
</el-form-item>
<el-form-item label="终端名称">
<el-input v-model="config.name" placeholder="终端名称" />
</el-form-item>
<el-form-item label="信令地址">
<el-input v-model="config.host" placeholder="信令地址" />
</el-form-item>
<el-form-item label="信令端口">
<el-input v-model="config.port" placeholder="信令端口" />
</el-form-item>
<el-form-item label="信令帐号">
<el-input v-model="config.username" placeholder="信令帐号" />
</el-form-item>
<el-form-item label="信令密码">
<el-input v-model="config.password" placeholder="信令密码" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="connectSignal">连接信令</el-button>
</template>
</el-dialog>
<div id="taoyao">
<!-- 信令 -->
<el-dialog
center
width="30%"
title="终端设置"
:show-close="false"
v-if="taoyao === null"
v-model="signalVisible"
>
<el-form ref="SignalSetting">
<el-form-item label="终端标识">
<el-input v-model="config.clientId" placeholder="终端标识" />
</el-form-item>
<el-form-item label="终端名称">
<el-input v-model="config.name" placeholder="终端名称" />
</el-form-item>
<el-form-item label="信令地址">
<el-input v-model="config.host" placeholder="信令地址" />
</el-form-item>
<el-form-item label="信令端口">
<el-input v-model="config.port" placeholder="信令端口" />
</el-form-item>
<el-form-item label="信令帐号">
<el-input v-model="config.username" placeholder="信令帐号" />
</el-form-item>
<el-form-item label="信令密码">
<el-input v-model="config.password" placeholder="信令密码" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="connectSignal">连接信令</el-button>
</template>
</el-dialog>
<!-- 房间 -->
<el-dialog
center
width="30%"
title="房间设置"
@open="loadList"
:show-close="false"
v-model="roomVisible"
>
<el-form ref="RoomSetting" :model="room">
<el-tabs v-model="roomActive">
<el-tab-pane label="进入房间" name="enter">
<el-form-item label="房间标识">
<el-select v-model="room.roomId" placeholder="房间标识">
<el-option
v-for="value in rooms"
:key="value.roomId"
:label="value.name || value.roomId"
:value="value.roomId"
/>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="创建房间" name="create">
<el-form-item label="媒体服务">
<el-select v-model="room.mediaClientId" placeholder="媒体服务标识">
<el-option
v-for="value in medias"
:key="value.clientId"
:label="value.name || value.clientId"
:value="value.clientId"
/>
</el-select>
</el-form-item>
<el-form-item label="房间名称">
<el-input v-model="room.name" placeholder="房间名称" />
</el-form-item>
</el-tab-pane>
</el-tabs>
<el-form-item label="房间密码">
<el-input v-model="room.password" placeholder="房间密码" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="enterRoom" v-if="roomActive === 'enter'">进入</el-button>
<el-button type="primary" @click="roomCreate" v-if="roomActive === 'create'">创建</el-button>
</template>
</el-dialog>
<!-- 房间 -->
<el-dialog
center
width="30%"
title="房间设置"
@open="loadList"
:show-close="false"
v-model="roomVisible"
>
<el-form ref="RoomSetting" :model="room">
<el-tabs v-model="roomActive">
<el-tab-pane label="进入房间" name="enter">
<el-form-item label="房间标识">
<el-select v-model="room.roomId" placeholder="房间标识">
<el-option
v-for="value in rooms"
:key="value.roomId"
:label="value.name || value.roomId"
:value="value.roomId"
/>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="创建房间" name="create">
<el-form-item label="媒体服务">
<el-select v-model="room.mediaClientId" placeholder="媒体服务标识">
<el-option
v-for="value in medias"
:key="value.clientId"
:label="value.name || value.clientId"
:value="value.clientId"
/>
</el-select>
</el-form-item>
<el-form-item label="房间名称">
<el-input v-model="room.name" placeholder="房间名称" />
</el-form-item>
</el-tab-pane>
</el-tabs>
<el-form-item label="房间密码">
<el-input v-model="room.password" placeholder="房间密码" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="roomEnter" v-if="roomActive === 'enter'">进入</el-button>
<el-button type="primary" @click="roomCreate" v-if="roomActive === 'create'">创建</el-button>
</template>
</el-dialog>
<!-- 菜单 -->
<div class="menus">
<el-button type="primary" :disabled="taoyao !== null" @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 @click="closeRoom()" type="danger">关闭房间</el-button>
</div>
<!-- 菜单 -->
<div class="menus">
<el-button type="primary" :disabled="taoyao && taoyao.connect" @click="signalVisible = true">连接信令</el-button>
<el-button type="primary" :disabled="!taoyao" @click="roomActive = 'enter';roomVisible = true;">选择房间</el-button>
<el-button type="primary" :disabled="!taoyao" @click="roomActive = 'create';roomVisible = true;">创建房间</el-button>
<el-button :disabled="!taoyao || !room.roomId">邀请终端</el-button>
<el-button :disabled="!taoyao || !room.roomId">退出房间</el-button>
<el-button :disabled="!taoyao || !room.roomId" @click="roomClose()" type="danger">关闭房间</el-button>
</div>
<!-- 终端 -->
<div class="clients">
<LocalClient ref="local"></LocalClient>
<RemoteClient :ref="'remote-' + kv[0]" v-for="(kv, index) in remoteClients" :key="index"></RemoteClient>
<!-- 终端 -->
<div class="clients">
<LocalClient ref="local" :client="taoyao" :taoyao="taoyao"></LocalClient>
<RemoteClient :ref="'remote-' + kv[0]" v-for="(kv, index) in remoteClients" :key="index" :client="kv[1]" :taoyao="taoyao"></RemoteClient>
</div>
</div>
</template>
@@ -148,46 +150,48 @@ export default {
this.rooms = await this.taoyao.roomList();
this.medias = await this.taoyao.mediaList();
},
async enterRoom() {
await this.taoyao.enterRoom(this.room.roomId, this.room.password);
await this.taoyao.produceMedia();
this.roomVisible = false;
async roomClose() {
this.taoyao.roomClose();
},
async roomCreate() {
const room = await this.taoyao.roomCreate(this.room);
this.room.roomId = room.roomId;
await this.enterRoom();
await this.roomEnter();
},
async closeRoom() {
this.taoyao.closeRoom();
async roomEnter() {
await this.taoyao.roomEnter(this.room.roomId, this.room.password);
await this.taoyao.produceMedia();
this.roomVisible = false;
},
audioVolume(message) {
},
/**
* 信令回调
*
* @param {*} data 消息
* @param {*} message 消息
* @param {*} error 异常
*
* @return 是否继续执行
*/
async callback(data, error) {
let self = this;
switch (data.header.signal) {
async callback(message, error) {
const me = this;
switch (message.header.signal) {
case "client::config":
self.roomVisible = true;
me.roomVisible = true;
break;
case "media::audio::active::speaker":
me.audioVolume(message);
break;
case "client::register":
self.signalVisible = data.code !== "0000";
return true;
case "platform::error":
if (error) {
console.error("发生异常:", data, error);
console.error("发生异常:", message, error);
} else {
console.warn("发生错误:", data);
console.warn("发生错误:", message);
}
ElMessage({
showClose: true,
message: data.message,
type: "error",
message: message.message,
});
return true;
}
@@ -195,14 +199,20 @@ export default {
},
/**
* 媒体回调
*
* @param {*} type 类型
* @param {*} track 媒体Track
* @param {*} consumer 消费者
*/
callbackMedia(type, track, consumer) {
const self = this;
const me = this;
return new Promise((resolve, reject) => {
if(type === 'local') {
self.$refs.local.media(track);
me.$refs.local.media(track);
} else if(type === 'remote') {
me.$refs['remote-' + consumer.sourceId][0].media(track, consumer);
} else {
this.$refs['remote-' + consumer.sourceId][0].media(track, consumer);
// 其他
}
resolve();
});
@@ -219,7 +229,9 @@ export default {
.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;box-shadow:0 0 1px 0px rgba(0,0,0,0.4);}
.client .buttons{width:100%;bottom:1rem;left:0;text-align:center;position:absolute;padding:0.8rem 0;background:rgba(0,0,0,0.4);}
.client .buttons{width:100%;bottom:2px;left:0;text-align:center;position:absolute;padding:0.8rem 0;background:rgba(0,0,0,0.4);}
.client .buttons:after{width:0;height:2px;bottom:0;left:0;position:absolute;background:#C00;content:"";transition: all 400ms linear;}
.client audio{display:none;}
.client video{width:100%;height:100%;}
.client .title{position:absolute;top:0;left:0;text-align:center;width:100%;}
</style>

View File

@@ -3,7 +3,8 @@
<div class="client">
<audio ref="audio"></audio>
<video ref="video"></video>
<div class="buttons">
<p class="title">{{ client?.name || "" }}</p>
<div class="buttons" :style="{'--volume': client?.volume}">
<el-button type="danger" title="打开麦克风" :icon="Mute" circle />
<el-button type="primary" title="关闭麦克风" :icon="Microphone" circle />
<el-button type="danger" title="打开摄像头" :icon="VideoPause" circle />
@@ -54,7 +55,6 @@ export default {
},
data() {
return {
taoyao: null,
audio: null,
video: null,
audioStream: null,
@@ -94,6 +94,14 @@ export default {
this.audio = this.$refs.audio;
this.video = this.$refs.video;
},
props: {
"client": {
type: Object
},
"taoyao": {
type: Object
}
},
methods: {
media(track) {
if (track.kind === "video") {
@@ -112,3 +120,6 @@ export default {
},
};
</script>
<style scoped>
.client .buttons:after{width:var(--volume);}
</style>

View File

@@ -3,7 +3,8 @@
<div class="client">
<audio ref="audio"></audio>
<video ref="video"></video>
<div class="buttons">
<p class="title">{{ client?.name || "" }}</p>
<div class="buttons" :style="{'--volume': client?.volume}">
<el-button type="danger" title="打开麦克风" :icon="Mute" circle />
<el-button type="primary" title="关闭麦克风" :icon="Microphone" circle />
<el-button type="danger" title="打开摄像头" :icon="VideoPause" circle />
@@ -45,7 +46,6 @@ export default {
},
data() {
return {
taoyao: null,
audio: null,
video: null,
audioStream: null,
@@ -59,6 +59,14 @@ export default {
this.audio = this.$refs.audio;
this.video = this.$refs.video;
},
props: {
"client": {
type: Object
},
"taoyao": {
type: Object
}
},
methods: {
media(track, consumer) {
if(track.kind === 'audio') {
@@ -86,3 +94,6 @@ export default {
}
};
</script>
<style scoped>
.client .buttons:after{width:var(--volume);}
</style>

View File

@@ -219,21 +219,38 @@ const signalChannel = {
clearTimeout(me.reconnectTimer);
me.reconnection = false;
me.channel.close();
me.taoyao.connect = false;
},
};
/**
* 桃夭
* 远程终端
*/
class Taoyao {
// 信令连接
connect = false;
// 房间标识
roomId;
// 终端标识
clientId;
class RemoteClient {
// 终端名称
name;
// 终端标识
clientId;
// 音量
volume = 0;
constructor({
name,
clientId,
}) {
this.name = name;
this.clientId = clientId;
}
}
/**
* 桃夭
*/
class Taoyao extends RemoteClient {
// 信令连接
connect = false;
// 信令地址
host;
// 信令端口
@@ -242,6 +259,8 @@ class Taoyao {
username;
// 信令密码
password;
// 房间标识
roomId;
// 回调事件
callback;
// 媒体回调
@@ -249,13 +268,13 @@ class Taoyao {
// 请求回调
callbackMapping = new Map();
// 音频媒体配置
audio = defaultAudioConfig;
audioConfig = defaultAudioConfig;
// 视频媒体配置
video = defaultVideoConfig;
videoConfig = defaultVideoConfig;
// 媒体配置
media;
mediaConfig;
// WebRTC配置
webrtc;
webrtcConfig;
// 信令通道
signalChannel;
// 发送媒体通道
@@ -296,13 +315,13 @@ class Taoyao {
remoteClients = new Map();
constructor({
roomId,
clientId,
name,
clientId,
host,
port,
username,
password,
roomId,
consume = true,
produce = true,
audioProduce = true,
@@ -310,13 +329,14 @@ class Taoyao {
forceTcp = false,
dataProduce = true,
}) {
this.roomId = roomId;
this.clientId = clientId;
super({ name, clientId });
this.name = name;
this.clientId = clientId;
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.roomId = roomId;
this.consume = consume;
this.produce = produce;
this.dataProduce = produce && dataProduce;
@@ -401,6 +421,8 @@ class Taoyao {
* 2. 执行前置回调
* 3. 如果注册全局回调同时执行结果返回true不再执行后面所有回调。
* 4. 执行后置回调
*
* @param {*} message 消息
*/
async on(message) {
const me = this;
@@ -429,19 +451,22 @@ class Taoyao {
/**
* 前置回调
*
* @param {*} message
* @param {*} message 消息
*/
async preCallback(message) {
const self = this;
const me = this;
switch (message.header.signal) {
case "client::config":
self.defaultClientConfig(message);
me.defaultClientConfig(message);
break;
case "client::register":
protocol.clientIndex = message.body.index;
me.defaultClientRegister(message);
break;
case "media::consume":
await self.consumeMedia(message);
await me.defaultMediaConsume(message);
break;
case "platform::error":
me.defaultPlatformError(message);
break;
}
}
@@ -453,15 +478,18 @@ class Taoyao {
async postCallback(message) {
const me = this;
switch (message.header.signal) {
case "room::client::list":
me.defaultRoomClientList(message);
break;
case "client::reboot":
me.defaultClientReboot(message);
break;
case "client::shutdown":
me.defaultClientShutdown(message);
break;
case "media::audio::active::speaker":
me.defaultMediaAudioActiveSpeaker(message);
break;
case "room::client::list":
me.defaultRoomClientList(message);
break;
case "room::close":
me.defaultRoomClose(message);
break;
@@ -471,32 +499,29 @@ class Taoyao {
case "platform::error":
me.callbackError(message);
break;
default:
console.warn("不支持的信令:", message);
break;
}
}
/************************ 信令 ************************/
/**
* 配置默认回调
* 终端配置信令
*
* @param {*} message 消息
*/
defaultClientConfig(message) {
const me = this;
const { media, webrtc } = message.body;
const { audio, video} = media;
me.audio.sampleSize = { min: media.minSampleSize, ideal: audio.sampleSize, max: media.maxSampleSize };
me.audio.sampleRate = { min: media.minSampleRate, ideal: audio.sampleRate, max: media.maxSampleRate };
me.video.width = { min: media.minWidth, ideal: video.width, max: media.maxWidth };
me.video.height = { min: media.minHeight, ideal: video.height, max: media.maxHeight };
me.video.frameRate = { min: media.minFrameRate, ideal: video.frameRate, max: media.maxFrameRate };
me.media = media;
me.webrtc = webrtc;
console.debug("终端配置:", me.audio, me.video, me.media, me.webrtc);
const { audio, video } = media;
me.audioConfig.sampleSize = { min: media.minSampleSize, ideal: audio.sampleSize, max: media.maxSampleSize };
me.audioConfig.sampleRate = { min: media.minSampleRate, ideal: audio.sampleRate, max: media.maxSampleRate };
me.videoConfig.width = { min: media.minWidth, ideal: video.width, max: media.maxWidth };
me.videoConfig.height = { min: media.minHeight, ideal: video.height, max: media.maxHeight };
me.videoConfig.frameRate = { min: media.minFrameRate, ideal: video.frameRate, max: media.maxFrameRate };
me.mediaConfig = media;
me.webrtcConfig = webrtc;
console.debug("终端配置:", me.audioConfig, me.videoConfig, me.mediaConfig, me.webrtcConfig);
}
/**
* 终端重启默认回调
* 重启终端信令
*
* @param {*} message 消息
*/
@@ -505,7 +530,16 @@ class Taoyao {
location.reload();
}
/**
* 终端重启默认回调
* 终端注册信令
*
* @param {*} message 消息
*/
defaultClientRegister(message) {
const { index } = message.body;
protocol.clientIndex = index;
}
/**
* 关闭终端信令
*
* @param {*} message 消息
*/
@@ -513,6 +547,118 @@ class Taoyao {
console.info("关闭终端");
window.close();
}
/**
* 当前讲话终端信令
*
* @param {*} message 消息
*/
defaultMediaAudioActiveSpeaker(message) {
const me = this;
const { volume, clientId } = message.body;
if(!clientId) {
me.volume = 0;
me.remoteClients.forEach(v => v.volume = 0);
} if(me.clientId === clientId) {
me.volume = ((volume + 127) / 127 * 100) + "%";
} else {
const remoteClient = me.remoteClients.get(clientId);
if(remoteClient) {
remoteClient.volume = ((volume + 127) / 127 * 100) + "%";
}
}
}
/**
* 消费媒体信令
*
* @param {*} message 消息
*/
async defaultMediaConsume(message) {
const self = this;
if (!self.consume) {
console.log("没有消费媒体");
return;
}
const {
kind,
type,
roomId,
clientId,
sourceId,
streamId,
producerId,
consumerId,
rtpParameters,
appData,
producerPaused,
} = message.body;
try {
const consumer = await self.recvTransport.consume({
id: consumerId,
kind,
producerId,
rtpParameters,
// NOTE: Force streamId to be same in mic and webcam and different
// in screen sharing so libwebrtc will just try to sync mic and
// webcam streams from the same remote peer.
//streamId: `${peerId}-${appData.share ? "share" : "mic-webcam"}`,
streamId: `${clientId}-${appData.share ? "share" : "mic-webcam"}`,
appData, // Trick.
});
consumer.clientId = clientId;
consumer.sourceId = sourceId;
consumer.streamId = streamId;
self.consumers.set(consumer.id, consumer);
consumer.on("transportclose", () => {
self.consumers.delete(consumer.id);
});
const { spatialLayers, temporalLayers } =
mediasoupClient.parseScalabilityMode(
consumer.rtpParameters.encodings[0].scalabilityMode
);
// store.dispatch(
// stateActions.addConsumer(
// {
// id: consumer.id,
// type: type,
// locallyPaused: false,
// remotelyPaused: producerPaused,
// rtpParameters: consumer.rtpParameters,
// spatialLayers: spatialLayers,
// temporalLayers: temporalLayers,
// preferredSpatialLayer: spatialLayers - 1,
// preferredTemporalLayer: temporalLayers - 1,
// priority: 1,
// codec: consumer.rtpParameters.codecs[0].mimeType.split("/")[1],
// track: consumer.track,
// },
// peerId
// )
// );
self.push(message);
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);
// TODO实现
}
} catch (error) {
self.callbackError("消费媒体异常", error);
}
}
/**
* 平台异常信令
*
* @param {*} message 消息
*/
defaultPlatformError(message) {
const { code } = message;
if(code === "3401") {
signalChannel.close();
}
}
/**
* 房间终端列表信令
*
@@ -524,10 +670,23 @@ class Taoyao {
if (v.clientId === me.clientId) {
// 忽略自己
} else {
me.remoteClients.set(v.clientId, me.roomId);
me.remoteClients.set(v.clientId, new RemoteClient(v));
}
});
}
/**
* 关闭房间信令
*/
async roomClose() {
const me = this;
if(!me.roomId) {
console.warn("房间无效:", me.roomId);
return;
}
me.push(protocol.buildMessage("room::close", {
roomId: me.roomId
}));
}
/**
* 关闭房间信令
*
@@ -560,12 +719,48 @@ class Taoyao {
);
return response.body;
}
/**
* 进入房间信令
*
* @param {*} roomId 房间ID
* @param {*} password 房间密码
*/
async roomEnter(roomId, password) {
const me = this;
if (!roomId) {
this.callbackError("无效房间");
return;
}
me.roomId = roomId;
me.mediasoupDevice = new mediasoupClient.Device();
const response = await me.request(
protocol.buildMessage("media::router::rtp::capabilities", {
roomId: me.roomId
})
);
const routerRtpCapabilities = response.body.rtpCapabilities;
await me.mediasoupDevice.load({ routerRtpCapabilities });
await me.request(
protocol.buildMessage("room::enter", {
roomId: roomId,
password: password,
rtpCapabilities: me.consume ? me.mediasoupDevice.rtpCapabilities : undefined,
sctpCapabilities: me.consume && me.dataProduce ? me.mediasoupDevice.sctpCapabilities : undefined,
})
);
}
/**
* 进入房间信令
*
* @param {*} message 消息
*/
defaultRoomEnter(message) {
const { roomId, clientId } = message.body;
if (clientId === this.clientId) {
const me = this;
const { roomId, clientId, status } = message.body;
if (clientId === me.clientId) {
// 忽略自己
} else {
this.remoteClients.set(clientId, roomId);
me.remoteClients.set(clientId, new RemoteClient(status));
}
}
/**
@@ -607,45 +802,6 @@ class Taoyao {
);
return response.body;
}
async enterRoom(roomId, password) {
const self = this;
if (!roomId) {
this.callbackError("无效房间");
return;
}
self.roomId = roomId;
self.mediasoupDevice = new mediasoupClient.Device();
const response = await self.request(
protocol.buildMessage("media::router::rtp::capabilities", {
roomId: self.roomId
})
);
const routerRtpCapabilities = response.body.rtpCapabilities;
await self.mediasoupDevice.load({ routerRtpCapabilities });
await self.request(
protocol.buildMessage("room::enter", {
roomId: roomId,
password: password,
rtpCapabilities: self.consume
? self.mediasoupDevice.rtpCapabilities
: undefined,
sctpCapabilities:
self.consume && self.dataProduce
? self.mediasoupDevice.sctpCapabilities
: undefined,
})
);
}
async closeRoom() {
const me = this;
if(!me.roomId) {
console.warn("房间无效:", me.roomId);
return;
}
me.push(protocol.buildMessage("room::close", {
roomId: me.roomId
}));
}
/************************ 媒体 ************************/
/**
* 生产媒体
@@ -841,7 +997,7 @@ class Taoyao {
try {
console.debug("打开麦克风");
const stream = await navigator.mediaDevices.getUserMedia({
audio: self.audio,
audio: self.audioConfig,
});
const tracks = stream.getAudioTracks();
if (tracks.length > 1) {
@@ -1107,89 +1263,6 @@ class Taoyao {
}
}
/**
* 消费媒体
*
* @param {*} message
* @returns
*/
async consumeMedia(message) {
const self = this;
if (!self.consume) {
console.log("没有消费媒体");
return;
}
const {
kind,
type,
roomId,
clientId,
sourceId,
streamId,
producerId,
consumerId,
rtpParameters,
appData,
producerPaused,
} = message.body;
try {
const consumer = await self.recvTransport.consume({
id: consumerId,
kind,
producerId,
rtpParameters,
// NOTE: Force streamId to be same in mic and webcam and different
// in screen sharing so libwebrtc will just try to sync mic and
// webcam streams from the same remote peer.
//streamId: `${peerId}-${appData.share ? "share" : "mic-webcam"}`,
streamId: `${clientId}-${appData.share ? "share" : "mic-webcam"}`,
appData, // Trick.
});
consumer.clientId = clientId;
consumer.sourceId = sourceId;
consumer.streamId = streamId;
self.consumers.set(consumer.id, consumer);
consumer.on("transportclose", () => {
self.consumers.delete(consumer.id);
});
const { spatialLayers, temporalLayers } =
mediasoupClient.parseScalabilityMode(
consumer.rtpParameters.encodings[0].scalabilityMode
);
// store.dispatch(
// stateActions.addConsumer(
// {
// id: consumer.id,
// type: type,
// locallyPaused: false,
// remotelyPaused: producerPaused,
// rtpParameters: consumer.rtpParameters,
// spatialLayers: spatialLayers,
// temporalLayers: temporalLayers,
// preferredSpatialLayer: spatialLayers - 1,
// preferredTemporalLayer: temporalLayers - 1,
// priority: 1,
// codec: consumer.rtpParameters.codecs[0].mimeType.split("/")[1],
// track: consumer.track,
// },
// peerId
// )
// );
self.push(message);
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);
// TODO实现
}
} catch (error) {
self.callbackError("消费媒体异常", error);
}
}
async pauseConsumer(consumer) {
if (consumer.paused) return;
try {