rtpparameter包含了音视频的基本信息,拥塞控制方式以及重传方式约定。consumer和producer需要依据该参数来进行初始化rtp会话。音频相对比较简单,采用的适合低延迟语音通话的opus编码,在辅助以FEC扛丢包。下面以测试时实际的数据说一下视频流相关的rtp parameter及代码流程。
Producer video rtpparameters
{
"codecs": [{
"mimeType": "video/VP8",
"payloadType": 96,
"clockRate": 90000,
"parameters": {},
"rtcpFeedback": [{
"type": "goog-remb",
"parameter": ""
}, {
"type": "transport-cc",
"parameter": ""
}, {
"type": "ccm",
"parameter": "fir"
}, {
"type": "nack",
"parameter": ""
}, {
"type": "nack",
"parameter": "pli"
}]
}, {
"mimeType": "video/rtx",
"payloadType": 97,
"clockRate": 90000,
"parameters": {
"apt": 96
},
"rtcpFeedback": []
}],
"headerExtensions": [{
"uri": "urn:ietf:params:rtp-hdrext:sdes:mid",
"id": 4,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
"id": 5,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
"id": 6,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
"id": 2,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"id": 3,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:3gpp:video-orientation",
"id": 13,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:toffset",
"id": 14,
"encrypt": false,
"parameters": {}
}],
"encodings": [{
"active": true,
"scaleResolutionDownBy": 4,
"maxBitrate": 500000,
"rid": "r0",
"scalabilityMode": "S1T3",
"dtx": false
}, {
"active": true,
"scaleResolutionDownBy": 2,
"maxBitrate": 1000000,
"rid": "r1",
"scalabilityMode": "S1T3",
"dtx": false
}, {
"active": true,
"scaleResolutionDownBy": 1,
"maxBitrate": 5000000,
"rid": "r2",
"scalabilityMode": "S1T3",
"dtx": false
}],
"rtcp": {
"cname": "iml0fNPJK3oQ/YYA",
"reducedSize": true
},
"mid": "2"
}
rtp mapping
{
"codecs": [{
"payloadType": 96,
"mappedPayloadType": 101
}, {
"payloadType": 97,
"mappedPayloadType": 102
}],
"encodings": [{
"mappedSsrc": 573650944,
"rid": "r0",
"scalabilityMode": "S1T3"
}, {
"mappedSsrc": 573650945,
"rid": "r1",
"scalabilityMode": "S1T3"
}, {
"mappedSsrc": 573650946,
"rid": "r2",
"scalabilityMode": "S1T3"
}]
}
video producer.consumableRtpParameters
{
"codecs": [{
"mimeType": "video/VP8",
"payloadType": 101,
"clockRate": 90000,
"parameters": {},
"rtcpFeedback": [{
"type": "nack",
"parameter": ""
}, {
"type": "nack",
"parameter": "pli"
}, {
"type": "ccm",
"parameter": "fir"
}, {
"type": "goog-remb",
"parameter": ""
}, {
"type": "transport-cc",
"parameter": ""
}]
}, {
"mimeType": "video/rtx",
"payloadType": 102,
"clockRate": 90000,
"parameters": {
"apt": 101
},
"rtcpFeedback": []
}],
"headerExtensions": [{
"uri": "urn:ietf:params:rtp-hdrext:sdes:mid",
"id": 1,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
"id": 4,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"id": 5,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07",
"id": 6,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:framemarking",
"id": 7,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:3gpp:video-orientation",
"id": 11,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:toffset",
"id": 12,
"encrypt": false,
"parameters": {}
}],
"encodings": [{
"active": true,
"scaleResolutionDownBy": 4,
"maxBitrate": 500000,
"scalabilityMode": "S1T3",
"dtx": false,
"ssrc": 573650944
}, {
"active": true,
"scaleResolutionDownBy": 2,
"maxBitrate": 1000000,
"scalabilityMode": "S1T3",
"dtx": false,
"ssrc": 573650945
}, {
"active": true,
"scaleResolutionDownBy": 1,
"maxBitrate": 5000000,
"scalabilityMode": "S1T3",
"dtx": false,
"ssrc": 573650946
}],
"rtcp": {
"cname": "iml0fNPJK3oQ/YYA",
"reducedSize": true,
"mux": true
}
}
当服务器收到produce信令时,执行如下流程,初始化rtp prameters:
async produce({.. kind, rtpParameters, ... })
{
routerRtpCapabilities = this._getRouterRtpCapabilities()
rtpMapping = ortc.getProducerRtpParametersMapping(rtpParameters, routerRtpCapabilities)
consumableRtpParameters = ortc.getConsumableRtpParameters(kind, rtpParameters, routerRtpCapabilities, rtpMapping)
reqData = { kind, rtpParameters, rtpMapping, keyFrameRequestDelay, paused };
status = await this._channel.request('transport.produce', internal, reqData);
}
Producer::Producer(const std::string& id, RTC::Producer::Listener* listener, json& data)
{
auto jsonRtpParametersIt = data.find("rtpParameters");
this->rtpParameters = RTC::RtpParameters(*jsonRtpParametersIt);
auto jsonRtpMappingIt = data.find("rtpMapping");
}
Consumer video rtpparameters
{
"codecs": [{
"mimeType": "video/VP8",
"payloadType": 101,
"clockRate": 90000,
"parameters": {},
"rtcpFeedback": [{
"type": "transport-cc",
"parameter": ""
}, {
"type": "ccm",
"parameter": "fir"
}, {
"type": "nack",
"parameter": ""
}, {
"type": "nack",
"parameter": "pli"
}]
}, {
"mimeType": "video/rtx",
"payloadType": 102,
"clockRate": 90000,
"parameters": {
"apt": 101
},
"rtcpFeedback": []
}],
"headerExtensions": [{
"uri": "urn:ietf:params:rtp-hdrext:sdes:mid",
"id": 1,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
"id": 4,
"encrypt": false,
"parameters": {}
}, {
"uri": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"id": 5,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:3gpp:video-orientation",
"id": 11,
"encrypt": false,
"parameters": {}
}, {
"uri": "urn:ietf:params:rtp-hdrext:toffset",
"id": 12,
"encrypt": false,
"parameters": {}
}],
"encodings": [{
"ssrc": 291444635,
"rtx": {
"ssrc": 291444636
},
"scalabilityMode": "S3T3",
"maxBitrate": 5000000
}],
"rtcp": {
"cname": "fRqx0gt1GzHpOzJ5",
"reducedSize": true,
"mux": true
}
}
consumer参数则有别于producer,当某个peer需要消费某个producer上的流时,需要结合producer的 consumableRtpParameters和自身的capabilties来选择正确的rtp参数,详细流程如下:
async consume({ producerId, rtpCapabilities...preferredLayers... })
{
const rtpParameters = ortc.getConsumerRtpParameters(producer.consumableRtpParameters, rtpCapabilities, pipe);
const reqData = {
kind: producer.kind,
rtpParameters,
type: pipe ? 'pipe' : producer.type,
consumableRtpEncodings: producer.consumableRtpParameters.encodings,
paused,
preferredLayers
};
const status = await this._channel.request('transport.consume', internal, reqData);
}
SimulcastConsumer::SimulcastConsumer(const std::string& producerId...json& data)
{
RTC::Consumer::Consumer(producerId, listener, data, RTC::RtpParameters::Type::SIMULCAST)
{
auto jsonRtpParametersIt = data.find("rtpParameters");
this->rtpParameters = RTC::RtpParameters(*jsonRtpParametersIt);
auto jsonConsumableRtpEncodingsIt = data.find("consumableRtpEncodings");
this->consumableRtpEncodings.reserve(jsonConsumableRtpEncodingsIt->size());
}
auto jsonPreferredLayersIt = data.find("preferredLayers");
CreateRtpStream();
}
void SimulcastConsumer::CreateRtpStream()
{
params.ssrc = encoding.ssrc;
params.payloadType = mediaCodec->payloadType;
params.mimeType = mediaCodec->mimeType;
params.clockRate = mediaCodec->clockRate;
params.cname = this->rtpParameters.rtcp.cname;
params.spatialLayers = encoding.spatialLayers;
params.temporalLayers = encoding.temporalLayers;
params.useNack = true;
this->rtpStream = new RTC::RtpStreamSend(this, params, bufferSize);
this->rtpStreams.push_back(this->rtpStream);
}
上文描述的rtp 参数信息并没有提到rtpcapabilities, 均来自服务端配置的capability,详情可以查看mediasoup-demo/server/config.js。