rtpsynth 1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rtpsynth-1.0/LICENSE +25 -0
- rtpsynth-1.0/MANIFEST.in +3 -0
- rtpsynth-1.0/PKG-INFO +77 -0
- rtpsynth-1.0/README.md +63 -0
- rtpsynth-1.0/python/RtpJBuf.py +162 -0
- rtpsynth-1.0/python/RtpSynth.py +118 -0
- rtpsynth-1.0/python/__init__.py +0 -0
- rtpsynth-1.0/python/env.py +24 -0
- rtpsynth-1.0/rtpsynth.egg-info/PKG-INFO +77 -0
- rtpsynth-1.0/rtpsynth.egg-info/SOURCES.txt +24 -0
- rtpsynth-1.0/rtpsynth.egg-info/dependency_links.txt +1 -0
- rtpsynth-1.0/rtpsynth.egg-info/top_level.txt +2 -0
- rtpsynth-1.0/setup/RunCTest.py +27 -0
- rtpsynth-1.0/setup/__init__.py +0 -0
- rtpsynth-1.0/setup.cfg +4 -0
- rtpsynth-1.0/setup.py +62 -0
- rtpsynth-1.0/src/Symbol.map +18 -0
- rtpsynth-1.0/src/rsth_timeops.h +22 -0
- rtpsynth-1.0/src/rtp.c +225 -0
- rtpsynth-1.0/src/rtp.h +141 -0
- rtpsynth-1.0/src/rtp_info.h +40 -0
- rtpsynth-1.0/src/rtpjbuf.c +272 -0
- rtpsynth-1.0/src/rtpjbuf.h +38 -0
- rtpsynth-1.0/src/rtpsynth.c +172 -0
- rtpsynth-1.0/src/rtpsynth.h +42 -0
- rtpsynth-1.0/tests/test_jbuf.py +96 -0
rtpsynth-1.0/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018, Sippy Labs
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
rtpsynth-1.0/MANIFEST.in
ADDED
rtpsynth-1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rtpsynth
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Library optimized to generate/process sequence of the RTP packets
|
|
5
|
+
Home-page: https://github.com/sippy/librtpsynth.git
|
|
6
|
+
Author: Maksym Sobolyev
|
|
7
|
+
Author-email: sobomax@gmail.com
|
|
8
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
9
|
+
Classifier: Operating System :: POSIX
|
|
10
|
+
Classifier: Programming Language :: C
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
# librtpsynth
|
|
16
|
+
|
|
17
|
+
Low-level library optimized to generate and/or consume sequence of the RTP
|
|
18
|
+
packets. Based on some code and ideas from the RTPProxy projects.
|
|
19
|
+
|
|
20
|
+
Originally designed to supplement Python code to do low-level bits shuffling
|
|
21
|
+
for the proof of concept IoT implementation.
|
|
22
|
+
|
|
23
|
+
Reasonably fast, 100-200x real-time (i.e. 100-200K packets per second) when
|
|
24
|
+
used from the Python code. 100x of that (10-20M PPS) if used from C code
|
|
25
|
+
directly.
|
|
26
|
+
|
|
27
|
+
## RTP Generation
|
|
28
|
+
|
|
29
|
+
Generate continuous sequence of RTP packets of the same payload type.
|
|
30
|
+
|
|
31
|
+
### C API Functions
|
|
32
|
+
|
|
33
|
+
`#include <rtpsynth.h>`
|
|
34
|
+
|
|
35
|
+
- `void *rsynth_ctor(int srate, int ptime);`
|
|
36
|
+
Initializes the RTP synthesizer with given sample rate and packet time.
|
|
37
|
+
Returns a handle to be used in other calls.
|
|
38
|
+
|
|
39
|
+
- `void *rsynth_next_pkt(void *ri, int plen, int pt);`
|
|
40
|
+
Generates the next RTP packet. Takes the handle, packet length, and
|
|
41
|
+
payload type as parameters. Returns a pointer to the generated packet.
|
|
42
|
+
|
|
43
|
+
- `int rsynth_next_pkt_pa(void *ri, int plen, int pt, char *buf, unsigned int blen, int pa);`
|
|
44
|
+
Similar to `rsynth_next_pkt` but allows pre-allocated buffer and packet
|
|
45
|
+
attributes. Returns the length of the packet generated.
|
|
46
|
+
|
|
47
|
+
- `void rsynth_pkt_free(void *rnp);`
|
|
48
|
+
Frees the allocated packet. Takes a pointer to the packet as parameter.
|
|
49
|
+
|
|
50
|
+
- `void rsynth_dtor(void *ri);`
|
|
51
|
+
Destroys the RTP synthesizer and frees resources. Takes the handle as
|
|
52
|
+
parameter.
|
|
53
|
+
|
|
54
|
+
- `unsigned int rsynth_set_mbt(void *ri, unsigned int new_st);`
|
|
55
|
+
Sets a new marker bit toggle state. Takes the handle and the new state
|
|
56
|
+
as parameters. Returns the old state.
|
|
57
|
+
|
|
58
|
+
- `void rsynth_resync(void *ri, struct rsynth_seq *rsp);`
|
|
59
|
+
Resynchronizes the RTP packet sequence. Takes the handle and optionally
|
|
60
|
+
a sequence structure as parameters. Use this function when a time
|
|
61
|
+
discontinuity is expected in packet generation, such as when VAD (Voice
|
|
62
|
+
Activity Detection) is active. The library will recalculate the timestamp
|
|
63
|
+
for the next packet based on the current system clock and the time the
|
|
64
|
+
last packet was generated.
|
|
65
|
+
|
|
66
|
+
### RtpGen (Python)
|
|
67
|
+
|
|
68
|
+
## RTP Parser & Validator / Jitter Buffer
|
|
69
|
+
|
|
70
|
+
Simple RTP parser and validator to process incoming UDP datagrams,
|
|
71
|
+
parse & validate RTP headers. Resulting RTP stream is passed through
|
|
72
|
+
fixed-size jitter buffer to de-duplicate and re-order packets if
|
|
73
|
+
needed.
|
|
74
|
+
|
|
75
|
+
### rtpjbuf (c)
|
|
76
|
+
|
|
77
|
+
### RtpJBuf (Python)
|
rtpsynth-1.0/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# librtpsynth
|
|
2
|
+
|
|
3
|
+
Low-level library optimized to generate and/or consume sequence of the RTP
|
|
4
|
+
packets. Based on some code and ideas from the RTPProxy projects.
|
|
5
|
+
|
|
6
|
+
Originally designed to supplement Python code to do low-level bits shuffling
|
|
7
|
+
for the proof of concept IoT implementation.
|
|
8
|
+
|
|
9
|
+
Reasonably fast, 100-200x real-time (i.e. 100-200K packets per second) when
|
|
10
|
+
used from the Python code. 100x of that (10-20M PPS) if used from C code
|
|
11
|
+
directly.
|
|
12
|
+
|
|
13
|
+
## RTP Generation
|
|
14
|
+
|
|
15
|
+
Generate continuous sequence of RTP packets of the same payload type.
|
|
16
|
+
|
|
17
|
+
### C API Functions
|
|
18
|
+
|
|
19
|
+
`#include <rtpsynth.h>`
|
|
20
|
+
|
|
21
|
+
- `void *rsynth_ctor(int srate, int ptime);`
|
|
22
|
+
Initializes the RTP synthesizer with given sample rate and packet time.
|
|
23
|
+
Returns a handle to be used in other calls.
|
|
24
|
+
|
|
25
|
+
- `void *rsynth_next_pkt(void *ri, int plen, int pt);`
|
|
26
|
+
Generates the next RTP packet. Takes the handle, packet length, and
|
|
27
|
+
payload type as parameters. Returns a pointer to the generated packet.
|
|
28
|
+
|
|
29
|
+
- `int rsynth_next_pkt_pa(void *ri, int plen, int pt, char *buf, unsigned int blen, int pa);`
|
|
30
|
+
Similar to `rsynth_next_pkt` but allows pre-allocated buffer and packet
|
|
31
|
+
attributes. Returns the length of the packet generated.
|
|
32
|
+
|
|
33
|
+
- `void rsynth_pkt_free(void *rnp);`
|
|
34
|
+
Frees the allocated packet. Takes a pointer to the packet as parameter.
|
|
35
|
+
|
|
36
|
+
- `void rsynth_dtor(void *ri);`
|
|
37
|
+
Destroys the RTP synthesizer and frees resources. Takes the handle as
|
|
38
|
+
parameter.
|
|
39
|
+
|
|
40
|
+
- `unsigned int rsynth_set_mbt(void *ri, unsigned int new_st);`
|
|
41
|
+
Sets a new marker bit toggle state. Takes the handle and the new state
|
|
42
|
+
as parameters. Returns the old state.
|
|
43
|
+
|
|
44
|
+
- `void rsynth_resync(void *ri, struct rsynth_seq *rsp);`
|
|
45
|
+
Resynchronizes the RTP packet sequence. Takes the handle and optionally
|
|
46
|
+
a sequence structure as parameters. Use this function when a time
|
|
47
|
+
discontinuity is expected in packet generation, such as when VAD (Voice
|
|
48
|
+
Activity Detection) is active. The library will recalculate the timestamp
|
|
49
|
+
for the next packet based on the current system clock and the time the
|
|
50
|
+
last packet was generated.
|
|
51
|
+
|
|
52
|
+
### RtpGen (Python)
|
|
53
|
+
|
|
54
|
+
## RTP Parser & Validator / Jitter Buffer
|
|
55
|
+
|
|
56
|
+
Simple RTP parser and validator to process incoming UDP datagrams,
|
|
57
|
+
parse & validate RTP headers. Resulting RTP stream is passed through
|
|
58
|
+
fixed-size jitter buffer to de-duplicate and re-order packets if
|
|
59
|
+
needed.
|
|
60
|
+
|
|
61
|
+
### rtpjbuf (c)
|
|
62
|
+
|
|
63
|
+
### RtpJBuf (Python)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from ctypes import c_void_p, c_int, c_size_t, c_uint32, c_uint16, c_uint64, \
|
|
2
|
+
POINTER, Union, Structure, create_string_buffer, addressof, string_at, cast
|
|
3
|
+
|
|
4
|
+
from .RtpSynth import _rsth
|
|
5
|
+
|
|
6
|
+
class RTPInfo(Structure):
|
|
7
|
+
_fields_ = [
|
|
8
|
+
("data_size", c_size_t),
|
|
9
|
+
("data_offset", c_int),
|
|
10
|
+
("nsamples", c_int),
|
|
11
|
+
("ts", c_uint32),
|
|
12
|
+
("seq", c_uint16),
|
|
13
|
+
("ssrc", c_uint32),
|
|
14
|
+
("appendable", c_int),
|
|
15
|
+
("rtp_profile", c_void_p) # replace with actual type
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
class RTPPacket(Structure):
|
|
19
|
+
_fields_ = [
|
|
20
|
+
("info", RTPInfo),
|
|
21
|
+
("lseq", c_uint64),
|
|
22
|
+
("data", c_void_p)
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
class ERSFrame(Structure):
|
|
26
|
+
_fields_ = [
|
|
27
|
+
("lseq_start", c_uint64),
|
|
28
|
+
("lseq_end", c_uint64),
|
|
29
|
+
("ts_diff", c_uint32),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
class RTPFrameType():
|
|
33
|
+
RTP = 0
|
|
34
|
+
ERS = 1
|
|
35
|
+
|
|
36
|
+
class RTPFrameUnion(Union):
|
|
37
|
+
_fields_ = [
|
|
38
|
+
("rtp", RTPPacket),
|
|
39
|
+
("ers", ERSFrame)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
class RTPFrame(Structure):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
RTPFrame._fields_ = [
|
|
46
|
+
("type", c_int),
|
|
47
|
+
("frame", RTPFrameUnion),
|
|
48
|
+
("next", POINTER(RTPFrame))
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
class RJBUdpInR(Structure):
|
|
52
|
+
_fields_ = [
|
|
53
|
+
("error", c_int),
|
|
54
|
+
("ready", POINTER(RTPFrame)),
|
|
55
|
+
("drop", POINTER(RTPFrame)),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
_rsth.rtpjbuf_ctor.argtypes = [c_int]
|
|
59
|
+
_rsth.rtpjbuf_ctor.restype = c_void_p
|
|
60
|
+
_rsth.rtpjbuf_dtor.argtypes = [c_void_p,]
|
|
61
|
+
_rsth.rtpjbuf_frame_dtor.argtypes = [c_void_p,]
|
|
62
|
+
_rsth.rtpjbuf_udp_in.argtypes = [c_void_p, c_void_p, c_size_t]
|
|
63
|
+
_rsth.rtpjbuf_udp_in.restype = RJBUdpInR
|
|
64
|
+
_rsth.rtpjbuf_flush.argtypes = [c_void_p]
|
|
65
|
+
_rsth.rtpjbuf_flush.restype = RJBUdpInR
|
|
66
|
+
|
|
67
|
+
class ERSFrame():
|
|
68
|
+
lseq_start: int
|
|
69
|
+
lseq_end: int
|
|
70
|
+
ts_diff: int
|
|
71
|
+
type = RTPFrameType.ERS
|
|
72
|
+
|
|
73
|
+
def __init__(self, content):
|
|
74
|
+
self.lseq_start = content.frame.ers.lseq_start
|
|
75
|
+
self.lseq_end = content.frame.ers.lseq_end
|
|
76
|
+
self.ts_diff = content.frame.ers.ts_diff
|
|
77
|
+
|
|
78
|
+
class RTPParseError(Exception):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
class FrameWrapper():
|
|
82
|
+
_rsth = None
|
|
83
|
+
content = None
|
|
84
|
+
data = None
|
|
85
|
+
rtp_data: bytes
|
|
86
|
+
|
|
87
|
+
def __init__(self, _rsth, content: RTPFrame, data):
|
|
88
|
+
self._rsth = _rsth
|
|
89
|
+
if content.type == RTPFrameType.ERS:
|
|
90
|
+
self.content = ERSFrame(content)
|
|
91
|
+
else:
|
|
92
|
+
self.content = content
|
|
93
|
+
rtp_data = cast(content.frame.rtp.data + content.frame.rtp.info.data_offset, c_void_p)
|
|
94
|
+
self.rtp_data = string_at(rtp_data, content.frame.rtp.info.data_size)
|
|
95
|
+
self.data = data
|
|
96
|
+
|
|
97
|
+
def __del__(self):
|
|
98
|
+
if self.content.type == RTPFrameType.RTP:
|
|
99
|
+
self._rsth.rtpjbuf_frame_dtor(addressof(self.content))
|
|
100
|
+
|
|
101
|
+
def __str__(self):
|
|
102
|
+
if self.content.type == RTPFrameType.RTP:
|
|
103
|
+
return f'RTP_Frame(seq={self.content.frame.rtp.lseq})'
|
|
104
|
+
return f'RTP_Erasure(seq_range={self.content.lseq_start} ' + \
|
|
105
|
+
f'-- {self.content.lseq_end})'
|
|
106
|
+
|
|
107
|
+
def __repr__(self):
|
|
108
|
+
return self.__str__()
|
|
109
|
+
|
|
110
|
+
RTP_PARSER_OK = 1
|
|
111
|
+
|
|
112
|
+
class RtpJBuf(object):
|
|
113
|
+
_hndl = None
|
|
114
|
+
_rsth = None
|
|
115
|
+
_ref_cache = None
|
|
116
|
+
|
|
117
|
+
def __init__(self, capacity):
|
|
118
|
+
self._rsth = _rsth
|
|
119
|
+
self._hndl = self._rsth.rtpjbuf_ctor(capacity)
|
|
120
|
+
if not bool(self._hndl):
|
|
121
|
+
raise Exception('rtpjbuf_ctor() failed')
|
|
122
|
+
self._ref_cache = {}
|
|
123
|
+
|
|
124
|
+
def udp_in(self, data):
|
|
125
|
+
buffer = create_string_buffer(data)
|
|
126
|
+
size = len(data)
|
|
127
|
+
rval = self._rsth.rtpjbuf_udp_in(self._hndl, buffer, size)
|
|
128
|
+
return self._proc_RJBUdpInR(rval, (buffer, data))
|
|
129
|
+
|
|
130
|
+
def _proc_RJBUdpInR(self, rval, bdata = None):
|
|
131
|
+
if rval.error != 0:
|
|
132
|
+
if rval.error < RTP_PARSER_OK:
|
|
133
|
+
raise RTPParseError(f'rtpjbuf_udp_in(): error {rval.error}')
|
|
134
|
+
raise RuntimeError(f'rtpjbuf_udp_in(): error {rval.error}')
|
|
135
|
+
if bdata is not None:
|
|
136
|
+
self._ref_cache[addressof(bdata[0])] = bdata
|
|
137
|
+
ready = []
|
|
138
|
+
for i, bucket in enumerate((rval.ready, rval.drop)):
|
|
139
|
+
while bool(bucket):
|
|
140
|
+
current = bucket.contents
|
|
141
|
+
if current.type == RTPFrameType.RTP:
|
|
142
|
+
buffer, data = self._ref_cache.pop(current.frame.rtp.data)
|
|
143
|
+
else:
|
|
144
|
+
data = None
|
|
145
|
+
if i == 0:
|
|
146
|
+
ready.append(FrameWrapper(self._rsth, current, data))
|
|
147
|
+
else:
|
|
148
|
+
assert current.type == RTPFrameType.RTP
|
|
149
|
+
self._rsth.rtpjbuf_frame_dtor(addressof(current))
|
|
150
|
+
#print(current.frame.rtp.data, addressof(buffer))
|
|
151
|
+
bucket = current.next
|
|
152
|
+
return ready
|
|
153
|
+
|
|
154
|
+
def flush(self):
|
|
155
|
+
rval = self._rsth.rtpjbuf_flush(self._hndl)
|
|
156
|
+
rval = self._proc_RJBUdpInR(rval)
|
|
157
|
+
assert len(self._ref_cache.keys()) == 0
|
|
158
|
+
return rval
|
|
159
|
+
|
|
160
|
+
def __del__(self):
|
|
161
|
+
if bool(self._hndl):
|
|
162
|
+
self._rsth.rtpjbuf_dtor(self._hndl)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Copyright (c) 2018 Sippy Software, Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
|
5
|
+
#
|
|
6
|
+
# * Redistributions of source code must retain the above copyright notice, this
|
|
7
|
+
# list of conditions and the following disclaimer.
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
# and/or other materials provided with the distribution.
|
|
12
|
+
#
|
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
14
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
15
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
17
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
18
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
19
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
20
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
21
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
22
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
23
|
+
|
|
24
|
+
from ctypes import cdll, c_void_p, c_int, create_string_buffer, c_uint
|
|
25
|
+
from math import modf
|
|
26
|
+
import sys, os, site, sysconfig
|
|
27
|
+
|
|
28
|
+
from .env import RSTH_MOD_NAME
|
|
29
|
+
|
|
30
|
+
_esuf = sysconfig.get_config_var('EXT_SUFFIX')
|
|
31
|
+
if not _esuf:
|
|
32
|
+
_esuf = '.so'
|
|
33
|
+
try:
|
|
34
|
+
import pathlib
|
|
35
|
+
_ROOT = str(pathlib.Path(__file__).parent.absolute())
|
|
36
|
+
except ImportError:
|
|
37
|
+
_ROOT = os.path.abspath(os.path.dirname(__file__))
|
|
38
|
+
#print('ROOT: ' + str(_ROOT))
|
|
39
|
+
modloc = site.getsitepackages()
|
|
40
|
+
modloc.insert(0, os.path.join(_ROOT, ".."))
|
|
41
|
+
for p in modloc:
|
|
42
|
+
try:
|
|
43
|
+
#print("Trying %s" % os.path.join(p, RSTH_MOD_NAME + _esuf))
|
|
44
|
+
_rsth = cdll.LoadLibrary(os.path.join(p, RSTH_MOD_NAME + _esuf))
|
|
45
|
+
except:
|
|
46
|
+
continue
|
|
47
|
+
break
|
|
48
|
+
else:
|
|
49
|
+
_rsth = cdll.LoadLibrary('librtpsynth.so')
|
|
50
|
+
|
|
51
|
+
_rsth.rsynth_ctor.argtypes = [c_int, c_int]
|
|
52
|
+
_rsth.rsynth_ctor.restype = c_void_p
|
|
53
|
+
_rsth.rsynth_dtor.argtypes = [c_void_p,]
|
|
54
|
+
_rsth.rsynth_next_pkt.restype = c_void_p
|
|
55
|
+
_rsth.rsynth_next_pkt.argtypes = [c_void_p, c_int, c_int]
|
|
56
|
+
_rsth.rsynth_next_pkt_pa.restype = c_int
|
|
57
|
+
_rsth.rsynth_next_pkt_pa.argtypes = [c_void_p, c_int, c_int, c_void_p, c_uint, c_int]
|
|
58
|
+
_rsth.rsynth_set_mbt.argtypes = [c_void_p, c_uint]
|
|
59
|
+
_rsth.rsynth_set_mbt.restype = c_uint
|
|
60
|
+
_rsth.rsynth_resync.argtypes = [c_void_p, c_void_p]
|
|
61
|
+
_rsth.rsynth_skip.argtypes = [c_void_p, c_int]
|
|
62
|
+
|
|
63
|
+
_rsth.rsynth_pkt_free.argtypes = [c_void_p,]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RtpSynth(object):
|
|
67
|
+
_hndl = None
|
|
68
|
+
_rsth = None
|
|
69
|
+
|
|
70
|
+
def __init__(self, srate, ptime):
|
|
71
|
+
self._rsth = _rsth
|
|
72
|
+
self._hndl = self._rsth.rsynth_ctor(srate, ptime)
|
|
73
|
+
if not bool(self._hndl):
|
|
74
|
+
raise Exception('rsynth_ctor() failed')
|
|
75
|
+
|
|
76
|
+
def next_pkt(self, plen, pt, pload = None):
|
|
77
|
+
pktlen = plen + 32
|
|
78
|
+
if pload != None:
|
|
79
|
+
pkt = create_string_buffer(pload, pktlen)
|
|
80
|
+
filled = 1
|
|
81
|
+
else:
|
|
82
|
+
pkt = create_string_buffer(pktlen)
|
|
83
|
+
filled = 0
|
|
84
|
+
plen = self._rsth.rsynth_next_pkt_pa(self._hndl, plen, pt, pkt, pktlen, filled)
|
|
85
|
+
return (pkt.raw[:plen])
|
|
86
|
+
|
|
87
|
+
def pkt_free(self, pkt):
|
|
88
|
+
self._rsth.rsynth_pkt_free(pkt)
|
|
89
|
+
|
|
90
|
+
def set_mbt(self, mbt):
|
|
91
|
+
return self._rsth.rsynth_set_mbt(self._hndl, mbt)
|
|
92
|
+
|
|
93
|
+
def resync(self):
|
|
94
|
+
self._rsth.rsynth_resync(self._hndl, None)
|
|
95
|
+
|
|
96
|
+
def skip(self, n):
|
|
97
|
+
self._rsth.rsynth_skip(self._hndl, n)
|
|
98
|
+
|
|
99
|
+
def __del__(self):
|
|
100
|
+
if bool(self._hndl):
|
|
101
|
+
self._rsth.rsynth_dtor(self._hndl)
|
|
102
|
+
|
|
103
|
+
if __name__ == '__main__':
|
|
104
|
+
from time import monotonic
|
|
105
|
+
i = 0
|
|
106
|
+
rs = RtpSynth(8000, 30)
|
|
107
|
+
stime = monotonic()
|
|
108
|
+
while monotonic() - stime < 5.0:
|
|
109
|
+
if i % 43 == 0:
|
|
110
|
+
rs.resync()
|
|
111
|
+
rp = rs.next_pkt(170, 0)
|
|
112
|
+
i += 1
|
|
113
|
+
if i % 4242 == 0:
|
|
114
|
+
res = rs.set_mbt(1)
|
|
115
|
+
assert res == 0
|
|
116
|
+
res = rs.set_mbt(0)
|
|
117
|
+
assert res == 1
|
|
118
|
+
del rs
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copyright (c) 2023 Sippy Software, Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
|
5
|
+
#
|
|
6
|
+
# * Redistributions of source code must retain the above copyright notice, this
|
|
7
|
+
# list of conditions and the following disclaimer.
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
# and/or other materials provided with the distribution.
|
|
12
|
+
#
|
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
14
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
15
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
17
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
18
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
19
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
20
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
21
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
22
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
23
|
+
|
|
24
|
+
RSTH_MOD_NAME = '_rtpsynth'
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rtpsynth
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Library optimized to generate/process sequence of the RTP packets
|
|
5
|
+
Home-page: https://github.com/sippy/librtpsynth.git
|
|
6
|
+
Author: Maksym Sobolyev
|
|
7
|
+
Author-email: sobomax@gmail.com
|
|
8
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
9
|
+
Classifier: Operating System :: POSIX
|
|
10
|
+
Classifier: Programming Language :: C
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
# librtpsynth
|
|
16
|
+
|
|
17
|
+
Low-level library optimized to generate and/or consume sequence of the RTP
|
|
18
|
+
packets. Based on some code and ideas from the RTPProxy projects.
|
|
19
|
+
|
|
20
|
+
Originally designed to supplement Python code to do low-level bits shuffling
|
|
21
|
+
for the proof of concept IoT implementation.
|
|
22
|
+
|
|
23
|
+
Reasonably fast, 100-200x real-time (i.e. 100-200K packets per second) when
|
|
24
|
+
used from the Python code. 100x of that (10-20M PPS) if used from C code
|
|
25
|
+
directly.
|
|
26
|
+
|
|
27
|
+
## RTP Generation
|
|
28
|
+
|
|
29
|
+
Generate continuous sequence of RTP packets of the same payload type.
|
|
30
|
+
|
|
31
|
+
### C API Functions
|
|
32
|
+
|
|
33
|
+
`#include <rtpsynth.h>`
|
|
34
|
+
|
|
35
|
+
- `void *rsynth_ctor(int srate, int ptime);`
|
|
36
|
+
Initializes the RTP synthesizer with given sample rate and packet time.
|
|
37
|
+
Returns a handle to be used in other calls.
|
|
38
|
+
|
|
39
|
+
- `void *rsynth_next_pkt(void *ri, int plen, int pt);`
|
|
40
|
+
Generates the next RTP packet. Takes the handle, packet length, and
|
|
41
|
+
payload type as parameters. Returns a pointer to the generated packet.
|
|
42
|
+
|
|
43
|
+
- `int rsynth_next_pkt_pa(void *ri, int plen, int pt, char *buf, unsigned int blen, int pa);`
|
|
44
|
+
Similar to `rsynth_next_pkt` but allows pre-allocated buffer and packet
|
|
45
|
+
attributes. Returns the length of the packet generated.
|
|
46
|
+
|
|
47
|
+
- `void rsynth_pkt_free(void *rnp);`
|
|
48
|
+
Frees the allocated packet. Takes a pointer to the packet as parameter.
|
|
49
|
+
|
|
50
|
+
- `void rsynth_dtor(void *ri);`
|
|
51
|
+
Destroys the RTP synthesizer and frees resources. Takes the handle as
|
|
52
|
+
parameter.
|
|
53
|
+
|
|
54
|
+
- `unsigned int rsynth_set_mbt(void *ri, unsigned int new_st);`
|
|
55
|
+
Sets a new marker bit toggle state. Takes the handle and the new state
|
|
56
|
+
as parameters. Returns the old state.
|
|
57
|
+
|
|
58
|
+
- `void rsynth_resync(void *ri, struct rsynth_seq *rsp);`
|
|
59
|
+
Resynchronizes the RTP packet sequence. Takes the handle and optionally
|
|
60
|
+
a sequence structure as parameters. Use this function when a time
|
|
61
|
+
discontinuity is expected in packet generation, such as when VAD (Voice
|
|
62
|
+
Activity Detection) is active. The library will recalculate the timestamp
|
|
63
|
+
for the next packet based on the current system clock and the time the
|
|
64
|
+
last packet was generated.
|
|
65
|
+
|
|
66
|
+
### RtpGen (Python)
|
|
67
|
+
|
|
68
|
+
## RTP Parser & Validator / Jitter Buffer
|
|
69
|
+
|
|
70
|
+
Simple RTP parser and validator to process incoming UDP datagrams,
|
|
71
|
+
parse & validate RTP headers. Resulting RTP stream is passed through
|
|
72
|
+
fixed-size jitter buffer to de-duplicate and re-order packets if
|
|
73
|
+
needed.
|
|
74
|
+
|
|
75
|
+
### rtpjbuf (c)
|
|
76
|
+
|
|
77
|
+
### RtpJBuf (Python)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
python/RtpJBuf.py
|
|
6
|
+
python/RtpSynth.py
|
|
7
|
+
python/__init__.py
|
|
8
|
+
python/env.py
|
|
9
|
+
rtpsynth.egg-info/PKG-INFO
|
|
10
|
+
rtpsynth.egg-info/SOURCES.txt
|
|
11
|
+
rtpsynth.egg-info/dependency_links.txt
|
|
12
|
+
rtpsynth.egg-info/top_level.txt
|
|
13
|
+
setup/RunCTest.py
|
|
14
|
+
setup/__init__.py
|
|
15
|
+
src/Symbol.map
|
|
16
|
+
src/rsth_timeops.h
|
|
17
|
+
src/rtp.c
|
|
18
|
+
src/rtp.h
|
|
19
|
+
src/rtp_info.h
|
|
20
|
+
src/rtpjbuf.c
|
|
21
|
+
src/rtpjbuf.h
|
|
22
|
+
src/rtpsynth.c
|
|
23
|
+
src/rtpsynth.h
|
|
24
|
+
tests/test_jbuf.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from distutils.core import Command
|
|
2
|
+
|
|
3
|
+
class RunCTest(Command):
|
|
4
|
+
description = "Run C test"
|
|
5
|
+
user_options = []
|
|
6
|
+
extra_compile_args = []
|
|
7
|
+
extra_link_args = []
|
|
8
|
+
|
|
9
|
+
def initialize_options(self):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def finalize_options(self):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def run(self):
|
|
16
|
+
from distutils.ccompiler import new_compiler
|
|
17
|
+
import distutils.sysconfig as sysconfig
|
|
18
|
+
|
|
19
|
+
compiler = new_compiler()
|
|
20
|
+
|
|
21
|
+
# Compile and link
|
|
22
|
+
obj_files = compiler.compile(['src/rtpsynth.c', 'tests/test_synth.c'],
|
|
23
|
+
extra_preargs=self.extra_compile_args + ['-Isrc',])
|
|
24
|
+
compiler.link_executable(obj_files, 'build/test_synth', extra_postargs=self.extra_link_args)
|
|
25
|
+
|
|
26
|
+
import subprocess
|
|
27
|
+
subprocess.run(['./build/test_synth'])
|
|
File without changes
|
rtpsynth-1.0/setup.cfg
ADDED
rtpsynth-1.0/setup.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from distutils.core import setup
|
|
6
|
+
from distutils.core import Extension
|
|
7
|
+
|
|
8
|
+
from python.env import RSTH_MOD_NAME
|
|
9
|
+
from setup.RunCTest import RunCTest
|
|
10
|
+
|
|
11
|
+
rs_srcs = ['src/rtpsynth.c', 'src/rtp.c', 'src/rtpjbuf.c']
|
|
12
|
+
|
|
13
|
+
extra_compile_args = ['--std=c11', '-Wno-zero-length-array', '-Wall', '-pedantic', '-flto']
|
|
14
|
+
extra_link_args = ['-flto']
|
|
15
|
+
debug_opts = (('-g3', '-O0'))
|
|
16
|
+
nodebug_opts = (('-march=native', '-O3'))
|
|
17
|
+
if False:
|
|
18
|
+
extra_compile_args.extend(debug_opts)
|
|
19
|
+
extra_link_args.extend(debug_opts)
|
|
20
|
+
else:
|
|
21
|
+
extra_compile_args.extend(nodebug_opts)
|
|
22
|
+
extra_link_args.extend(nodebug_opts)
|
|
23
|
+
|
|
24
|
+
module1 = Extension(RSTH_MOD_NAME, sources = rs_srcs, \
|
|
25
|
+
extra_link_args = extra_link_args, \
|
|
26
|
+
extra_compile_args = extra_compile_args)
|
|
27
|
+
|
|
28
|
+
RunCTest.extra_link_args = extra_link_args.copy()
|
|
29
|
+
RunCTest.extra_compile_args = extra_compile_args
|
|
30
|
+
|
|
31
|
+
extra_link_args.append('-Wl,--version-script=src/Symbol.map')
|
|
32
|
+
|
|
33
|
+
def get_ex_mod():
|
|
34
|
+
if 'NO_PY_EXT' in os.environ:
|
|
35
|
+
return None
|
|
36
|
+
return [module1]
|
|
37
|
+
|
|
38
|
+
with open("README.md", "r") as fh:
|
|
39
|
+
long_description = fh.read()
|
|
40
|
+
|
|
41
|
+
kwargs = {'name':'rtpsynth',
|
|
42
|
+
'version':'1.0',
|
|
43
|
+
'description':'Library optimized to generate/process sequence of the RTP packets',
|
|
44
|
+
'long_description': long_description,
|
|
45
|
+
'long_description_content_type': "text/markdown",
|
|
46
|
+
'author':'Maksym Sobolyev',
|
|
47
|
+
'author_email':'sobomax@gmail.com',
|
|
48
|
+
'url':'https://github.com/sippy/librtpsynth.git',
|
|
49
|
+
'packages':['rtpsynth',],
|
|
50
|
+
'package_dir':{'rtpsynth':'python'},
|
|
51
|
+
'ext_modules': get_ex_mod(),
|
|
52
|
+
'cmdclass': {'runctest': RunCTest},
|
|
53
|
+
'classifiers': [
|
|
54
|
+
'License :: OSI Approved :: BSD License',
|
|
55
|
+
'Operating System :: POSIX',
|
|
56
|
+
'Programming Language :: C',
|
|
57
|
+
'Programming Language :: Python'
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if __name__ == '__main__':
|
|
62
|
+
setup(**kwargs)
|