summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/test_peerConnection_simulcastOddResolution.html
blob: c380b34f1a7642099f27e1c9f1165f80480794a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<!DOCTYPE HTML>
<html>
<head>
  <script type="application/javascript" src="pc.js"></script>
  <script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
  <script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
  <script type="application/javascript" src="simulcast.js"></script>
  <script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
  createHTML({
    bug: "1432793",
    title: "Simulcast with odd resolution",
    visible: true
  });

  runNetworkTest(async () => {
    const helper = new VideoStreamHelper();
    const emitter = new VideoFrameEmitter(helper.green, helper.red, 705, 528);

    async function checkVideoElement(senderElement, receiverElement, encoding) {
      info(`Waiting for receiver video element ${encoding.rid} to start playing`);
      await helper.checkVideoPlaying(receiverElement);
      const srcWidth = senderElement.videoWidth;
      const srcHeight = senderElement.videoHeight;
      info(`Source resolution is ${srcWidth}x${srcHeight}`);

      const scaleDownBy = encoding.scaleResolutionDownBy;
      const expectedWidth = srcWidth / scaleDownBy;
      const expectedHeight = srcHeight / scaleDownBy;
      const margin = srcWidth * 0.1;
      const width = receiverElement.videoWidth;
      const height = receiverElement.videoHeight;
      const rid = encoding.rid;
      ok(width >= expectedWidth - margin && width <= expectedWidth + margin,
        `Width ${width} should be within 10% of ${expectedWidth} for rid '${rid}'`);
      ok(height >= expectedHeight - margin && height <= expectedHeight + margin,
        `Height ${height} should be within 10% of ${expectedHeight} for rid '${rid}'`);
    }

    async function checkVideoElements(senderElement, receiverElements, encodings) {
      is(receiverElements.length, encodings.length, 'Number of video elements should match number of encodings');
      info('Waiting for sender video element to start playing');
      await helper.checkVideoPlaying(senderElement);
      for (let i = 0; i < encodings.length; i++) {
        await checkVideoElement(senderElement, receiverElements[i], encodings[i]);
      }
    }

    const sendEncodings = [{ rid: "0", maxBitrate: 40000, scaleResolutionDownBy: 1.9 },
                           { rid: "1", maxBitrate: 40000, scaleResolutionDownBy: 3.5 },
                           { rid: "2", maxBitrate: 40000, scaleResolutionDownBy: 17.8 }];

    async function checkSenderStats(sender) {
      const senderStats = await sender.getStats();
      checkSenderStats(senderStats, sendEncodings.length);
      checkExpectedFields(senderStats);
      pedanticChecks(senderStats);
    }

    async function waitForResizeEvents(elements) {
      return Promise.all(elements.map(elem => haveEvent(elem, 'resize')));
    }

    await pushPrefs(
        // 180Kbps was determined empirically, set well-higher than
        // the 80Kbps+overhead needed for the two simulcast streams.
        // 100Kbps was apparently too low.
        ['media.peerconnection.video.min_bitrate_estimate', 180*1000]);


    const offerer = new RTCPeerConnection();
    const answerer = new RTCPeerConnection();

    const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
    offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
    answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());

    const metadataToBeLoaded = [];
    answerer.ontrack = (e) => {
      metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
    };

    // One send transceiver, that will be used to send both simulcast streams
    const videoStream = emitter.stream();
    offerer.addTransceiver(videoStream.getVideoTracks()[0], {sendEncodings});
    const senderElement = document.createElement('video');
    senderElement.autoplay = true;
    senderElement.srcObject = videoStream;
    senderElement.id = videoStream.id

    const sender = offerer.getSenders()[0];
    let parameters = sender.getParameters();
    is(parameters.encodings[0].maxBitrate, sendEncodings[0].maxBitrate);
    isfuzzy(parameters.encodings[0].scaleResolutionDownBy,
      sendEncodings[0].scaleResolutionDownBy, 0.01);
    is(parameters.encodings[1].maxBitrate, sendEncodings[1].maxBitrate);
    isfuzzy(parameters.encodings[1].scaleResolutionDownBy,
      sendEncodings[1].scaleResolutionDownBy, 0.01);
    is(parameters.encodings[2].maxBitrate, sendEncodings[2].maxBitrate);
    isfuzzy(parameters.encodings[2].scaleResolutionDownBy,
      sendEncodings[2].scaleResolutionDownBy, 0.01);

    const offer = await offerer.createOffer();

    const mungedOffer = ridToMid(offer);
    info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`);

    await answerer.setRemoteDescription({type: 'offer', sdp: mungedOffer});
    await offerer.setLocalDescription(offer);

    const rids = answerer.getTransceivers().map(t => t.mid);
    is(rids.length, 3, 'Should have 3 mids in offer');
    ok(rids[0], 'First mid should be non-empty');
    ok(rids[1], 'Second mid should be non-empty');
    ok(rids[2], 'Third mid should be non-empty');
    info(`rids: ${JSON.stringify(rids)}`);

    const answer = await answerer.createAnswer();

    const mungedAnswer = midToRid(answer);
    info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`);
    await offerer.setRemoteDescription({type: 'answer', sdp: mungedAnswer});
    await answerer.setLocalDescription(answer);

    is(metadataToBeLoaded.length, 3, 'Offerer should have gotten 3 ontrack events');
    emitter.start();
    info('Waiting for 3 loadedmetadata events');
    const videoElems = await Promise.all(metadataToBeLoaded);
    await checkVideoElements(senderElement, videoElems, parameters.encodings);
    emitter.stop();

    await Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);

    info(`Changing source resolution to 1280x720`);
    emitter.size(1280, 720);
    emitter.start();
    await waitForResizeEvents([senderElement, ...videoElems]);
    await checkVideoElements(senderElement, videoElems, parameters.encodings);
    await checkSenderStats(sender);

    parameters = sender.getParameters();
    parameters.encodings[0].scaleResolutionDownBy = 1;
    parameters.encodings[1].scaleResolutionDownBy = 2;
    parameters.encodings[2].scaleResolutionDownBy = 3;
    info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`);
    await sender.setParameters(parameters);
    await waitForResizeEvents(videoElems);
    await checkVideoElements(senderElement, videoElems, parameters.encodings);
    await checkSenderStats(sender);

    parameters = sender.getParameters();
    parameters.encodings[0].scaleResolutionDownBy = 6;
    parameters.encodings[1].scaleResolutionDownBy = 5;
    parameters.encodings[2].scaleResolutionDownBy = 4;
    info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`);
    await sender.setParameters(parameters);
    await waitForResizeEvents(videoElems);
    await checkVideoElements(senderElement, videoElems, parameters.encodings);
    await checkSenderStats(sender);

    parameters = sender.getParameters();
    parameters.encodings[0].scaleResolutionDownBy = 4;
    parameters.encodings[1].scaleResolutionDownBy = 1;
    parameters.encodings[2].scaleResolutionDownBy = 2;
    info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`);
    await sender.setParameters(parameters);
    await waitForResizeEvents(videoElems);
    await checkVideoElements(senderElement, videoElems, parameters.encodings);
    await checkSenderStats(sender);

    emitter.stop();
    videoStream.getVideoTracks()[0].stop();
    offerer.close();
    answerer.close();
  });

</script>
</pre>
</body>
</html>