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_rtp_rtx(self): receiver = RTCRtpReceiver('video', self.local_transport) self.assertEqual(receiver.transport, self.local_transport) receiver._track = RemoteStreamTrack(kind='video') run( receiver.receive( RTCRtpReceiveParameters( codecs=[ VP8_CODEC, RTCRtpCodecParameters(mimeType='video/rtx', clockRate=90000, payloadType=101, parameters={'apt': 100}), ], encodings=[ RTCRtpEncodingParameters( ssrc=1234, payloadType=100, rtx=RTCRtpRtxParameters(ssrc=2345)) ]))) # receive RTX with payload packet = RtpPacket(payload_type=101, ssrc=2345, payload=b'\x00\x00') run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # receive RTX without payload packet = RtpPacket(payload_type=101, ssrc=2345) run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # shutdown run(receiver.stop())
async def test_rtp_rtx(self): async with create_receiver("video") as receiver: receiver._track = RemoteStreamTrack(kind="video") await receiver.receive( RTCRtpReceiveParameters( codecs=[ VP8_CODEC, RTCRtpCodecParameters( mimeType="video/rtx", clockRate=90000, payloadType=101, parameters={"apt": 100}, ), ], encodings=[ RTCRtpEncodingParameters( ssrc=1234, payloadType=100, rtx=RTCRtpRtxParameters(ssrc=2345), ) ], ) ) # receive RTX with payload packet = RtpPacket(payload_type=101, ssrc=2345, payload=b"\x00\x00") await receiver._handle_rtp_packet(packet, arrival_time_ms=0) # receive RTX without payload packet = RtpPacket(payload_type=101, ssrc=2345) await receiver._handle_rtp_packet(packet, arrival_time_ms=0)
def test_add_ordered(self): jbuffer = JitterBuffer(capacity=4) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=0, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [0, None, None, None]) self.assertEqual(jbuffer._origin, 0) self.assertFalse(pli_flag) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=1, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [0, 1, None, None]) self.assertEqual(jbuffer._origin, 0) self.assertFalse(pli_flag) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=2, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [0, 1, 2, None]) self.assertEqual(jbuffer._origin, 0) self.assertFalse(pli_flag) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=3, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [0, 1, 2, 3]) self.assertEqual(jbuffer._origin, 0) self.assertFalse(pli_flag)
def test_add_seq_too_high_reset(self): jbuffer = JitterBuffer(capacity=4) jbuffer.add(RtpPacket(sequence_number=0, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) self.assertPackets(jbuffer, [0, None, None, None]) jbuffer.add(RtpPacket(sequence_number=3000, timestamp=1234)) self.assertEqual(jbuffer._origin, 3000) self.assertPackets(jbuffer, [3000, None, None, None])
def test_add_seq_too_low_reset(self): jbuffer = JitterBuffer(capacity=4) frame = jbuffer.add(RtpPacket(sequence_number=2000, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [2000, None, None, None]) self.assertEqual(jbuffer._origin, 2000) frame = jbuffer.add(RtpPacket(sequence_number=1, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, 1, None, None]) self.assertEqual(jbuffer._origin, 1)
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_smart_remove(self): jbuffer = JitterBuffer(capacity=4) jbuffer.add(RtpPacket(sequence_number=0, timestamp=1234)) jbuffer.add(RtpPacket(sequence_number=1, timestamp=1234)) jbuffer.add(RtpPacket(sequence_number=3, timestamp=1235)) self.assertEqual(jbuffer._origin, 0) self.assertPackets(jbuffer, [0, 1, None, 3]) # remove 1 packet jbuffer.smart_remove(1) self.assertEqual(jbuffer._origin, 3) self.assertPackets(jbuffer, [None, None, None, 3])
def test_get_header_extensions(self): packet = RtpPacket() # none self.assertEqual(get_header_extensions(packet), []) # one-byte, single value packet.extension_profile = 0xBEDE packet.extension_value = b'\x900\x00\x00' self.assertEqual(get_header_extensions(packet), [ (9, b'0'), ]) # one-byte, two values packet.extension_profile = 0xBEDE packet.extension_value = b'\x10\xc18sdparta_0' self.assertEqual(get_header_extensions(packet), [ (1, b'\xc1'), (3, b'sdparta_0'), ]) # two-byte, single value packet.extension_profile = 0x1000 packet.extension_value = b'\xff\x010\x00' self.assertEqual(get_header_extensions(packet), [ (255, b'0'), ])
def test_add_seq_too_low_drop(self): jbuffer = JitterBuffer(capacity=4) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=2, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, None, 2, None]) self.assertEqual(jbuffer._origin, 2) self.assertFalse(pli_flag) pli_flag, frame = jbuffer.add( RtpPacket(sequence_number=1, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, None, 2, None]) self.assertEqual(jbuffer._origin, 2) self.assertFalse(pli_flag)
def test_add_seq_too_high_discard_four(self): jbuffer = JitterBuffer(capacity=4) jbuffer.add(RtpPacket(sequence_number=0, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=1, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=3, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=7, timestamp=1235)) self.assertEqual(jbuffer._origin, 7) self.assertPackets(jbuffer, [None, None, None, 7])
def test_add_seq_too_high_discard_one_v2(self): jbuffer = JitterBuffer(capacity=4) jbuffer.add(RtpPacket(sequence_number=0, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=2, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=3, timestamp=1235)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=4, timestamp=1235)) self.assertEqual(jbuffer._origin, 3) self.assertPackets(jbuffer, [4, None, None, 3])
def test_rtp_rtx_unknown_ssrc(self): receiver = RTCRtpReceiver("video", self.local_transport) self.assertEqual(receiver.transport, self.local_transport) receiver._track = RemoteStreamTrack(kind="video") run( receiver.receive( RTCRtpReceiveParameters( codecs=[ VP8_CODEC, RTCRtpCodecParameters( mimeType="video/rtx", clockRate=90000, payloadType=101, parameters={"apt": 100}, ), ] ) ) ) # receive RTX with unknown SSRC packet = RtpPacket(payload_type=101, ssrc=1234) run(receiver._handle_rtp_packet(packet, arrival_time_ms=0)) # shutdown run(receiver.stop())
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_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_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_set_header_extensions(self): packet = RtpPacket() # none set_header_extensions(packet, {}) self.assertEqual(packet.extension_profile, 0) self.assertEqual(packet.extension_value, None) # one-byte, single value set_header_extensions(packet, [ (9, b'0'), ]) self.assertEqual(packet.extension_profile, 0xBEDE) self.assertEqual(packet.extension_value, b'\x900\x00\x00') # one-byte, two values set_header_extensions(packet, [ (1, b'\xc1'), (3, b'sdparta_0'), ]) self.assertEqual(packet.extension_profile, 0xBEDE) self.assertEqual(packet.extension_value, b'\x10\xc18sdparta_0') # two-byte, single value set_header_extensions(packet, [ (255, b'0'), ]) self.assertEqual(packet.extension_profile, 0x1000) self.assertEqual(packet.extension_value, b'\xff\x010\x00')
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 create_rtp_video_packets(self, codec, frames, seq=0): encoder = get_encoder(codec) packets = [] for frame in self.create_video_frames(width=640, height=480, count=frames): payloads, timestamp = encoder.encode(frame) self.assertEqual(len(payloads), 1) packet = RtpPacket(payload_type=codec.payloadType, sequence_number=seq, ssrc=1234, timestamp=timestamp) packet.payload = payloads[0] packet.marker = 1 packets.append(packet) seq = uint16_add(seq, 1) return packets
def create_packets(self, count, seq=0): packets = [] for i in range(count): packets.append( RtpPacket(payload_type=0, sequence_number=(seq + i) % RTP_SEQ_MODULO, timestamp=i * 160)) return packets
def test_add_unordered(self): jbuffer = JitterBuffer(capacity=4) frame = jbuffer.add(RtpPacket(sequence_number=1, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, 1, None, None]) self.assertEqual(jbuffer._origin, 1) frame = jbuffer.add(RtpPacket(sequence_number=3, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, 1, None, 3]) self.assertEqual(jbuffer._origin, 1) frame = jbuffer.add(RtpPacket(sequence_number=2, timestamp=1234)) self.assertIsNone(frame) self.assertPackets(jbuffer, [None, 1, 2, 3]) self.assertEqual(jbuffer._origin, 1)
def create_rtp_packets(count, seq=0): packets = [] for i in range(count): packets.append(RtpPacket( payload_type=0, sequence_number=uint16_add(seq, i), ssrc=1234, timestamp=i * 160)) return packets
async def test_rtp_unknown_payload_type(self): async with create_receiver("video") as receiver: receiver._track = RemoteStreamTrack(kind="video") await receiver.receive(RTCRtpReceiveParameters(codecs=[VP8_CODEC])) # receive RTP with unknown payload type packet = RtpPacket(payload_type=123) await receiver._handle_rtp_packet(packet, arrival_time_ms=0)
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_add_seq_too_high_discard_more(self): jbuffer = JitterBuffer(capacity=4) jbuffer.add(RtpPacket(sequence_number=0, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=1, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=2, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=3, timestamp=1234)) self.assertEqual(jbuffer._origin, 0) jbuffer.add(RtpPacket(sequence_number=8, timestamp=1234)) self.assertEqual(jbuffer._origin, 8) self.assertPackets(jbuffer, [8, None, None, None])
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_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_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_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)