Skip to content

Commit 9c904ec

Browse files
authored
Make Python ByteVector can be used more efficiently (#4588)
1 parent 670a3c9 commit 9c904ec

File tree

3 files changed

+69
-17
lines changed

3 files changed

+69
-17
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pjsip-apps/src/pjsua/ios-swift/Libraries/
4949
pjsip-apps/src/pjsua2/ios-swift-pjsua2/Libraries/
5050

5151
# SWIG Java/Android stuff
52+
.gradle
5253
pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/*.java
5354
pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/PjCamera*.java
5455
pjsip-apps/src/swig/java/android/app/src/main/jniLibs/
@@ -60,12 +61,14 @@ pjsip-apps/src/swig/csharp/sample/SimplePjsua2CS/obj
6061
pjsip-apps/src/swig/csharp/sample/SimplePjsua2CS/*.user
6162

6263
# SWIG Python stuff
64+
__pycache__
6365
pjsip-apps/src/swig/python/build/
66+
pjsip-apps/src/swig/python/dist/
6467
pjsip-apps/src/swig/python/pjsua2.py
6568
pjsip-apps/src/swig/python/pjsua2_wrap.*
69+
pjsip-apps/src/swig/python/pjsua2.egg-info
6670

6771
# unit tests files
68-
tests/pjsua/**/__pycache__/
6972
tests/pjsua/*.pyc
7073
tests/pjsua/scripts-*/*.pyc
7174
tests/pjsua/*.log

pjsip-apps/src/swig/pjsua2.i

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ using namespace pj;
4040
//Py_Exit(1);
4141
}
4242
}
43+
44+
%extend std::vector<unsigned char> {
45+
void assign_from_bytes(PyObject* obj) {
46+
Py_buffer view;
47+
if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) {
48+
SWIG_Python_RaiseOrModifyTypeError("expected a bytes-like object (bytes, bytearray, memoryview)");
49+
return;
50+
}
51+
$self->assign((unsigned char*)view.buf, (unsigned char*)view.buf + view.len);
52+
PyBuffer_Release(&view);
53+
}
54+
void copy_to_bytearray(PyObject* obj) {
55+
// Not using return value here, because in SWIG, using return value will cause one more copy
56+
Py_buffer view;
57+
if (PyObject_GetBuffer(obj, &view, PyBUF_WRITABLE) != 0) {
58+
SWIG_Python_RaiseOrModifyTypeError("expected a writable bytes-like object (bytearray, memoryview)");
59+
return;
60+
}
61+
if (view.len < (Py_ssize_t)$self->size()) {
62+
PyBuffer_Release(&view);
63+
SWIG_Python_RaiseOrModifyTypeError("bytearray too small");
64+
return;
65+
}
66+
if ($self->size() > 0) {
67+
memcpy(view.buf, $self->data(), $self->size());
68+
}
69+
PyBuffer_Release(&view);
70+
}
71+
}
4372
#endif
4473

4574
#ifdef SWIGCSHARP

pjsip-apps/src/swig/python/test.py

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ def ua_data_test():
5050
write(s + "\r\n")
5151
write("\r\n")
5252

53+
#
54+
# ByteVector
55+
# Assign bytes/bytearray/memoryview to a ByteVector
56+
# Copy the content of a ByteVector to a bytearray/memoryview
57+
#
58+
bv = pj.ByteVector()
59+
bv.assign_from_bytes(bytearray([1, 2, 3]))
60+
assert bv.size() == 3
61+
assert bv[0] == 1 and bv[1] == 2 and bv[2] == 3
62+
63+
ba = memoryview(bytearray(5))
64+
bv.copy_to_bytearray(ba[1:])
65+
assert ba == b'\x00\x01\x02\x03\x00'
66+
5367
#
5468
# Exception test
5569
#
@@ -78,39 +92,45 @@ def write(self, entry):
7892
write("This is Python:" + entry.msg + "\r\n")
7993

8094
class AMP(pj.AudioMediaPort):
81-
frames = deque()
95+
buffers = deque()
8296

8397
def onFrameRequested(self, frame):
84-
if len(self.frames):
85-
# Get a frame from the queue and pass it to PJSIP
86-
frame_ = self.frames.popleft()
98+
if len(self.buffers):
99+
# Get a buffer from the queue
100+
buffer_out = self.buffers.popleft()
101+
# Copy the buffer to PJSIP's frame
87102
frame.type = pj.PJMEDIA_TYPE_AUDIO
88-
frame.buf = frame_
103+
frame.buf.assign_from_bytes(buffer_out)
89104

90105
def onFrameReceived(self, frame):
91-
frame_ = pj.ByteVector()
92-
for i in range(frame.buf.size()):
106+
# Get the incoming audio buffer from PJSIP's frame
107+
buffer_in = bytearray(frame.buf.size())
108+
frame.buf.copy_to_bytearray(buffer_in)
109+
# Prepare an output buffer
110+
buffer_out = bytearray(len(buffer_in))
111+
112+
# Now you can process these byte arrays directly, e.g. use numpy
113+
for i in range(len(buffer_in)):
93114
if (i % 2 == 1):
94115
# Convert it to signed 16-bit integer
95-
x = frame.buf[i] << 8 | frame.buf[i-1]
116+
x = buffer_in[i] << 8 | buffer_in[i-1]
96117
x = struct.unpack('<h', struct.pack('<H', x))[0]
97118

98119
# Amplify the signal by 50% and clip it
99120
x = int(x * 1.5)
100-
if (x > 32767):
121+
if x > 32767:
101122
x = 32767
102-
else:
103-
if (x < -32768):
104-
x = -32768
123+
elif x < -32768:
124+
x = -32768
105125

106126
# Convert it to unsigned 16-bit integer
107127
x = struct.unpack('<H', struct.pack('<h', x))[0]
108128

109-
# Put it back in the vector in little endian order
110-
frame_.append(x & 0xff)
111-
frame_.append((x & 0xff00) >> 8)
129+
# Put it in the output buffer in little endian order
130+
buffer_out[i-1] = (x & 0xff)
131+
buffer_out[i] = (x & 0xff00) >> 8
112132

113-
self.frames.append(frame_)
133+
self.buffers.append(buffer_out)
114134

115135
#
116136
# Testing log writer callback

0 commit comments

Comments
 (0)