Examples without filterParameters and createParameters

Peter Thatcher said: 

"Now that we've removed ​filterParameters/createParameters, I think we need
to find another way to make the simple use case and simple example work.  I
really don't want our "simple example" to be dependent on a JS library, or
have to choosing ssrcs.   I'd like the simple examples to remain simple.

So what can we do?  Here is an idea:

1.  Allow passing in RtpCapabilities into RtpSender.send and
RtpReceiver.receive.  That way, the signalling could just exchange
capabilities and not have to create RtpParameters for the simple case.

2.  Send and receive with the preferredPayloadType.  The sender just picks
some SSRC, and the receiver automatically locks onto them, as if the JS had
listened to "onunsignalledrtp" and added SSRCs manually.

That doesn't support header extensions (since the receiver wouldn't know
the IDs) or layering or FEC, but that might be OK for the simple use case.

Are there dragons that I'm not thinking of that would make this not work?"

[BA]  I've been looking at how to rewrite the sample code in Examples 8 & 9 without filterParameters and createParameters. 

So far, I haven't found any dragons, but there are a few salamanders to remove. 

Assuming that each side exchanges capabilities, the question is how the send and receiver parameter are generated. 

To accomplish this, we need to take the information in RTCRtpCapabilities and use it to fill out the RTCRtpParameters. 

So the first question is whether we have enough information in RTCRtpCapabilities in order to do this.  
If we add RTCRtpHeaderExtensionParameters and put a preferredPayloadType into the RTCRtpCodec, 
we should have the information to set up the Codecs, Payload Types and Header Extensions.  

For example, to determine the parameters, we could take the intersection of the codecs and header extensions, and then
determine the Payload Type based on a simple rule (e.g. initiator preferredPayloadType wins).   So far so good. 

dictionary RTCRtpCapabilities {
    sequence<RTCRtpCodec>                     audioCodecs;
    sequence<RTCRtpCodec>                     videoCodecs;
    sequence<RTCRtpHeaderExtensionParameters> headerExtensionPreferences;
    Capabilities                              features; 
    Capabilities                              rtcpFeedback;
};

dictionary RTCRtpCodec {
    DOMString       name = "";
    unsigned short? hzRate = null;
    unsigned short  preferredPayloadType;
    unsigned short? numChannels = 1;
    Capabilities    formats;
};

In terms of filling out the RTCRtpParameters structure, for the simple case we can assume no receiverId usage, 
the headerExtensions can be set based on the intersection of capabilities, and we can ignore most of the RTCRtpEncodingParameters. 
We can determine whether rtcpMux is supported based on the intersection of the capabilities. 


dictionary RTCRtpParameters {
    DOMString                                 receiverId = "";
    sequence<RTCRtpCodecParameters>           codecs;
    sequence<RTCRtpHeaderExtensionParameters> headerExtensions;
    sequence<RTCRtpEncodingParameters>        encodings;
    unsigned short                            rtcpSsrc;
    boolean                                   rtcpMux = true;
};

dictionary RTCRtpEncodingParameters {
    unsigned short?      ssrc = null;
    DOMString            codecName = "";
    RTCRtpFecParameters? fec = null;
    RTCRtpRtxParameters? rtx = null;
    double               priority = 1.0;
    double?              maxBitrate = null;
    double               minQuality = 0;
    double               frameratebias = 0.5;
    double               scale = null;
    double               framerate = null;
    double               quality = null;
    boolean              active = true;
    DOMString?           encodingId;
    sequence<DOMString>  dependencyEncodingIds;
};

That leaves the RTCRtpCodecParameters:

dictionary RTCRtpCodecParameters {
    unsigned short payloadType;
    RTCRtpCodec    codec;
    Settings       formats;
    Settings       rtcpFeedback;
};

We have the codecs and payloadType values, but then there are the per-codec settings for formats and rtcpFeedback. 
For audio codecs (G.711 and Opus) things should work even if no formats are set.  
We still need to articulate the video capabilities, but hopefully these won't be too complicated.  

That leaves the rtcpFeedback settings, which are per-codec. 

The problem is that in RTCRtpCapabilities, the rtcpFeedback capabilities are global, not per codec, so there is a mismatch here. 

Assuming that we address this, here is what the sample code would look like.  I am assuming that the above logic can be encapsulated
in two functions myCapsToSendParams and myCapsToRecvParams. 


Example 8

// Assume we already have a way to signal, a transport
// (RTCDtlsTransport), and audio and video tracks. This is an example
// of  how to offer them  and get back an answer with audio and
// video tracks, and begin sending and receiving them.
// The example assumes that RTP and RTCP are multiplexed. 
function initiate(signaller, transport, audioTrack, videoTrack) {
  var audioSender = new RTCRtpSender(audioTrack, transport);
  var videoSender = new RTCRtpSender(videoTrack, transport);
  var audioReceiver = new RTCRtpReceiver(transport);
  var videoReceiver = new RTCRtpReceiver(transport);

// Retrieve the audio and video receiver capabilities
  var recvAudioCaps = RTCRtpReceiver.getCapabilities("audio");
  var recvVideoCaps = RTCRtpReceiver.getCapabilities("video");   
// Retrieve the audio and video sender capabilities
  var sendAudioCaps = RTCRtpSender.getCapabilities("audio"); 
  var sendVideoCaps = RTCRtpSender.getCapabilities("video"); 

  signaller.offerTracks({
    // The initiator offers its receiver and sender capabilities. 
    "recvAudioCaps": recvAudioCaps, 
    "recvVideoCaps": recvVideoCaps,
    "sendAudioCaps": sendAudioCaps,
    "sendVideoCaps": sendVideoCaps 
  }, function(answer) {
    // The responder answers with its receiver capabilities

    // Derive the send and receive parameters
    var audioSendParams = myCapsToSendParams(sendAudioCaps, answer.recvAudioCaps); 
    var videoSendParams = myCapsToSendParams(sendVideoCaps, answer.recvVideoCaps); 
    var audioRecvParams = myCapsToRecvParams(recvAudioCaps, answer.sendAudioCaps);
    var videoRecvParams = myCapsToRecvParams(recvVideoCaps, answer.sendVideoCaps); 
    audioSender.send(audioSendParams);
    videoSender.send(videoSendParams);
    audioReceiver.receive(audioRecvParams);
    videoReceiver.receive(videoRecvParams);

    // Now we can render/play
    // audioReceiver.track and videoReceiver.track.
  });
}

Example 9

// Assume we already have a way to signal, a transport (RTCDtlsTransport)
// and audio and video tracks. This is an example of how to answer an
// offer with audio and video tracks, and begin sending and receiving them. 
// The example assumes that RTP and RTCP are multiplexed. 
function accept(
  signaller, remote, transport, audioTrack, videoTrack) {
  var audioSender = new RTCRtpSender(audioTrack, transport);
  var videoSender = new RTCRtpSender(videoTrack, transport);
  var audioReceiver = new RTCRtpReceiver(transport);
  var videoReceiver = new RTCRtpReceiver(transport);

// Retrieve the send and receive capabilities
  var recvAudioCaps = RTCRtpReceiver.getCapabilities("audio");
  var recvVideoCaps = RTCRtpReceiver.getCapabilities("video");
  var sendAudioCaps = RTCRtpSender.getCapabilities("audio"); 
  var sendVideoCaps = RTCRtpSender.getCapabilities("video"); 

  signaller.answerTracks({
    "recvAudioCaps": recvAudioCaps,
    "recvVideoCaps": recvVideoCaps,
    "sendAudioCaps": sendAudioCaps,
    "sendVideoCaps": sendVideoCaps
  });

    // Derive the send and receive parameters using Javascript functions defined in Section 15.2.
    var audioSendParams = myCapsToSendParams(sendAudioCaps, remote.recvAudioCaps);
    var videoSendParams = myCapsToSendParams(sendVideoCaps, remote.recvVideoCaps);
    var audioRecvParams = myCapsToRecvParams(recvAudioCaps, remote.sendAudioCaps);
    var videoRecvParams = myCapsToRecvParams(recvVideoCaps, remote.sendVideoCaps);
    audioSender.send(audioSendParams);
    videoSender.send(videoSendParams);
    audioReceiver.receive(audioRecvParams);
    videoReceiver.receive(videoRecvParams);

  // Now we can render/play
  // audioReceiver.track and videoReceiver.track.
}

RTCRtpParameters function myCapsToSendParams (RTCRtpCapabilities sendCaps, RTCRtpCapabilities remoteRecvCaps) {
// Function returning the sender RTCRtpParameters, based on the local sender and remote receiver capabilities.
// The goal is to enable a single stream audio and video call with minimum fuss. 
//
// Steps to be followed: 
// 1. Determine the RTP features that the receiver and sender have in common. 
// 2. Determine the codecs that the sender and receiver have in common.
// 3. Within each common codec, determine the common features and rtcpFeedback mechanisms.
// 4. Determine the payloadType to be used, based on the receiver preferredPayloadType.  
// 5. Return RTCRtpParameters enabling the jointly supported RTP features and common codecs.
}

RTCRtpParameters function myCapsToRecvParams (RTCRtpCapabilities recvCaps, RTCRtpCapabilities remoteSendCaps) {
Function returning the receiver RTCRtpParameters, based on the local receiver and remote sender capabilities.
return (myCapsToSendParams(remoteSendCaps, recvCaps));
}

Received on Wednesday, 4 June 2014 19:54:17 UTC