summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toxav/Makefile.inc4
-rw-r--r--toxav/audio.c383
-rw-r--r--toxav/audio.h60
-rw-r--r--toxav/av_test.c19
-rw-r--r--toxav/codec.c10
-rw-r--r--toxav/codec.h3
-rw-r--r--toxav/msi.c21
-rw-r--r--toxav/msi.h3
-rw-r--r--toxav/rtp.c27
-rw-r--r--toxav/toxav.c11
-rw-r--r--toxav/toxav.h2
-rw-r--r--toxav/video.c332
-rw-r--r--toxav/video.h81
13 files changed, 923 insertions, 33 deletions
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
index a73ddc91..d9adb4fe 100644
--- a/toxav/Makefile.inc
+++ b/toxav/Makefile.inc
@@ -12,6 +12,10 @@ libtoxav_la_SOURCES = ../toxav/rtp.h \
12 ../toxav/group.c \ 12 ../toxav/group.c \
13 ../toxav/codec.h \ 13 ../toxav/codec.h \
14 ../toxav/codec.c \ 14 ../toxav/codec.c \
15 ../toxav/audio.h \
16 ../toxav/audio.c \
17 ../toxav/video.h \
18 ../toxav/video.c \
15 ../toxav/toxav.h \ 19 ../toxav/toxav.h \
16 ../toxav/toxav.c 20 ../toxav/toxav.c
17 21
diff --git a/toxav/audio.c b/toxav/audio.c
new file mode 100644
index 00000000..f3e969e9
--- /dev/null
+++ b/toxav/audio.c
@@ -0,0 +1,383 @@
1/** audio.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 */
21
22#include <stdlib.h>
23
24#include "audio.h"
25#include "rtp.h"
26
27#include "../toxcore/logger.h"
28
29static struct JitterBuffer *jbuf_new(uint32_t capacity);
30static void jbuf_clear(struct JitterBuffer *q);
31static void jbuf_free(struct JitterBuffer *q);
32static int jbuf_write(struct JitterBuffer *q, RTPMessage *m);
33static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
34
35OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count);
36bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels);
37
38
39
40ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data)
41{
42 ACSession *ac = calloc(sizeof(ACSession), 1);
43
44 if (!ac) {
45 LOGGER_WARNING("Allocation failed! Application might misbehave!");
46 return NULL;
47 }
48
49 if (create_recursive_mutex(ac->queue_mutex) != 0) {
50 LOGGER_WARNING("Failed to create recursive mutex!");
51 free(ac);
52 return NULL;
53 }
54
55 int status;
56 ac->decoder = opus_decoder_create(48000, 2, &status );
57
58 if ( status != OPUS_OK ) {
59 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
60 goto BASE_CLEANUP;
61 }
62
63 if ( !(ac->j_buf = jbuf_new(3)) ) {
64 LOGGER_WARNING("Jitter buffer creaton failed!");
65 opus_decoder_destroy(ac->decoder);
66 goto BASE_CLEANUP;
67 }
68
69 /* Initialize encoders with default values */
70 ac->encoder = create_audio_encoder(48000, 48000, 2);
71 if (ac->encoder == NULL)
72 goto DECODER_CLEANUP;
73
74 ac->last_encoding_bitrate = 48000;
75 ac->last_encoding_sampling_rate = 48000;
76 ac->last_encoding_channel_count = 2;
77
78 ac->last_decoding_channel_count = 2;
79 ac->last_decoding_sampling_rate = 48000;
80 ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */
81
82 /* These need to be set in order to properly
83 * do error correction with opus */
84 ac->last_packet_frame_duration = 120;
85 ac->last_packet_sampling_rate = 48000;
86
87 ac->av = av;
88 ac->friend_id = friend_id;
89 ac->acb.first = cb;
90 ac->acb.second = cb_data;
91
92 return ac;
93
94DECODER_CLEANUP:
95 opus_decoder_destroy(ac->decoder);
96 jbuf_free(ac->j_buf);
97BASE_CLEANUP:
98 pthread_mutex_destroy(ac->queue_mutex);
99 free(ac);
100 return NULL;
101}
102void ac_kill(ACSession* ac)
103{
104 if (!ac)
105 return;
106
107 opus_encoder_destroy(ac->encoder);
108 opus_decoder_destroy(ac->decoder);
109 jbuf_free(ac->j_buf);
110
111 pthread_mutex_destroy(ac->queue_mutex);
112
113 LOGGER_DEBUG("Terminated audio handler: %p", ac);
114 free(ac);
115}
116void ac_do(ACSession* ac)
117{
118 if (!ac)
119 return;
120
121 /* Enough space for the maximum frame size (120 ms 48 KHz audio) */
122 int16_t tmp[5760];
123
124 RTPMessage *msg;
125 int rc = 0;
126
127 pthread_mutex_lock(ac->queue_mutex);
128 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
129 pthread_mutex_unlock(ac->queue_mutex);
130
131 if (rc == 2) {
132 LOGGER_DEBUG("OPUS correction");
133 rc = opus_decode(ac->decoder, NULL, 0, tmp,
134 (ac->last_packet_sampling_rate * ac->last_packet_frame_duration / 1000) /
135 ac->last_packet_channel_count, 1);
136 } else {
137 /* Get values from packet and decode. */
138 /* NOTE: This didn't work very well
139 rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data));
140 if (rc != -1) {
141 cs->last_packet_sampling_rate = rc;
142 } else {
143 LOGGER_WARNING("Failed to load packet values!");
144 rtp_free_msg(NULL, msg);
145 continue;
146 }*/
147
148
149 /* Pick up sampling rate from packet */
150 memcpy(&ac->last_packet_sampling_rate, msg->data, 4);
151 ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate);
152
153 ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4);
154
155 /*
156 * NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
157 * it didn't work quite well.
158 */
159 if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) {
160 LOGGER_WARNING("Failed to reconfigure decoder!");
161 rtp_free_msg(NULL, msg);
162 continue;
163 }
164
165 rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
166 rtp_free_msg(NULL, msg);
167 }
168
169 if (rc < 0) {
170 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
171 } else if (ac->acb.first) {
172 ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate * ac->last_packet_channel_count;
173
174 ac->acb.first(ac->av, ac->friend_id, tmp, rc * ac->last_packet_channel_count,
175 ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second);
176 }
177
178 return;
179 }
180 pthread_mutex_unlock(ac->queue_mutex);
181}
182int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels)
183{
184 if (!ac)
185 return;
186
187 /* Values are checked in toxav.c */
188 if (ac->last_encoding_sampling_rate != sampling_rate || ac->last_encoding_channel_count != channels) {
189 OpusEncoder* new_encoder = create_audio_encoder(bitrate, sampling_rate, channels);
190 if (new_encoder == NULL)
191 return -1;
192
193 opus_encoder_destroy(ac->encoder);
194 ac->encoder = new_encoder;
195 } else if (ac->last_encoding_bitrate == bitrate)
196 return 0; /* Nothing changed */
197 else {
198 int status = opus_encoder_ctl(ac->encoder, OPUS_SET_BITRATE(bitrate));
199
200 if ( status != OPUS_OK ) {
201 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
202 return -1;
203 }
204 }
205
206 ac->last_encoding_bitrate = bitrate;
207 ac->last_encoding_sampling_rate = sampling_rate;
208 ac->last_encoding_channel_count = channels;
209
210 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels);
211 return 0;
212}
213/* called from rtp */
214void ac_queue_message(void* acp, RTPMessage *msg)
215{
216 if (!acp || !msg)
217 return;
218
219 ACSession* ac = acp;
220
221 pthread_mutex_lock(ac->queue_mutex);
222 int ret = jbuf_write(ac->j_buf, msg);
223 pthread_mutex_unlock(ac->queue_mutex);
224
225 if (ret == -1)
226 rtp_free_msg(NULL, msg);
227}
228
229
230/* JITTER BUFFER WORK */
231struct JitterBuffer {
232 RTPMessage **queue;
233 uint32_t size;
234 uint32_t capacity;
235 uint16_t bottom;
236 uint16_t top;
237};
238
239static struct JitterBuffer *jbuf_new(uint32_t capacity)
240{
241 unsigned int size = 1;
242
243 while (size <= (capacity * 4)) {
244 size *= 2;
245 }
246
247 struct JitterBuffer *q;
248
249 if ( !(q = calloc(sizeof(struct JitterBuffer), 1)) ) return NULL;
250
251 if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
252 free(q);
253 return NULL;
254 }
255
256 q->size = size;
257 q->capacity = capacity;
258 return q;
259}
260static void jbuf_clear(struct JitterBuffer *q)
261{
262 for (; q->bottom != q->top; ++q->bottom) {
263 if (q->queue[q->bottom % q->size]) {
264 rtp_free_msg(NULL, q->queue[q->bottom % q->size]);
265 q->queue[q->bottom % q->size] = NULL;
266 }
267 }
268}
269static void jbuf_free(struct JitterBuffer *q)
270{
271 if (!q) return;
272
273 jbuf_clear(q);
274 free(q->queue);
275 free(q);
276}
277static int jbuf_write(struct JitterBuffer *q, RTPMessage *m)
278{
279 uint16_t sequnum = m->header->sequnum;
280
281 unsigned int num = sequnum % q->size;
282
283 if ((uint32_t)(sequnum - q->bottom) > q->size) {
284 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
285
286 jbuf_clear(q);
287 q->bottom = sequnum - q->capacity;
288 q->queue[num] = m;
289 q->top = sequnum + 1;
290 return 0;
291 }
292
293 if (q->queue[num])
294 return -1;
295
296 q->queue[num] = m;
297
298 if ((sequnum - q->bottom) >= (q->top - q->bottom))
299 q->top = sequnum + 1;
300
301 return 0;
302}
303static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
304{
305 if (q->top == q->bottom) {
306 *success = 0;
307 return NULL;
308 }
309
310 unsigned int num = q->bottom % q->size;
311
312 if (q->queue[num]) {
313 RTPMessage *ret = q->queue[num];
314 q->queue[num] = NULL;
315 ++q->bottom;
316 *success = 1;
317 return ret;
318 }
319
320 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
321 ++q->bottom;
322 *success = 2;
323 return NULL;
324 }
325
326 *success = 0;
327 return NULL;
328}
329OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count)
330{
331 int status = OPUS_OK;
332 OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status);
333
334 if ( status != OPUS_OK ) {
335 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
336 return NULL;
337 }
338
339 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bitrate));
340
341 if ( status != OPUS_OK ) {
342 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
343 goto FAILURE;
344 }
345
346 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
347
348 if ( status != OPUS_OK ) {
349 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
350 goto FAILURE;
351 }
352
353 return rc;
354
355FAILURE:
356 opus_encoder_destroy(rc);
357 return NULL;
358}
359bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels)
360{
361 if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) {
362 if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500)
363 return false;
364
365 int status;
366 OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status );
367 if ( status != OPUS_OK ) {
368 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
369 return false;
370 }
371
372 ac->last_decoding_sampling_rate = sampling_rate;
373 ac->last_decoding_channel_count = channels;
374 ac->last_decoder_reconfiguration = current_time_monotonic();
375
376 opus_decoder_destroy(ac->decoder);
377 ac->decoder = new_dec;
378
379 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
380 }
381
382 return true;
383} \ No newline at end of file
diff --git a/toxav/audio.h b/toxav/audio.h
new file mode 100644
index 00000000..62a28cdf
--- /dev/null
+++ b/toxav/audio.h
@@ -0,0 +1,60 @@
1/** audio.h
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 */
21
22#ifndef AUDIO_H
23#define AUDIO_H
24
25#include <opus.h>
26#include <pthread.h>
27
28#include "toxav.h"
29
30#include "../toxcore/util.h"
31
32typedef struct ACSession_s {
33 /* encoding */
34 OpusEncoder *encoder;
35 int32_t last_encoding_sampling_rate;
36 int32_t last_encoding_channel_count;
37 int32_t last_encoding_bitrate;
38
39 /* decoding */
40 OpusDecoder *decoder;
41 int32_t last_packet_channel_count;
42 int32_t last_packet_sampling_rate;
43 int32_t last_packet_frame_duration;
44 int32_t last_decoding_sampling_rate;
45 int32_t last_decoding_channel_count;
46 uint64_t last_decoder_reconfiguration;
47 void *j_buf;
48
49 pthread_mutex_t queue_mutex[1];
50
51 ToxAV* av;
52 uint32_t friend_id;
53 PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */
54} ACSession;
55
56ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data);
57void ac_kill(ACSession* ac);
58void ac_do(ACSession* ac);
59int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels);
60#endif /* AUDIO_H */ \ No newline at end of file
diff --git a/toxav/av_test.c b/toxav/av_test.c
index d669e380..994f9cf6 100644
--- a/toxav/av_test.c
+++ b/toxav/av_test.c
@@ -77,8 +77,8 @@
77#define TEST_REJECT 0 77#define TEST_REJECT 0
78#define TEST_CANCEL 0 78#define TEST_CANCEL 0
79#define TEST_MUTE_UNMUTE 0 79#define TEST_MUTE_UNMUTE 0
80#define TEST_TRANSFER_A 0 80#define TEST_TRANSFER_A 1
81#define TEST_TRANSFER_V 1 81#define TEST_TRANSFER_V 0
82 82
83 83
84typedef struct { 84typedef struct {
@@ -320,21 +320,28 @@ int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV)
320void* iterate_toxav (void * data) 320void* iterate_toxav (void * data)
321{ 321{
322 struct toxav_thread_data* data_cast = data; 322 struct toxav_thread_data* data_cast = data;
323 323#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
324// cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE); 324 cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE);
325#endif
325 326
326 while (data_cast->sig == 0) { 327 while (data_cast->sig == 0) {
327 toxav_iterate(data_cast->AliceAV); 328 toxav_iterate(data_cast->AliceAV);
328 toxav_iterate(data_cast->BobAV); 329 toxav_iterate(data_cast->BobAV);
329 int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); 330 int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV));
330 331
331// cvWaitKey(rc); 332#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
333 cvWaitKey(rc);
334#else
332 c_sleep(rc); 335 c_sleep(rc);
336#endif
333 } 337 }
334 338
335 data_cast->sig = 1; 339 data_cast->sig = 1;
336 340
337// cvDestroyWindow(vdout); 341#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
342 cvDestroyWindow(vdout);
343#endif
344
338 pthread_exit(NULL); 345 pthread_exit(NULL);
339} 346}
340 347
diff --git a/toxav/codec.c b/toxav/codec.c
index e64c4d52..57e43c67 100644
--- a/toxav/codec.c
+++ b/toxav/codec.c
@@ -328,7 +328,6 @@ void cs_do(CSession *cs)
328 LOGGER_WARNING("Failed to reconfigure decoder!"); 328 LOGGER_WARNING("Failed to reconfigure decoder!");
329 rtp_free_msg(NULL, msg); 329 rtp_free_msg(NULL, msg);
330 continue; 330 continue;
331// goto DONE;
332 } 331 }
333 332
334 rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); 333 rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
@@ -347,7 +346,6 @@ void cs_do(CSession *cs)
347 346
348 LOGGED_LOCK(cs->queue_mutex); 347 LOGGED_LOCK(cs->queue_mutex);
349 } 348 }
350// DONE:;
351 } 349 }
352 350
353 /********************* VIDEO *********************/ 351 /********************* VIDEO *********************/
@@ -497,12 +495,12 @@ void cs_kill(CSession *cs)
497 * the callback is unregistered before cs_kill is called. 495 * the callback is unregistered before cs_kill is called.
498 */ 496 */
499 497
500 vpx_codec_destroy(cs->v_encoder);
501 vpx_codec_destroy(cs->v_decoder);
502 opus_encoder_destroy(cs->audio_encoder); 498 opus_encoder_destroy(cs->audio_encoder);
503 opus_decoder_destroy(cs->audio_decoder); 499 opus_decoder_destroy(cs->audio_decoder);
504 rb_free(cs->vbuf_raw);
505 jbuf_free(cs->j_buf); 500 jbuf_free(cs->j_buf);
501 vpx_codec_destroy(cs->v_encoder);
502 vpx_codec_destroy(cs->v_decoder);
503 rb_free(cs->vbuf_raw);
506 free(cs->frame_buf); 504 free(cs->frame_buf);
507 free(cs->split_video_frame); 505 free(cs->split_video_frame);
508 506
@@ -600,7 +598,7 @@ void queue_message(RTPSession *session, RTPMessage *msg)
600{ 598{
601 CSession *cs = session->cs; 599 CSession *cs = session->cs;
602 600
603 if (!cs) 601 if (!cs)
604 return; 602 return;
605 603
606 /* Audio */ 604 /* Audio */
diff --git a/toxav/codec.h b/toxav/codec.h
index 7cc9b15d..497016eb 100644
--- a/toxav/codec.h
+++ b/toxav/codec.h
@@ -42,9 +42,6 @@
42/* Audio encoding/decoding */ 42/* Audio encoding/decoding */
43#include <opus.h> 43#include <opus.h>
44 44
45#define PACKED_AUDIO_SIZE(x) (x + 5)
46#define UNPACKED_AUDIO_SIZE(x) (x - 5)
47
48typedef struct CSession_s { 45typedef struct CSession_s {
49 46
50 /* VIDEO 47 /* VIDEO
diff --git a/toxav/msi.c b/toxav/msi.c
index c313e288..b7926e07 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -188,7 +188,7 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
188 msg.capabilities.value = capabilities; 188 msg.capabilities.value = capabilities;
189 189
190 msg.vfpsz.exists = true; 190 msg.vfpsz.exists = true;
191 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 191 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
192 192
193 send_message ( (*call)->session->messenger, (*call)->friend_id, &msg ); 193 send_message ( (*call)->session->messenger, (*call)->friend_id, &msg );
194 194
@@ -238,7 +238,7 @@ int msi_answer ( MSICall* call, uint8_t capabilities )
238 msg.capabilities.value = capabilities; 238 msg.capabilities.value = capabilities;
239 239
240 msg.vfpsz.exists = true; 240 msg.vfpsz.exists = true;
241 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 241 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
242 242
243 send_message ( session->messenger, call->friend_id, &msg ); 243 send_message ( session->messenger, call->friend_id, &msg );
244 244
@@ -349,6 +349,12 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
349 case IDVFPSZ: 349 case IDVFPSZ:
350 CHECK_SIZE(it, size_constraint, 2); 350 CHECK_SIZE(it, size_constraint, 2);
351 SET_UINT16(it, dest->vfpsz); 351 SET_UINT16(it, dest->vfpsz);
352 dest->vfpsz = ntohs(dest->vfpsz);
353
354 if (dest->vfpsz.value > 1200) {
355 LOGGER_ERROR("Invalid vfpsz param");
356 return -1;
357 }
352 break; 358 break;
353 359
354 default: 360 default:
@@ -419,8 +425,9 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg )
419 } 425 }
420 426
421 if (msg->vfpsz.exists) { 427 if (msg->vfpsz.exists) {
422 it = msg_parse_header_out(IDVFPSZ, it, &msg->vfpsz.value, 428 uint16_t nb_vfpsz = htons(msg->vfpsz);
423 sizeof(msg->vfpsz.value), &size); 429 it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz,
430 sizeof(nb_vfpsz), &size);
424 } 431 }
425 432
426 if ( it == parsed ) { 433 if ( it == parsed ) {
@@ -620,7 +627,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
620 goto FAILURE; 627 goto FAILURE;
621 } 628 }
622 629
623 call->peer_vfpsz = ntohs(msg->vfpsz.value); 630 call->peer_vfpsz = msg->vfpsz.value;
624 } 631 }
625 632
626 633
@@ -645,7 +652,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
645 * is not terminated on our side. We can assume that 652 * is not terminated on our side. We can assume that
646 * in this case we can automatically answer the re-call. 653 * in this case we can automatically answer the re-call.
647 */ 654 */
648 if (call->peer_vfpsz != ntohs(msg->vfpsz.value)) { 655 if (call->peer_vfpsz != msg->vfpsz.value) {
649 LOGGER_WARNING("Friend sent invalid parameters for re-call"); 656 LOGGER_WARNING("Friend sent invalid parameters for re-call");
650 call->error = msi_EInvalidParam; 657 call->error = msi_EInvalidParam;
651 invoke_callback(call, msi_OnError); 658 invoke_callback(call, msi_OnError);
@@ -661,7 +668,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
661 msg.capabilities.value = call->self_capabilities; 668 msg.capabilities.value = call->self_capabilities;
662 669
663 msg.vfpsz.exists = true; 670 msg.vfpsz.exists = true;
664 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 671 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
665 672
666 send_message ( call->session->messenger, call->friend_id, &msg ); 673 send_message ( call->session->messenger, call->friend_id, &msg );
667 674
diff --git a/toxav/msi.h b/toxav/msi.h
index 7d82afc8..457d3148 100644
--- a/toxav/msi.h
+++ b/toxav/msi.h
@@ -25,7 +25,8 @@
25#include <inttypes.h> 25#include <inttypes.h>
26#include <pthread.h> 26#include <pthread.h>
27 27
28#include "codec.h" 28#include "audio.h"
29#include "video.h"
29#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
30 31
31/** Preconfigured value for video splitting */ 32/** Preconfigured value for video splitting */
diff --git a/toxav/rtp.c b/toxav/rtp.c
index e1e6dbac..9ef41b35 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -69,9 +69,11 @@ typedef struct RTCPSession_s {
69 69
70 70
71 71
72/* These are defined externally */
73void ac_queue_message(void *acp, RTPMessage *msg);
74void vc_queue_message(void *vcp, RTPMessage *msg);
75
72 76
73/* queue_message() is defined in codec.c */
74void queue_message(RTPSession *session, RTPMessage *msg);
75RTPHeader *parse_header_in ( const uint8_t *payload, int length ); 77RTPHeader *parse_header_in ( const uint8_t *payload, int length );
76RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); 78RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
77RTPMessage *msg_parse ( const uint8_t *data, int length ); 79RTPMessage *msg_parse ( const uint8_t *data, int length );
@@ -395,6 +397,10 @@ RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
395} 397}
396RTPMessage *msg_parse ( const uint8_t *data, int length ) 398RTPMessage *msg_parse ( const uint8_t *data, int length )
397{ 399{
400 /* TODO: data dynamic, [0]
401 * TODO: dummy payload type
402 * TODO: parse header before allocating message
403 */
398 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 404 RTPMessage *retu = calloc(1, sizeof (RTPMessage));
399 405
400 retu->header = parse_header_in ( data, length ); /* It allocates memory and all */ 406 retu->header = parse_header_in ( data, length ); /* It allocates memory and all */
@@ -540,6 +546,7 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
540} 546}
541int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) 547int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
542{ 548{
549 /* TODO on message callback */
543 RTPSession *session = object; 550 RTPSession *session = object;
544 RTPMessage *msg; 551 RTPMessage *msg;
545 552
@@ -570,8 +577,20 @@ int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data
570 } 577 }
571 578
572 session->rtcp_session->last_received_packets ++; 579 session->rtcp_session->last_received_packets ++;
573 580
574 queue_message(session, msg); 581 /* Check if this session can handle the packet */
582 if (session->payload_type != session->prefix % 128) {
583 LOGGER_WARNING("Friend %d sent invalid payload type!", session->dest);
584 rtp_free_msg(msg);
585 return -1;
586 }
587
588 /* Handle */
589 if (session->payload_type == rtp_TypeAudio % 128)
590 ac_queue_message(session->cs, msg);
591 else /* It can only be video */
592 vc_queue_message(session->cs, msg);
593
575 return 0; 594 return 0;
576} 595}
577int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) 596int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
diff --git a/toxav/toxav.c b/toxav/toxav.c
index 01b2670d..6f712af9 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -1170,11 +1170,6 @@ void call_kill_transmission(ToxAVCall* call)
1170 1170
1171 call->active = 0; 1171 call->active = 0;
1172 1172
1173 rtp_kill(call->rtps[audio_index]);
1174 call->rtps[audio_index] = NULL;
1175 rtp_kill(call->rtps[video_index]);
1176 call->rtps[video_index] = NULL;
1177
1178 LOGGED_LOCK(call->mutex_audio_sending); 1173 LOGGED_LOCK(call->mutex_audio_sending);
1179 LOGGED_UNLOCK(call->mutex_audio_sending); 1174 LOGGED_UNLOCK(call->mutex_audio_sending);
1180 LOGGED_LOCK(call->mutex_video_sending); 1175 LOGGED_LOCK(call->mutex_video_sending);
@@ -1182,6 +1177,12 @@ void call_kill_transmission(ToxAVCall* call)
1182 LOGGED_LOCK(call->mutex_decoding); 1177 LOGGED_LOCK(call->mutex_decoding);
1183 LOGGED_UNLOCK(call->mutex_decoding); 1178 LOGGED_UNLOCK(call->mutex_decoding);
1184 1179
1180
1181 rtp_kill(call->rtps[audio_index]);
1182 call->rtps[audio_index] = NULL;
1183 rtp_kill(call->rtps[video_index]);
1184 call->rtps[video_index] = NULL;
1185
1185 cs_kill(call->cs); 1186 cs_kill(call->cs);
1186 call->cs = NULL; 1187 call->cs = NULL;
1187 1188
diff --git a/toxav/toxav.h b/toxav/toxav.h
index 2571bfff..bf8756eb 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -499,7 +499,7 @@ bool toxav_send_audio_frame(ToxAV *av, uint32_t friend_number,
499 * Y = width * height, U = (width/2) * (height/2) and V = (width/2) * (height/2). 499 * Y = width * height, U = (width/2) * (height/2) and V = (width/2) * (height/2).
500 * @param ystride 500 * @param ystride
501 * @param ustride 501 * @param ustride
502 * @param vstride Strides data. Indexing is the same as in 'planes' param. 502 * @param vstride Strides data.
503 */ 503 */
504typedef void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, 504typedef void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
505 uint16_t width, uint16_t height, 505 uint16_t width, uint16_t height,
diff --git a/toxav/video.c b/toxav/video.c
new file mode 100644
index 00000000..d51cfd4a
--- /dev/null
+++ b/toxav/video.c
@@ -0,0 +1,332 @@
1/** video.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 */
21
22#include <stdlib.h>
23#include <assert.h>
24
25#include "video.h"
26#include "msi.h"
27
28#include "../toxcore/logger.h"
29#include "../toxcore/network.h"
30
31/* Good quality encode. */
32#define MAX_DECODE_TIME_US 0
33
34#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
35#define VIDEOFRAME_HEADER_SIZE 0x2
36
37/* FIXME: Might not be enough? NOTE: I think it is enough */
38#define VIDEO_DECODE_BUFFER_SIZE 20
39
40typedef struct { uint16_t size; uint8_t data[]; } Payload;
41
42bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate);
43
44
45VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb* cb, void* cb_data, uint32_t mvfpsz)
46{
47 VCSession *vc = calloc(sizeof(VCSession), 1);
48
49 if (!vc) {
50 LOGGER_WARNING("Allocation failed! Application might misbehave!");
51 return NULL;
52 }
53
54 if (create_recursive_mutex(vc->queue_mutex) != 0) {
55 LOGGER_WARNING("Failed to create recursive mutex!");
56 free(vc);
57 return NULL;
58 }
59
60 if ( !(vc->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) )
61 goto BASE_CLEANUP;
62 if ( !(vc->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) )
63 goto BASE_CLEANUP;
64 if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) )
65 goto BASE_CLEANUP;
66
67 int rc = vpx_codec_dec_init_ver(vc->v_decoder, VIDEO_CODEC_DECODER_INTERFACE,
68 NULL, 0, VPX_DECODER_ABI_VERSION);
69 if ( rc != VPX_CODEC_OK) {
70 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
71 goto BASE_CLEANUP;
72 }
73
74 if (!create_video_encoder(vc->v_encoder, 500000)) {
75 vpx_codec_destroy(vc->v_decoder);
76 goto BASE_CLEANUP;
77 }
78
79 vc->linfts = current_time_monotonic();
80 vc->lcfd = 60;
81
82 vc->peer_video_frame_piece_size = mvfpsz;
83
84 return vc;
85
86BASE_CLEANUP:
87 pthread_mutex_destroy(vc->queue_mutex);
88 rb_free(vc->vbuf_raw);
89 free(vc->split_video_frame);
90 free(vc->frame_buf);
91 free(vc);
92 return NULL;
93}
94void vc_kill(VCSession* vc)
95{
96 if (!vc)
97 return;
98
99 vpx_codec_destroy(vc->v_encoder);
100 vpx_codec_destroy(vc->v_decoder);
101 rb_free(vc->vbuf_raw);
102 free(vc->split_video_frame);
103 free(vc->frame_buf);
104
105 pthread_mutex_destroy(vc->queue_mutex);
106
107 LOGGER_DEBUG("Terminated video handler: %p", vc);
108 free(vc);
109}
110void vc_do(VCSession* vc)
111{
112 if (!vc)
113 return;
114
115 Payload *p;
116 int rc;
117
118 pthread_mutex_lock(vc->queue_mutex);
119 if (rb_read(vc->vbuf_raw, (void**)&p)) {
120 pthread_mutex_unlock(vc->queue_mutex);
121
122 rc = vpx_codec_decode(vc->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
123 free(p);
124
125 if (rc != VPX_CODEC_OK) {
126 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
127 } else {
128 vpx_codec_iter_t iter = NULL;
129 vpx_image_t *dest = vpx_codec_get_frame(vc->v_decoder, &iter);
130
131 /* Play decoded images */
132 for (; dest; dest = vpx_codec_get_frame(vc->v_decoder, &iter)) {
133 if (vc->vcb.first)
134 vc->vcb.first(vc->av, vc->friend_id, dest->d_w, dest->d_h,
135 (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2],
136 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
137
138 vpx_img_free(dest);
139 }
140 }
141
142 return;
143 }
144 pthread_mutex_unlock(vc->queue_mutex);
145}
146void vc_init_video_splitter_cycle(VCSession* vc)
147{
148 if (!vc)
149 return;
150
151 vc->split_video_frame[0] = vc->frameid_out++;
152 vc->split_video_frame[1] = 0;
153}
154int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length)
155{
156 if (!vc)
157 return;
158
159 vc->processing_video_frame = payload;
160 vc->processing_video_frame_size = length;
161
162 return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1;
163}
164const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size)
165{
166 if (!vc || !size)
167 return NULL;
168
169 if (vc->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) {
170 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
171 vc->processing_video_frame,
172 VIDEOFRAME_PIECE_SIZE);
173
174 vc->processing_video_frame += VIDEOFRAME_PIECE_SIZE;
175 vc->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE;
176
177 *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE;
178 } else {
179 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
180 vc->processing_video_frame,
181 vc->processing_video_frame_size);
182
183 *size = vc->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
184 }
185
186 vc->split_video_frame[1]++;
187
188 return vc->split_video_frame;
189}
190int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height)
191{
192 if (!vc)
193 return;
194
195 vpx_codec_enc_cfg_t cfg = *vc->v_encoder[0].config.enc;
196 if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height)
197 return 0; /* Nothing changed */
198
199 cfg.rc_target_bitrate = bitrate;
200 cfg.g_w = width;
201 cfg.g_h = height;
202
203 int rc = vpx_codec_enc_config_set(vc->v_encoder, &cfg);
204 if ( rc != VPX_CODEC_OK) {
205 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
206 return -1;
207 }
208
209 return 0;
210}
211/* Called from RTP */
212void vc_queue_message(void* vcp, RTPMessage *msg)
213{
214 /* This function does the reconstruction of video packets.
215 * See more info about video splitting in docs
216 */
217 if (!vcp || !msg)
218 return;
219
220 VCSession* vc = vcp;
221
222 uint8_t *packet = msg->data;
223 uint32_t packet_size = msg->length;
224
225 if (packet_size < VIDEOFRAME_HEADER_SIZE)
226 goto end;
227
228 uint8_t diff = packet[0] - vc->frameid_in;
229
230 if (diff != 0) {
231 if (diff < 225) { /* New frame */
232 /* Flush last frames' data and get ready for this frame */
233 Payload *p = malloc(sizeof(Payload) + vc->frame_size);
234
235 if (p) {
236 LOGGED_LOCK(vc->queue_mutex);
237
238 if (rb_full(vc->vbuf_raw)) {
239 LOGGER_DEBUG("Dropped video frame");
240 Payload *tp;
241 rb_read(vc->vbuf_raw, (void**)&tp);
242 free(tp);
243 } else {
244 p->size = vc->frame_size;
245 memcpy(p->data, vc->frame_buf, vc->frame_size);
246 }
247
248 /* Calculate time took for peer to send us this frame */
249 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
250 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
251 vc->linfts = current_time_monotonic();
252
253 rb_write(vc->vbuf_raw, p);
254 LOGGED_UNLOCK(vc->queue_mutex);
255 } else {
256 LOGGER_WARNING("Allocation failed! Program might misbehave!");
257 goto end;
258 }
259
260 vc->frameid_in = packet[0];
261 memset(vc->frame_buf, 0, vc->frame_size);
262 vc->frame_size = 0;
263
264 } else { /* Old frame; drop */
265 LOGGER_DEBUG("Old packet: %u", packet[0]);
266 goto end;
267 }
268 }
269
270 uint8_t piece_number = packet[1];
271
272 uint32_t length_before_piece = ((piece_number - 1) * vc->peer_video_frame_piece_size);
273 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
274
275 if (framebuf_new_length > MAX_VIDEOFRAME_SIZE)
276 goto end;
277
278
279 /* Otherwise it's part of the frame so just process */
280 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
281
282 memcpy(vc->frame_buf + length_before_piece,
283 packet + VIDEOFRAME_HEADER_SIZE,
284 packet_size - VIDEOFRAME_HEADER_SIZE);
285
286 if (framebuf_new_length > vc->frame_size)
287 vc->frame_size = framebuf_new_length;
288
289end:
290 rtp_free_msg(NULL, msg);
291}
292
293
294bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate)
295{
296 assert(dest);
297
298 vpx_codec_enc_cfg_t cfg;
299 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
300
301 if (rc != VPX_CODEC_OK) {
302 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
303 return false;
304 }
305
306 rc = vpx_codec_enc_init_ver(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0,
307 VPX_ENCODER_ABI_VERSION);
308
309 if ( rc != VPX_CODEC_OK) {
310 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
311 return false;
312 }
313
314 cfg.rc_target_bitrate = bitrate;
315 cfg.g_w = 800;
316 cfg.g_h = 600;
317 cfg.g_pass = VPX_RC_ONE_PASS;
318 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
319 cfg.g_lag_in_frames = 0;
320 cfg.kf_min_dist = 0;
321 cfg.kf_max_dist = 48;
322 cfg.kf_mode = VPX_KF_AUTO;
323
324 rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8);
325
326 if ( rc != VPX_CODEC_OK) {
327 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
328 vpx_codec_destroy(dest);
329 }
330
331 return true;
332} \ No newline at end of file
diff --git a/toxav/video.h b/toxav/video.h
new file mode 100644
index 00000000..c1678ad2
--- /dev/null
+++ b/toxav/video.h
@@ -0,0 +1,81 @@
1/** video.h
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 */
21
22#ifndef VIDEO_H
23#define VIDEO_H
24
25#include <vpx/vpx_decoder.h>
26#include <vpx/vpx_encoder.h>
27#include <vpx/vp8dx.h>
28#include <vpx/vp8cx.h>
29#include <vpx/vpx_image.h>
30#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
31#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
32
33#include <pthread.h>
34
35#include "toxav.h"
36
37#include "../toxcore/util.h"
38
39typedef struct VCSession_s {
40
41 /* encoding */
42 vpx_codec_ctx_t v_encoder[1];
43 uint32_t frame_counter;
44
45 /* decoding */
46 vpx_codec_ctx_t v_decoder[1];
47 void *vbuf_raw; /* Un-decoded data */
48
49 /* Data handling */
50 uint8_t *frame_buf; /* buffer for split video payloads */
51 uint32_t frame_size; /* largest address written to in frame_buf for current input frame */
52 uint8_t frameid_in, frameid_out; /* id of input and output video frame */
53 uint64_t linfts; /* Last received frame time stamp */
54 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
55
56 /* Limits */
57 uint32_t peer_video_frame_piece_size;
58
59 /* Splitting */
60 uint8_t *split_video_frame;
61 const uint8_t *processing_video_frame;
62 uint16_t processing_video_frame_size;
63
64
65 ToxAV *av;
66 int32_t friend_id;
67
68 PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */
69
70 pthread_mutex_t queue_mutex[1];
71} VCSession;
72
73VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb *cb, void *cb_data, uint32_t mvfpsz);
74void vc_kill(VCSession* vc);
75void vc_do(VCSession* vc);
76void vc_init_video_splitter_cycle(VCSession* vc);
77int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length);
78const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size);
79int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height);
80
81#endif /* VIDEO_H */ \ No newline at end of file