def test_video(self): recorder = MediaBlackhole() recorder.addTrack(VideoStreamTrack()) run(recorder.start()) run(asyncio.sleep(2)) run(recorder.stop())
async def run(pc, player, session, bitrate=512000, record=False): @pc.on('track') def on_track(track): logger.info(f'Track {track.kind} received') @track.on('ended') def on_ended(): print(f'Track {track.kind} ended') @pc.on('iceconnectionstatechange') def on_ice_state_change(): # logger.info(f'ICE state changed to {pc.iceConnectionState}') pass # create session await session.create() # configure media media = {'audio': True, 'video': True} if player and player.audio: pc.addTrack(player.audio) else: pc.addTrack(AudioStreamTrack()) if player and player.video: pc.addTrack(player.video) else: pc.addTrack(VideoStreamTrack()) # attach to echotest plugin plugin = await session.attach('janus.plugin.echotest') # create data-channel channel = pc.createDataChannel('JanusDataChannel') logger.info(f'DataChannel ({channel.label}) created') dc_probe_message = 'echo-ping' dc_open = False dc_probe_received = False @channel.on('open') def on_open(): nonlocal dc_open dc_open = True logger.info(f'DataChannel ({channel.label}) open') logger.info( f'DataChannel ({channel.label}) sending: {dc_probe_message}') channel.send(dc_probe_message) @channel.on('close') def on_close(): nonlocal dc_open dc_open = False logger.info(f'DataChannel ({channel.label}) closed') @channel.on('message') def on_message(message): logger.info(f'DataChannel ({channel.label}) received: {message}') if dc_probe_message in message: nonlocal dc_probe_received dc_probe_received = True # send offer await pc.setLocalDescription(await pc.createOffer()) request = {'record': record, 'bitrate': bitrate} request.update(media) response = await plugin.sendMessage({ 'body': request, 'jsep': { 'sdp': pc.localDescription.sdp, 'trickle': False, 'type': pc.localDescription.type, }, }) assert response['plugindata']['data']['result'] == 'ok' # apply answer answer = RTCSessionDescription(sdp=response['jsep']['sdp'], type=response['jsep']['type']) await pc.setRemoteDescription(answer) logger.info('Running for a while...') await asyncio.sleep(5) # Check WebSocket status assert session._websocket.connection.open # Get RTC stats and check the status rtcstats = await pc.getStats() rtp = {'audio': {'in': 0, 'out': 0}, 'video': {'in': 0, 'out': 0}} dtls_state = None for stat in rtcstats.values(): if stat.type == 'inbound-rtp': rtp[stat.kind]['in'] = stat.packetsReceived elif stat.type == 'outbound-rtp': rtp[stat.kind]['out'] = stat.packetsSent elif stat.type == 'transport': dtls_state = stat.dtlsState # ICE succeded assert pc.iceConnectionState == 'completed' # DTLS succeded assert dtls_state == 'connected' # Janus echoed the sent packets assert rtp['audio']['out'] >= rtp['audio']['in'] > 0 assert rtp['video']['out'] >= rtp['video']['in'] > 0 # DataChannels worked assert dc_open assert dc_probe_received logger.info('Ending the test now')
def test_connect_video_bidirectional(self): pc1 = RTCPeerConnection() pc1_states = track_states(pc1) pc2 = RTCPeerConnection() pc2_states = track_states(pc2) self.assertEqual(pc1.iceConnectionState, 'new') self.assertEqual(pc1.iceGatheringState, 'new') self.assertIsNone(pc1.localDescription) self.assertIsNone(pc1.remoteDescription) self.assertEqual(pc2.iceConnectionState, 'new') self.assertEqual(pc2.iceGatheringState, 'new') self.assertIsNone(pc2.localDescription) self.assertIsNone(pc2.remoteDescription) # create offer pc1.addTrack(VideoStreamTrack()) offer = run(pc1.createOffer()) self.assertEqual(offer.type, 'offer') self.assertTrue('m=video ' in offer.sdp) self.assertFalse('a=candidate:' in offer.sdp) run(pc1.setLocalDescription(offer)) self.assertEqual(pc1.iceConnectionState, 'new') self.assertEqual(pc1.iceGatheringState, 'complete') self.assertTrue('m=video ' in pc1.localDescription.sdp) self.assertTrue('a=candidate:' in pc1.localDescription.sdp) self.assertTrue('a=sendrecv' in pc1.localDescription.sdp) self.assertTrue('a=fingerprint:sha-256' in pc1.localDescription.sdp) self.assertTrue('a=setup:actpass' in pc1.localDescription.sdp) # handle offer run(pc2.setRemoteDescription(pc1.localDescription)) self.assertEqual(pc2.remoteDescription, pc1.localDescription) self.assertEqual(len(pc2.getReceivers()), 1) # create answer pc2.addTrack(VideoStreamTrack()) answer = run(pc2.createAnswer()) self.assertEqual(answer.type, 'answer') self.assertTrue('m=video ' in answer.sdp) self.assertFalse('a=candidate:' in answer.sdp) run(pc2.setLocalDescription(answer)) self.assertEqual(pc2.iceConnectionState, 'checking') self.assertEqual(pc2.iceGatheringState, 'complete') self.assertTrue('m=video ' in pc2.localDescription.sdp) self.assertTrue('a=candidate:' in pc2.localDescription.sdp) self.assertTrue('a=sendrecv' in pc1.localDescription.sdp) self.assertTrue('a=fingerprint:sha-256' in pc2.localDescription.sdp) self.assertTrue('a=setup:active' in pc2.localDescription.sdp) # handle answer run(pc1.setRemoteDescription(pc2.localDescription)) self.assertEqual(pc1.remoteDescription, pc2.localDescription) self.assertEqual(pc1.iceConnectionState, 'checking') # check outcome run(asyncio.sleep(1)) self.assertEqual(pc1.iceConnectionState, 'completed') self.assertEqual(pc2.iceConnectionState, 'completed') # close run(pc1.close()) run(pc2.close()) self.assertEqual(pc1.iceConnectionState, 'closed') self.assertEqual(pc2.iceConnectionState, 'closed') # check state changes self.assertEqual(pc1_states['iceConnectionState'], ['new', 'checking', 'completed', 'closed']) self.assertEqual(pc1_states['iceGatheringState'], ['new', 'gathering', 'complete']) self.assertEqual(pc1_states['signalingState'], ['stable', 'have-local-offer', 'stable', 'closed']) self.assertEqual(pc2_states['iceConnectionState'], ['new', 'checking', 'completed', 'closed']) self.assertEqual(pc2_states['iceGatheringState'], ['new', 'gathering', 'complete']) self.assertEqual(pc2_states['signalingState'], ['stable', 'have-remote-offer', 'stable', 'closed'])
def test_connect_audio_and_video_and_data_channel(self): pc1 = RTCPeerConnection() pc1_states = track_states(pc1) pc2 = RTCPeerConnection() pc2_states = track_states(pc2) self.assertEqual(pc1.iceConnectionState, 'new') self.assertEqual(pc1.iceGatheringState, 'new') self.assertIsNone(pc1.localDescription) self.assertIsNone(pc1.remoteDescription) self.assertEqual(pc2.iceConnectionState, 'new') self.assertEqual(pc2.iceGatheringState, 'new') self.assertIsNone(pc2.localDescription) self.assertIsNone(pc2.remoteDescription) # create offer pc1.addTrack(AudioStreamTrack()) pc1.addTrack(VideoStreamTrack()) pc1.createDataChannel('chat', protocol='bob') offer = run(pc1.createOffer()) self.assertEqual(offer.type, 'offer') self.assertTrue('m=audio ' in offer.sdp) self.assertTrue('m=video ' in offer.sdp) self.assertTrue('m=application ' in offer.sdp) run(pc1.setLocalDescription(offer)) self.assertEqual(pc1.iceConnectionState, 'new') self.assertEqual(pc1.iceGatheringState, 'complete') # handle offer run(pc2.setRemoteDescription(pc1.localDescription)) self.assertEqual(pc2.remoteDescription, pc1.localDescription) self.assertEqual(len(pc2.getSenders()), 2) self.assertEqual(len(pc2.getReceivers()), 2) # create answer pc2.addTrack(AudioStreamTrack()) pc2.addTrack(VideoStreamTrack()) answer = run(pc2.createAnswer()) self.assertEqual(answer.type, 'answer') self.assertTrue('m=audio ' in answer.sdp) self.assertTrue('m=video ' in answer.sdp) self.assertTrue('m=application ' in answer.sdp) run(pc2.setLocalDescription(answer)) self.assertEqual(pc2.iceConnectionState, 'checking') self.assertEqual(pc2.iceGatheringState, 'complete') self.assertTrue('m=audio ' in pc2.localDescription.sdp) self.assertTrue('m=video ' in pc2.localDescription.sdp) self.assertTrue('m=application ' in pc2.localDescription.sdp) # handle answer run(pc1.setRemoteDescription(pc2.localDescription)) self.assertEqual(pc1.remoteDescription, pc2.localDescription) self.assertEqual(pc1.iceConnectionState, 'checking') # check outcome run(asyncio.sleep(1)) self.assertEqual(pc1.iceConnectionState, 'completed') self.assertEqual(pc2.iceConnectionState, 'completed') # check a single transport is used self.assertBundled(pc1) self.assertBundled(pc2) # close run(pc1.close()) run(pc2.close()) self.assertEqual(pc1.iceConnectionState, 'closed') self.assertEqual(pc2.iceConnectionState, 'closed') # check state changes self.assertEqual(pc1_states['iceConnectionState'], ['new', 'checking', 'completed', 'closed']) self.assertEqual(pc1_states['iceGatheringState'], [ 'new', 'gathering', 'new', 'gathering', 'new', 'gathering', 'complete' ]) self.assertEqual(pc1_states['signalingState'], ['stable', 'have-local-offer', 'stable', 'closed']) self.assertEqual(pc2_states['iceConnectionState'], ['new', 'checking', 'completed', 'closed']) self.assertEqual(pc2_states['iceGatheringState'], ['new', 'gathering', 'complete']) self.assertEqual(pc2_states['signalingState'], ['stable', 'have-remote-offer', 'stable', 'closed'])