summaryrefslogtreecommitdiff
path: root/toxav/phone.c
diff options
context:
space:
mode:
authorirungentoo <irungentoo@gmail.com>2014-02-16 19:13:34 -0500
committerirungentoo <irungentoo@gmail.com>2014-02-16 19:13:34 -0500
commit9d1eb27717472f5655242fac7b8123a44f1ff33c (patch)
treefd205f1ea53749717d9930d93ddb153fc8a16929 /toxav/phone.c
parentfd3dc22ef3bd77beb78faf24cd43e9d2b9706f5d (diff)
parentf79b327fd639b2dcca9d4d2c137f93c1dce622fc (diff)
Merge branch 'av-fix' into av-good
Diffstat (limited to 'toxav/phone.c')
-rwxr-xr-xtoxav/phone.c1308
1 files changed, 1308 insertions, 0 deletions
diff --git a/toxav/phone.c b/toxav/phone.c
new file mode 100755
index 00000000..161275d9
--- /dev/null
+++ b/toxav/phone.c
@@ -0,0 +1,1308 @@
1/** phone.c
2 *
3 * NOTE NOTE NOTE NOTE NOTE NOTE
4 *
5 * This file is for testing/reference purposes only, hence
6 * it is _poorly_ designed and it does not fully reflect the
7 * quaility of msi nor rtp. Although toxmsi* and toxrtp* are tested
8 * there is always possiblity of crashes. If crash occures,
9 * contact me ( mannol ) on either irc channel #tox-dev @ freenode.net:6667
10 * or eniz_vukovic@hotmail.com
11 *
12 * NOTE NOTE NOTE NOTE NOTE NOTE
13 *
14 * Copyright (C) 2013 Tox project All Rights Reserved.
15 *
16 * This file is part of Tox.
17 *
18 * Tox is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * Tox is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
30 *
31 */
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif /* HAVE_CONFIG_H */
36
37#define _BSD_SOURCE
38
39#include <stdio.h>
40#include <string.h>
41#include <stdlib.h>
42#include <stdarg.h>
43#include <unistd.h>
44#include <assert.h>
45#include <math.h>
46#include <pthread.h>
47
48//#include "media.h"
49#include "toxav.h"
50#include "event.h"
51#include "../toxcore/tox.h"
52
53#ifdef TOX_FFMPEG
54/* Video encoding/decoding */
55#include <libavcodec/avcodec.h>
56#include <libavformat/avformat.h>
57#include <libswscale/swscale.h>
58#include <libavdevice/avdevice.h>
59#include <libavutil/opt.h>
60#endif
61
62#include <AL/al.h>
63#include <AL/alc.h>
64#include <SDL/SDL.h>
65#include <SDL/SDL_thread.h>
66
67/* the quit event for SDL */
68#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
69
70#ifdef __linux__
71#define VIDEO_DRIVER "video4linux2"
72#define DEFAULT_WEBCAM "/dev/video0"
73#endif
74
75#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
76#define VIDEO_DRIVER "vfwcap"
77#define DEFAULT_WEBCAM "0"
78#endif
79
80
81/* Define client version */
82#define _USERAGENT "v.0.3.0"
83
84
85struct SDL_Surface *screen;
86
87typedef struct {
88 struct SDL_Overlay *bmp;
89 int width, height;
90} VideoPicture;
91
92
93typedef struct av_friend_s {
94 int _id;
95 int _active; /* 0=false; 1=true; */
96} av_friend_t;
97
98typedef struct av_session_s {
99 /* Encoding/decoding/capturing/playing */
100 ToxAv* av;
101
102 VideoPicture video_picture;
103 struct ALCdevice *audio_capture_device;
104
105 /* context for converting image format to something SDL can use*/
106 struct SwsContext *sws_SDL_r_ctx;
107
108 /* context for converting webcam image format to something the video encoder can use */
109 struct SwsContext *sws_ctx;
110
111 /* Thread running control */
112 int running_decaud, running_encaud,
113 running_decvid, running_encvid;
114
115 pthread_mutex_t _mutex;
116
117 Tox* _messenger;
118 av_friend_t* _friends;
119 int _friend_cout;
120 char _my_public_id[200];
121#ifdef TOX_FFMPEG
122 AVInputFormat *video_input_format;
123 AVFormatContext *video_format_ctx;
124 uint8_t video_stream;
125 AVCodecContext *webcam_decoder_ctx;
126 AVCodec *webcam_decoder;
127#endif
128} av_session_t;
129
130
131void av_allocate_friend(av_session_t* _phone, int _id, int _active)
132{
133 static int _new_id = 0;
134
135 if ( !_phone->_friends ) {
136 _phone->_friends = calloc(sizeof(av_friend_t), 1);
137 _phone->_friend_cout = 1;
138 } else{
139 _phone->_friend_cout ++;
140 _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout);
141 }
142
143 if ( _id == -1 ) {
144 _phone->_friends->_id = _new_id;
145 _new_id ++;
146 } else _phone->_friends->_id = _id;
147
148 _phone->_friends->_active = _active;
149}
150av_friend_t* av_get_friend(av_session_t* _phone, int _id)
151{
152 av_friend_t* _friends = _phone->_friends;
153
154 if ( !_friends ) return NULL;
155
156 int _it = 0;
157 for (; _it < _phone->_friend_cout; _it ++)
158 if ( _friends[_it]._id == _id )
159 return _friends + _it;
160
161 return NULL;
162}
163
164
165/***************** MISC *****************/
166
167void INFO (const char* _format, ...)
168{
169 printf("\r[!] ");
170 va_list _arg;
171 va_start (_arg, _format);
172 vfprintf (stdout, _format, _arg);
173 va_end (_arg);
174 printf("\n\r >> ");
175 fflush(stdout);
176}
177
178unsigned char *hex_string_to_bin(char hex_string[])
179{
180 size_t i, len = strlen(hex_string);
181 unsigned char *val = calloc(sizeof(unsigned char), len);
182 char *pos = hex_string;
183
184 for (i = 0; i < len; ++i, pos += 2)
185 sscanf(pos, "%2hhx", &val[i]);
186
187 return val;
188}
189
190int getinput( char* _buff, size_t _limit, int* _len )
191{
192 if ( fgets(_buff, _limit, stdin) == NULL )
193 return -1;
194
195 *_len = strlen(_buff) - 1;
196
197 /* Get rid of newline */
198 _buff[*_len] = '\0';
199
200 return 0;
201}
202
203char* trim_spaces ( char* buff )
204{
205
206 int _i = 0, _len = strlen(buff);
207
208 char* container = calloc(sizeof(char), _len);
209 int _ci = 0;
210
211 for ( ; _i < _len; _i++ ) {
212 while ( _i < _len && buff[_i] == ' ' )
213 _i++;
214
215 if ( _i < _len ){
216 container[_ci] = buff[_i];
217 _ci ++;
218 }
219 }
220
221 memcpy( buff, container, _ci );
222 buff[_ci] = '\0';
223 free(container);
224 return buff;
225}
226
227#define FRADDR_TOSTR_CHUNK_LEN 8
228
229static void fraddr_to_str(uint8_t *id_bin, char *id_str)
230{
231 uint i, delta = 0, pos_extra = 0, sum_extra = 0;
232
233 for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
234 sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
235
236 if ((i + 1) == TOX_CLIENT_ID_SIZE)
237 pos_extra = 2 * (i + 1) + delta;
238
239 if (i >= TOX_CLIENT_ID_SIZE)
240 sum_extra |= id_bin[i];
241
242 if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
243 id_str[2 * (i + 1) + delta] = ' ';
244 delta++;
245 }
246 }
247
248 id_str[2 * i + delta] = 0;
249
250 if (!sum_extra)
251 id_str[pos_extra] = 0;
252}
253
254/*********************************************
255 *********************************************
256 *********************************************
257 *********************************************
258 *********************************************
259 *********************************************
260 *********************************************
261 *********************************************
262 */
263
264
265/*
266 * How av stuff _should_ look like
267 */
268/*
269int display_received_frame(av_session_t* _phone, vpx_image_t *image)
270{
271 CodecState* cs = get_cs_temp(_phone->av);
272 AVPicture pict;
273 SDL_LockYUVOverlay(_phone->video_picture.bmp);
274
275 pict.data[0] = _phone->video_picture.bmp->pixels[0];
276 pict.data[1] = _phone->video_picture.bmp->pixels[2];
277 pict.data[2] = _phone->video_picture.bmp->pixels[1];
278 pict.linesize[0] = _phone->video_picture.bmp->pitches[0];
279 pict.linesize[1] = _phone->video_picture.bmp->pitches[2];
280 pict.linesize[2] = _phone->video_picture.bmp->pitches[1];
281 */
282 /* Convert the image into YUV format that SDL uses *//*
283 sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
284 cs->video_decoder_ctx->height, pict.data, pict.linesize );
285
286 SDL_UnlockYUVOverlay(_phone->video_picture.bmp);
287 SDL_Rect rect;
288 rect.x = 0;
289 rect.y = 0;
290 rect.w = cs->video_decoder_ctx->width;
291 rect.h = cs->video_decoder_ctx->height;
292 SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);
293 return 1;
294}
295*/
296#ifdef TOX_FFMPEG
297void *encode_video_thread(void *arg)
298{
299 INFO("Started encode video thread!");
300
301 av_session_t* _phone = arg;
302
303 _phone->running_encvid = 1;
304 //CodecState *cs = get_cs_temp(_phone->av);
305 AVPacket pkt1, *packet = &pkt1;
306 //int p = 0;
307 //int got_packet;
308 int video_frame_finished;
309 AVFrame *s_video_frame;
310 AVFrame *webcam_frame;
311 s_video_frame = avcodec_alloc_frame();
312 webcam_frame = avcodec_alloc_frame();
313 //AVPacket enc_video_packet;
314
315 uint8_t *buffer;
316 int numBytes;
317 /* Determine required buffer size and allocate buffer */
318 numBytes = avpicture_get_size(PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height);
319 buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1);
320 avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width,
321 _phone->webcam_decoder_ctx->height);
322 _phone->sws_ctx = sws_getContext(_phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height,
323 _phone->webcam_decoder_ctx->pix_fmt, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
324 SWS_BILINEAR, NULL, NULL, NULL);
325
326
327 vpx_image_t *image =
328 vpx_img_alloc(NULL, VPX_IMG_FMT_I420, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, 1);
329
330 //uint32_t frame_counter = 0;
331 while (_phone->running_encvid) {
332
333 if (av_read_frame(_phone->video_format_ctx, packet) < 0) {
334 printf("error reading frame\n");
335
336 if (_phone->video_format_ctx->pb->error != 0)
337 break;
338
339 continue;
340 }
341
342 if (packet->stream_index == _phone->video_stream) {
343 if (avcodec_decode_video2(_phone->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
344 printf("couldn't decode\n");
345 continue;
346 }
347
348 av_free_packet(packet);
349 sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
350 _phone->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
351 /* create a new I-frame every 60 frames */
352 //++p;
353 /*
354 if (p == 60) {
355
356 s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
357 } else if (p == 61) {
358 s_video_frame->pict_type = AV_PICTURE_TYPE_I ;
359 p = 0;
360 } else {
361 s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
362 }*/
363
364 if (video_frame_finished) {
365 memcpy(image->planes[VPX_PLANE_Y], s_video_frame->data[0], s_video_frame->linesize[0] * _phone->webcam_decoder_ctx->height);
366 memcpy(image->planes[VPX_PLANE_U], s_video_frame->data[1], s_video_frame->linesize[1] * _phone->webcam_decoder_ctx->height / 2);
367 memcpy(image->planes[VPX_PLANE_V], s_video_frame->data[2], s_video_frame->linesize[2] * _phone->webcam_decoder_ctx->height / 2);
368 toxav_send_video (_phone->av, image);
369 //if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) {
370 /*if (vpx_codec_encode(&cs->v_encoder, image, frame_counter, 1, 0, 0) != VPX_CODEC_OK) {
371 printf("could not encode video frame\n");
372 continue;
373 }
374 ++frame_counter;
375
376 vpx_codec_iter_t iter = NULL;
377 vpx_codec_cx_pkt_t *pkt;
378 while( (pkt = vpx_codec_get_cx_data(&cs->v_encoder, &iter)) ) {
379 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
380 toxav_send_rtp_payload(_phone->av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz);
381 }*/
382 //if (!got_packet) {
383 // continue;
384 //}
385
386 //if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
387
388 //toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
389
390 //av_free_packet(&enc_video_packet);
391 }
392 } else {
393 av_free_packet(packet);
394 }
395 }
396
397 vpx_img_free(image);
398
399 /* clean up codecs */
400 //pthread_mutex_lock(&cs->ctrl_mutex);
401 av_free(buffer);
402 av_free(webcam_frame);
403 av_free(s_video_frame);
404 sws_freeContext(_phone->sws_ctx);
405 //avcodec_close(webcam_decoder_ctx);
406 //avcodec_close(cs->video_encoder_ctx);
407 //pthread_mutex_unlock(&cs->ctrl_mutex);
408
409 _phone->running_encvid = -1;
410
411 pthread_exit ( NULL );
412}
413#endif
414
415void *encode_audio_thread(void *arg)
416{
417 INFO("Started encode audio thread!");
418 av_session_t* _phone = arg;
419 _phone->running_encaud = 1;
420
421 int ret = 0;
422 int16_t frame[4096];
423 int frame_size = AUDIO_FRAME_SIZE;
424 ALint sample = 0;
425 alcCaptureStart((ALCdevice*)_phone->audio_capture_device);
426
427 while (_phone->running_encaud) {
428 alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
429
430 if (sample >= frame_size) {
431 alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size);
432
433 ret = toxav_send_audio(_phone->av, frame, frame_size);
434
435 if (ret < 0)
436 printf("Could not encode or send audio packet\n");
437
438 } else {
439 usleep(1000);
440 }
441 }
442
443 /* clean up codecs *
444 pthread_mutex_lock(&cs->ctrl_mutex);* /
445 alcCaptureStop((ALCdevice*)_phone->audio_capture_device);
446 alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device);
447 / *pthread_mutex_unlock(&cs->ctrl_mutex);*/
448 _phone->running_encaud = -1;
449 pthread_exit ( NULL );
450}
451
452void convert_to_rgb(vpx_image_t *img, unsigned char *out)
453{
454 const int w = img->d_w;
455 const int w2 = w/2;
456 const int pstride = w*3;
457 const int h = img->d_h;
458 const int h2 = h/2;
459
460 const int strideY = img->stride[0];
461 const int strideU = img->stride[1];
462 const int strideV = img->stride[2];
463 int posy, posx;
464 for (posy = 0; posy < h2; posy++) {
465 unsigned char *dst = out + pstride * (posy * 2);
466 unsigned char *dst2 = out + pstride * (posy * 2 + 1);
467 const unsigned char *srcY = img->planes[0] + strideY * posy * 2;
468 const unsigned char *srcY2 = img->planes[0] + strideY * (posy * 2 + 1);
469 const unsigned char *srcU = img->planes[1] + strideU * posy;
470 const unsigned char *srcV = img->planes[2] + strideV * posy;
471
472 for (posx = 0; posx < w2; posx++) {
473 unsigned char Y,U,V;
474 short R,G,B;
475 short iR,iG,iB;
476
477 U = *(srcU++); V = *(srcV++);
478 iR = (351 * (V-128)) / 256;
479 iG = - (179 * (V-128)) / 256 - (86 * (U-128)) / 256;
480 iB = (444 * (U-128)) / 256;
481
482 Y = *(srcY++);
483 R = Y + iR ; G = Y + iG ; B = Y + iB ;
484 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
485 *(dst++) = R; *(dst++) = G; *(dst++) = B;
486
487 Y = *(srcY2++);
488 R = Y + iR ; G = Y + iG ; B = Y + iB ;
489 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
490 *(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
491
492 Y = *(srcY++) ;
493 R = Y + iR ; G = Y + iG ; B = Y + iB ;
494 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
495 *(dst++) = R; *(dst++) = G; *(dst++) = B;
496
497 Y = *(srcY2++);
498 R = Y + iR ; G = Y + iG ; B = Y + iB ;
499 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
500 *(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
501 }
502 }
503}
504
505#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff })
506
507void *decode_video_thread(void *arg)
508{
509 INFO("Started decode video thread!");
510 av_session_t* _phone = arg;
511 _phone->running_decvid = 1;
512
513 //CodecState *cs = get_cs_temp(_phone->av);
514 //cs->video_stream = 0;
515
516 //int recved_size;
517 //uint8_t dest[RTP_PAYLOAD_SIZE];
518
519 //int dec_frame_finished;
520 //AVFrame *r_video_frame;
521 //r_video_frame = avcodec_alloc_frame();
522 //AVPacket dec_video_packet;
523 //av_new_packet (&dec_video_packet, 65536);
524 int width = 0;
525 int height = 0;
526
527 while (_phone->running_decvid) {
528 //recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, dest);
529 //if (recved_size) {
530 vpx_image_t *image;
531 if (toxav_recv_video(_phone->av, &image) == 0) {
532 //memcpy(dec_video_packet.data, dest, recved_size);
533 //dec_video_packet.size = recved_size;
534
535 //avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
536
537 //if (dec_frame_finished) {
538
539 /* Check if size has changed */
540 if (image->d_w != width || image->d_h != height) {
541
542 width = image->d_w;
543 height = image->d_h;
544
545 printf("w: %d h: %d \n", width, height);
546
547 screen = SDL_SetVideoMode(width, height, 0, 0);
548
549 //if (_phone->video_picture.bmp)
550 // SDL_FreeYUVOverlay(_phone->video_picture.bmp);
551
552 //_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
553 // _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
554 // SWS_BILINEAR, NULL, NULL, NULL);
555 }
556 uint8_t *rgb_image = malloc(width*height*3);
557 convert_to_rgb(image, rgb_image);
558 SDL_Surface* img_surface = SDL_CreateRGBSurfaceFrom(rgb_image, width, height, 24, width * 3, mask32(0), mask32(1), mask32(2), 0);
559 if(SDL_BlitSurface(img_surface, NULL, screen, NULL) == 0)
560 SDL_UpdateRect(screen, 0, 0, 0, 0);
561 /*
562 SDL_LockYUVOverlay(_phone->video_picture.bmp);
563 memcpy(_phone->video_picture.bmp->pixels[0], image->planes[VPX_PLANE_Y], _phone->video_picture.bmp->pitches[0] * height);
564 memcpy(_phone->video_picture.bmp->pixels[1], image->planes[VPX_PLANE_V], _phone->video_picture.bmp->pitches[1] * height / 2);
565 memcpy(_phone->video_picture.bmp->pixels[2], image->planes[VPX_PLANE_U], _phone->video_picture.bmp->pitches[2] * height / 2);
566
567 SDL_Rect rect;
568 rect.x = 0;
569 rect.y = 0;
570 rect.w = width;
571 rect.h = height;
572 SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);*/
573 free(rgb_image);
574 //display_received_frame(_phone, image);
575
576 } //else {
577 /* TODO: request the sender to create a new i-frame immediatly */
578 //printf("Bad video packet\n");
579 //}
580 //}
581
582 usleep(1000);
583 }
584
585 /* clean up codecs */
586 //av_free(r_video_frame);
587
588 //pthread_mutex_lock(&cs->ctrl_mutex);
589 //avcodec_close(cs->video_decoder_ctx);
590 //pthread_mutex_unlock(&cs->ctrl_mutex);
591
592 _phone->running_decvid = -1;
593
594 pthread_exit ( NULL );
595}
596
597void *decode_audio_thread(void *arg)
598{
599 INFO("Started decode audio thread!");
600 av_session_t* _phone = arg;
601 _phone->running_decaud = 1;
602
603 //int recved_size;
604 //uint8_t dest [RTP_PAYLOAD_SIZE];
605
606 int frame_size = AUDIO_FRAME_SIZE;
607 //int data_size;
608
609 ALCdevice *dev;
610 ALCcontext *ctx;
611 ALuint source, *buffers;
612 dev = alcOpenDevice(NULL);
613 ctx = alcCreateContext(dev, NULL);
614 alcMakeContextCurrent(ctx);
615 int openal_buffers = 5;
616
617 buffers = calloc(sizeof(ALuint) * openal_buffers,1);
618 alGenBuffers(openal_buffers, buffers);
619 alGenSources((ALuint)1, &source);
620 alSourcei(source, AL_LOOPING, AL_FALSE);
621
622 ALuint buffer;
623 ALint ready;
624
625 uint16_t zeros[frame_size];
626 memset(zeros, 0, frame_size);
627 int16_t PCM[frame_size];
628
629 int i;
630 for (i = 0; i < openal_buffers; ++i) {
631 alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
632 }
633
634 alSourceQueueBuffers(source, openal_buffers, buffers);
635 alSourcePlay(source);
636
637 if (alGetError() != AL_NO_ERROR) {
638 fprintf(stderr, "Error starting audio\n");
639 goto ending;
640 }
641
642 int dec_frame_len = 0;
643
644 while (_phone->running_decaud) {
645
646 alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
647 if (ready <= 0)
648 continue;
649
650 dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM);
651
652 /* Play the packet */
653 if (dec_frame_len > 0) {
654 alSourceUnqueueBuffers(source, 1, &buffer);
655 alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
656 int error = alGetError();
657
658 if (error != AL_NO_ERROR) {
659 fprintf(stderr, "Error setting buffer %d\n", error);
660 break;
661 }
662
663 alSourceQueueBuffers(source, 1, &buffer);
664
665 if (alGetError() != AL_NO_ERROR) {
666 fprintf(stderr, "Error: could not buffer audio\n");
667 break;
668 }
669
670 alGetSourcei(source, AL_SOURCE_STATE, &ready);
671
672 if (ready != AL_PLAYING) alSourcePlay(source);
673 }
674
675 usleep(1000);
676 }
677
678
679ending:
680 /* clean up codecs */
681 //pthread_mutex_lock(&cs->ctrl_mutex);
682 /*
683 alDeleteSources(1, &source);
684 alDeleteBuffers(openal_buffers, buffers);
685 alcMakeContextCurrent(NULL);
686 alcDestroyContext(ctx);
687 alcCloseDevice(dev);
688 */
689 //pthread_mutex_unlock(&cs->ctrl_mutex);
690
691 _phone->running_decaud = -1;
692
693 pthread_exit ( NULL );
694}
695
696
697
698
699
700int phone_startmedia_loop ( ToxAv* arg )
701{
702 if ( !arg ){
703 return -1;
704 }
705
706 toxav_prepare_transmission(arg);
707
708 /*
709 * Rise all threads
710 */
711#ifdef TOX_FFMPEG
712 /* Only checks for last peer */
713 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
714 0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) )
715 {
716 INFO("Error while starting encode_video_thread()");
717 return -1;
718 }
719#endif
720 /* Always send audio */
721 if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) )
722 {
723 INFO("Error while starting encode_audio_thread()");
724 return -1;
725 }
726
727 /* Only checks for last peer */
728 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
729 0 > event.rise(decode_video_thread, toxav_get_agent_handler(arg)) )
730 {
731 INFO("Error while starting decode_video_thread()");
732 return -1;
733 }
734
735 if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) )
736 {
737 INFO("Error while starting decode_audio_thread()");
738 return -1;
739 }
740
741
742 return 0;
743}
744
745
746
747
748
749
750/*********************************************
751 *********************************************
752 *********************************************
753 *********************************************
754 *********************************************
755 *********************************************
756 *********************************************
757 *********************************************
758 */
759
760
761/* Some example callbacks */
762
763void* callback_recv_invite ( void* _arg )
764{
765 assert(_arg);
766
767 switch ( toxav_get_peer_transmission_type(_arg, 0) ){
768 case TypeAudio:
769 INFO( "Incoming audio call!");
770 break;
771 case TypeVideo:
772 INFO( "Incoming video call!");
773 break;
774 }
775
776 pthread_exit(NULL);
777}
778void* callback_recv_ringing ( void* _arg )
779{
780 INFO ( "Ringing!" );
781 pthread_exit(NULL);
782}
783void* callback_recv_starting ( void* _arg )
784{
785 if ( 0 != phone_startmedia_loop(_arg) ){
786 INFO("Starting call failed!");
787 } else {
788 INFO ("Call started! ( press h to hangup )");
789 }
790 pthread_exit(NULL);
791}
792void* callback_recv_ending ( void* _arg )
793{
794 av_session_t* _phone = toxav_get_agent_handler(_arg);
795
796 _phone->running_encaud = 0;
797 _phone->running_decaud = 0;
798 _phone->running_encvid = 0;
799 _phone->running_decvid = 0;
800
801 /* Wait until all threads are done */
802
803 while ( _phone->running_encaud != -1 ||
804 _phone->running_decaud != -1 ||
805 _phone->running_encvid != -1 ||
806 _phone->running_decvid != -1 )
807
808 usleep(10000000);
809
810 INFO ( "Call ended!" );
811 pthread_exit(NULL);
812}
813
814void* callback_recv_error ( void* _arg )
815{
816 /*MSISession* _session = _arg;
817
818 INFO( "Error: %s", _session->last_error_str ); */
819 pthread_exit(NULL);
820}
821
822void* callback_call_started ( void* _arg )
823{
824 if ( 0 != phone_startmedia_loop(_arg) ){
825 INFO("Starting call failed!");
826 } else {
827 INFO ("Call started! ( press h to hangup )");
828 }
829
830 pthread_exit(NULL);
831}
832void* callback_call_canceled ( void* _arg )
833{
834 INFO ( "Call canceled!" );
835 pthread_exit(NULL);
836}
837void* callback_call_rejected ( void* _arg )
838{
839 INFO ( "Call rejected!" );
840 pthread_exit(NULL);
841}
842void* callback_call_ended ( void* _arg )
843{
844 av_session_t* _phone = toxav_get_agent_handler(_arg);
845
846 _phone->running_encaud = 0;
847 _phone->running_decaud = 0;
848 _phone->running_encvid = 0;
849 _phone->running_decvid = 0;
850
851 /* Wait until all threads are done */
852
853 while ( _phone->running_encaud != -1 ||
854 _phone->running_decaud != -1 ||
855 _phone->running_encvid != -1 ||
856 _phone->running_decvid != -1 )
857
858 usleep(10000000);
859
860 toxav_kill_transmission(_phone->av);
861 INFO ( "Call ended!" );
862 pthread_exit(NULL);
863}
864
865void* callback_requ_timeout ( void* _arg )
866{
867 INFO( "No answer! " );
868 pthread_exit(NULL);
869}
870
871av_session_t* av_init_session()
872{
873 av_session_t* _retu = malloc(sizeof(av_session_t));
874
875 /* Initialize our mutex */
876 pthread_mutex_init ( &_retu->_mutex, NULL );
877
878 _retu->_messenger = tox_new(1);
879
880 if ( !_retu->_messenger ) {
881 fprintf ( stderr, "tox_new() failed!\n" );
882 return NULL;
883 }
884
885 _retu->_friends = NULL;
886
887
888 const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
889 int i = 0;
890 const ALchar *device_names[20];
891
892 if ( _device_list ) {
893 INFO("\nAvailable Capture Devices are:");
894
895 while (*_device_list ) {
896 device_names[i] = _device_list;
897 INFO("%d) %s", i, device_names[i]);
898 _device_list += strlen( _device_list ) + 1;
899 ++i;
900 }
901 }
902
903 INFO("Enter capture device number");
904
905 char dev[2]; char* left;
906 char* warned_ = fgets(dev, 2, stdin);
907 (void)warned_;
908 long selection = strtol(dev, &left, 10);
909
910 if ( *left ) {
911 printf("'%s' is not a number!", dev);
912 fflush(stdout);
913 exit(EXIT_FAILURE);
914 }
915 else {
916 INFO("Selected: %d ( %s )", selection, device_names[selection]);
917 }
918
919 _retu->audio_capture_device =
920 (struct ALCdevice*)alcCaptureOpenDevice(
921 device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
922
923
924 if (alcGetError((ALCdevice*)_retu->audio_capture_device) != AL_NO_ERROR) {
925 printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device));
926 return 0;
927 }
928 uint16_t height = 0, width = 0;
929#ifdef TOX_FFMPEG
930 avdevice_register_all();
931 avcodec_register_all();
932 av_register_all();
933
934 _retu->video_input_format = av_find_input_format(VIDEO_DRIVER);
935 if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
936 fprintf(stderr, "Opening video_input_format failed!\n");
937 //return -1;
938 return NULL;
939 }
940
941 avformat_find_stream_info(_retu->video_format_ctx, NULL);
942 av_dump_format(_retu->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
943
944 for (i = 0; i < _retu->video_format_ctx->nb_streams; ++i) {
945 if (_retu->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
946 _retu->video_stream = i;
947 break;
948 }
949 }
950
951 _retu->webcam_decoder_ctx = _retu->video_format_ctx->streams[_retu->video_stream]->codec;
952 _retu->webcam_decoder = avcodec_find_decoder(_retu->webcam_decoder_ctx->codec_id);
953
954 if (_retu->webcam_decoder == NULL) {
955 fprintf(stderr, "Unsupported codec!\n");
956 //return -1;
957 return NULL;
958 }
959
960 if (_retu->webcam_decoder_ctx == NULL) {
961 fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
962 //return -1;
963 return NULL;
964 }
965
966 if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
967 fprintf(stderr, "Opening webcam decoder failed!\n");
968 //return -1;
969 return NULL;
970 }
971 width = _retu->webcam_decoder_ctx->width;
972 height = _retu->webcam_decoder_ctx->height;
973#endif
974 uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
975 tox_get_address(_retu->_messenger, _byte_address );
976 fraddr_to_str( _byte_address, _retu->_my_public_id );
977
978
979 _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT, width, height);
980
981 /* ------------------ */
982
983 toxav_register_callstate_callback(callback_call_started, OnStart);
984 toxav_register_callstate_callback(callback_call_canceled, OnCancel);
985 toxav_register_callstate_callback(callback_call_rejected, OnReject);
986 toxav_register_callstate_callback(callback_call_ended, OnEnd);
987 toxav_register_callstate_callback(callback_recv_invite, OnInvite);
988
989 toxav_register_callstate_callback(callback_recv_ringing, OnRinging);
990 toxav_register_callstate_callback(callback_recv_starting, OnStarting);
991 toxav_register_callstate_callback(callback_recv_ending, OnEnding);
992
993 toxav_register_callstate_callback(callback_recv_error, OnError);
994 toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout);
995
996 /* ------------------ */
997
998 return _retu;
999}
1000
1001int av_terminate_session(av_session_t* _phone)
1002{
1003 toxav_hangup(_phone->av);
1004
1005 free(_phone->_friends);
1006 pthread_mutex_destroy ( &_phone->_mutex );
1007
1008 Tox* _p = _phone->_messenger;
1009 _phone->_messenger = NULL; usleep(100000); /* Wait for tox_poll to end */
1010
1011 tox_kill(_p);
1012 toxav_kill(_phone->av);
1013
1014 free(_phone);
1015
1016 printf("\r[i] Quit!\n");
1017 return 0;
1018}
1019
1020/****** AV HELPER FUNCTIONS ******/
1021
1022/* Auto accept friend request */
1023void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata)
1024{
1025 av_session_t* _phone = _userdata;
1026 av_allocate_friend (_phone, -1, 0);
1027
1028 INFO("Got friend request with message: %s", _data);
1029
1030 tox_add_friend_norequest(_phone->_messenger, _public_key);
1031
1032 INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id );
1033}
1034
1035void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata)
1036{
1037 av_session_t* _phone = _userdata;
1038 INFO("Friend no. %d is online", _friendnumber);
1039
1040 av_friend_t* _this_friend = av_get_friend(_phone, _friendnumber);
1041
1042 if ( !_this_friend ) {
1043 INFO("But it's not registered!");
1044 return;
1045 }
1046
1047 (*_this_friend)._active = 1;
1048}
1049
1050int av_add_friend(av_session_t* _phone, char* _friend_hash)
1051{
1052 trim_spaces(_friend_hash);
1053
1054 unsigned char *_bin_string = hex_string_to_bin(_friend_hash);
1055 int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT, sizeof("Tox phone "_USERAGENT));
1056 free(_bin_string);
1057
1058 if ( _number >= 0) {
1059 INFO("Added friend as %d", _number );
1060 av_allocate_friend(_phone, _number, 0);
1061 }
1062 else
1063 INFO("Unknown error %i", _number );
1064
1065 return _number;
1066}
1067
1068int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port)
1069{
1070 unsigned char *_binary_string = hex_string_to_bin(_dht_key);
1071
1072 uint16_t _port = htons(_dht_port);
1073
1074 int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string );
1075
1076 free(_binary_string);
1077
1078 return _if ? 0 : -1;
1079}
1080
1081/*********************************/
1082
1083void do_phone ( av_session_t* _phone )
1084{
1085 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
1086 "Usage: \n"
1087 "f [pubkey] (add friend)\n"
1088 "c [a/v] (type) [friend] (friend id) (calls friend if online)\n"
1089 "h (if call is active hang up)\n"
1090 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
1091 "r (reject incoming call)\n"
1092 "q (quit)\n"
1093 "================================================================================"
1094 );
1095
1096 while ( 1 )
1097 {
1098 char _line [ 1500 ];
1099 int _len;
1100
1101 if ( -1 == getinput(_line, 1500, &_len) ){
1102 printf(" >> ");
1103 fflush(stdout);
1104 continue;
1105 }
1106
1107 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){
1108 INFO("Invalid input!");
1109 continue;
1110 }
1111
1112 switch (_line[0]){
1113
1114 case 'f':
1115 {
1116 char _id [128];
1117 strncpy(_id, _line + 2, 128);
1118
1119 av_add_friend(_phone, _id);
1120
1121 } break;
1122 case 'c':
1123 {
1124 ToxAvCallType _ctype;
1125
1126 if ( _len < 5 ){
1127 INFO("Invalid input; usage: c a/v [friend]");
1128 break;
1129 }
1130 else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
1131 _ctype = TypeAudio;
1132 }
1133 else { /* video */
1134 _ctype = TypeVideo;
1135 }
1136
1137 char* _end;
1138 int _friend = strtol(_line + 4, &_end, 10);
1139
1140 if ( *_end ){
1141 INFO("Friend num has to be numerical value");
1142 break;
1143 }
1144
1145 if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ){
1146 INFO("Already in a call");
1147 break;
1148 }
1149 else INFO("Calling friend: %d!", _friend);
1150
1151 } break;
1152 case 'h':
1153 {
1154 if ( toxav_hangup(_phone->av) == ErrorNoCall ) {
1155 INFO("No call!");
1156 break;
1157 }
1158 else INFO("Hung up...");
1159
1160 } break;
1161 case 'a':
1162 {
1163 ToxAvError rc;
1164
1165 if ( _len > 1 && _line[2] == 'v' ) {
1166 rc = toxav_answer(_phone->av, TypeVideo);
1167 } else
1168 rc = toxav_answer(_phone->av, TypeAudio);
1169
1170 if ( rc == ErrorInvalidState ) {
1171 INFO("No call to answer!");
1172 }
1173
1174 } break;
1175 case 'r':
1176 {
1177 if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState )
1178 INFO("No state to cancel!");
1179 else INFO("Call Rejected...");
1180
1181 } break;
1182 case 'q':
1183 {
1184 INFO("Quitting!");
1185 return;
1186 }
1187 case '\n':
1188 {
1189 }
1190 default:
1191 {
1192 } break;
1193
1194 }
1195
1196 }
1197}
1198
1199void* tox_poll (void* _messenger_p)
1200{
1201 Tox** _messenger = _messenger_p;
1202 while( *_messenger ) {
1203 tox_do(*_messenger);
1204 usleep(10000);
1205 }
1206
1207 pthread_exit(NULL);
1208}
1209
1210int av_wait_dht(av_session_t* _phone, int _wait_seconds, const char* _ip, char* _key, unsigned short _port)
1211{
1212 if ( !_wait_seconds )
1213 return -1;
1214
1215 int _waited = 0;
1216
1217 while( !tox_isconnected(_phone->_messenger) ) {
1218
1219 if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) )
1220 {
1221 INFO("Could not connect to: %s", _ip);
1222 av_terminate_session(_phone);
1223 return -1;
1224 }
1225
1226 if ( _waited >= _wait_seconds ) return 0;
1227
1228 printf(".");
1229 fflush(stdout);
1230
1231 _waited ++;
1232 usleep(1000000);
1233 }
1234
1235 int _r = _wait_seconds - _waited;
1236 return _r ? _r : 1;
1237}
1238/* ---------------------- */
1239
1240int print_help ( const char* _name )
1241{
1242 printf ( "Usage: %s [IP] [PORT] [KEY]\n"
1243 "\t[IP] (DHT ip)\n"
1244 "\t[PORT] (DHT port)\n"
1245 "\t[KEY] (DHT public key)\n"
1246 "P.S. Friends and key are stored in ./tox_phone.conf\n"
1247 ,_name );
1248 return 1;
1249}
1250
1251int main ( int argc, char* argv [] )
1252{
1253 if ( argc < 1 || argc < 4 )
1254 return print_help(argv[0]);
1255
1256 char* _convertable;
1257
1258
1259 const char* _ip = argv[1];
1260 char* _key = argv[3];
1261 unsigned short _port = strtol(argv[2], &_convertable, 10);
1262
1263 if ( *_convertable ) {
1264 printf("Invalid port: cannot convert string to long: %s", _convertable);
1265 return 1;
1266 }
1267
1268 av_session_t* _phone = av_init_session();
1269
1270 tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
1271 tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
1272
1273
1274 INFO("\r================================================================================\n"
1275 "[!] Trying dht@%s:%d"
1276 , _ip, _port);
1277
1278 /* Start tox protocol */
1279 event.rise( tox_poll, &_phone->_messenger );
1280
1281 /* Just clean one line */
1282 printf("\r \r");
1283 fflush(stdout);
1284
1285 int _r;
1286 int _wait_seconds = 5;
1287 for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --;
1288
1289
1290 if ( -1 == _r ) {
1291 INFO("Error while connecting to dht: %s:%d", _ip, _port);
1292 av_terminate_session(_phone);
1293 return 1;
1294 }
1295
1296 INFO("CONNECTED!\n"
1297 "================================================================================\n"
1298 "%s\n"
1299 "================================================================================"
1300 , _phone->_my_public_id );
1301
1302
1303 do_phone (_phone);
1304
1305 av_terminate_session(_phone);
1306
1307 return 0;
1308}