def test_track_ended(self): track = AudioStreamTrack() sender = RTCRtpSender(track, self.local_transport) run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) track.stop() run(asyncio.sleep(0.5))
def test_retransmit(self): """ Ask for an RTP packet retransmission. """ transport = FakeDtlsTransport() sender = RTCRtpSender(VideoStreamTrack(), transport) self.assertEqual(sender.kind, 'video') self.assertEqual(sender.transport, transport) run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # wait for one packet to be transmitted, and ask to retransmit packet = run(transport.queue.get()) run(sender._retransmit(packet.sequence_number)) # wait for packet to be retransmitted, then shutdown run(asyncio.sleep(0.5)) run(sender.stop()) # check packet was retransmitted found_rtx = False while not transport.queue.empty(): queue_packet = transport.queue.get_nowait() if queue_packet.sequence_number == packet.sequence_number: found_rtx = True self.assertTrue(found_rtx)
def test_retransmit(self): """ Ask for an RTP packet retransmission. """ transport = FakeDtlsTransport() sender = RTCRtpSender(VideoStreamTrack(), transport) self.assertEqual(sender.kind, 'video') self.assertEqual(sender.transport, transport) run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # wait for one packet to be transmitted, and ask to retransmit packet = run(transport.queue.get()) run(sender._retransmit(packet.sequence_number)) # wait for packet to be transmitted rtx_packet = run(transport.queue.get()) self.assertEqual(rtx_packet.sequence_number, packet.sequence_number) # clean shutdown run(sender.stop())
async def test_send_keyframe(self): """ Ask for a keyframe. """ queue = asyncio.Queue() async def mock_send_rtp(data): if not is_rtcp(data): await queue.put(RtpPacket.parse(data)) async with dummy_dtls_transport_pair() as (local_transport, _): local_transport._send_rtp = mock_send_rtp sender = RTCRtpSender(VideoStreamTrack(), local_transport) self.assertEqual(sender.kind, "video") await sender.send(RTCRtpParameters(codecs=[VP8_CODEC])) # wait for one packet to be transmitted, and ask for keyframe await queue.get() sender._send_keyframe() # wait for packet to be transmitted, then shutdown await asyncio.sleep(0.1) await sender.stop()
def test_stop(self): sender = RTCRtpSender(AudioStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'audio') run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) # clean shutdown run(sender.stop())
def test_track_ended(self): track = AudioStreamTrack() sender = RTCRtpSender(track, self.local_transport) run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) # stop track and wait for RTP loop to exit track.stop() run(asyncio.sleep(0.1))
def test_stop_on_exception(self): sender = RTCRtpSender(BuggyStreamTrack(), self.local_transport) self.assertEqual(sender.kind, "audio") run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) # clean shutdown run(sender.stop())
def test_connection_error(self): """ Close the underlying transport before the sender. """ sender = RTCRtpSender(AudioStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'audio') run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) run(self.local_transport.stop())
def test_track_ended(self): transport, _ = dummy_dtls_transport_pair() track = AudioStreamTrack() sender = RTCRtpSender(track, transport) run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) track.stop() run(asyncio.sleep(0.5)) run(transport.stop())
def test_connection_error(self): """ Close the underlying transport before the sender. """ transport, _ = dummy_dtls_transport_pair() sender = RTCRtpSender(AudioStreamTrack(), transport) self.assertEqual(sender.kind, 'audio') self.assertEqual(sender.transport, transport) run(asyncio.gather(sender._run(codec=PCMU_CODEC), transport.close()))
def test_stop(self): transport, _ = dummy_dtls_transport_pair() sender = RTCRtpSender(AudioStreamTrack(), transport) self.assertEqual(sender.kind, 'audio') self.assertEqual(sender.transport, transport) run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) # clean shutdown run(sender.stop())
def test_handle_rtcp_pli(self): sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, "video") run(sender.send(RTCRtpParameters(codecs=[VP8_CODEC]))) # receive RTCP feedback NACK packet = RtcpPsfbPacket(fmt=RTCP_PSFB_PLI, ssrc=1234, media_ssrc=sender._ssrc) run(sender._handle_rtcp_packet(packet)) # clean shutdown run(sender.stop())
def test_connection_error(self): """ Close the underlying transport before the sender. """ transport, _ = dummy_dtls_transport_pair() sender = RTCRtpSender(AudioStreamTrack(), transport) self.assertEqual(sender.kind, 'audio') self.assertEqual(sender.transport, transport) run(sender.send(RTCRtpParameters(codecs=[PCMU_CODEC]))) run(transport.stop())
def test_capabilities(self): # audio capabilities = RTCRtpSender.getCapabilities('audio') self.assertTrue(isinstance(capabilities, RTCRtpCapabilities)) self.assertEqual(capabilities.codecs, [ RTCRtpCodecCapability( mimeType='audio/opus', clockRate=48000, channels=2), RTCRtpCodecCapability( mimeType='audio/PCMU', clockRate=8000, channels=1), RTCRtpCodecCapability( mimeType='audio/PCMA', clockRate=8000, channels=1), ]) self.assertEqual(capabilities.headerExtensions, [ RTCRtpHeaderExtensionCapability( uri='urn:ietf:params:rtp-hdrext:sdes:mid'), ]) # video capabilities = RTCRtpSender.getCapabilities('video') self.assertTrue(isinstance(capabilities, RTCRtpCapabilities)) self.assertEqual(capabilities.codecs, [ RTCRtpCodecCapability(mimeType='video/VP8', clockRate=90000), RTCRtpCodecCapability(mimeType='video/rtx', clockRate=90000), RTCRtpCodecCapability(mimeType='video/H264', clockRate=90000, parameters=OrderedDict( [('packetization-mode', '1'), ('level-asymmetry-allowed', '1'), ('profile-level-id', '42001f')])), RTCRtpCodecCapability(mimeType='video/H264', clockRate=90000, parameters=OrderedDict( [('packetization-mode', '1'), ('level-asymmetry-allowed', '1'), ('profile-level-id', '42e01f')])), ]) self.assertEqual( capabilities.headerExtensions, [ RTCRtpHeaderExtensionCapability( uri='urn:ietf:params:rtp-hdrext:sdes:mid'), RTCRtpHeaderExtensionCapability( uri= 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' ), # noqa ]) # bogus capabilities = RTCRtpSender.getCapabilities('bogus') self.assertIsNone(capabilities)
async def test_retransmit_with_rtx(self): """ Ask for an RTP packet retransmission. """ queue = asyncio.Queue() async def mock_send_rtp(data): if not is_rtcp(data): await queue.put(RtpPacket.parse(data)) async with dummy_dtls_transport_pair() as (local_transport, _): local_transport._send_rtp = mock_send_rtp sender = RTCRtpSender(VideoStreamTrack(), local_transport) sender._ssrc = 1234 sender._rtx_ssrc = 2345 self.assertEqual(sender.kind, "video") await sender.send( RTCRtpParameters(codecs=[ VP8_CODEC, RTCRtpCodecParameters( mimeType="video/rtx", clockRate=90000, payloadType=101, parameters={"apt": 100}, ), ])) # wait for one packet to be transmitted, and ask to retransmit packet = await queue.get() await sender._retransmit(packet.sequence_number) # wait for packet to be retransmitted, then shutdown await asyncio.sleep(0.1) await sender.stop() # check packet was retransmitted found_rtx = None while not queue.empty(): queue_packet = queue.get_nowait() if queue_packet.payload_type == 101: found_rtx = queue_packet break self.assertIsNotNone(found_rtx) self.assertEqual(found_rtx.payload_type, 101) self.assertEqual(found_rtx.ssrc, 2345) self.assertEqual(found_rtx.payload[0:2], pack("!H", packet.sequence_number))
def test_handle_rtcp_nack(self): sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run(sender.send(RTCRtpParameters(codecs=[VP8_CODEC]))) # receive RTCP feedback NACK packet = RtcpRtpfbPacket(fmt=RTCP_RTPFB_NACK, ssrc=1234, media_ssrc=sender._ssrc) packet.lost.append(7654) run(sender._handle_rtcp_packet(packet)) # clean shutdown run(sender.stop())
async def test_handle_rtcp_remb(self): async with dummy_dtls_transport_pair() as (local_transport, _): sender = RTCRtpSender(VideoStreamTrack(), local_transport) self.assertEqual(sender.kind, "video") await sender.send(RTCRtpParameters(codecs=[VP8_CODEC])) # receive RTCP feedback REMB packet = RtcpPsfbPacket( fmt=RTCP_PSFB_APP, ssrc=1234, media_ssrc=0, fci=pack_remb_fci(4160000, [sender._ssrc]), ) await sender._handle_rtcp_packet(packet) # receive RTCP feedback REMB (malformed) packet = RtcpPsfbPacket(fmt=RTCP_PSFB_APP, ssrc=1234, media_ssrc=0, fci=b"JUNK") await sender._handle_rtcp_packet(packet) # clean shutdown await sender.stop()
async def test_handle_rtcp_rr(self): async with dummy_dtls_transport_pair() as (local_transport, _): sender = RTCRtpSender(VideoStreamTrack(), local_transport) self.assertEqual(sender.kind, "video") await sender.send(RTCRtpParameters(codecs=[VP8_CODEC])) # receive RTCP RR packet = RtcpRrPacket( ssrc=1234, reports=[ RtcpReceiverInfo( ssrc=sender._ssrc, fraction_lost=0, packets_lost=0, highest_sequence=630, jitter=1906, lsr=0, dlsr=0, ) ], ) await sender._handle_rtcp_packet(packet) # check stats report = await sender.getStats() self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual( sorted([s.type for s in report.values()]), ["outbound-rtp", "remote-inbound-rtp", "transport"], ) # clean shutdown await sender.stop()
async def __offer(self, request): """ Generates JSON Response with a WebRTC Peer Connection of Video Server. """ # get offer from params params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) # initiate stream if not (self.__default_rtc_server is None) and not (self.__default_rtc_server.is_launched): if self.__logging: logger.debug("Initiating Video Streaming.") self.__default_rtc_server.launch() # setup RTC peer connection - interface represents a WebRTC connection # between the local computer and a remote peer. pc = RTCPeerConnection() self.__pcs.add(pc) if self.__logging: logger.info("Created WebRTC Peer Connection.") # track ICE connection state changes @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): logger.debug("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": logger.error("ICE connection state failed.") # check if Live Broadcasting is enabled if self.__relay is None: # if not, close connection. await pc.close() self.__pcs.discard(pc) # Change the remote description associated with the connection. await pc.setRemoteDescription(offer) # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection for t in pc.getTransceivers(): # Increments performance significantly, IDK why this works as H265 codec is not even supported :D capabilities = RTCRtpSender.getCapabilities("video") preferences = list( filter(lambda x: x.name == "H265", capabilities.codecs)) t.setCodecPreferences(preferences) # add video server to peer track if t.kind == "video": pc.addTrack( self.__relay.subscribe(self.config["server"]) if not ( self.__relay is None) else self.config["server"]) # Create an SDP answer to an offer received from a remote peer answer = await pc.createAnswer() # Change the local description for the answer await pc.setLocalDescription(answer) # return Starlette json response return JSONResponse({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type })
def test_handle_rtcp_pli(self): sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # receive RTCP feedback NACK packet = RtcpPsfbPacket(fmt=RTCP_PSFB_PLI, ssrc=1234, media_ssrc=5678) run(sender._handle_rtcp_packet(packet)) # clean shutdown run(sender.stop())
async def test_stop_on_exception(self): async with dummy_dtls_transport_pair() as (local_transport, _): sender = RTCRtpSender(BuggyStreamTrack(), local_transport) self.assertEqual(sender.kind, "audio") await sender.send(RTCRtpParameters(codecs=[PCMU_CODEC])) # clean shutdown await sender.stop()
async def test_log_debug(self, mock_is_enabled_for): mock_is_enabled_for.return_value = True async with dummy_dtls_transport_pair() as (local_transport, _): sender = RTCRtpSender(VideoStreamTrack(), local_transport) await sender.send(RTCRtpParameters(codecs=[VP8_CODEC])) # clean shutdown await sender.stop()
def test_handle_rtcp_nack(self): transport, remote = dummy_dtls_transport_pair() sender = RTCRtpSender(VideoStreamTrack(), transport) self.assertEqual(sender.kind, 'video') self.assertEqual(sender.transport, transport) run(sender.send(RTCRtpParameters(codecs=[ RTCRtpCodecParameters(name='VP8', clockRate=90000, payloadType=100), ]))) # receive RTCP feedback NACK packet = RtcpRtpfbPacket(fmt=RTCP_RTPFB_NACK, ssrc=1234, media_ssrc=5678) packet.lost.append(7654) run(sender._handle_rtcp_packet(packet)) # clean shutdown run(sender.stop())
async def test_track_ended(self): async with dummy_dtls_transport_pair() as (local_transport, _): track = AudioStreamTrack() sender = RTCRtpSender(track, local_transport) await sender.send(RTCRtpParameters(codecs=[PCMU_CODEC])) # stop track and wait for RTP loop to exit track.stop() await asyncio.sleep(0.1)
async def test_connection_error(self): """ Close the underlying transport before the sender. """ async with dummy_dtls_transport_pair() as (local_transport, _): sender = RTCRtpSender(AudioStreamTrack(), local_transport) self.assertEqual(sender.kind, "audio") await sender.send(RTCRtpParameters(codecs=[PCMU_CODEC])) await local_transport.stop()
def test_handle_rtcp_rr(self): transport, remote = dummy_dtls_transport_pair() sender = RTCRtpSender(VideoStreamTrack(), transport) self.assertEqual(sender.kind, 'video') self.assertEqual(sender.transport, transport) run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # receive RTCP RR for packet in RtcpPacket.parse(load('rtcp_rr.bin')): run(sender._handle_rtcp_packet(packet)) # check stats report = run(sender.getStats()) self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual(sorted([s.type for s in report.values()]), ['outbound-rtp', 'remote-inbound-rtp', 'transport']) # clean shutdown run(sender.stop())
def test_handle_rtcp_rr(self): sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run(sender.send(RTCRtpParameters(codecs=[VP8_CODEC]))) # receive RTCP RR packet = RtcpRrPacket(ssrc=1234, reports=[ RtcpReceiverInfo(ssrc=sender._ssrc, fraction_lost=0, packets_lost=0, highest_sequence=630, jitter=1906, lsr=0, dlsr=0) ]) run(sender._handle_rtcp_packet(packet)) # check stats report = run(sender.getStats()) self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual(sorted([s.type for s in report.values()]), ['outbound-rtp', 'remote-inbound-rtp', 'transport']) # clean shutdown run(sender.stop())
def test_handle_rtcp_remb(self): sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # receive RTCP feedback REMB packet = RtcpPsfbPacket(fmt=RTCP_PSFB_APP, ssrc=1234, media_ssrc=0, fci=b'REMB\x01\x13\xf7\xa0\x96\xbe\x96\xcf') run(sender._handle_rtcp_packet(packet)) # receive RTCP feedback REMB (malformed) packet = RtcpPsfbPacket(fmt=RTCP_PSFB_APP, ssrc=1234, media_ssrc=0, fci=b'JUNK') run(sender._handle_rtcp_packet(packet)) # clean shutdown run(sender.stop())
def test_send_keyframe(self): """ Ask for a keyframe. """ queue = asyncio.Queue() async def mock_send_rtp(data): if not is_rtcp(data): await queue.put(RtpPacket.parse(data)) self.local_transport._send_rtp = mock_send_rtp sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # wait for one packet to be transmitted, and ask for keyframe run(queue.get()) sender._send_keyframe() # wait for packet to be transmitted, then shutdown run(asyncio.sleep(0.1)) run(sender.stop())
def test_retransmit(self): """ Ask for an RTP packet retransmission. """ queue = asyncio.Queue() async def mock_send_rtp(data): if not is_rtcp(data): await queue.put(RtpPacket.parse(data)) self.local_transport._send_rtp = mock_send_rtp sender = RTCRtpSender(VideoStreamTrack(), self.local_transport) self.assertEqual(sender.kind, 'video') run( sender.send( RTCRtpParameters(codecs=[ RTCRtpCodecParameters( name='VP8', clockRate=90000, payloadType=100), ]))) # wait for one packet to be transmitted, and ask to retransmit packet = run(queue.get()) run(sender._retransmit(packet.sequence_number)) # wait for packet to be retransmitted, then shutdown run(asyncio.sleep(0.5)) run(sender.stop()) # check packet was retransmitted found_rtx = False while not queue.empty(): queue_packet = queue.get_nowait() if queue_packet.sequence_number == packet.sequence_number: found_rtx = True self.assertTrue(found_rtx)