Hi!
I am trying to create an application that has a mediasoup server publishing a video stream from local images injected via ffmpeg and a react-native-client consuming the video stream. I did my best accommodating all functions such that it should comply with mediasoup documentation, however, I am experiencing a problem with ICE negotiation.
Here is the server code:
const WebSocket = require('ws');
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const { PassThrough } = require('stream');
const mediasoup = require('mediasoup');
const server = new WebSocket.Server({ port: 8080 });
const mediasoupOptions = {
worker: {
rtcMinPort: 10000,
rtcMaxPort: 10100,
},
router: {
mediaCodecs: [
{
kind: 'video',
mimeType: 'video/h264',
clockRate: 90000,
parameters: {
'packetization-mode': 1,
'profile-level-id': '42e01f',
'level-asymmetry-allowed': 1,
},
},
],
},
};
(async () => {
const worker = await mediasoup.createWorker(mediasoupOptions.worker);
worker.on('died', () => {
console.error('mediasoup Worker died, exiting...');
process.exit(1);
});
const router = await worker.createRouter(mediasoupOptions.router);
let currentChunk = null;
const inputStream = new PassThrough();
const outputStream = new PassThrough();
const videoStream = ffmpeg()
.addInput(inputStream)
.inputFPS(30)
.inputFormat('image2pipe')
.inputOptions(['-pix_fmt yuv420p'])
.videoCodec('libx264')
.size('640x480')
.format('rtp')
.output(outputStream)
.on('error', (err) => console.error('ffmpeg error:', err));
videoStream.run();
outputStream.on('data', (chunk) => {
currentChunk = chunk;
});
server.on('connection', async (socket) => {
console.log('Client connected');
const transport = await router.createWebRtcTransport({
listenIps: [{ ip: '192.168.178.108' }],
enableSctp: true,
enableUdp: true,
enableTcp: true,
preferUdp: true,
initialAvailableOutgoingBitrate: 1000000,
});
socket.on('message', async (message) => {
console.log('Received message:', message);
const { action, data } = JSON.parse(message);
console.log("action: ", action)
console.log("data", data)
if (action === 'transport-connect') {
// Debugging: Check if the transport is created
console.log('Transport created:', transport);
// Listen for the 'icestatechange' event and log the changes
transport.on('icestatechange', (iceState) => {
console.log(`transport iceState changed to: ${iceState}`);
});
transport.on('routerclose', () => {
console.log('transport "routerclose" event triggered');
socket.send(JSON.stringify({ action: 'transport-router-close' }));
});
transport.on('dtlsstatechange', async (dtlsState) => {
console.log(`transport dtlsState changed to: ${dtlsState}`);
if (dtlsState === 'connected') {
console.log('DTLS connected, calling transport.connect');
await transport.connect({ dtlsParameters: data.dtlsParameters });
const codec = { ...mediasoupOptions.router.mediaCodecs[0], payloadType: 102 };
const producer = await transport.produce({ kind: 'video', rtpParameters: { codecs: [codec], encodings: [] } });
// Send the correct rtpParameters to the client
socket.send(JSON.stringify({
action: 'transport-connected',
data: {
id: producer.id,
rtpParameters: producer.rtpParameters
}
}));
}
});
// Debugging: Check if the transport is connecting
console.log('Transport connecting:', transport);
} else if (action === 'produce') {
console.log('Received produce action');
if (currentChunk) {
console.log('Producidtlsstatechangeng with currentChunk');
// Create an RtpStream for the video stream
const rtpStream = {
ssrc: Math.floor(Math.random() * 0xffffffff),
payloadType: 102, // Use a dynamic payload type
mimeType: 'video/h264',
clockRate: 90000,
payload: currentChunk,
};
const codec = { ...mediasoupOptions.router.mediaCodecs[0], payloadType: rtpStream.payloadType };
const producer = await transport.produce({ kind: 'video', rtpParameters: { codecs: [codec], encodings: [rtpStream] } });
socket.send(JSON.stringify({ action: 'produced', data: { id: producer.id } }));
}
} else if (action === 'transport-ready') {
sendNextImage();
}
});
socket.send(
JSON.stringify({
action: 'transport-create',
data: {
id: transport.id,
iceParameters: transport.iceParameters,
iceCandidates: transport.iceCandidates,
dtlsParameters: transport.dtlsParameters,
sctpParameters: transport.sctpParameters,
routerRtpCapabilities: router.rtpCapabilities,
},
})
);
const images = [];
for (let i = 1; i <= 60; i++) {
const imageIndex = String(i).padStart(5, '0');
images.push(`data/video-frames/frame_${imageIndex}.jpg`);
}
async function sendNextImage() {
if (transport.dtlsState === 'connected') {
console.log('Sending images');
for (const image of images) {
const imageData = await fs.promises.readFile(image);
inputStream.write(imageData);
await new Promise((resolve) => setTimeout(resolve, 1000 / 30));
}
}
if (socket.readyState === WebSocket.OPEN) {
setTimeout(sendNextImage, 1000 / 30);
}
}
socket.on('close', () => {
console.log('Client disconnected');
transport.close();
});
});
})();
console.log('Server listening on port 8080');
And here is the client code:
import React, { useEffect, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import * as mediasoupClient from 'mediasoup-client';
import { registerGlobals } from 'react-native-webrtc';
import { RTCView } from 'react-native-webrtc';
registerGlobals(); // Register WebRTC globals
const App = () => {
const [socket, setSocket] = useState(null);
const [videoTrack, setVideoTrack] = useState(null);
WebSocket.prototype.sendAsync = function (data) {
return new Promise((resolve, reject) => {
this.send(JSON.stringify(data), (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
let device;
useEffect(() => {
const setupWebrtc = async (data) => {
let recvTransport;
device = new mediasoupClient.Device();
await device.load({ routerRtpCapabilities: data.routerRtpCapabilities });
recvTransport = device.createRecvTransport({
id: data.id,
iceParameters: data.iceParameters,
iceCandidates: data.iceCandidates,
dtlsParameters: data.dtlsParameters,
sctpParameters: data.sctpParameters,
});
recvTransport.on('connectionstatechange', async (state) => {
console.log(`recvTransport connection state changed to: ${state}`);
if (state === 'connected') {
ws.sendAsync({ action: 'transport-ready' });
}
});
recvTransport.on('dtlsstatechange', (state) => {
console.log(`recvTransport DTLS state changed to: ${state}`);
});
recvTransport.on('icestatechange', (state) => {
console.log(`recvTransport ICE state changed to: ${state}`);
});
// Send the transport-connect message
console.log("dtlsParameters:", recvTransport._handler._remoteSdp._dtlsParameters);
await ws.sendAsync({
action: 'transport-connect',
data: { dtlsParameters: recvTransport._handler._remoteSdp._dtlsParameters },
});
};
const ws = new WebSocket('ws://192.168.178.108:8080');
ws.onopen = async () => {
console.log('Connected to server');
setSocket(ws);
};
ws.onmessage = async (event) => {
const { action, data } = JSON.parse(event.data);
if (action === 'transport-create') {
console.log("ws.onmessage action === 'transport-create'");
console.log("data: ", data)
setupWebrtc(data);
} else if (action === 'transport-connected') {
const { id, rtpParameters } = data;
console.log("ws.onmessage action === 'transport-connected'");
const stream = await recvTransport.consume({
producerId: id, // Pass the received producerId to the consume method
rtpCapabilities: device.rtpCapabilities, // RTP capabilities of the consuming endpoint
rtpParameters: rtpParameters,
paused: true,
enableRtx: true, // Set enableRtx option as specified in the documentation
});
console.log('Received stream:', stream);
// Set the video track in the state
setVideoTrack(stream.track);
// Send the 'produce' action to the server
await ws.sendAsync({
action: 'produce',
data: { id: stream.id, kind: 'video', rtpParameters: stream.rtpParameters },
});
} else if (action === 'transport-router-close') {
console.log("ws.onmessage action === 'transport-router-close'");
recvTransport.close();
}
};
ws.onerror = (event) => {
console.error('WebSocket error:', event);
};
ws.onclose = (event) => {
console.log('Disconnected from server', event);
// Clean up transports and close the device
device.close();
};
}, []);
return (
<View style={styles.container}>
{videoTrack && (
<RTCView
style={styles.video}
streamURL={videoTrack.toURL()}
objectFit="cover"
/>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
video: {
width: '100%',
height: '100%',
},
});
export default App;
The client manages to connect to the server. Also, the “transport-connect” and “ransport-create” are sent. However, the “on(‘icestatechange’”) event fails to trigger on either the server or client.
Let me provide also the full logs.
Server logs:
Server listening on port 8080
Client connected
Received message: <Buffer 7b 22 61 63 74 69 6f 6e 22 3a 22 74 72 61 6e 73 70 6f 72 74 2d 63 6f 6e 6e 65 63 74 22 2c 22 64 61 74 61 22 3a 7b 22 64 74 6c 73 50 61 72 61 6d 65 74 ... 783 more bytes>
action: transport-connect
data {
dtlsParameters: {
fingerprints: [ [Object], [Object], [Object], [Object], [Object] ],
role: 'auto'
}
}
Transport created: WebRtcTransport {
_events: [Object: null prototype] {
'@close': [Function (anonymous)],
'@listenserverclose': [Function (anonymous)],
'@newproducer': [Function (anonymous)],
'@producerclose': [Function (anonymous)],
'@newdataproducer': [Function (anonymous)],
'@dataproducerclose': [Function (anonymous)]
},
_eventsCount: 6,
_maxListeners: Infinity,
internal: {
routerId: '8b01e899-3b3a-48bb-b389-5bb5fd702e70',
transportId: '321461af-b1f7-47db-ac19-acb6324d5a9f'
},
channel: Channel {
_events: [Object: null prototype] {
'321461af-b1f7-47db-ac19-acb6324d5a9f': [Function (anonymous)]
},
_eventsCount: 1,
_maxListeners: Infinity,
[Symbol(kCapture)]: false
},
payloadChannel: PayloadChannel {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: Infinity,
[Symbol(kCapture)]: false
},
getProducerById: [Function: getProducerById],
getDataProducerById: [Function: getDataProducerById],
consumers: Map(0) {},
dataProducers: Map(0) {},
dataConsumers: Map(0) {},
[Symbol(kCapture)]: false
}
Transport connecting: WebRtcTransport {
_events: [Object: null prototype] {
'@close': [Function (anonymous)],
'@listenserverclose': [Function (anonymous)],
'@newproducer': [Function (anonymous)],
'@producerclose': [Function (anonymous)],
'@newdataproducer': [Function (anonymous)],
'@dataproducerclose': [Function (anonymous)],
icestatechange: [Function (anonymous)],
routerclose: [Function (anonymous)],
dtlsstatechange: [AsyncFunction (anonymous)]
},
_eventsCount: 9,
_maxListeners: Infinity,
internal: {
routerId: '8b01e899-3b3a-48bb-b389-5bb5fd702e70',
transportId: '321461af-b1f7-47db-ac19-acb6324d5a9f'
},
channel: Channel {
_events: [Object: null prototype] {
'321461af-b1f7-47db-ac19-acb6324d5a9f': [Function (anonymous)]
},
_eventsCount: 1,
_maxListeners: Infinity,
[Symbol(kCapture)]: false
},
payloadChannel: PayloadChannel {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: Infinity,
[Symbol(kCapture)]: false
},
getProducerById: [Function: getProducerById],
getDataProducerById: [Function: getDataProducerById],
consumers: Map(0) {},
dataProducers: Map(0) {},
dataConsumers: Map(0) {},
[Symbol(kCapture)]: false
}
Client logs:
LOG Running "WebrtcImageApp" with {"rootTag":11}
LOG Connected to server
LOG ws.onmessage action === 'transport-create'
INFO mediasoup-client:Device constructor() +0ms
INFO mediasoup-client:Device this._detectDevice() | ReactNative UnifiedPlan handler chosen +0ms
INFO mediasoup-client:Device constructor() | detected handler: ReactNativeUnifiedPlan +1ms
INFO mediasoup-client:ReactNativeUnifiedPlan close() +0ms
INFO mediasoup-client:Device load() [routerRtpCapabilities:'{"codecs": [{"clockRate": 90000, "kind": "video", "mimeType": "video/H264", "parameters": [Object], "preferredPayloadType": 100, "rtcpFeedback": [Array]}, {"clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 101, "rtcpFeedback": [Array]}], "headerExtensions": [{"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 1, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 1, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "recvonly", "kind": "video", "preferredEncrypt": false, "preferredId": 2, "uri": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, {"direction": "recvonly", "kind": "video", "preferredEncrypt": false, "preferredId": 3, "uri": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"}, {"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 4, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 4, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "recvonly", "kind": "audio", "preferredEncrypt": false, "preferredId": 5, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 5, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 6, "uri": "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 7, "uri": "urn:ietf:params:rtp-hdrext:framemarking"}, {"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 10, "uri": "urn:ietf:params:rtp-hdrext:ssrc-audio-level"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 11, "uri": "urn:3gpp:video-orientation"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 12, "uri": "urn:ietf:params:rtp-hdrext:toffset"}, {"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 13, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 13, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"}]}'] +7ms
INFO mediasoup-client:ReactNativeUnifiedPlan getNativeRtpCapabilities() +12ms
LOG rn-webrtc:pc:DEBUG 0 ctor +0ms
LOG rn-webrtc:pc:DEBUG 0 addTransceiver +1ms
LOG rn-webrtc:pc:DEBUG 0 addTransceiver +8ms
LOG rn-webrtc:pc:DEBUG 0 createOffer +4ms
LOG rn-webrtc:pc:DEBUG 0 createOffer OK +5ms
LOG rn-webrtc:pc:DEBUG 0 close +0ms
INFO mediasoup-client:Device load() | got native RTP capabilities:'{"codecs": [{"channels": 2, "clockRate": 48000, "kind": "audio", "mimeType": "audio/opus", "parameters": [Object], "preferredPayloadType": 111, "rtcpFeedback": [Array]}, {"channels": 2, "clockRate": 48000, "kind": "audio", "mimeType": "audio/red", "parameters": [Object], "preferredPayloadType": 63, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/G722", "parameters": [Object], "preferredPayloadType": 9, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/ILBC", "parameters": [Object], "preferredPayloadType": 102, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/PCMU", "parameters": [Object], "preferredPayloadType": 0, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/PCMA", "parameters": [Object], "preferredPayloadType": 8, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/CN", "parameters": [Object], "preferredPayloadType": 13, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 48000, "kind": "audio", "mimeType": "audio/telephone-event", "parameters": [Object], "preferredPayloadType": 110, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 8000, "kind": "audio", "mimeType": "audio/telephone-event", "parameters": [Object], "preferredPayloadType": 126, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/VP8", "parameters": [Object], "preferredPayloadType": 98, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 99, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/AV1", "parameters": [Object], "preferredPayloadType": 39, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 40, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/VP9", "parameters": [Object], "preferredPayloadType": 100, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 101, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/VP9", "parameters": [Object], "preferredPayloadType": 127, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 103, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/red", "parameters": [Object], "preferredPayloadType": 104, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/rtx", "parameters": [Object], "preferredPayloadType": 105, "rtcpFeedback": [Array]}, {"channels": undefined, "clockRate": 90000, "kind": "video", "mimeType": "video/ulpfec", "parameters": [Object], "preferredPayloadType": 106, "rtcpFeedback": [Array]}], "headerExtensions": [{"kind": "audio", "preferredId": 1, "uri": "urn:ietf:params:rtp-hdrext:ssrc-audio-level"}, {"kind": "audio", "preferredId": 2, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"kind": "audio", "preferredId": 3, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"kind": "audio", "preferredId": 4, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"kind": "video", "preferredId": 14, "uri": "urn:ietf:params:rtp-hdrext:toffset"}, {"kind": "video", "preferredId": 2, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"kind": "video", "preferredId": 13, "uri": "urn:3gpp:video-orientation"}, {"kind": "video", "preferredId": 3, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"kind": "video", "preferredId": 5, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"}, {"kind": "video", "preferredId": 6, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"}, {"kind": "video", "preferredId": 7, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/video-timing"}, {"kind": "video", "preferredId": 8, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/color-space"}, {"kind": "video", "preferredId": 4, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"kind": "video", "preferredId": 10, "uri": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, {"kind": "video", "preferredId": 11, "uri": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"}]}' +43ms
INFO mediasoup-client:Device load() | got extended RTP capabilities:'{"codecs": [], "headerExtensions": [{"direction": "sendrecv", "encrypt": false, "kind": "audio", "recvId": 1, "sendId": 4, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "sendrecv", "encrypt": false, "kind": "video", "recvId": 1, "sendId": 4, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "sendonly", "encrypt": false, "kind": "video", "recvId": 2, "sendId": 10, "uri": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, {"direction": "sendonly", "encrypt": false, "kind": "video", "recvId": 3, "sendId": 11, "uri": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"}, {"direction": "sendrecv", "encrypt": false, "kind": "audio", "recvId": 4, "sendId": 2, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "sendrecv", "encrypt": false, "kind": "video", "recvId": 4, "sendId": 2, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "sendonly", "encrypt": false, "kind": "audio", "recvId": 5, "sendId": 3, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"direction": "sendrecv", "encrypt": false, "kind": "video", "recvId": 5, "sendId": 3, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"direction": "sendrecv", "encrypt": false, "kind": "audio", "recvId": 10, "sendId": 1, "uri": "urn:ietf:params:rtp-hdrext:ssrc-audio-level"}, {"direction": "sendrecv", "encrypt": false, "kind": "video", "recvId": 11, "sendId": 13, "uri": "urn:3gpp:video-orientation"}, {"direction": "sendrecv", "encrypt": false, "kind": "video", "recvId": 12, "sendId": 14, "uri": "urn:ietf:params:rtp-hdrext:toffset"}]}' +5ms
INFO mediasoup-client:Device load() | got receiving RTP capabilities:'{"codecs": [], "headerExtensions": [{"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 1, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 1, "uri": "urn:ietf:params:rtp-hdrext:sdes:mid"}, {"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 4, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 4, "uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 5, "uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"}, {"direction": "sendrecv", "kind": "audio", "preferredEncrypt": false, "preferredId": 10, "uri": "urn:ietf:params:rtp-hdrext:ssrc-audio-level"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 11, "uri": "urn:3gpp:video-orientation"}, {"direction": "sendrecv", "kind": "video", "preferredEncrypt": false, "preferredId": 12, "uri": "urn:ietf:params:rtp-hdrext:toffset"}]}' +1ms
INFO mediasoup-client:ReactNativeUnifiedPlan getNativeSctpCapabilities() +39ms
INFO mediasoup-client:Device load() | got native SCTP capabilities:'{"numStreams": {"MIS": 1024, "OS": 1024}}' +1ms
INFO mediasoup-client:Device load() succeeded +1ms
INFO mediasoup-client:ReactNativeUnifiedPlan close() +1ms
INFO mediasoup-client:Device createRecvTransport() +0ms
INFO mediasoup-client:Transport constructor() [id:a9748ec6-6f55-48bf-9e0d-3087eef4883d, direction:recv] +0ms
INFO mediasoup-client:ReactNativeUnifiedPlan run() +2ms
LOG rn-webrtc:pc:DEBUG 1 ctor +21ms
LOG dtlsParameters: {"fingerprints": [{"algorithm": "sha-256", "value": "7C:72:BC:97:31:9D:ED:20:52:53:71:ED:67:28:91:F4:F2:9C:0E:9F:54:53:06:F1:AF:21:52:AC:B1:8C:F1:E7"}, {"algorithm": "sha-224", "value": "84:36:39:51:23:75:A2:73:D1:85:A5:05:C4:6C:F2:D1:64:0D:F9:65:57:D9:EA:F1:73:F4:F8:98"}, {"algorithm": "sha-384", "value": "5E:71:88:8A:0D:C1:98:88:73:40:DA:16:DB:C8:89:0B:40:DE:2A:DB:85:C3:85:4B:1E:DA:29:DF:0A:83:36:DB:3C:34:4B:3C:EB:7B:05:9B:5B:C0:DA:C8:B8:54:A6:ED"}, {"algorithm": "sha-512", "value": "7C:2F:87:5A:8C:3E:2E:D2:C6:CC:2E:31:69:45:27:46:B5:F9:F4:F9:FA:57:E2:C6:8D:6F:01:F3:7F:DB:76:FD:72:D7:18:71:CF:F6:A3:23:98:DE:EE:63:A3:D6:CB:AD:C2:C4:04:AE:93:4B:16:DE:D2:FA:A8:3B:67:2A:A0:A5"}, {"algorithm": "sha-1", "value": "4B:7F:AE:70:0F:B6:1E:96:71:93:3A:9E:2E:4F:C4:25:80:B2:93:85"}], "role": "auto"}
Any help would be greatly appreciated!