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