blob: a2a59ab5d5b49f1cccfb40c08f7ca70743ac4af5 [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia.h>
21#include <pjlib.h>
22#include <stdio.h>
23
24#define THIS_FILE "audio_tool.c"
25
26static pj_caching_pool caching_pool;
27static pj_pool_factory *pf;
28static FILE *fhnd;
29static pj_med_mgr_t *mm;
30static pjmedia_codec *codec;
31static pjmedia_codec_param cattr;
32
33
34#define WRITE_ORIGINAL_PCM 0
35#if WRITE_ORIGINAL_PCM
36static FILE *fhnd_pcm;
37#endif
38
39static char talker_sdp[] =
40 "v=0\r\n"
41 "o=- 0 0 IN IP4 127.0.0.1\r\n"
42 "s=-\r\n"
43 "c=IN IP4 127.0.0.1\r\n"
44 "t=0 0\r\n"
45 "m=audio 4002 RTP/AVP 0\r\n"
46 "a=rtpmap:0 PCMU/8000\r\n"
47 "a=sendonly\r\n";
48static char listener_sdp[] =
49 "v=0\r\n"
50 "o=- 0 0 IN IP4 127.0.0.1\r\n"
51 "s=-\r\n"
52 "c=IN IP4 127.0.0.1\r\n"
53 "t=0 0\r\n"
54 "m=audio 4000 RTP/AVP 0\r\n"
55 "a=rtpmap:0 PCMU/8000\r\n"
56 "a=recvonly\r\n";
57
58static pj_status_t play_callback(/* in */ void *user_data,
59 /* in */ pj_uint32_t timestamp,
60 /* out */ void *frame,
61 /* out */ unsigned size)
62{
63 char pkt[160];
64 struct pjmedia_frame in, out;
65 int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
66
67 if (fread(pkt, frmsz, 1, fhnd) != 1) {
68 puts("EOF");
69 return -1;
70 } else {
71 in.type = PJMEDIA_FRAME_TYPE_AUDIO;
72 in.buf = pkt;
73 in.size = frmsz;
74 out.buf = frame;
75 if (codec->op->decode (codec, &in, size, &out) != 0)
76 return -1;
77
78 size = out.size;
79 return 0;
80 }
81}
82
83static pj_status_t rec_callback( /* in */ void *user_data,
84 /* in */ pj_uint32_t timestamp,
85 /* in */ const void *frame,
86 /* in*/ unsigned size)
87{
88 char pkt[160];
89 struct pjmedia_frame in, out;
90 //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
91
92#if WRITE_ORIGINAL_PCM
93 fwrite(frame, size, 1, fhnd_pcm);
94#endif
95
96 in.type = PJMEDIA_FRAME_TYPE_AUDIO;
97 in.buf = (void*)frame;
98 in.size = size;
99 out.buf = pkt;
100
101 if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
102 return -1;
103
104 if (fwrite(pkt, out.size, 1, fhnd) != 1)
105 return -1;
106 return 0;
107}
108
109static pj_status_t init()
110{
111 pjmedia_codec_mgr *cm;
112 pjmedia_codec_info id;
113 int i;
114
115 pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
116 pf = &caching_pool.factory;
117
118 if (pj_snd_init(&caching_pool.factory))
119 return -1;
120
121 PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
122 for (i=0; i<pj_snd_get_dev_count(); ++i) {
123 const pj_snd_dev_info *info;
124 info = pj_snd_get_dev_info(i);
125 PJ_LOG(3,(THIS_FILE, " %d: %s\t(%d in, %d out",
126 i, info->name,
127 info->input_count, info->output_count));
128 }
129
130 mm = pj_med_mgr_create (&caching_pool.factory);
131 cm = pj_med_mgr_get_codec_mgr (mm);
132
133 id.type = PJMEDIA_TYPE_AUDIO;
134 id.pt = 0;
135 id.encoding_name = pj_str("PCMU");
136 id.sample_rate = 8000;
137
138 codec = pjmedia_codec_mgr_alloc_codec (cm, &id);
139 codec->op->default_attr(codec, &cattr);
140 codec->op->open(codec, &cattr);
141 return 0;
142}
143
144static pj_status_t deinit()
145{
146 pjmedia_codec_mgr *cm;
147 cm = pj_med_mgr_get_codec_mgr (mm);
148 codec->op->close(codec);
149 pjmedia_codec_mgr_dealloc_codec (cm, codec);
150 pj_med_mgr_destroy (mm);
151 pj_caching_pool_destroy(&caching_pool);
152 return 0;
153}
154
155static pj_status_t record_file (const char *filename)
156{
157 pj_snd_stream *stream;
158 pj_snd_stream_info info;
159 int status;
160 char s[10];
161
162 printf("Recording to file %s...\n", filename);
163
164 fhnd = fopen(filename, "wb");
165 if (!fhnd)
166 return -1;
167
168#if WRITE_ORIGINAL_PCM
169 fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
170 if (!fhnd_pcm)
171 return -1;
172#endif
173
174 pj_bzero(&info, sizeof(info));
175 info.bits_per_sample = 16;
176 info.bytes_per_frame = 2;
177 info.frames_per_packet = 160;
178 info.samples_per_frame = 1;
179 info.samples_per_sec = 8000;
180
181 stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
182 if (!stream)
183 return -1;
184
185 status = pj_snd_stream_start(stream);
186 if (status != 0)
187 goto on_error;
188
189 puts("Press <ENTER> to exit recording");
190 fgets(s, sizeof(s), stdin);
191
192 pj_snd_stream_stop(stream);
193 pj_snd_stream_close(stream);
194
195#if WRITE_ORIGINAL_PCM
196 fclose(fhnd_pcm);
197#endif
198 fclose(fhnd);
199 return 0;
200
201on_error:
202 pj_snd_stream_stop(stream);
203 pj_snd_stream_close(stream);
204 return -1;
205}
206
207
208static pj_status_t play_file (const char *filename)
209{
210 pj_snd_stream *stream;
211 pj_snd_stream_info info;
212 int status;
213 char s[10];
214
215 printf("Playing file %s...\n", filename);
216
217 fhnd = fopen(filename, "rb");
218 if (!fhnd)
219 return -1;
220
221 pj_bzero(&info, sizeof(info));
222 info.bits_per_sample = 16;
223 info.bytes_per_frame = 2;
224 info.frames_per_packet = 160;
225 info.samples_per_frame = 1;
226 info.samples_per_sec = 8000;
227
228 stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
229 if (!stream)
230 return -1;
231
232 status = pj_snd_stream_start(stream);
233 if (status != 0)
234 goto on_error;
235
236 puts("Press <ENTER> to exit playing");
237 fgets(s, sizeof(s), stdin);
238
239 pj_snd_stream_stop(stream);
240 pj_snd_stream_close(stream);
241
242 fclose(fhnd);
243 return 0;
244
245on_error:
246 pj_snd_stream_stop(stream);
247 pj_snd_stream_close(stream);
248 return -1;
249}
250
251static int create_ses_by_remote_sdp(int local_port, char *sdp)
252{
253 pj_media_session_t *ses = NULL;
254 pjsdp_session_desc *sdp_ses;
255 pj_media_sock_info skinfo;
256 pj_pool_t *pool;
257 char s[4];
258 const pj_media_stream_info *info[2];
259 int i, count;
260
261 pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
262 if (!pool) {
263 PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
264 return -1;
265 }
266
267 pj_bzero(&skinfo, sizeof(skinfo));
268 skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 0);
269 if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
270 PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
271 goto on_error;
272 }
273
274 pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
275 if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
276 PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
277 goto on_error;
278 }
279
280 sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
281 if (!sdp_ses) {
282 PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
283 goto on_error;
284 }
285
286 ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
287 if (!ses) {
288 PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
289 goto on_error;
290 }
291
292 if (pj_media_session_activate(ses) != 0) {
293 PJ_LOG(1,(THIS_FILE, "Error activating session"));
294 goto on_error;
295 }
296
297 count = pj_media_session_enum_streams(ses, 2, info);
298 printf("\nDumping streams: \n");
299 for (i=0; i<count; ++i) {
300 const char *dir;
301 char *local_ip;
302
303 switch (info[i]->dir) {
304 case PJMEDIA_DIR_NONE:
305 dir = "- NONE -"; break;
306 case PJMEDIA_DIR_ENCODING:
307 dir = "SENDONLY"; break;
308 case PJMEDIA_DIR_DECODING:
309 dir = "RECVONLY"; break;
310 case PJMEDIA_DIR_ENCODING_DECODING:
311 dir = "SENDRECV"; break;
312 default:
313 dir = "?UNKNOWN"; break;
314 }
315
316 local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
317
318 printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
319 i, info[i]->type.slen, info[i]->type.ptr,
320 dir,
321 local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
322 info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
323 }
324
325 puts("Press <ENTER> to quit");
326 fgets(s, sizeof(s), stdin);
327
328 pj_media_session_destroy(ses);
329 pj_sock_close(skinfo.rtp_sock);
330 pj_pool_release(pool);
331
332 return 0;
333
334on_error:
335 if (ses)
336 pj_media_session_destroy(ses);
337 if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
338 pj_sock_close(skinfo.rtp_sock);
339 if (pool)
340 pj_pool_release(pool);
341 return -1;
342}
343
344#if WRITE_ORIGINAL_PCM
345static pj_status_t convert(const char *src, const char *dst)
346{
347 char pcm[320];
348 char frame[160];
349 struct pjmedia_frame in, out;
350
351 fhnd_pcm = fopen(src, "rb");
352 if (!fhnd_pcm)
353 return -1;
354 fhnd = fopen(dst, "wb");
355 if (!fhnd)
356 return -1;
357
358 while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
359
360 in.type = PJMEDIA_FRAME_TYPE_AUDIO;
361 in.buf = pcm;
362 in.size = 320;
363 out.buf = frame;
364
365 if (codec->op->encode(codec, &in, 160, &out) != 0)
366 break;
367
368 if (fwrite(frame, out.size, 1, fhnd) != 1)
369 break;
370
371 }
372
373 fclose(fhnd);
374 fclose(fhnd_pcm);
375 return 0;
376}
377#endif
378
379static void usage(const char *exe)
380{
381 printf("Usage: %s <command> <file>\n", exe);
382 puts("where:");
383 puts(" <command> play|record|send|recv");
384}
385
386int main(int argc, char *argv[])
387{
388 if (argc < 2) {
389 usage(argv[0]);
390 return 1;
391 }
392
393 pj_init();
394
395 init();
396
397 if (stricmp(argv[1], "record")==0) {
398 record_file("FILE.PCM");
399 } else if (stricmp(argv[1], "play")==0) {
400 play_file("FILE.PCM");
401 } else if (stricmp(argv[1], "send")==0) {
402 create_ses_by_remote_sdp(4002, listener_sdp);
403 } else if (stricmp(argv[1], "recv")==0) {
404 create_ses_by_remote_sdp(4000, talker_sdp);
405 } else {
406 usage(argv[0]);
407 }
408 deinit();
409 return 0;
410}