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