summaryrefslogtreecommitdiff
path: root/toxmsi/phone.c
diff options
context:
space:
mode:
authorMartijn <martijnvdc@gmail.com>2013-10-13 16:14:27 +0200
committerBtbN <btbn@btbn.de>2013-10-13 16:14:27 +0200
commit1b971de651278429eea312f3240e1c5b8fbc67a4 (patch)
tree83e57945f3e7e9b0a305947935aed9d49735d2ab /toxmsi/phone.c
parentf2497b65897f034e736c234242fb08b2fc3b4a24 (diff)
tox A/V: encode/decode and display/playback
Diffstat (limited to 'toxmsi/phone.c')
-rw-r--r--toxmsi/phone.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/toxmsi/phone.c b/toxmsi/phone.c
new file mode 100644
index 00000000..010f26aa
--- /dev/null
+++ b/toxmsi/phone.c
@@ -0,0 +1,645 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif /* HAVE_CONFIG_H */
4
5#define _BSD_SOURCE
6#define _GNU_SOURCE
7
8#define _CT_PHONE
9
10#ifdef _CT_PHONE
11#include "phone.h"
12#include <stdarg.h>
13#include <unistd.h>
14#include <stdio.h>
15#include <string.h>
16#include <stdlib.h>
17#include <termios.h>
18#include <pthread.h>
19#include "AV_codec.h"
20
21
22
23void INFO (const char* _format, ...)
24{
25 printf("\r[!] ");
26 va_list _arg;
27 va_start (_arg, _format);
28 vfprintf (stdout, _format, _arg);
29 va_end (_arg);
30 printf("\n\r >> ");
31 fflush(stdout);
32}
33
34int rtp_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length )
35{
36 phone_t* _phone = _object;
37 rtp_msg_t* _msg;
38 uint8_t _payload_id;
39
40 if ( _phone->_msi->_call && _phone->_msi->_call->_state == call_active ){
41
42 _msg = rtp_msg_parse ( NULL, data + 1, length - 1 ); /* ignore marker byte */
43
44 if ( !_msg )
45 return 0;
46
47 _payload_id = rtp_header_get_setting_payload_type(_msg->_header);
48
49 if ( _payload_id == _PAYLOAD_OPUS && _phone->_rtp_audio )
50 rtp_store_msg(_phone->_rtp_audio, _msg);
51 else if ( _payload_id == _PAYLOAD_VP8 && _phone->_rtp_video )
52 rtp_store_msg(_phone->_rtp_video, _msg);
53 else rtp_free_msg( NULL, _msg);
54 }
55
56 return SUCCESS;
57}
58int msi_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length )
59{
60 msi_session_t* _session = _object;
61 msi_msg_t* _msg;
62
63 _msg = msi_parse_msg ( data + 1 ); /* ignore marker byte */
64
65 if ( _msg ) {
66 /* my current solution for "hole punching" */
67 _session->_friend_id = ip_port;
68 } else {
69 return FAILURE;
70 }
71
72 /* place message in a session */
73 msi_store_msg(_session, _msg);
74
75 return SUCCESS;
76}
77
78void* phone_receivepacket ( void* _phone_p )
79{
80 phone_t* _phone = _phone_p;
81
82
83 networking_registerhandler(_phone->_networking, MSI_PACKET, msi_handlepacket, _phone->_msi);
84 networking_registerhandler(_phone->_networking, RTP_PACKET, rtp_handlepacket, _phone);
85
86 /* Now start main networking loop */
87 while ( _phone->_networking ) { /* so not thread safe */
88 networking_poll(_phone->_networking);
89 usleep(10000);
90 }
91
92 pthread_exit ( NULL );
93}
94
95/* Media transport callback */
96typedef struct hmtc_args_s {
97 rtp_session_t** _rtp_audio;
98 rtp_session_t** _rtp_video;
99 call_type* _local_type_call;
100 call_state* _this_call;
101 void *_core_handler;
102} hmtc_args_t;
103
104void* phone_handle_media_transport_poll ( void* _hmtc_args_p )
105{
106 rtp_msg_t* _audio_msg, * _video_msg;
107
108 hmtc_args_t* _hmtc_args = _hmtc_args_p;
109
110 rtp_session_t* _rtp_audio = *_hmtc_args->_rtp_audio;
111 rtp_session_t* _rtp_video = *_hmtc_args->_rtp_video;
112
113 call_type* _type = _hmtc_args->_local_type_call;
114 void* _core_handler = _hmtc_args->_core_handler;
115
116
117 call_state* _this_call = _hmtc_args->_this_call;
118
119 while ( *_this_call == call_active ) {
120
121 // THREADLOCK()
122
123 _audio_msg = rtp_recv_msg ( _rtp_audio );
124 _video_msg = rtp_recv_msg ( _rtp_video );
125
126 if ( _audio_msg ) {
127 /* Do whatever with msg */
128 puts("audio");
129 /* Do whatever with msg
130 puts(_audio_msg->_data);*/
131 rtp_free_msg ( _rtp_audio, _audio_msg );
132 }
133
134 if ( _video_msg ) {
135 /* Do whatever with msg */
136 puts("video");
137 /* Do whatever with msg
138 puts(_video_msg->_data); */
139 rtp_free_msg ( _rtp_video, _video_msg );
140 _video_msg = NULL;
141 }
142 /* -------------------- */
143
144 _audio_msg = rtp_msg_new ( _rtp_audio, (const uint8_t*)"audio\0", 6 ) ;
145 rtp_send_msg ( _rtp_audio, _audio_msg, _core_handler );
146 _audio_msg = NULL;
147
148 if ( *_type == type_video ){ /* if local call send video */
149 _video_msg = rtp_msg_new ( _rtp_video, (const uint8_t*)"video\0", 6 ) ;
150 rtp_send_msg ( _rtp_video, _video_msg, _core_handler );
151 _video_msg = NULL;
152 }
153
154 //THREADUNLOCK()
155
156 usleep ( 10000 );
157 /* -------------------- */
158 }
159
160 //THREADLOCK()
161
162 if ( _audio_msg ){
163 rtp_free_msg(_rtp_audio, _audio_msg);
164 }
165
166 if ( _video_msg ) {
167 rtp_free_msg(_rtp_video, _video_msg);
168 }
169
170 rtp_release_session_recv(_rtp_video);
171 rtp_release_session_recv(_rtp_audio);
172
173 rtp_terminate_session(_rtp_audio);
174 rtp_terminate_session(_rtp_video);
175
176 *_hmtc_args->_rtp_audio = NULL;
177 *_hmtc_args->_rtp_video = NULL;
178
179 free(_hmtc_args_p);
180
181 //THREADUNLOCK()
182
183 INFO("Media thread finished!");
184
185 pthread_exit ( NULL );
186}
187
188pthread_t phone_startmedia_loop ( phone_t* _phone )
189{
190 if ( !_phone ){
191 return 0;
192 }
193
194 int _status;
195
196 uint8_t _prefix = RTP_PACKET;
197
198 pthread_t _rtp_tid;
199 int _rtp_thread_running = 1;
200
201 _phone->_rtp_audio = rtp_init_session ( -1, 1 );
202 rtp_set_prefix ( _phone->_rtp_audio, &_prefix, 1 );
203 rtp_add_receiver ( _phone->_rtp_audio, &_phone->_msi->_friend_id );
204 rtp_set_payload_type(_phone->_rtp_audio, _PAYLOAD_OPUS);
205
206 _phone->_rtp_video = rtp_init_session ( -1, 1 );
207 rtp_set_prefix ( _phone->_rtp_video, &_prefix, 1 );
208 rtp_add_receiver ( _phone->_rtp_video, &_phone->_msi->_friend_id );
209 rtp_set_payload_type(_phone->_rtp_video, _PAYLOAD_VP8);
210
211
212
213 hmtc_args_t* rtp_targs = calloc(sizeof(hmtc_args_t),1);
214
215
216 rtp_targs->_rtp_audio = &_phone->_rtp_audio;
217 rtp_targs->_rtp_video = &_phone->_rtp_video;
218 rtp_targs->_local_type_call = &_phone->_msi->_call->_type_local;
219 rtp_targs->_this_call = &_phone->_msi->_call->_state;
220 rtp_targs->_core_handler = _phone->_networking;
221
222 codec_state *cs;
223 cs=_phone->cs;
224 //_status = pthread_create ( &_rtp_tid, NULL, phone_handle_media_transport_poll, rtp_targs );
225 cs->_rtp_audio=_phone->_rtp_audio;
226 cs->_rtp_video=_phone->_rtp_video;
227 cs->_networking=_phone->_networking;
228 cs->socket=_phone->_tox_sock;
229 cs->quit = 0;
230
231 printf("support: %d %d\n",cs->support_send_audio,cs->support_send_video);
232
233 if(cs->support_send_audio&&cs->support_send_video) /* quick fix */
234 pthread_create(&_phone->cs->encode_audio_thread, NULL, encode_audio_thread, _phone->cs);
235 if(cs->support_receive_audio)
236 pthread_create(&_phone->cs->decode_audio_thread, NULL, decode_audio_thread, _phone->cs);
237
238 if(cs->support_send_video)
239 pthread_create(&_phone->cs->encode_video_thread, NULL, encode_video_thread, _phone->cs);
240 if(cs->support_receive_video)
241 pthread_create(&_phone->cs->decode_video_thread, NULL, decode_video_thread, _phone->cs);
242//
243 return 1;
244
245
246
247
248}
249
250
251/* Some example callbacks */
252
253MCBTYPE callback_recv_invite ( MCBARGS )
254{
255 const char* _call_type;
256
257 msi_session_t* _msi = _arg;
258
259 /* Get the last one */
260 call_type _type = _msi->_call->_type_peer[_msi->_call->_participants - 1];
261
262 switch ( _type ){
263 case type_audio:
264 _call_type = "audio";
265 break;
266 case type_video:
267 _call_type = "video";
268 break;
269 }
270
271 INFO( "Incoming %s call!", _call_type );
272
273}
274MCBTYPE callback_recv_trying ( MCBARGS )
275{
276 INFO ( "Trying...");
277}
278MCBTYPE callback_recv_ringing ( MCBARGS )
279{
280 INFO ( "Ringing!" );
281}
282MCBTYPE callback_recv_starting ( MCBARGS )
283{
284 msi_session_t* _session = _arg;
285 if ( !phone_startmedia_loop(_session->_agent_handler) ){
286 INFO("Starting call failed!");
287 } else {
288 INFO ("Call started! ( press h to hangup )");
289 }
290}
291MCBTYPE callback_recv_ending ( MCBARGS )
292{
293 msi_session_t* _session = _arg;
294 phone_t * _phone = _session->_agent_handler;
295 _phone->cs->quit=1;
296 if(_phone->cs->encode_video_thread)
297 pthread_join(_phone->cs->encode_video_thread,NULL);
298 if(_phone->cs->encode_audio_thread)
299 pthread_join(_phone->cs->encode_audio_thread,NULL);
300 if(_phone->cs->decode_audio_thread)
301 pthread_join(_phone->cs->decode_audio_thread,NULL);
302 if(_phone->cs->decode_video_thread)
303 pthread_join(_phone->cs->decode_video_thread,NULL);
304 SDL_Quit();
305 printf("all A/V threads successfully shut down\n");
306
307 INFO ( "Call ended!" );
308}
309
310MCBTYPE callback_recv_error ( MCBARGS )
311{
312 msi_session_t* _session = _arg;
313
314 INFO( "Error: %s", _session->_last_error_str );
315}
316
317MCBTYPE callback_call_started ( MCBARGS )
318{
319 msi_session_t* _session = _arg;
320 if ( !phone_startmedia_loop(_session->_agent_handler) ){
321 INFO("Starting call failed!");
322 } else {
323 INFO ("Call started! ( press h to hangup )");
324 }
325
326}
327MCBTYPE callback_call_canceled ( MCBARGS )
328{
329 INFO ( "Call canceled!" );
330}
331MCBTYPE callback_call_rejected ( MCBARGS )
332{
333 INFO ( "Call rejected!\n" );
334}
335MCBTYPE callback_call_ended ( MCBARGS )
336{
337
338 msi_session_t* _session = _arg;
339 phone_t * _phone = _session->_agent_handler;
340 _phone->cs->quit=1;
341 if(_phone->cs->encode_video_thread)
342 pthread_join(_phone->cs->encode_video_thread,NULL);
343 if(_phone->cs->encode_audio_thread)
344 pthread_join(_phone->cs->encode_audio_thread,NULL);
345 if(_phone->cs->decode_audio_thread)
346 pthread_join(_phone->cs->decode_audio_thread,NULL);
347 if(_phone->cs->decode_video_thread)
348 pthread_join(_phone->cs->decode_video_thread,NULL);
349 SDL_Quit();
350 printf("all A/V threads successfully shut down\n");
351
352 INFO ( "Call ended!" );
353}
354
355MCBTYPE callback_requ_timeout ( MCBARGS )
356{
357 INFO( "No answer! " );
358}
359
360
361phone_t* initPhone(uint16_t _listen_port, uint16_t _send_port)
362{
363 phone_t* _retu = calloc(sizeof(phone_t),1);
364 _retu->cs = av_calloc(sizeof(codec_state),1);
365
366 /* Initialize our mutex */
367 pthread_mutex_init ( &_mutex, NULL );
368
369 IP_Port _local;
370 ip_init(&_local.ip, 0);
371 _local.ip.ip4.uint32 = htonl ( INADDR_ANY );
372
373 /* Bind local receive port to any address */
374 _retu->_networking = new_networking ( _local.ip, _listen_port );
375
376 if ( !_retu->_networking ) {
377 fprintf ( stderr, "new_networking() failed!\n" );
378 return NULL;
379 }
380
381 _retu->_send_port = _send_port;
382 _retu->_recv_port = _listen_port;
383
384 _retu->_tox_sock = _retu->_networking->sock;
385
386 _retu->_rtp_audio = NULL;
387 _retu->_rtp_video = NULL;
388
389
390 /* Initialize msi */
391 _retu->_msi = msi_init_session ( _retu->_networking, (const uint8_t*)_USERAGENT );
392
393 if ( !_retu->_msi ) {
394 fprintf ( stderr, "msi_init_session() failed\n" );
395 return NULL;
396 }
397
398 /* Initiate codecs */
399 init_encoder(_retu->cs);
400 init_decoder(_retu->cs);
401
402 _retu->_msi->_agent_handler = _retu;
403 /* Initiate callbacks */
404 msi_register_callback_send ( sendpacket ); /* Using core's send */
405
406 msi_register_callback_call_started ( callback_call_started );
407 msi_register_callback_call_canceled ( callback_call_canceled );
408 msi_register_callback_call_rejected ( callback_call_rejected );
409 msi_register_callback_call_ended ( callback_call_ended );
410
411 msi_register_callback_recv_invite ( callback_recv_invite );
412 msi_register_callback_recv_ringing ( callback_recv_ringing );
413 msi_register_callback_recv_starting ( callback_recv_starting );
414 msi_register_callback_recv_ending ( callback_recv_ending );
415 msi_register_callback_recv_error(callback_recv_error);
416
417 msi_register_callback_requ_timeout ( callback_requ_timeout );
418 /* ------------------ */
419
420 /* Now start msi main loop. It's a must!
421 * Define a frequency in ms; 10 ms is just fine
422 */
423 msi_start_main_loop ( _retu->_msi, 10 );
424
425 return _retu;
426}
427
428pthread_t phone_startmain_loop(phone_t* _phone)
429{
430 int _status;
431 /* Start receive thread */
432 pthread_t _recv_thread, _phone_loop_thread;
433 _status = pthread_create ( &_recv_thread, NULL, phone_receivepacket, _phone );
434
435 if ( _status < 0 ) {
436 printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
437 return 0;
438 }
439
440 _status = pthread_detach ( _recv_thread );
441
442 if ( _status < 0 ) {
443 printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
444 return 0;
445 }
446
447 _status = pthread_create ( &_phone_loop_thread, NULL, phone_poll, _phone );
448
449 if ( _status < 0 ) {
450 printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
451 return 0;
452 }
453
454 _status = pthread_join ( _phone_loop_thread, NULL );
455
456 if ( _status < 0 ) {
457 printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
458 return 0;
459 }
460
461 return _phone_loop_thread;
462}
463
464void* phone_poll ( void* _p_phone )
465{
466 phone_t* _phone = _p_phone;
467
468 int _status = SUCCESS;
469
470 char _line[100];
471 size_t _len;
472
473
474 char _dest[17]; /* For parsing destination ip */
475 memset(_dest, '\0', 17);
476
477 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
478 "Usage: \n"
479 "c [a/v] (type) [0.0.0.0] (dest ip) (calls dest ip)\n"
480 "h (if call is active hang up)\n"
481 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
482 "r (reject incoming call)\n"
483 "q (quit)\n"
484 "================================================================================"
485 );
486
487 while ( 1 )
488 {
489 fgets(_line, sizeof(_line), stdin);
490 int i;
491 for (i = 0; i < 100; i++) {
492 if (_line[i] == '\n') {
493 _line[i] = '\0';
494 }
495 }
496 _len = strlen(_line);
497
498 if ( !_len ){
499 printf(" >> "); fflush(stdout);
500 continue;
501 }
502
503 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){
504 INFO("Invalid input!");
505 continue;
506 }
507
508 switch (_line[0]){
509
510 case 'c':
511 {
512 if ( _phone->_msi->_call ){
513 INFO("Already in a call");
514 break;
515 }
516
517 call_type _ctype;
518 if ( _len < 11 ){
519 INFO("Invalid input; usage: c a/v 0.0.0.0");
520 break;
521 }
522 else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
523 _ctype = type_audio;
524 }
525 else { /* video */
526 _ctype = type_video;
527 }
528
529 strcpy(_dest, _line + 4 );
530 _status = t_setipport(_dest, _phone->_send_port, &(_phone->_msi->_friend_id));
531
532 if ( _status < 0 ){
533 INFO("Could not resolve address!");
534 } else {
535 /* Set timeout */
536 msi_invite ( _phone->_msi, _ctype, 30 * 1000 );
537 INFO("Calling!");
538 }
539
540 t_memset((uint8_t*)_dest, '\0', 17);
541
542 } break;
543 case 'h':
544 {
545 if ( !_phone->_msi->_call ){
546 break;
547 }
548
549 msi_hangup(_phone->_msi);
550
551 INFO("Hung up...");
552
553 } break;
554 case 'a':
555 {
556 if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) {
557 break;
558 }
559
560 if ( _len > 1 && _line[2] == 'v' )
561 msi_answer(_phone->_msi, type_video);
562 else
563 msi_answer(_phone->_msi, type_audio);
564
565 } break;
566 case 'r':
567 {
568 if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ){
569 break;
570 }
571
572 msi_reject(_phone->_msi);
573
574 INFO("Call Rejected...");
575
576 } break;
577 case 'q':
578 {
579 INFO("Quitting!");
580 pthread_exit(NULL);
581 }
582 default:
583 {
584 INFO("Invalid command!");
585 } break;
586
587 }
588 usleep(1000);
589 }
590
591 pthread_exit(NULL);
592}
593
594int quitPhone(phone_t* _phone)
595{
596 if ( _phone->_msi->_call ){
597 msi_hangup(_phone->_msi); /* Hangup the phone first */
598 }
599
600 msi_terminate_session(_phone->_msi);
601 pthread_mutex_destroy ( &_mutex );
602
603 printf("\r[i] Quit!\n");
604 return SUCCESS;
605}
606
607/* ---------------------- */
608
609int print_help ( const char* _name )
610{
611 printf ( "Usage: %s -m (mode) -r/s ( for setting the ports on test version )\n", _name );
612 return FAILURE;
613}
614
615int main ( int argc, char* argv [] )
616{
617 arg_t* _args = parse_args ( argc, argv );
618
619 const char* _mode = find_arg_duble ( _args, "-m" );
620 uint16_t _listen_port;
621 uint16_t _send_port;
622
623 if ( !_mode )
624 return print_help ( argv[0] );
625
626 if ( _mode[0] == 'r' ) {
627 _send_port = 31000;
628 _listen_port = 31001;
629 } else if ( _mode[0] == 's' ) {
630 _send_port = 31001;
631 _listen_port = 31000;
632 } else return print_help ( argv[0] );
633
634 phone_t* _phone = initPhone(_listen_port, _send_port);
635
636 if ( _phone ){
637 phone_startmain_loop(_phone);
638
639 quitPhone(_phone);
640 }
641
642 return SUCCESS;
643}
644
645#endif /* _CT_PHONE */