summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/test_peerConnection_replaceTrack.html
blob: 9befc5c5648760be9c0f0f7b372140647fbd6402 (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
184
185
186
187
<!DOCTYPE HTML>
<html>
<head>
  <script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
  createHTML({
    bug: "1032839",
    title: "Replace video and audio (with WebAudio) tracks",
    visible: true
  });

  function allLocalStreamsHaveSender(pc) {
    return pc.getLocalStreams()
      .every(s => s.getTracks()        // Every local stream,
        .some(t => pc.getSenders()     // should have some track,
          .some(sn => sn.track == t))) // that's being sent over |pc|.
  }

  function allRemoteStreamsHaveReceiver(pc) {
    return pc.getRemoteStreams()
      .every(s => s.getTracks()        // Every remote stream,
        .some(t => pc.getReceivers()   // should have some track,
          .some(sn => sn.track == t))) // that's being received over |pc|.
  }

  function replacetest(wrapper) {
    var pc = wrapper._pc;
    var oldSenderCount = pc.getSenders().length;
    var sender = pc.getSenders().find(sn => sn.track.kind == "video");
    var oldTrack = sender.track;
    ok(sender, "We have a sender for video");
    ok(allLocalStreamsHaveSender(pc),
       "Shouldn't have any local streams without a corresponding sender");
    ok(allRemoteStreamsHaveReceiver(pc),
       "Shouldn't have any remote streams without a corresponding receiver");

    var newTrack;
    var audiotrack;
    return getUserMedia({video:true, audio:true})
      .then(newStream => {
        window.grip = newStream;
        newTrack = newStream.getVideoTracks()[0];
        audiotrack = newStream.getAudioTracks()[0];
        isnot(newTrack, sender.track, "replacing with a different track");
        ok(!pc.getLocalStreams().some(s => s == newStream),
           "from a different stream");
        // Use wrapper function, since it updates expected tracks
        return wrapper.senderReplaceTrack(sender, newTrack, newStream);
      })
      .then(() => {
        is(pc.getSenders().length, oldSenderCount, "same sender count");
        is(sender.track, newTrack, "sender.track has been replaced");
        ok(!pc.getSenders().map(sn => sn.track).some(t => t == oldTrack),
           "old track not among senders");
        // Spec does not say we add this new track to any stream
        ok(!pc.getLocalStreams().some(s => s.getTracks()
                                           .some(t => t == sender.track)),
           "track does not exist among pc's local streams");
        return sender.replaceTrack(audiotrack)
          .then(() => ok(false, "replacing with different kind should fail"),
                e => is(e.name, "TypeError",
                        "replacing with different kind should fail"));
      });
  }

  runNetworkTest(function () {
    test = new PeerConnectionTest();
    test.audioCtx = new AudioContext();
    test.setMediaConstraints([{video: true, audio: true}], [{video: true}]);
    test.chain.removeAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW");

    // Test replaceTrack on pcRemote separately since it's video only.
    test.chain.append([
      function PC_REMOTE_VIDEOONLY_REPLACE_VIDEOTRACK(test) {
        return replacetest(test.pcRemote);
      },
      function PC_LOCAL_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW(test) {
        return test.pcLocal.waitForMediaFlow();
      }
    ]);

    // Replace video twice on pcLocal to make sure it still works
    // (does audio twice too, but hey)
    test.chain.append([
      function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_1(test) {
        return replacetest(test.pcLocal);
      },
      function PC_REMOTE_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW_1(test) {
        return test.pcRemote.waitForMediaFlow();
      },
      function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_2(test) {
        return replacetest(test.pcLocal);
      },
      function PC_REMOTE_NEW_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW_2(test) {
        return test.pcRemote.waitForMediaFlow();
      }
    ]);

    test.chain.append([
      function PC_LOCAL_AUDIOVIDEO_REPLACE_VIDEOTRACK_WITHSAME(test) {
        var pc = test.pcLocal._pc;
        var sender = pc.getSenders().find(sn => sn.track.kind == "video");
        ok(sender, "should still have a sender of video");
        return sender.replaceTrack(sender.track)
          .then(() => ok(true, "replacing with itself should succeed"));
      },
      function PC_REMOTE_NEW_SAME_VIDEOTRACK_WAIT_FOR_MEDIA_FLOW(test) {
        return test.pcRemote.waitForMediaFlow();
      }
    ]);

    // Replace the gUM audio track on pcLocal with a WebAudio track.
    test.chain.append([
      function PC_LOCAL_AUDIOVIDEO_REPLACE_AUDIOTRACK_WEBAUDIO(test) {
        var pc = test.pcLocal._pc;
        var sender = pc.getSenders().find(sn => sn.track.kind == "audio");
        ok(sender, "track has a sender");
        var oldSenderCount = pc.getSenders().length;
        var oldTrack = sender.track;

        var sourceNode = test.audioCtx.createOscillator();
        sourceNode.type = 'sine';
        // We need a frequency not too close to the fake audio track
        // (440Hz for loopback devices, 1kHz for fake tracks).
        sourceNode.frequency.value = 2000;
        sourceNode.start();

        var destNode = test.audioCtx.createMediaStreamDestination();
        sourceNode.connect(destNode);
        var newTrack = destNode.stream.getAudioTracks()[0];

        return test.pcLocal.senderReplaceTrack(
            sender, newTrack, destNode.stream)
          .then(() => {
            is(pc.getSenders().length, oldSenderCount, "same sender count");
            ok(!pc.getSenders().some(sn => sn.track == oldTrack),
               "Replaced track should be removed from senders");
            // TODO: Should PC remove local streams when there are no senders
            // associated with it? getLocalStreams() isn't in the spec anymore,
            // so I guess it is pretty arbitrary?
            is(sender.track, newTrack, "sender.track has been replaced");
            // Spec does not say we add this new track to any stream
            ok(!pc.getLocalStreams().some(s => s.getTracks()
                                               .some(t => t == sender.track)),
               "track exists among pc's local streams");
          });
      }
    ]);
    test.chain.append([
      function PC_LOCAL_CHECK_WEBAUDIO_FLOW_PRESENT(test) {
        return test.pcRemote.checkReceivingToneFrom(test.audioCtx, test.pcLocal);
      }
    ]);
    test.chain.append([
      function PC_LOCAL_INVALID_ADD_VIDEOTRACKS(test) {
        let videoTransceivers = test.pcLocal._pc.getTransceivers()
          .filter(transceiver => {
            return !transceiver.stopped &&
                   transceiver.receiver.track.kind == "video" &&
                   transceiver.sender.track;
          });

        ok(videoTransceivers.length,
           "There is at least one non-stopped video transceiver with a track.");

        videoTransceivers.forEach(transceiver => {
            var stream = test.pcLocal._pc.getLocalStreams()[0];;
            var track = transceiver.sender.track;
            try {
              test.pcLocal._pc.addTrack(track, stream);
              ok(false, "addTrack existing track should fail");
            } catch (e) {
              is(e.name, "InvalidAccessError",
                 "addTrack existing track should fail");
            }
          });
      }
    ]);
    return test.run();
  });
</script>
</pre>
</body>
</html>