def test_with_csrc_truncated(self): data = load("rtp_with_csrc.bin") for length in range(12, 20): with self.assertRaises(ValueError) as cm: RtpPacket.parse(data[0:length]) self.assertEqual(str(cm.exception), "RTP packet has truncated CSRC")
def test_with_sdes_mid_truncated(self): data = load('rtp_with_sdes_mid.bin') for length in range(12, 16): with self.assertRaises(ValueError) as cm: RtpPacket.parse(data[0:length]) self.assertEqual(str(cm.exception), 'RTP packet has truncated extension profile / length') for length in range(16, 20): with self.assertRaises(ValueError) as cm: RtpPacket.parse(data[0:length]) self.assertEqual(str(cm.exception), 'RTP packet has truncated extension value')
def test_rtp_and_rtcp(self): transport, remote = dummy_dtls_transport_pair() receiver = RTCRtpReceiver('audio', transport) self.assertEqual(receiver.transport, transport) receiver._track = RemoteStreamTrack(kind='audio') self.assertEqual(receiver._track.readyState, 'live') run(receiver.receive(RTCRtpParameters(codecs=[PCMU_CODEC]))) # receive RTP packet = RtpPacket.parse(load('rtp.bin')) run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # receive RTCP SR for packet in RtcpPacket.parse(load('rtcp_sr.bin')): run(receiver._handle_rtcp_packet(packet)) # check stats report = run(receiver.getStats()) self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual(sorted(report.keys()), ['inbound-rtp', 'remote-outbound-rtp']) # check remote track frame = run(receiver._track.recv()) self.assertTrue(isinstance(frame, AudioFrame)) # shutdown run(receiver.stop()) self.assertEqual(receiver._track.readyState, 'ended')
def test_rtp_and_rtcp(self): transport, remote = dummy_dtls_transport_pair() receiver = RTCRtpReceiver('audio', transport) self.assertEqual(receiver.transport, transport) receiver._track = RemoteStreamTrack(kind='audio') run(receiver.receive(RTCRtpParameters(codecs=[PCMU_CODEC]))) # receive RTP packet = RtpPacket.parse(load('rtp.bin')) run(receiver._handle_rtp_packet(packet)) # receive RTCP for packet in RtcpPacket.parse(load('rtcp_sr.bin')): run(receiver._handle_rtcp_packet(packet)) self.assertEqual(sorted(receiver._stats.keys()), ['remote-inbound-rtp', 'remote-outbound-rtp']) # check remote track frame = run(receiver._track.recv()) self.assertTrue(isinstance(frame, AudioFrame)) # shutdown run(receiver.stop()) run(asyncio.sleep(0))
def test_padding_only_with_header_extensions(self): extensions_map = rtp.HeaderExtensionsMap() extensions_map.configure( RTCRtpParameters(headerExtensions=[ RTCRtpHeaderExtensionParameters( id=2, uri= "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", ) ])) data = load("rtp_only_padding_with_header_extensions.bin") packet = RtpPacket.parse(data, extensions_map) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 98) self.assertEqual(packet.sequence_number, 22138) self.assertEqual(packet.timestamp, 3171065731) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extensions, rtp.HeaderExtensions(abs_send_time=15846540)) self.assertEqual(len(packet.payload), 0) self.assertEqual(packet.padding_size, 224) serialized = packet.serialize(extensions_map) self.assertEqual(len(serialized), len(data)) self.assertEqual(serialized[0:20], data[0:20]) self.assertEqual(serialized[-1], data[-1])
def test_connection_error(self): """ Close the underlying transport before the receiver. """ transport, _ = dummy_dtls_transport_pair() receiver = RTCRtpReceiver('audio', transport) self.assertEqual(receiver.transport, transport) receiver._track = RemoteStreamTrack(kind='audio') receiver._ssrc = 1234 run(receiver.receive(RTCRtpParameters(codecs=[PCMU_CODEC]))) # receive a packet to prime RTCP packet = RtpPacket.parse(load('rtp.bin')) run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # break connection run(transport.stop()) # give RTCP time to send a report run(asyncio.sleep(2)) # shutdown run(receiver.stop())
def test_rtx(self): extensions_map = rtp.HeaderExtensionsMap() extensions_map.configure( RTCRtpParameters(headerExtensions=[ RTCRtpHeaderExtensionParameters( id=9, uri="urn:ietf:params:rtp-hdrext:sdes:mid") ])) data = load("rtp_with_sdes_mid.bin") packet = RtpPacket.parse(data, extensions_map) # wrap / unwrap RTX rtx = wrap_rtx(packet, payload_type=112, sequence_number=12345, ssrc=1234) recovered = unwrap_rtx(rtx, payload_type=111, ssrc=4084547440) # check roundtrip self.assertEqual(recovered.version, packet.version) self.assertEqual(recovered.marker, packet.marker) self.assertEqual(recovered.payload_type, packet.payload_type) self.assertEqual(recovered.sequence_number, packet.sequence_number) self.assertEqual(recovered.timestamp, packet.timestamp) self.assertEqual(recovered.ssrc, packet.ssrc) self.assertEqual(recovered.csrc, packet.csrc) self.assertEqual(recovered.extensions, packet.extensions) self.assertEqual(recovered.payload, packet.payload)
def test_rtp_and_rtcp(self): receiver = RTCRtpReceiver("audio", self.local_transport) self.assertEqual(receiver.transport, self.local_transport) receiver._track = RemoteStreamTrack(kind="audio") self.assertEqual(receiver._track.readyState, "live") run(receiver.receive(RTCRtpReceiveParameters(codecs=[PCMU_CODEC]))) # receive RTP for i in range(10): packet = RtpPacket.parse(load("rtp.bin")) packet.sequence_number += i packet.timestamp += i * 160 run(receiver._handle_rtp_packet(packet, arrival_time_ms=i * 20)) # receive RTCP SR for packet in RtcpPacket.parse(load("rtcp_sr.bin")): run(receiver._handle_rtcp_packet(packet)) # check stats report = run(receiver.getStats()) self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual( sorted([s.type for s in report.values()]), ["inbound-rtp", "remote-outbound-rtp", "transport"], ) # check sources sources = receiver.getSynchronizationSources() self.assertEqual(len(sources), 1) self.assertTrue(isinstance(sources[0], RTCRtpSynchronizationSource)) self.assertEqual(sources[0].source, 4028317929) # check remote track frame = run(receiver._track.recv()) self.assertEqual(frame.pts, 0) self.assertEqual(frame.sample_rate, 8000) self.assertEqual(frame.time_base, fractions.Fraction(1, 8000)) frame = run(receiver._track.recv()) self.assertEqual(frame.pts, 160) self.assertEqual(frame.sample_rate, 8000) self.assertEqual(frame.time_base, fractions.Fraction(1, 8000)) # shutdown run(receiver.stop()) # read until end with self.assertRaises(MediaStreamError): while True: run(receiver._track.recv()) self.assertEqual(receiver._track.readyState, "ended") # try reading again with self.assertRaises(MediaStreamError): run(receiver._track.recv())
def test_dtmf(self): data = load("rtp_dtmf.bin") packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 1) self.assertEqual(packet.payload_type, 101) self.assertEqual(packet.sequence_number, 24152) self.assertEqual(packet.timestamp, 4021352124) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extensions, rtp.HeaderExtensions()) self.assertEqual(len(packet.payload), 4) self.assertEqual(packet.serialize(), data)
def test_dtmf(self): data = load('rtp_dtmf.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.extension, 0) self.assertEqual(packet.marker, 1) self.assertEqual(packet.payload_type, 101) self.assertEqual(packet.sequence_number, 24152) self.assertEqual(packet.timestamp, 4021352124) self.assertEqual(packet.csrc, []) self.assertEqual(len(packet.payload), 4) self.assertEqual(bytes(packet), data)
def test_padding_only(self): data = load('rtp_only_padding.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.extension, 0) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 120) self.assertEqual(packet.sequence_number, 27759) self.assertEqual(packet.timestamp, 4044047131) self.assertEqual(packet.csrc, []) self.assertEqual(len(packet.payload), 0) self.assertEqual(bytes(packet), b'\x80' + data[1:12])
def test_with_csrc(self): data = load('rtp_with_csrc.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.extension, 0) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 0) self.assertEqual(packet.sequence_number, 16082) self.assertEqual(packet.timestamp, 144) self.assertEqual(packet.csrc, [2882400001, 3735928559]) self.assertEqual(len(packet.payload), 160) self.assertEqual(bytes(packet), data)
def test_with_csrc(self): data = load("rtp_with_csrc.bin") packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 0) self.assertEqual(packet.sequence_number, 16082) self.assertEqual(packet.timestamp, 144) self.assertEqual(packet.csrc, [2882400001, 3735928559]) self.assertEqual(packet.extensions, rtp.HeaderExtensions()) self.assertEqual(len(packet.payload), 160) self.assertEqual(packet.serialize(), data)
def test_map_header_extensions(self): data = bytearray([ 0x90, 0x64, 0x00, 0x58, 0x65, 0x43, 0x12, 0x78, 0x12, 0x34, 0x56, 0x78, # SSRC 0xbe, 0xde, 0x00, 0x08, # Extension of size 8x32bit words. 0x40, 0xda, # AudioLevel. 0x22, 0x01, 0x56, 0xce, # TransmissionOffset. 0x62, 0x12, 0x34, 0x56, # AbsoluteSendTime. 0x81, 0xce, 0xab, # TransportSequenceNumber. 0xa0, 0x03, # VideoRotation. 0xb2, 0x12, 0x48, 0x76, # PlayoutDelayLimits. 0xc2, 0x72, 0x74, 0x78, # RtpStreamId 0xd5, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, # RepairedRtpStreamId 0x00, 0x00, # Padding to 32bit boundary. ]) extensions_map = rtp.HeaderExtensionsMap() extensions_map.configure(RTCRtpParameters( headerExtensions=[ RTCRtpHeaderExtensionParameters( id=2, uri='urn:ietf:params:rtp-hdrext:toffset'), RTCRtpHeaderExtensionParameters( id=4, uri='urn:ietf:params:rtp-hdrext:ssrc-audio-level'), RTCRtpHeaderExtensionParameters( id=6, uri='http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time'), RTCRtpHeaderExtensionParameters( id=8, uri='http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01'), # noqa RTCRtpHeaderExtensionParameters( id=12, uri='urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id'), RTCRtpHeaderExtensionParameters( id=13, uri='urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id'), ])) packet = RtpPacket.parse(data, extensions_map) # check mapped values self.assertEqual(packet.extensions.abs_send_time, 0x123456) self.assertEqual(packet.extensions.audio_level, (True, 90)) self.assertEqual(packet.extensions.mid, None) self.assertEqual(packet.extensions.repaired_rtp_stream_id, 'stream') self.assertEqual(packet.extensions.rtp_stream_id, 'rtx') self.assertEqual(packet.extensions.transmission_offset, 0x156ce) self.assertEqual(packet.extensions.transport_sequence_number, 0xceab) # TODO: check packet.serialize(extensions_map)
def test_with_sdes_mid(self): data = load('rtp_with_sdes_mid.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 1) self.assertEqual(packet.payload_type, 111) self.assertEqual(packet.sequence_number, 14156) self.assertEqual(packet.timestamp, 1327210925) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extension_profile, 0xBEDE) self.assertEqual(packet.extension_value, b'\x900\x00\x00') self.assertEqual(len(packet.payload), 54) self.assertEqual(bytes(packet), data)
def test_rtp_and_rtcp(self): transport, remote = dummy_dtls_transport_pair() receiver = RTCRtpReceiver('audio', transport) self.assertEqual(receiver.transport, transport) receiver._track = RemoteStreamTrack(kind='audio') self.assertEqual(receiver._track.readyState, 'live') run(receiver.receive(RTCRtpParameters(codecs=[PCMU_CODEC]))) # receive RTP for i in range(10): packet = RtpPacket.parse(load('rtp.bin')) packet.sequence_number += i packet.timestamp += i * 160 run(receiver._handle_rtp_packet(packet, arrival_time_ms=i * 20)) # receive RTCP SR for packet in RtcpPacket.parse(load('rtcp_sr.bin')): run(receiver._handle_rtcp_packet(packet)) # check stats report = run(receiver.getStats()) self.assertTrue(isinstance(report, RTCStatsReport)) self.assertEqual(sorted([s.type for s in report.values()]), ['inbound-rtp', 'remote-outbound-rtp', 'transport']) # check remote track frame = run(receiver._track.recv()) self.assertEqual(frame.pts, 0) self.assertEqual(frame.sample_rate, 8000) self.assertEqual(frame.time_base, fractions.Fraction(1, 8000)) frame = run(receiver._track.recv()) self.assertEqual(frame.pts, 160) self.assertEqual(frame.sample_rate, 8000) self.assertEqual(frame.time_base, fractions.Fraction(1, 8000)) # shutdown run(receiver.stop()) # read until end with self.assertRaises(MediaStreamError): while True: run(receiver._track.recv()) self.assertEqual(receiver._track.readyState, 'ended') # try reading again with self.assertRaises(MediaStreamError): run(receiver._track.recv())
def test_no_ssrc(self): data = load('rtp.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 0) self.assertEqual(packet.sequence_number, 15743) self.assertEqual(packet.timestamp, 3937035252) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extensions, rtp.HeaderExtensions()) self.assertEqual(len(packet.payload), 160) self.assertEqual(packet.serialize(), data) self.assertEqual(repr(packet), 'RtpPacket(seq=15743, ts=3937035252, marker=0, payload=0, 160 bytes)')
def test_padding_only(self): data = load("rtp_only_padding.bin") packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 120) self.assertEqual(packet.sequence_number, 27759) self.assertEqual(packet.timestamp, 4044047131) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extensions, rtp.HeaderExtensions()) self.assertEqual(len(packet.payload), 0) self.assertEqual(packet.padding_size, 224) serialized = packet.serialize() self.assertEqual(len(serialized), len(data)) self.assertEqual(serialized[0:12], data[0:12]) self.assertEqual(serialized[-1], data[-1])
def test_padding_only(self): data = load('rtp_only_padding.bin') packet = RtpPacket.parse(data) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 0) self.assertEqual(packet.payload_type, 120) self.assertEqual(packet.sequence_number, 27759) self.assertEqual(packet.timestamp, 4044047131) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extension_profile, 0) self.assertEqual(packet.extension_value, None) self.assertEqual(len(packet.payload), 0) self.assertEqual(packet.padding_size, 224) serialized = bytes(packet) self.assertEqual(len(serialized), len(data)) self.assertEqual(serialized[0:12], data[0:12]) self.assertEqual(serialized[-1], data[-1])
async def test_connection_error(self): """ Close the underlying transport before the receiver. """ async with create_receiver("audio") as receiver: receiver._track = RemoteStreamTrack(kind="audio") receiver._set_rtcp_ssrc(1234) await receiver.receive(RTCRtpReceiveParameters(codecs=[PCMU_CODEC])) # receive a packet to prime RTCP packet = RtpPacket.parse(load("rtp.bin")) await receiver._handle_rtp_packet(packet, arrival_time_ms=0) # break connection await receiver.transport.stop() # give RTCP time to send a report await asyncio.sleep(2)
def test_with_sdes_mid(self): extensions_map = rtp.HeaderExtensionsMap() extensions_map.configure( RTCRtpParameters(headerExtensions=[ RTCRtpHeaderExtensionParameters( id=9, uri="urn:ietf:params:rtp-hdrext:sdes:mid") ])) data = load("rtp_with_sdes_mid.bin") packet = RtpPacket.parse(data, extensions_map) self.assertEqual(packet.version, 2) self.assertEqual(packet.marker, 1) self.assertEqual(packet.payload_type, 111) self.assertEqual(packet.sequence_number, 14156) self.assertEqual(packet.timestamp, 1327210925) self.assertEqual(packet.csrc, []) self.assertEqual(packet.extensions, rtp.HeaderExtensions(mid="0")) self.assertEqual(len(packet.payload), 54) self.assertEqual(packet.serialize(extensions_map), data)
def _crypt_packet(self, rtp_packet: bytes, encrypt: bool) -> RtpPacket: rtp_header = rtp_packet[:12] parsed = RtpPacket.parse(rtp_packet) if parsed.sequence_number < self.seq: self.roc += 1 self.seq = parsed.sequence_number pkt_i = SrtpContext.packet_index(self.roc, self.seq) iv = SrtpContext._calc_iv(self.session_keys.salt_key[2:], parsed.ssrc, pkt_i) if encrypt: transformed_payload = SrtpContext._encrypt(self.crypto_ctx, iv, parsed.payload, rtp_header) else: transformed_payload = SrtpContext._decrypt(self.crypto_ctx, iv, parsed.payload, rtp_header) parsed.payload = transformed_payload return parsed
def test_connection_error(self): """ Close the underlying transport before the receiver. """ receiver = RTCRtpReceiver("audio", self.local_transport) self.assertEqual(receiver.transport, self.local_transport) receiver._track = RemoteStreamTrack(kind="audio") receiver._set_rtcp_ssrc(1234) run(receiver.receive(RTCRtpReceiveParameters(codecs=[PCMU_CODEC]))) # receive a packet to prime RTCP packet = RtpPacket.parse(load("rtp.bin")) run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # break connection run(self.local_transport.stop()) # give RTCP time to send a report run(asyncio.sleep(2)) # shutdown run(receiver.stop())
def test_padding_too_long(self): data = load("rtp_only_padding.bin")[0:12] + b"\x02" with self.assertRaises(ValueError) as cm: RtpPacket.parse(data) self.assertEqual(str(cm.exception), "RTP packet padding length is invalid")
def test_bad_version(self): data = b'\xc0' + load('rtp.bin')[1:] with self.assertRaises(ValueError) as cm: RtpPacket.parse(data) self.assertEqual(str(cm.exception), 'RTP packet has invalid version')
def test_truncated(self): data = load('rtp.bin')[0:11] with self.assertRaises(ValueError) as cm: RtpPacket.parse(data) self.assertEqual(str(cm.exception), 'RTP packet length is less than 12 bytes')
async def _send_rtp(self, data): if not is_rtcp(data): packet = RtpPacket.parse(data) await self.queue.put(packet)
def test_padding_zero(self): data = load('rtp_only_padding.bin')[0:12] + b'\x00' with self.assertRaises(ValueError) as cm: RtpPacket.parse(data) self.assertEqual(str(cm.exception), 'RTP packet padding length is invalid')
async def mock_send_rtp(data): if not is_rtcp(data): await queue.put(RtpPacket.parse(data))