diff options
Diffstat (limited to 'testing/av_test.c')
-rw-r--r-- | testing/av_test.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/testing/av_test.c b/testing/av_test.c new file mode 100644 index 00000000..fa6a831f --- /dev/null +++ b/testing/av_test.c | |||
@@ -0,0 +1,731 @@ | |||
1 | /** av_test.c | ||
2 | * | ||
3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | * Compile with (Linux only; in newly created directory toxcore/dir_name): | ||
21 | * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \ | ||
22 | * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio | ||
23 | */ | ||
24 | |||
25 | |||
26 | #include "../toxav/toxav.h" | ||
27 | #include "../toxcore/tox.h" | ||
28 | #include "../toxcore/util.h" | ||
29 | #include "../toxcore/network.h" /* current_time_monotonic() */ | ||
30 | |||
31 | /* Playing audio data */ | ||
32 | #include <portaudio.h> | ||
33 | /* Reading audio */ | ||
34 | #include <sndfile.h> | ||
35 | |||
36 | /* Reading and Displaying video data */ | ||
37 | #include <opencv/cv.h> | ||
38 | #include <opencv/highgui.h> | ||
39 | #include <opencv/cvwimage.h> | ||
40 | |||
41 | #include <sys/stat.h> | ||
42 | #include <assert.h> | ||
43 | #include <stdio.h> | ||
44 | #include <stdlib.h> | ||
45 | #include <time.h> | ||
46 | #include <string.h> | ||
47 | #include <errno.h> | ||
48 | #include <unistd.h> | ||
49 | |||
50 | #define c_sleep(x) usleep(1000*x) | ||
51 | |||
52 | |||
53 | #define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X) | ||
54 | |||
55 | // RGB -> YUV | ||
56 | #define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) | ||
57 | #define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) | ||
58 | #define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) | ||
59 | |||
60 | // YUV -> RGB | ||
61 | #define C(Y) ((Y) - 16 ) | ||
62 | #define D(U) ((U) - 128 ) | ||
63 | #define E(V) ((V) - 128 ) | ||
64 | |||
65 | #define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8) | ||
66 | #define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) | ||
67 | #define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8) | ||
68 | |||
69 | |||
70 | #define TEST_TRANSFER_A 0 | ||
71 | #define TEST_TRANSFER_V 1 | ||
72 | |||
73 | |||
74 | typedef struct { | ||
75 | bool incoming; | ||
76 | uint32_t state; | ||
77 | pthread_mutex_t arb_mutex[1]; | ||
78 | RingBuffer* arb; /* Audio ring buffer */ | ||
79 | |||
80 | } CallControl; | ||
81 | |||
82 | struct toxav_thread_data { | ||
83 | ToxAV* AliceAV; | ||
84 | ToxAV* BobAV; | ||
85 | int32_t sig; | ||
86 | }; | ||
87 | |||
88 | const char* vdout = "AV Test"; /* Video output */ | ||
89 | PaStream* adout = NULL; /* Audio output */ | ||
90 | |||
91 | typedef struct { | ||
92 | uint16_t size; | ||
93 | int16_t data[]; | ||
94 | } frame; | ||
95 | |||
96 | void* pa_write_thread (void* d) | ||
97 | { | ||
98 | /* The purpose of this thread is to make sure Pa_WriteStream will not block | ||
99 | * toxav_iterate thread | ||
100 | */ | ||
101 | CallControl* cc = d; | ||
102 | |||
103 | while (Pa_IsStreamActive(adout)) { | ||
104 | frame* f; | ||
105 | pthread_mutex_lock(cc->arb_mutex); | ||
106 | if (rb_read(cc->arb, (void**)&f)) { | ||
107 | pthread_mutex_unlock(cc->arb_mutex); | ||
108 | Pa_WriteStream(adout, f->data, f->size); | ||
109 | free(f); | ||
110 | } else { | ||
111 | pthread_mutex_unlock(cc->arb_mutex); | ||
112 | c_sleep(10); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Callbacks | ||
119 | */ | ||
120 | void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) | ||
121 | { | ||
122 | printf("Handling CALL callback\n"); | ||
123 | ((CallControl*)user_data)->incoming = true; | ||
124 | } | ||
125 | void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) | ||
126 | { | ||
127 | printf("Handling CALL STATE callback: %d\n", state); | ||
128 | ((CallControl*)user_data)->state = state; | ||
129 | } | ||
130 | void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||
131 | uint16_t width, uint16_t height, | ||
132 | uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||
133 | int32_t ystride, int32_t ustride, int32_t vstride, | ||
134 | void *user_data) | ||
135 | { | ||
136 | ystride = abs(ystride); | ||
137 | ustride = abs(ustride); | ||
138 | vstride = abs(vstride); | ||
139 | |||
140 | uint16_t *img_data = malloc(height * width * 6); | ||
141 | |||
142 | unsigned long int i, j; | ||
143 | for (i = 0; i < height; ++i) { | ||
144 | for (j = 0; j < width; ++j) { | ||
145 | uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j); | ||
146 | int yx = y[(i * ystride) + j]; | ||
147 | int ux = u[((i / 2) * ustride) + (j / 2)]; | ||
148 | int vx = v[((i / 2) * vstride) + (j / 2)]; | ||
149 | |||
150 | point[0] = YUV2R(yx, ux, vx); | ||
151 | point[1] = YUV2G(yx, ux, vx); | ||
152 | point[2] = YUV2B(yx, ux, vx); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | |||
157 | CvMat mat = cvMat(height, width, CV_8UC3, img_data); | ||
158 | |||
159 | CvSize sz = {.height = height, .width = width}; | ||
160 | |||
161 | IplImage* header = cvCreateImageHeader(sz, 1, 3); | ||
162 | IplImage* img = cvGetImage(&mat, header); | ||
163 | cvShowImage(vdout, img); | ||
164 | free(img_data); | ||
165 | } | ||
166 | void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||
167 | int16_t const *pcm, | ||
168 | size_t sample_count, | ||
169 | uint8_t channels, | ||
170 | uint32_t sampling_rate, | ||
171 | void *user_data) | ||
172 | { | ||
173 | CallControl* cc = user_data; | ||
174 | frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels); | ||
175 | memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels); | ||
176 | f->size = sample_count; | ||
177 | |||
178 | pthread_mutex_lock(cc->arb_mutex); | ||
179 | free(rb_write(cc->arb, f)); | ||
180 | pthread_mutex_unlock(cc->arb_mutex); | ||
181 | } | ||
182 | void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, | ||
183 | uint32_t audio_bit_rate, uint32_t video_bit_rate, | ||
184 | void *user_data) | ||
185 | { | ||
186 | printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate); | ||
187 | } | ||
188 | void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) | ||
189 | { | ||
190 | if (length == 7 && memcmp("gentoo", data, 7) == 0) { | ||
191 | assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | */ | ||
197 | void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC) | ||
198 | { | ||
199 | Tox* Alice; | ||
200 | Tox* Bob; | ||
201 | |||
202 | struct Tox_Options opts; | ||
203 | tox_options_default(&opts); | ||
204 | |||
205 | opts.end_port = 0; | ||
206 | opts.ipv6_enabled = false; | ||
207 | |||
208 | { | ||
209 | TOX_ERR_NEW error; | ||
210 | |||
211 | opts.start_port = 33445; | ||
212 | *bootstrap = tox_new(&opts, &error); | ||
213 | assert(error == TOX_ERR_NEW_OK); | ||
214 | |||
215 | opts.start_port = 33455; | ||
216 | Alice = tox_new(&opts, &error); | ||
217 | assert(error == TOX_ERR_NEW_OK); | ||
218 | |||
219 | opts.start_port = 33465; | ||
220 | Bob = tox_new(&opts, &error); | ||
221 | assert(error == TOX_ERR_NEW_OK); | ||
222 | } | ||
223 | |||
224 | printf("Created 3 instances of Tox\n"); | ||
225 | printf("Preparing network...\n"); | ||
226 | long long unsigned int cur_time = time(NULL); | ||
227 | |||
228 | uint32_t to_compare = 974536; | ||
229 | uint8_t address[TOX_ADDRESS_SIZE]; | ||
230 | |||
231 | tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); | ||
232 | tox_self_get_address(Alice, address); | ||
233 | |||
234 | |||
235 | assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); | ||
236 | |||
237 | uint8_t off = 1; | ||
238 | |||
239 | while (1) { | ||
240 | tox_iterate(*bootstrap); | ||
241 | tox_iterate(Alice); | ||
242 | tox_iterate(Bob); | ||
243 | |||
244 | if (tox_self_get_connection_status(*bootstrap) && | ||
245 | tox_self_get_connection_status(Alice) && | ||
246 | tox_self_get_connection_status(Bob) && off) { | ||
247 | printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); | ||
248 | off = 0; | ||
249 | } | ||
250 | |||
251 | if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && | ||
252 | tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) | ||
253 | break; | ||
254 | |||
255 | c_sleep(20); | ||
256 | } | ||
257 | |||
258 | |||
259 | TOXAV_ERR_NEW rc; | ||
260 | *AliceAV = toxav_new(Alice, &rc); | ||
261 | assert(rc == TOXAV_ERR_NEW_OK); | ||
262 | |||
263 | *BobAV = toxav_new(Bob, &rc); | ||
264 | assert(rc == TOXAV_ERR_NEW_OK); | ||
265 | |||
266 | |||
267 | /* Alice */ | ||
268 | toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); | ||
269 | toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); | ||
270 | toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC); | ||
271 | toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); | ||
272 | toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); | ||
273 | |||
274 | /* Bob */ | ||
275 | toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); | ||
276 | toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); | ||
277 | toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC); | ||
278 | toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); | ||
279 | toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); | ||
280 | |||
281 | |||
282 | printf("Created 2 instances of ToxAV\n"); | ||
283 | printf("All set after %llu seconds!\n", time(NULL) - cur_time); | ||
284 | } | ||
285 | int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV) | ||
286 | { | ||
287 | tox_iterate(bootstrap); | ||
288 | tox_iterate(toxav_get_tox(AliceAV)); | ||
289 | tox_iterate(toxav_get_tox(BobAV)); | ||
290 | |||
291 | return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV))); | ||
292 | } | ||
293 | void* iterate_toxav (void * data) | ||
294 | { | ||
295 | struct toxav_thread_data* data_cast = data; | ||
296 | #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 | ||
297 | cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE); | ||
298 | #endif | ||
299 | |||
300 | while (data_cast->sig == 0) { | ||
301 | toxav_iterate(data_cast->AliceAV); | ||
302 | toxav_iterate(data_cast->BobAV); | ||
303 | int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); | ||
304 | |||
305 | printf("\rIteration interval: %d ", rc); | ||
306 | fflush(stdout); | ||
307 | |||
308 | #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 | ||
309 | if (!rc) | ||
310 | rc = 1; | ||
311 | |||
312 | cvWaitKey(rc); | ||
313 | #else | ||
314 | c_sleep(rc); | ||
315 | #endif | ||
316 | } | ||
317 | |||
318 | data_cast->sig = 1; | ||
319 | |||
320 | #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 | ||
321 | cvDestroyWindow(vdout); | ||
322 | #endif | ||
323 | |||
324 | pthread_exit(NULL); | ||
325 | } | ||
326 | |||
327 | int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) | ||
328 | { | ||
329 | int32_t strides[3] = { 1280, 640, 640 }; | ||
330 | uint8_t* planes[3] = { | ||
331 | malloc(img->height * img->width), | ||
332 | malloc(img->height * img->width / 4), | ||
333 | malloc(img->height * img->width / 4), | ||
334 | }; | ||
335 | |||
336 | int x_chroma_shift = 1; | ||
337 | int y_chroma_shift = 1; | ||
338 | |||
339 | int x, y; | ||
340 | for (y = 0; y < img->height; ++y) { | ||
341 | for (x = 0; x < img->width; ++x) { | ||
342 | uint8_t r = img->imageData[(x + y * img->width) * 3 + 0]; | ||
343 | uint8_t g = img->imageData[(x + y * img->width) * 3 + 1]; | ||
344 | uint8_t b = img->imageData[(x + y * img->width) * 3 + 2]; | ||
345 | |||
346 | planes[0][x + y * strides[0]] = RGB2Y(r, g, b); | ||
347 | if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) { | ||
348 | const int i = x / (1 << x_chroma_shift); | ||
349 | const int j = y / (1 << y_chroma_shift); | ||
350 | planes[1][i + j * strides[1]] = RGB2U(r, g, b); | ||
351 | planes[2][i + j * strides[2]] = RGB2V(r, g, b); | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | |||
356 | int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, | ||
357 | planes[0], planes[1], planes[2], NULL); | ||
358 | free(planes[0]); | ||
359 | free(planes[1]); | ||
360 | free(planes[2]); | ||
361 | return rc; | ||
362 | } | ||
363 | int print_audio_devices() | ||
364 | { | ||
365 | int i = 0; | ||
366 | for (i = 0; i < Pa_GetDeviceCount(); ++i) { | ||
367 | const PaDeviceInfo* info = Pa_GetDeviceInfo(i); | ||
368 | if (info) | ||
369 | printf("%d) %s\n", i, info->name); | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | int print_help (const char* name) | ||
375 | { | ||
376 | printf("Usage: %s -[a:v:o:dh]\n" | ||
377 | "-a <path> audio input file\n" | ||
378 | "-b <ms> audio frame duration\n" | ||
379 | "-v <path> video input file\n" | ||
380 | "-x <ms> video frame duration\n" | ||
381 | "-o <idx> output audio device index\n" | ||
382 | "-d print output audio devices\n" | ||
383 | "-h print this help\n", name); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | int main (int argc, char** argv) | ||
389 | { | ||
390 | freopen("/dev/zero", "w", stderr); | ||
391 | Pa_Initialize(); | ||
392 | |||
393 | struct stat st; | ||
394 | |||
395 | /* AV files for testing */ | ||
396 | const char* af_name = NULL; | ||
397 | const char* vf_name = NULL; | ||
398 | long audio_out_dev_idx = -1; | ||
399 | |||
400 | int32_t audio_frame_duration = 20; | ||
401 | int32_t video_frame_duration = 10; | ||
402 | |||
403 | /* Parse settings */ | ||
404 | CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) { | ||
405 | case 'a': | ||
406 | af_name = optarg; | ||
407 | goto CHECK_ARG; | ||
408 | case 'b':{ | ||
409 | char *d; | ||
410 | audio_frame_duration = strtol(optarg, &d, 10); | ||
411 | if (*d) { | ||
412 | printf("Invalid value for argument: 'b'"); | ||
413 | exit(1); | ||
414 | } | ||
415 | goto CHECK_ARG; | ||
416 | } | ||
417 | case 'v': | ||
418 | vf_name = optarg; | ||
419 | goto CHECK_ARG; | ||
420 | case 'x':{ | ||
421 | char *d; | ||
422 | video_frame_duration = strtol(optarg, &d, 10); | ||
423 | if (*d) { | ||
424 | printf("Invalid value for argument: 'x'"); | ||
425 | exit(1); | ||
426 | } | ||
427 | goto CHECK_ARG; | ||
428 | } | ||
429 | case 'o': { | ||
430 | char *d; | ||
431 | audio_out_dev_idx = strtol(optarg, &d, 10); | ||
432 | if (*d) { | ||
433 | printf("Invalid value for argument: 'o'"); | ||
434 | exit(1); | ||
435 | } | ||
436 | goto CHECK_ARG; | ||
437 | } | ||
438 | case 'd': | ||
439 | return print_audio_devices(); | ||
440 | case 'h': | ||
441 | return print_help(argv[0]); | ||
442 | case '?': | ||
443 | exit(1); | ||
444 | case -1:; | ||
445 | } | ||
446 | |||
447 | { /* Check files */ | ||
448 | if (!af_name) { | ||
449 | printf("Required audio input file!\n"); | ||
450 | exit(1); | ||
451 | } | ||
452 | |||
453 | if (!vf_name) { | ||
454 | printf("Required video input file!\n"); | ||
455 | exit(1); | ||
456 | } | ||
457 | |||
458 | /* Check for files */ | ||
459 | if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) | ||
460 | { | ||
461 | printf("%s doesn't seem to be a regular file!\n", af_name); | ||
462 | exit(1); | ||
463 | } | ||
464 | |||
465 | if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) | ||
466 | { | ||
467 | printf("%s doesn't seem to be a regular file!\n", vf_name); | ||
468 | exit(1); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | if (audio_out_dev_idx < 0) | ||
473 | audio_out_dev_idx = Pa_GetDefaultOutputDevice(); | ||
474 | |||
475 | const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx); | ||
476 | if (!audio_dev) { | ||
477 | fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx); | ||
478 | return 1; | ||
479 | } | ||
480 | |||
481 | printf("Using audio device: %s\n", audio_dev->name); | ||
482 | printf("Using audio file: %s\n", af_name); | ||
483 | printf("Using video file: %s\n", vf_name); | ||
484 | |||
485 | /* START TOX NETWORK */ | ||
486 | |||
487 | Tox *bootstrap; | ||
488 | ToxAV *AliceAV; | ||
489 | ToxAV *BobAV; | ||
490 | |||
491 | CallControl AliceCC; | ||
492 | CallControl BobCC; | ||
493 | |||
494 | initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC); | ||
495 | |||
496 | if (TEST_TRANSFER_A) { | ||
497 | SNDFILE* af_handle; | ||
498 | SF_INFO af_info; | ||
499 | |||
500 | printf("\nTrying audio enc/dec...\n"); | ||
501 | |||
502 | memset(&AliceCC, 0, sizeof(CallControl)); | ||
503 | memset(&BobCC, 0, sizeof(CallControl)); | ||
504 | |||
505 | pthread_mutex_init(AliceCC.arb_mutex, NULL); | ||
506 | pthread_mutex_init(BobCC.arb_mutex, NULL); | ||
507 | |||
508 | AliceCC.arb = rb_new(16); | ||
509 | BobCC.arb = rb_new(16); | ||
510 | |||
511 | { /* Call */ | ||
512 | TOXAV_ERR_CALL rc; | ||
513 | toxav_call(AliceAV, 0, 48, 0, &rc); | ||
514 | |||
515 | if (rc != TOXAV_ERR_CALL_OK) { | ||
516 | printf("toxav_call failed: %d\n", rc); | ||
517 | exit(1); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | while (!BobCC.incoming) | ||
522 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
523 | |||
524 | { /* Answer */ | ||
525 | TOXAV_ERR_ANSWER rc; | ||
526 | toxav_answer(BobAV, 0, 48, 0, &rc); | ||
527 | |||
528 | if (rc != TOXAV_ERR_ANSWER_OK) { | ||
529 | printf("toxav_answer failed: %d\n", rc); | ||
530 | exit(1); | ||
531 | } | ||
532 | } | ||
533 | |||
534 | while (AliceCC.state == 0) | ||
535 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
536 | |||
537 | /* Open audio file */ | ||
538 | af_handle = sf_open(af_name, SFM_READ, &af_info); | ||
539 | if (af_handle == NULL) { | ||
540 | printf("Failed to open the file.\n"); | ||
541 | exit(1); | ||
542 | } | ||
543 | |||
544 | int16_t PCM[5760]; | ||
545 | |||
546 | time_t start_time = time(NULL); | ||
547 | time_t expected_time = af_info.frames / af_info.samplerate + 2; | ||
548 | |||
549 | |||
550 | /* Start decode thread */ | ||
551 | struct toxav_thread_data data = { | ||
552 | .AliceAV = AliceAV, | ||
553 | .BobAV = BobAV, | ||
554 | .sig = 0 | ||
555 | }; | ||
556 | |||
557 | pthread_t dect; | ||
558 | pthread_create(&dect, NULL, iterate_toxav, &data); | ||
559 | pthread_detach(dect); | ||
560 | |||
561 | int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels; | ||
562 | |||
563 | struct PaStreamParameters output; | ||
564 | output.device = audio_out_dev_idx; | ||
565 | output.channelCount = af_info.channels; | ||
566 | output.sampleFormat = paInt16; | ||
567 | output.suggestedLatency = audio_dev->defaultHighOutputLatency; | ||
568 | output.hostApiSpecificStreamInfo = NULL; | ||
569 | |||
570 | PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL); | ||
571 | assert(err == paNoError); | ||
572 | |||
573 | err = Pa_StartStream(adout); | ||
574 | assert(err == paNoError); | ||
575 | |||
576 | // toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL); | ||
577 | |||
578 | /* Start write thread */ | ||
579 | pthread_t t; | ||
580 | pthread_create(&t, NULL, pa_write_thread, &BobCC); | ||
581 | pthread_detach(t); | ||
582 | |||
583 | printf("Sample rate %d\n", af_info.samplerate); | ||
584 | while (start_time + expected_time > time(NULL) ) { | ||
585 | uint64_t enc_start_time = current_time_monotonic(); | ||
586 | int64_t count = sf_read_short(af_handle, PCM, frame_size); | ||
587 | if (count > 0) { | ||
588 | TOXAV_ERR_SEND_FRAME rc; | ||
589 | if (toxav_audio_send_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { | ||
590 | printf("Error sending frame of size %ld: %d\n", count, rc); | ||
591 | } | ||
592 | } | ||
593 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
594 | c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1)); | ||
595 | } | ||
596 | |||
597 | printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time); | ||
598 | |||
599 | Pa_StopStream(adout); | ||
600 | sf_close(af_handle); | ||
601 | |||
602 | { /* Hangup */ | ||
603 | TOXAV_ERR_CALL_CONTROL rc; | ||
604 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); | ||
605 | |||
606 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { | ||
607 | printf("toxav_call_control failed: %d\n", rc); | ||
608 | exit(1); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
613 | assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); | ||
614 | |||
615 | /* Stop decode thread */ | ||
616 | data.sig = -1; | ||
617 | while(data.sig != 1) | ||
618 | pthread_yield(); | ||
619 | |||
620 | pthread_mutex_destroy(AliceCC.arb_mutex); | ||
621 | pthread_mutex_destroy(BobCC.arb_mutex); | ||
622 | |||
623 | void* f = NULL; | ||
624 | while(rb_read(AliceCC.arb, &f)) | ||
625 | free(f); | ||
626 | |||
627 | while(rb_read(BobCC.arb, &f)) | ||
628 | free(f); | ||
629 | |||
630 | printf("Success!"); | ||
631 | } | ||
632 | |||
633 | if (TEST_TRANSFER_V) { | ||
634 | printf("\nTrying video enc/dec...\n"); | ||
635 | |||
636 | memset(&AliceCC, 0, sizeof(CallControl)); | ||
637 | memset(&BobCC, 0, sizeof(CallControl)); | ||
638 | |||
639 | { /* Call */ | ||
640 | TOXAV_ERR_CALL rc; | ||
641 | toxav_call(AliceAV, 0, 0, 2000, &rc); | ||
642 | |||
643 | if (rc != TOXAV_ERR_CALL_OK) { | ||
644 | printf("toxav_call failed: %d\n", rc); | ||
645 | exit(1); | ||
646 | } | ||
647 | } | ||
648 | |||
649 | while (!BobCC.incoming) | ||
650 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
651 | |||
652 | { /* Answer */ | ||
653 | TOXAV_ERR_ANSWER rc; | ||
654 | toxav_answer(BobAV, 0, 0, 5000, &rc); | ||
655 | |||
656 | if (rc != TOXAV_ERR_ANSWER_OK) { | ||
657 | printf("toxav_answer failed: %d\n", rc); | ||
658 | exit(1); | ||
659 | } | ||
660 | } | ||
661 | |||
662 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
663 | |||
664 | /* Start decode thread */ | ||
665 | struct toxav_thread_data data = { | ||
666 | .AliceAV = AliceAV, | ||
667 | .BobAV = BobAV, | ||
668 | .sig = 0 | ||
669 | }; | ||
670 | |||
671 | pthread_t dect; | ||
672 | pthread_create(&dect, NULL, iterate_toxav, &data); | ||
673 | pthread_detach(dect); | ||
674 | |||
675 | CvCapture* capture = cvCreateFileCapture(vf_name); | ||
676 | if (!capture) { | ||
677 | printf("Failed to open video file: %s\n", vf_name); | ||
678 | exit(1); | ||
679 | } | ||
680 | |||
681 | // toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL); | ||
682 | |||
683 | time_t start_time = time(NULL); | ||
684 | while(start_time + 90 > time(NULL)) { | ||
685 | IplImage* frame = cvQueryFrame(capture ); | ||
686 | if (!frame) | ||
687 | break; | ||
688 | |||
689 | send_opencv_img(AliceAV, 0, frame); | ||
690 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
691 | c_sleep(10); | ||
692 | } | ||
693 | |||
694 | cvReleaseCapture(&capture); | ||
695 | |||
696 | { /* Hangup */ | ||
697 | TOXAV_ERR_CALL_CONTROL rc; | ||
698 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); | ||
699 | |||
700 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { | ||
701 | printf("toxav_call_control failed: %d\n", rc); | ||
702 | exit(1); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | iterate_tox(bootstrap, AliceAV, BobAV); | ||
707 | assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); | ||
708 | |||
709 | /* Stop decode thread */ | ||
710 | printf("Stopping decode thread\n"); | ||
711 | data.sig = -1; | ||
712 | while(data.sig != 1) | ||
713 | pthread_yield(); | ||
714 | |||
715 | printf("Success!"); | ||
716 | } | ||
717 | |||
718 | |||
719 | Tox* Alice = toxav_get_tox(AliceAV); | ||
720 | Tox* Bob = toxav_get_tox(BobAV); | ||
721 | toxav_kill(BobAV); | ||
722 | toxav_kill(AliceAV); | ||
723 | tox_kill(Bob); | ||
724 | tox_kill(Alice); | ||
725 | tox_kill(bootstrap); | ||
726 | |||
727 | printf("\nTest successful!\n"); | ||
728 | |||
729 | Pa_Terminate(); | ||
730 | return 0; | ||
731 | } | ||