summaryrefslogtreecommitdiff
path: root/testing
diff options
context:
space:
mode:
Diffstat (limited to 'testing')
-rw-r--r--testing/av_test.c731
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
74typedef 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
82struct toxav_thread_data {
83 ToxAV* AliceAV;
84 ToxAV* BobAV;
85 int32_t sig;
86};
87
88const char* vdout = "AV Test"; /* Video output */
89PaStream* adout = NULL; /* Audio output */
90
91typedef struct {
92 uint16_t size;
93 int16_t data[];
94} frame;
95
96void* 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 */
120void 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}
125void 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}
130void 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}
166void 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}
182void 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}
188void 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 */
197void 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}
285int 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}
293void* 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
327int 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}
363int 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}
374int 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
388int 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}