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