sip-lab 1.17.10 → 1.19.0
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.
- package/binding.gyp +5 -10
- package/build_deps.sh +3 -3
- package/package.json +1 -1
- package/prebuilds/linux-x64/sip-lab.node +0 -0
- package/samples/100_calls.js +1 -1
- package/samples/multiple_audio_streams.js +183 -0
- package/src/event_templates.cpp +4 -4
- package/src/pjmedia/include/pjmedia/fax_port.h +25 -0
- package/src/pjmedia/src/pjmedia/dtmfdet.c +7 -5
- package/src/pjmedia/src/pjmedia/fax_port.c +278 -0
- package/src/sip.cpp +507 -509
package/binding.gyp
CHANGED
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
"src",
|
|
15
15
|
"src/pjmedia/include",
|
|
16
16
|
"src/pjmedia/include/pjmedia",
|
|
17
|
-
"src/pjmedia/include/chainlink",
|
|
18
17
|
"3rdParty/rapidjson/include",
|
|
19
|
-
"3rdParty/
|
|
18
|
+
"3rdParty/boost_1_66_0",
|
|
20
19
|
"3rdParty/spandsp/src",
|
|
21
20
|
"<!@(node -p \"require('node-addon-api').include\")",
|
|
22
21
|
],
|
|
@@ -50,11 +49,11 @@
|
|
|
50
49
|
'-l speex-x86_64-unknown-linux-gnu',
|
|
51
50
|
'-l gsmcodec-x86_64-unknown-linux-gnu',
|
|
52
51
|
'-l g7221codec-x86_64-unknown-linux-gnu',
|
|
53
|
-
'-l resample-x86_64-unknown-linux-gnu',
|
|
54
52
|
'-l pjmedia-audiodev-x86_64-unknown-linux-gnu',
|
|
53
|
+
'-l pjmedia-x86_64-unknown-linux-gnu',
|
|
54
|
+
'-l resample-x86_64-unknown-linux-gnu',
|
|
55
55
|
'-l pjmedia-codec-x86_64-unknown-linux-gnu',
|
|
56
56
|
'-l pjmedia-videodev-x86_64-unknown-linux-gnu',
|
|
57
|
-
'-l pjmedia-x86_64-unknown-linux-gnu',
|
|
58
57
|
'-l pjsdp-x86_64-unknown-linux-gnu',
|
|
59
58
|
'-l pjsip-x86_64-unknown-linux-gnu',
|
|
60
59
|
'-l pjsua2-x86_64-unknown-linux-gnu',
|
|
@@ -108,12 +107,8 @@
|
|
|
108
107
|
'src/idmanager.cpp',
|
|
109
108
|
'src/sip.cpp',
|
|
110
109
|
'src/addon.cpp',
|
|
111
|
-
'src/pjmedia/src/
|
|
112
|
-
'src/pjmedia/src/
|
|
113
|
-
'src/pjmedia/src/chainlink/chainlink_wav_player.c',
|
|
114
|
-
'src/pjmedia/src/chainlink/chainlink_wav_writer.c',
|
|
115
|
-
'src/pjmedia/src/chainlink/chainlink_wire_port.c',
|
|
116
|
-
'src/pjmedia/src/chainlink/chainlink_fax.c',
|
|
110
|
+
'src/pjmedia/src/pjmedia/dtmfdet.c',
|
|
111
|
+
'src/pjmedia/src/pjmedia/fax_port.c',
|
|
117
112
|
],
|
|
118
113
|
},
|
|
119
114
|
],
|
package/build_deps.sh
CHANGED
|
@@ -74,10 +74,10 @@ fi
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
cd $START_DIR/3rdParty
|
|
77
|
-
if [[ ! -d
|
|
77
|
+
if [[ ! -d boost_1_66_0 ]]
|
|
78
78
|
then
|
|
79
|
-
wget
|
|
80
|
-
tar xf
|
|
79
|
+
wget https://downloads.sourceforge.net/project/boost/boost/1.66.0/boost_1_66_0.tar.bz2
|
|
80
|
+
tar xf boost_1_66_0.tar.bz2
|
|
81
81
|
fi
|
|
82
82
|
|
|
83
83
|
|
package/package.json
CHANGED
|
Binary file
|
package/samples/100_calls.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// This test creates
|
|
1
|
+
// This test creates 100 caller UDP SIP endpoints, 100 callee UDP SIP endpoines and makes one call between them, test dtmf and disconnects.
|
|
2
2
|
|
|
3
3
|
const sip = require ('../index.js')
|
|
4
4
|
const Zeq = require('@mayama/zeq')
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const sip = require ('../index.js')
|
|
2
|
+
const Zeq = require('@mayama/zeq')
|
|
3
|
+
const m = require('data-matching')
|
|
4
|
+
const sip_msg = require('sip-matching')
|
|
5
|
+
const _ = require('lodash')
|
|
6
|
+
|
|
7
|
+
// here we create our Zeq instance
|
|
8
|
+
var z = new Zeq()
|
|
9
|
+
|
|
10
|
+
// here we are restricting codec to PCMU to avoid having the SDP to become too large
|
|
11
|
+
sip.set_codecs('pcmu/8000/1:128')
|
|
12
|
+
|
|
13
|
+
// here we asking for accumulated DTMF to be reported in case of no more digits after 500 ms
|
|
14
|
+
sip.dtmf_aggregation_on(500)
|
|
15
|
+
|
|
16
|
+
async function test() {
|
|
17
|
+
// here we set our Zeq instance to trap events generated by sip-lab event_source
|
|
18
|
+
z.trap_events(sip.event_source, 'event', (evt) => {
|
|
19
|
+
var e = evt.args[0]
|
|
20
|
+
return e
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// here we start sip-lab
|
|
24
|
+
console.log(sip.start((data) => { console.log(data)} ))
|
|
25
|
+
|
|
26
|
+
// Here we create the SIP endpoints (transports).
|
|
27
|
+
// Since we don't specify the port, an available port will be allocated.
|
|
28
|
+
// Since we don't specify the type ('udp' or 'tcp' or 'tls'), 'udp' will be used by default.
|
|
29
|
+
const t1 = sip.transport.create({address: "127.0.0.1", type: 'tcp'})
|
|
30
|
+
const t2 = sip.transport.create({address: "127.0.0.1", type: 'tcp'})
|
|
31
|
+
|
|
32
|
+
// here we just print the transports
|
|
33
|
+
console.log("t1", t1)
|
|
34
|
+
console.log("t2", t2)
|
|
35
|
+
|
|
36
|
+
const NUM_AUDIO_STREAMS = 8
|
|
37
|
+
const media = new Array(NUM_AUDIO_STREAMS).fill('audio').join(',')
|
|
38
|
+
|
|
39
|
+
const oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`, media: media})
|
|
40
|
+
|
|
41
|
+
// Here we will wait for the call to arrive at t2
|
|
42
|
+
// We will also get a '100 Trying' that is sent by sip-lab automatically
|
|
43
|
+
// We will wait for at most 1000ms. If all events don't arrive within 1000ms, an exception will be thrown and the test will fail due to timeout.
|
|
44
|
+
await z.wait([
|
|
45
|
+
{
|
|
46
|
+
event: "incoming_call",
|
|
47
|
+
call_id: m.collect("call_id"),
|
|
48
|
+
transport_id: t2.id,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
event: 'response',
|
|
52
|
+
call_id: oc.id,
|
|
53
|
+
method: 'INVITE',
|
|
54
|
+
msg: sip_msg({
|
|
55
|
+
$rs: '100',
|
|
56
|
+
$rr: 'Trying',
|
|
57
|
+
'$(hdrcnt(via))': 1,
|
|
58
|
+
'$hdr(call-id)': m.collect('sip_call_id'),
|
|
59
|
+
$fU: 'alice',
|
|
60
|
+
$fd: 'test.com',
|
|
61
|
+
$tU: 'bob',
|
|
62
|
+
'$hdr(l)': '0',
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
], 1000)
|
|
66
|
+
// Details about zeq wait(list_of_events_to_wait_for, timeout_in_ms):
|
|
67
|
+
// The order of events in the list is irrelevant.
|
|
68
|
+
// What matters is that all events arrive within the specified timeout.
|
|
69
|
+
// When specifying events, you can be as detailed or succinct as you need.
|
|
70
|
+
// For example, the above event 'response' is waiting for a SIP '100 Trying' to arrive,
|
|
71
|
+
// but we are specifying things to match just to show that we can be very detailed when performing a match.
|
|
72
|
+
// But it could have been just like this:
|
|
73
|
+
//
|
|
74
|
+
// {
|
|
75
|
+
// event: 'response',
|
|
76
|
+
// call_id: oc.id,
|
|
77
|
+
// method: 'INVITE',
|
|
78
|
+
// msg: sip_msg({
|
|
79
|
+
// $rs: '100',
|
|
80
|
+
// }),
|
|
81
|
+
// }
|
|
82
|
+
// Regarding the function sip_msg() this is a special matching function provided by https://github.com/MayamaTakeshi/sip-matching that makes it
|
|
83
|
+
// easy to match a SIP message using openser/kamailio/opensips pseudo-variables syntax.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// Here we store data for the incoming call
|
|
87
|
+
// just to organize our code (not really needed)
|
|
88
|
+
const ic = {
|
|
89
|
+
id: z.store.call_id,
|
|
90
|
+
sip_call_id: z.store.sip_call_id,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Now we answer the call at t2 side and accept all 2 streams
|
|
94
|
+
sip.call.respond(ic.id, {code: 200, reason: 'OK', media: media})
|
|
95
|
+
|
|
96
|
+
// Then we wait for the '200 OK' at the t1 side
|
|
97
|
+
// We will also get event 'media_update' for both sides indicating media streams (RTP) were set up successfully
|
|
98
|
+
await z.wait([
|
|
99
|
+
{
|
|
100
|
+
event: 'response',
|
|
101
|
+
call_id: oc.id,
|
|
102
|
+
method: 'INVITE',
|
|
103
|
+
msg: sip_msg({
|
|
104
|
+
$rs: '200',
|
|
105
|
+
$rr: 'OK',
|
|
106
|
+
'$(hdrcnt(VIA))': 1,
|
|
107
|
+
$fU: 'alice',
|
|
108
|
+
$fd: 'test.com',
|
|
109
|
+
$tU: 'bob',
|
|
110
|
+
'$hdr(content-type)': 'application/sdp',
|
|
111
|
+
$rb: '!{_}a=sendrecv',
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
event: 'media_update',
|
|
116
|
+
call_id: oc.id,
|
|
117
|
+
status: 'ok',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
event: 'media_update',
|
|
121
|
+
call_id: ic.id,
|
|
122
|
+
status: 'ok',
|
|
123
|
+
},
|
|
124
|
+
], 1000)
|
|
125
|
+
|
|
126
|
+
sip.call.send_dtmf(oc.id, {digits: '1234', mode: 0})
|
|
127
|
+
|
|
128
|
+
await z.wait(_.chain(_.range(NUM_AUDIO_STREAMS)).map(n => ({
|
|
129
|
+
event: 'dtmf',
|
|
130
|
+
call_id: ic.id,
|
|
131
|
+
digits: '1234',
|
|
132
|
+
mode: 0,
|
|
133
|
+
media_id: n,
|
|
134
|
+
})).value(), 3000)
|
|
135
|
+
|
|
136
|
+
sip.call.send_dtmf(ic.id, {digits: '4321', mode: 1})
|
|
137
|
+
|
|
138
|
+
await z.wait(_.chain(_.range(NUM_AUDIO_STREAMS)).map(n => ({
|
|
139
|
+
event: 'dtmf',
|
|
140
|
+
call_id: oc.id,
|
|
141
|
+
digits: '4321',
|
|
142
|
+
mode: 1,
|
|
143
|
+
media_id: n,
|
|
144
|
+
})).value(), 3000)
|
|
145
|
+
|
|
146
|
+
// now we terminate the call from t1 side
|
|
147
|
+
sip.call.terminate(oc.id)
|
|
148
|
+
|
|
149
|
+
// and wait for termination events
|
|
150
|
+
await z.wait([
|
|
151
|
+
{
|
|
152
|
+
event: 'response',
|
|
153
|
+
call_id: oc.id,
|
|
154
|
+
method: 'BYE',
|
|
155
|
+
msg: sip_msg({
|
|
156
|
+
$rs: '200',
|
|
157
|
+
$rr: 'OK',
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
event: 'call_ended',
|
|
162
|
+
call_id: oc.id,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
event: 'call_ended',
|
|
166
|
+
call_id: ic.id,
|
|
167
|
+
},
|
|
168
|
+
], 1000)
|
|
169
|
+
|
|
170
|
+
await z.sleep(100) // wait for any unexpected events
|
|
171
|
+
|
|
172
|
+
console.log("Success")
|
|
173
|
+
|
|
174
|
+
sip.stop()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
test()
|
|
179
|
+
.catch(e => {
|
|
180
|
+
console.error(e)
|
|
181
|
+
process.exit(1)
|
|
182
|
+
})
|
|
183
|
+
|
package/src/event_templates.cpp
CHANGED
|
@@ -49,7 +49,7 @@ int make_evt_dtmf(char *dest, int size, long call_id, int digits_len,
|
|
|
49
49
|
|
|
50
50
|
int make_evt_call_ended(char *dest, int size, long call_id, int sip_msg_len,
|
|
51
51
|
const char *sip_msg) {
|
|
52
|
-
printf("make_evt_call_ended sip_msg_len=%i sip_msg=%
|
|
52
|
+
printf("make_evt_call_ended sip_msg_len=%i sip_msg=%s\n", sip_msg_len,
|
|
53
53
|
sip_msg);
|
|
54
54
|
if (!sip_msg || sip_msg == (char *)0xc000000000000) {
|
|
55
55
|
// received invalid pointer to sip_msg so do not add the message to the
|
|
@@ -72,8 +72,8 @@ int make_evt_non_dialog_request(char *dest, int size, long transport_id,
|
|
|
72
72
|
long request_id, int sip_msg_len,
|
|
73
73
|
const char *sip_msg) {
|
|
74
74
|
return snprintf(dest, size,
|
|
75
|
-
"{\"event\": \"non_dialog_request\", \"request_id\": %
|
|
76
|
-
"\"transport_id\": %
|
|
75
|
+
"{\"event\": \"non_dialog_request\", \"request_id\": %li, "
|
|
76
|
+
"\"transport_id\": %li}\n%.*s",
|
|
77
77
|
request_id, transport_id, sip_msg_len, sip_msg);
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -85,7 +85,7 @@ int make_evt_internal_error(char *dest, int size, const char *msg) {
|
|
|
85
85
|
int make_evt_reinvite(char *dest, int size, long call_id, int sip_msg_len,
|
|
86
86
|
char *sip_msg) {
|
|
87
87
|
return snprintf(dest, size,
|
|
88
|
-
"{\"event\": \"reinvite\", \"call_id\": %
|
|
88
|
+
"{\"event\": \"reinvite\", \"call_id\": %li}\n%.*s", call_id,
|
|
89
89
|
sip_msg_len, sip_msg);
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#ifndef __FAX_PORT_H__
|
|
2
|
+
#define __FAX_PORT_H__
|
|
3
|
+
|
|
4
|
+
#include <pjmedia/port.h>
|
|
5
|
+
|
|
6
|
+
PJ_BEGIN_DECL
|
|
7
|
+
|
|
8
|
+
PJ_DECL(pj_status_t) pjmedia_fax_port_create( pj_pool_t *pool,
|
|
9
|
+
unsigned clock_rate,
|
|
10
|
+
unsigned channel_count,
|
|
11
|
+
unsigned samples_per_frame,
|
|
12
|
+
unsigned bits_per_sample,
|
|
13
|
+
void (*cb)(pjmedia_port*,
|
|
14
|
+
void *user_data,
|
|
15
|
+
int result),
|
|
16
|
+
void *user_data,
|
|
17
|
+
int is_sender,
|
|
18
|
+
const char *file,
|
|
19
|
+
unsigned flags,
|
|
20
|
+
pjmedia_port **p_port);
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
PJ_END_DECL
|
|
24
|
+
|
|
25
|
+
#endif /* __FAX_PORT_H__ */
|
|
@@ -20,13 +20,15 @@
|
|
|
20
20
|
|
|
21
21
|
#include <pjmedia/dtmfdet.h>
|
|
22
22
|
#include <pjmedia/errno.h>
|
|
23
|
+
#include <pjmedia/port.h>
|
|
23
24
|
#include <pj/assert.h>
|
|
24
25
|
#include <pj/pool.h>
|
|
25
26
|
#include <pj/string.h>
|
|
26
27
|
|
|
27
28
|
#include <spandsp.h>
|
|
29
|
+
#include <spandsp/expose.h>
|
|
28
30
|
|
|
29
|
-
#define SIGNATURE
|
|
31
|
+
#define SIGNATURE PJMEDIA_SIGNATURE('d', 't', 'd', 't')
|
|
30
32
|
#define THIS_FILE "dtmfdet.c"
|
|
31
33
|
|
|
32
34
|
#if 0
|
|
@@ -36,12 +38,12 @@
|
|
|
36
38
|
#endif
|
|
37
39
|
|
|
38
40
|
static pj_status_t dtmfdet_put_frame(pjmedia_port *this_port,
|
|
39
|
-
|
|
41
|
+
pjmedia_frame *frame);
|
|
40
42
|
static pj_status_t dtmfdet_on_destroy(pjmedia_port *this_port);
|
|
41
43
|
|
|
42
44
|
struct dtmfdet
|
|
43
45
|
{
|
|
44
|
-
pjmedia_port base;
|
|
46
|
+
struct pjmedia_port base;
|
|
45
47
|
dtmf_rx_state_t state;
|
|
46
48
|
void (*dtmf_cb)(pjmedia_port*, void*, char);
|
|
47
49
|
void *dtmf_cb_user_data;
|
|
@@ -106,13 +108,13 @@ PJ_DEF(pj_status_t) pjmedia_dtmfdet_create( pj_pool_t *pool,
|
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
static pj_status_t dtmfdet_put_frame(pjmedia_port *this_port,
|
|
109
|
-
|
|
111
|
+
pjmedia_frame *frame)
|
|
110
112
|
{
|
|
111
113
|
if(frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
|
|
112
114
|
|
|
113
115
|
struct dtmfdet *dport = (struct dtmfdet*) this_port;
|
|
114
116
|
dtmf_rx(&dport->state, (const pj_int16_t*)frame->buf,
|
|
115
|
-
dport->base.info
|
|
117
|
+
PJMEDIA_PIA_SPF(&dport->base.info));
|
|
116
118
|
|
|
117
119
|
return PJ_SUCCESS;
|
|
118
120
|
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
|
|
2
|
+
#include "fax_port.h"
|
|
3
|
+
#include <pjmedia/errno.h>
|
|
4
|
+
#include <pjmedia/port.h>
|
|
5
|
+
#include <pj/assert.h>
|
|
6
|
+
#include <pj/lock.h>
|
|
7
|
+
#include <pj/pool.h>
|
|
8
|
+
#include <pj/string.h>
|
|
9
|
+
|
|
10
|
+
#include "siplab_constants.h"
|
|
11
|
+
|
|
12
|
+
#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
|
|
13
|
+
#include <spandsp.h>
|
|
14
|
+
|
|
15
|
+
#define SIGNATURE PJMEDIA_SIGNATURE('f', 'a', 'x', 'p')
|
|
16
|
+
#define THIS_FILE "fax_port.c"
|
|
17
|
+
|
|
18
|
+
#if 0
|
|
19
|
+
# define TRACE_(expr) PJ_LOG(4,expr)
|
|
20
|
+
#else
|
|
21
|
+
# define TRACE_(expr)
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
#define FAX_DATA_CHUNK 320
|
|
25
|
+
#define T38_DATA_CHUNK 160
|
|
26
|
+
|
|
27
|
+
enum
|
|
28
|
+
{
|
|
29
|
+
/*! No compression */
|
|
30
|
+
T30_SUPPORT_NO_COMPRESSION = 0x01,
|
|
31
|
+
/*! T.1 1D compression */
|
|
32
|
+
T30_SUPPORT_T4_1D_COMPRESSION = 0x02,
|
|
33
|
+
/*! T.4 2D compression */
|
|
34
|
+
T30_SUPPORT_T4_2D_COMPRESSION = 0x04,
|
|
35
|
+
/*! T.6 2D compression */
|
|
36
|
+
T30_SUPPORT_T6_COMPRESSION = 0x08,
|
|
37
|
+
/*! T.85 monochrome JBIG compression */
|
|
38
|
+
T30_SUPPORT_T85_COMPRESSION = 0x10,
|
|
39
|
+
/*! T.43 colour JBIG compression */
|
|
40
|
+
T30_SUPPORT_T43_COMPRESSION = 0x20,
|
|
41
|
+
/*! T.45 run length colour compression */
|
|
42
|
+
T30_SUPPORT_T45_COMPRESSION = 0x40,
|
|
43
|
+
/*! T.81 + T.30 Annex E colour JPEG compression */
|
|
44
|
+
T30_SUPPORT_T81_COMPRESSION = 0x80,
|
|
45
|
+
/*! T.81 + T.30 Annex K colour sYCC-JPEG compression */
|
|
46
|
+
T30_SUPPORT_SYCC_T81_COMPRESSION = 0x100,
|
|
47
|
+
/*! T.88 monochrome JBIG2 compression */
|
|
48
|
+
T30_SUPPORT_T88_COMPRESSION = 0x200
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
static pj_status_t fax_get_frame(pjmedia_port *this_port,
|
|
52
|
+
pjmedia_frame *frame);
|
|
53
|
+
static pj_status_t fax_put_frame(pjmedia_port *this_port,
|
|
54
|
+
pjmedia_frame *frame);
|
|
55
|
+
static pj_status_t fax_on_destroy(pjmedia_port *this_port);
|
|
56
|
+
|
|
57
|
+
struct fax_device
|
|
58
|
+
{
|
|
59
|
+
struct pjmedia_port base;
|
|
60
|
+
fax_state_t fax;
|
|
61
|
+
void (*fax_cb)(pjmedia_port*, void*, int);
|
|
62
|
+
void *fax_cb_user_data;
|
|
63
|
+
int is_sender;
|
|
64
|
+
bool result_sent;
|
|
65
|
+
|
|
66
|
+
pj_lock_t *lock;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
static int phase_b_handler(void* user_data, int result)
|
|
70
|
+
{
|
|
71
|
+
printf("fax phase_b_handler user_data=%p result=%i\n", user_data, result);
|
|
72
|
+
return T30_ERR_OK;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static int phase_d_handler(void* user_data, int result)
|
|
76
|
+
{
|
|
77
|
+
printf("fax phase_b_handler user_data=%p result=%i\n", user_data, result);
|
|
78
|
+
return T30_ERR_OK;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static void phase_e_handler(void* user_data, int result)
|
|
82
|
+
{
|
|
83
|
+
printf("fax phase_e_handler user_data=%p result=%i\n", user_data, result);
|
|
84
|
+
|
|
85
|
+
if (!user_data) {
|
|
86
|
+
printf("not user_data\n");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
struct fax_device *fd = (struct fax_device*)user_data;
|
|
91
|
+
if(!fd->fax_cb) {
|
|
92
|
+
printf("not fax_cb\n");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if(!fd->result_sent) {
|
|
97
|
+
fd->fax_cb((pjmedia_port*)fd, fd->fax_cb_user_data, result);
|
|
98
|
+
fd->result_sent = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static int document_handler(void* user_data, int result)
|
|
103
|
+
{
|
|
104
|
+
printf("fax document_handler user_data=%p result=%i\n", user_data, result);
|
|
105
|
+
|
|
106
|
+
if (!user_data) return 0;
|
|
107
|
+
|
|
108
|
+
struct fax_device *fd = (struct fax_device*)user_data;
|
|
109
|
+
if(!fd->fax_cb) return 0;
|
|
110
|
+
|
|
111
|
+
if(!fd->result_sent) {
|
|
112
|
+
fd->fax_cb((pjmedia_port*)fd, fd->fax_cb_user_data, result);
|
|
113
|
+
fd->result_sent = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
PJ_DEF(pj_status_t) pjmedia_fax_port_create( pj_pool_t *pool,
|
|
120
|
+
unsigned clock_rate,
|
|
121
|
+
unsigned channel_count,
|
|
122
|
+
unsigned samples_per_frame,
|
|
123
|
+
unsigned bits_per_sample,
|
|
124
|
+
void (*cb)(pjmedia_port*,
|
|
125
|
+
void *user_data,
|
|
126
|
+
int result),
|
|
127
|
+
void *user_data,
|
|
128
|
+
int is_sender,
|
|
129
|
+
const char *file,
|
|
130
|
+
unsigned flags,
|
|
131
|
+
pjmedia_port **p_port)
|
|
132
|
+
{
|
|
133
|
+
struct fax_device *fd;
|
|
134
|
+
const pj_str_t name = pj_str("fax_device");
|
|
135
|
+
|
|
136
|
+
PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
|
|
137
|
+
samples_per_frame && bits_per_sample == 16 &&
|
|
138
|
+
p_port != NULL, PJ_EINVAL);
|
|
139
|
+
|
|
140
|
+
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
|
|
141
|
+
|
|
142
|
+
fd = PJ_POOL_ZALLOC_T(pool, struct fax_device);
|
|
143
|
+
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
|
|
144
|
+
|
|
145
|
+
pjmedia_port_info_init(&fd->base.info, &name, SIGNATURE, clock_rate,
|
|
146
|
+
channel_count, bits_per_sample, samples_per_frame);
|
|
147
|
+
|
|
148
|
+
fd->base.get_frame = &fax_get_frame;
|
|
149
|
+
fd->base.put_frame = &fax_put_frame;
|
|
150
|
+
fd->base.on_destroy = &fax_on_destroy;
|
|
151
|
+
|
|
152
|
+
fax_init(&fd->fax, is_sender);
|
|
153
|
+
|
|
154
|
+
t30_state_t *t30 = fax_get_t30_state(&fd->fax);
|
|
155
|
+
|
|
156
|
+
span_log_set_level(fax_get_logging_state(&fd->fax), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
157
|
+
span_log_set_level(t30_get_logging_state(t30), SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
|
|
158
|
+
|
|
159
|
+
char ident[] = "fax_port";
|
|
160
|
+
|
|
161
|
+
t30_set_tx_ident(t30, ident);
|
|
162
|
+
t30_set_phase_b_handler(t30, &phase_b_handler, (void*)fd);
|
|
163
|
+
t30_set_phase_d_handler(t30, &phase_d_handler, (void*)fd);
|
|
164
|
+
t30_set_phase_e_handler(t30, &phase_e_handler, (void*)fd);
|
|
165
|
+
//printf("setting document_handler with user_data=%p\n", (void*)fd);
|
|
166
|
+
t30_set_document_handler(t30, &document_handler, (void*)fd);
|
|
167
|
+
|
|
168
|
+
fd->is_sender = is_sender;
|
|
169
|
+
fd->result_sent = false;
|
|
170
|
+
|
|
171
|
+
pj_status_t status = pj_lock_create_simple_mutex(pool, "fax", &fd->lock);
|
|
172
|
+
|
|
173
|
+
if (status != PJ_SUCCESS) {
|
|
174
|
+
printf("failed to create lock\n");
|
|
175
|
+
return status;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (is_sender)
|
|
179
|
+
t30_set_tx_file(t30,file,-1,-1);
|
|
180
|
+
else
|
|
181
|
+
t30_set_rx_file(t30,file,-1);
|
|
182
|
+
|
|
183
|
+
t30_set_ecm_capability(t30,1);
|
|
184
|
+
t30_set_supported_compressions(t30,T30_SUPPORT_T4_1D_COMPRESSION |
|
|
185
|
+
T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
|
|
186
|
+
|
|
187
|
+
if(flags & FAX_FLAG_TRANSMIT_ON_IDLE) {
|
|
188
|
+
fax_set_transmit_on_idle(&fd->fax, 1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fd->fax_cb = cb;
|
|
192
|
+
fd->fax_cb_user_data = user_data;
|
|
193
|
+
|
|
194
|
+
TRACE_((THIS_FILE, "fax_device created: %u/%u/%u/%u", clock_rate,
|
|
195
|
+
channel_count, samples_per_frame, bits_per_sample));
|
|
196
|
+
|
|
197
|
+
*p_port = &fd->base;
|
|
198
|
+
return PJ_SUCCESS;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// called when pjmedia needs data to be sent out
|
|
202
|
+
static pj_status_t fax_get_frame(pjmedia_port *this_port,
|
|
203
|
+
pjmedia_frame *frame) {
|
|
204
|
+
|
|
205
|
+
//printf("ENTER fax_get_frame frame_size=%i\n", frame->size);
|
|
206
|
+
|
|
207
|
+
PJ_ASSERT_RETURN(this_port && frame, PJ_EINVAL);
|
|
208
|
+
//char *p = (char*)frame->buf; // TODO: remove
|
|
209
|
+
|
|
210
|
+
struct fax_device *fd = (struct fax_device*)this_port;
|
|
211
|
+
pj_lock_acquire(fd->lock);
|
|
212
|
+
|
|
213
|
+
int tx = 0;
|
|
214
|
+
|
|
215
|
+
if ((tx = fax_tx(&fd->fax, (int16_t *)frame->buf, frame->size/2)) < 0) {
|
|
216
|
+
printf("fax_tx reported an error\n");
|
|
217
|
+
pj_lock_release(fd->lock);
|
|
218
|
+
printf("EXIT fax_get_frame\n");
|
|
219
|
+
return PJ_FALSE;
|
|
220
|
+
}
|
|
221
|
+
pj_lock_release(fd->lock);
|
|
222
|
+
|
|
223
|
+
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
|
224
|
+
frame->timestamp.u64 = 0;
|
|
225
|
+
|
|
226
|
+
//printf("EXIT fax_get_frame\n");
|
|
227
|
+
return PJ_SUCCESS;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// called when pjmedia has received data
|
|
231
|
+
static pj_status_t fax_put_frame(pjmedia_port *this_port,
|
|
232
|
+
pjmedia_frame *frame)
|
|
233
|
+
{
|
|
234
|
+
if(frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
|
|
235
|
+
//printf("ENTER fax_put_frame frame->buf=%x frame->size=%i\n", frame->buf, frame->size);
|
|
236
|
+
|
|
237
|
+
struct fax_device *fd = (struct fax_device*) this_port;
|
|
238
|
+
pj_lock_acquire(fd->lock);
|
|
239
|
+
|
|
240
|
+
unsigned int pos = 0;
|
|
241
|
+
|
|
242
|
+
while (pos < frame->size)
|
|
243
|
+
{
|
|
244
|
+
// feed the decoder with small chunks of data (16 bytes/ms)
|
|
245
|
+
int len = frame->size - pos;
|
|
246
|
+
if (len > FAX_DATA_CHUNK) len = FAX_DATA_CHUNK;
|
|
247
|
+
|
|
248
|
+
/* Pass the new incoming audio frame to the fax_rx function */
|
|
249
|
+
if (fax_rx(&fd->fax, (int16_t *)(frame->buf)+pos, len/2)) {
|
|
250
|
+
printf("fax_rx reported an error\n");
|
|
251
|
+
pj_lock_release(fd->lock);
|
|
252
|
+
printf("EXIT fax_put_frame\n");
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
pos += len;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
pj_lock_release(fd->lock);
|
|
260
|
+
|
|
261
|
+
return PJ_SUCCESS;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* Destroy port.
|
|
266
|
+
*/
|
|
267
|
+
static pj_status_t fax_on_destroy(pjmedia_port *this_port)
|
|
268
|
+
{
|
|
269
|
+
printf("fax_on_destroy\n");
|
|
270
|
+
|
|
271
|
+
struct fax_device *fd = (struct fax_device*)this_port;
|
|
272
|
+
|
|
273
|
+
fax_release(&fd->fax);
|
|
274
|
+
|
|
275
|
+
if(fd->lock) pj_lock_destroy(fd->lock);
|
|
276
|
+
return PJ_SUCCESS;
|
|
277
|
+
}
|
|
278
|
+
|