diff options
Diffstat (limited to 'toxav/phone.c')
-rw-r--r-- | toxav/phone.c | 1352 |
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 | |||
85 | struct SDL_Surface *screen; | ||
86 | |||
87 | typedef struct { | ||
88 | struct SDL_Overlay *bmp; | ||
89 | int width, height; | ||
90 | } VideoPicture; | ||
91 | |||
92 | |||
93 | typedef struct av_friend_s { | ||
94 | int _id; | ||
95 | int _active; /* 0=false; 1=true; */ | ||
96 | } av_friend_t; | ||
97 | |||
98 | typedef 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 | |||
131 | void 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 | } | ||
150 | av_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 | |||
168 | void 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 | |||
179 | unsigned 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 | |||
191 | int 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 | |||
204 | char *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 | |||
230 | static 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 | /* | ||
270 | int 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 *//* | ||
284 | sws_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 | |||
287 | SDL_UnlockYUVOverlay(_phone->video_picture.bmp); | ||
288 | SDL_Rect rect; | ||
289 | rect.x = 0; | ||
290 | rect.y = 0; | ||
291 | rect.w = cs->video_decoder_ctx->width; | ||
292 | rect.h = cs->video_decoder_ctx->height; | ||
293 | SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect); | ||
294 | return 1; | ||
295 | } | ||
296 | */ | ||
297 | #ifdef TOX_FFMPEG | ||
298 | void *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 | |||
420 | void *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 | |||
457 | void 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 | |||
538 | void *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 | |||
634 | void *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 | |||
718 | ending: | ||
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 | |||
739 | int 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 | |||
801 | void *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 | } | ||
817 | void *callback_recv_ringing ( void *_arg ) | ||
818 | { | ||
819 | INFO ( "Ringing!" ); | ||
820 | pthread_exit(NULL); | ||
821 | } | ||
822 | void *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 | } | ||
832 | void *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 | |||
854 | void *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 | |||
862 | void *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 | } | ||
872 | void *callback_call_canceled ( void *_arg ) | ||
873 | { | ||
874 | INFO ( "Call canceled!" ); | ||
875 | pthread_exit(NULL); | ||
876 | } | ||
877 | void *callback_call_rejected ( void *_arg ) | ||
878 | { | ||
879 | INFO ( "Call rejected!" ); | ||
880 | pthread_exit(NULL); | ||
881 | } | ||
882 | void *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 | |||
905 | void *callback_requ_timeout ( void *_arg ) | ||
906 | { | ||
907 | INFO( "No answer! " ); | ||
908 | pthread_exit(NULL); | ||
909 | } | ||
910 | |||
911 | av_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 | |||
1044 | int 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 */ | ||
1067 | void 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 | |||
1079 | void 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 | |||
1094 | int 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 | |||
1112 | int 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 | |||
1127 | void 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 | |||
1242 | void *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 | |||
1254 | int 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 | |||
1283 | int 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 | |||
1294 | int 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 | } | ||