diff options
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/group.c | 182 | ||||
-rw-r--r-- | toxav/group.h | 21 | ||||
-rw-r--r-- | toxav/toxav.c | 42 | ||||
-rw-r--r-- | toxav/toxav.h | 25 |
4 files changed, 260 insertions, 10 deletions
diff --git a/toxav/group.c b/toxav/group.c index f5707aa6..a899239c 100644 --- a/toxav/group.c +++ b/toxav/group.c | |||
@@ -135,19 +135,25 @@ static Group_Audio_Packet *dequeue(Group_JitterBuffer *q, int *success) | |||
135 | return NULL; | 135 | return NULL; |
136 | } | 136 | } |
137 | 137 | ||
138 | |||
139 | typedef struct { | 138 | typedef struct { |
139 | Group_Chats *g_c; | ||
140 | OpusEncoder *audio_encoder; | 140 | OpusEncoder *audio_encoder; |
141 | 141 | ||
142 | unsigned int audio_channels, audio_sample_rate, audio_bitrate; | 142 | unsigned int audio_channels, audio_sample_rate, audio_bitrate; |
143 | 143 | ||
144 | uint16_t audio_sequnum; | 144 | uint16_t audio_sequnum; |
145 | |||
146 | void (*audio_data)(Messenger *m, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, | ||
147 | uint8_t channels, unsigned int sample_rate, void *userdata); | ||
148 | void *userdata; | ||
145 | } Group_AV; | 149 | } Group_AV; |
146 | 150 | ||
147 | typedef struct { | 151 | typedef struct { |
148 | Group_JitterBuffer *buffer; | 152 | Group_JitterBuffer *buffer; |
149 | 153 | ||
150 | OpusDecoder *audio_decoder; | 154 | OpusDecoder *audio_decoder; |
155 | int decoder_channels; | ||
156 | unsigned int last_packet_samples; | ||
151 | } Group_Peer_AV; | 157 | } Group_Peer_AV; |
152 | 158 | ||
153 | static void kill_group_av(Group_AV *group_av) | 159 | static void kill_group_av(Group_AV *group_av) |
@@ -156,8 +162,13 @@ static void kill_group_av(Group_AV *group_av) | |||
156 | free(group_av); | 162 | free(group_av); |
157 | } | 163 | } |
158 | 164 | ||
159 | static Group_AV *new_group_av(unsigned int audio_channels, unsigned int audio_sample_rate, unsigned int audio_bitrate) | 165 | static Group_AV *new_group_av(Group_Chats *g_c, unsigned int audio_channels, unsigned int audio_sample_rate, |
166 | unsigned int audio_bitrate, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, | ||
167 | unsigned int, void *), void *userdata) | ||
160 | { | 168 | { |
169 | if (!g_c) | ||
170 | return NULL; | ||
171 | |||
161 | Group_AV *group_av = calloc(1, sizeof(Group_AV)); | 172 | Group_AV *group_av = calloc(1, sizeof(Group_AV)); |
162 | 173 | ||
163 | int rc = OPUS_OK; | 174 | int rc = OPUS_OK; |
@@ -190,6 +201,10 @@ static Group_AV *new_group_av(unsigned int audio_channels, unsigned int audio_sa | |||
190 | group_av->audio_channels = audio_channels; | 201 | group_av->audio_channels = audio_channels; |
191 | group_av->audio_sample_rate = audio_sample_rate; | 202 | group_av->audio_sample_rate = audio_sample_rate; |
192 | group_av->audio_bitrate = audio_bitrate; | 203 | group_av->audio_bitrate = audio_bitrate; |
204 | group_av->g_c = g_c; | ||
205 | |||
206 | group_av->audio_data = audio_callback; | ||
207 | group_av->userdata = userdata; | ||
193 | return 0; | 208 | return 0; |
194 | } | 209 | } |
195 | 210 | ||
@@ -217,6 +232,100 @@ static void group_av_peer_delete(void *object, int groupnumber, int friendgroupn | |||
217 | free(peer_object); | 232 | free(peer_object); |
218 | } | 233 | } |
219 | 234 | ||
235 | static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int groupnumber, int friendgroupnumber) | ||
236 | { | ||
237 | if (!group_av || !peer_av) | ||
238 | return -1; | ||
239 | |||
240 | int success; | ||
241 | Group_Audio_Packet *pk = dequeue(peer_av->buffer, &success); | ||
242 | |||
243 | if (success == 0) | ||
244 | return -1; | ||
245 | |||
246 | int16_t *out_audio = NULL; | ||
247 | unsigned int out_audio_samples = 0; | ||
248 | |||
249 | if (success == 1) { | ||
250 | int channels = opus_packet_get_nb_channels(pk->data); | ||
251 | |||
252 | if (channels == OPUS_INVALID_PACKET) { | ||
253 | free(pk); | ||
254 | return -1; | ||
255 | } | ||
256 | |||
257 | if (channels != 1 && channels != 2) { | ||
258 | free(pk); | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | if (channels != peer_av->decoder_channels) { | ||
263 | if (peer_av->audio_decoder) { | ||
264 | opus_decoder_destroy(peer_av->audio_decoder); | ||
265 | peer_av->audio_decoder = NULL; | ||
266 | } | ||
267 | |||
268 | int rc; | ||
269 | peer_av->audio_decoder = opus_decoder_create(group_av->audio_sample_rate, channels, &rc); | ||
270 | |||
271 | if ( rc != OPUS_OK ) { | ||
272 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
273 | free(pk); | ||
274 | return -1; | ||
275 | } | ||
276 | |||
277 | peer_av->decoder_channels = channels; | ||
278 | } | ||
279 | |||
280 | int num_samples = opus_decoder_get_nb_samples(peer_av->audio_decoder, pk->data, pk->length); | ||
281 | |||
282 | out_audio = malloc(num_samples * peer_av->decoder_channels * sizeof(int16_t)); | ||
283 | |||
284 | if (!out_audio) { | ||
285 | free(pk); | ||
286 | return -1; | ||
287 | } | ||
288 | |||
289 | out_audio_samples = opus_decode(peer_av->audio_decoder, pk->data, pk->length, out_audio, num_samples, 0); | ||
290 | free(pk); | ||
291 | |||
292 | if (out_audio_samples <= 0) | ||
293 | return -1; | ||
294 | |||
295 | peer_av->last_packet_samples = out_audio_samples; | ||
296 | } else { | ||
297 | if (!peer_av->audio_decoder) | ||
298 | return -1; | ||
299 | |||
300 | if (!peer_av->last_packet_samples) | ||
301 | return -1; | ||
302 | |||
303 | out_audio = malloc(peer_av->last_packet_samples * peer_av->decoder_channels * sizeof(int16_t)); | ||
304 | |||
305 | if (!out_audio) { | ||
306 | free(pk); | ||
307 | return -1; | ||
308 | } | ||
309 | |||
310 | out_audio_samples = opus_decode(peer_av->audio_decoder, NULL, 0, out_audio, peer_av->last_packet_samples, 1); | ||
311 | |||
312 | if (out_audio_samples <= 0) | ||
313 | return -1; | ||
314 | |||
315 | } | ||
316 | |||
317 | if (out_audio) { | ||
318 | //TODO callback | ||
319 | /* | ||
320 | if (group_av->audio_data) | ||
321 | audio_data( | ||
322 | */ | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | return -1; | ||
327 | } | ||
328 | |||
220 | static int handle_group_audio_packet(void *object, int groupnumber, int friendgroupnumber, void *peer_object, | 329 | static int handle_group_audio_packet(void *object, int groupnumber, int friendgroupnumber, void *peer_object, |
221 | const uint8_t *packet, uint16_t length) | 330 | const uint8_t *packet, uint16_t length) |
222 | { | 331 | { |
@@ -244,13 +353,21 @@ static int handle_group_audio_packet(void *object, int groupnumber, int friendgr | |||
244 | return 0; | 353 | return 0; |
245 | } | 354 | } |
246 | 355 | ||
247 | static int groupchat_enable_av(Group_Chats *g_c, int groupnumber) | 356 | /* Convert groupchat to an A/V groupchat. |
357 | * | ||
358 | * return 0 on success. | ||
359 | * return -1 on failure. | ||
360 | */ | ||
361 | static int groupchat_enable_av(Group_Chats *g_c, int groupnumber, void (*audio_callback)(Messenger *, int, int, | ||
362 | const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata) | ||
248 | { | 363 | { |
249 | Group_AV *group_av = new_group_av(1, 48000, 64000); //TODO: Use variables instead. | 364 | if (groupnumber == -1) |
365 | return -1; | ||
250 | 366 | ||
251 | if (group_av == NULL) { | 367 | Group_AV *group_av = new_group_av(g_c, 1, 48000, 64000, audio_callback, userdata); //TODO: Use variables instead. |
368 | |||
369 | if (group_av == NULL) | ||
252 | return -1; | 370 | return -1; |
253 | } | ||
254 | 371 | ||
255 | if (group_set_object(g_c, groupnumber, group_av) == -1 | 372 | if (group_set_object(g_c, groupnumber, group_av) == -1 |
256 | || callback_groupchat_peer_new(g_c, groupnumber, group_av_peer_new) == -1 | 373 | || callback_groupchat_peer_new(g_c, groupnumber, group_av_peer_new) == -1 |
@@ -268,15 +385,39 @@ static int groupchat_enable_av(Group_Chats *g_c, int groupnumber) | |||
268 | * return group number on success. | 385 | * return group number on success. |
269 | * return -1 on failure. | 386 | * return -1 on failure. |
270 | */ | 387 | */ |
271 | int add_av_groupchat(Group_Chats *g_c) | 388 | int add_av_groupchat(Group_Chats *g_c, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, |
389 | uint8_t, unsigned int, void *), void *userdata) | ||
272 | { | 390 | { |
273 | int groupnumber = add_groupchat(g_c); | 391 | int groupnumber = add_groupchat(g_c, GROUPCHAT_TYPE_AV); |
274 | 392 | ||
275 | if (groupnumber == -1) { | 393 | if (groupnumber == -1) { |
276 | return -1; | 394 | return -1; |
277 | } | 395 | } |
278 | 396 | ||
279 | if (groupchat_enable_av(g_c, groupnumber) == -1) { | 397 | if (groupchat_enable_av(g_c, groupnumber, audio_callback, userdata) == -1) { |
398 | del_groupchat(g_c, groupnumber); | ||
399 | return -1; | ||
400 | } | ||
401 | |||
402 | return groupnumber; | ||
403 | } | ||
404 | |||
405 | /* Join a AV group (you need to have been invited first.) | ||
406 | * | ||
407 | * returns group number on success | ||
408 | * returns -1 on failure. | ||
409 | */ | ||
410 | int join_av_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
411 | void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), | ||
412 | void *userdata) | ||
413 | { | ||
414 | int groupnumber = join_groupchat(g_c, friendnumber, GROUPCHAT_TYPE_AV, data, length); | ||
415 | |||
416 | if (groupnumber == -1) { | ||
417 | return -1; | ||
418 | } | ||
419 | |||
420 | if (groupchat_enable_av(g_c, groupnumber, audio_callback, userdata) == -1) { | ||
280 | del_groupchat(g_c, groupnumber); | 421 | del_groupchat(g_c, groupnumber); |
281 | return -1; | 422 | return -1; |
282 | } | 423 | } |
@@ -308,3 +449,26 @@ static int send_audio_packet(Group_Chats *g_c, int groupnumber, uint8_t *packet, | |||
308 | ++group_av->audio_sequnum; | 449 | ++group_av->audio_sequnum; |
309 | return 0; | 450 | return 0; |
310 | } | 451 | } |
452 | |||
453 | /* Send audio to the group chat. | ||
454 | * | ||
455 | * return 0 on success. | ||
456 | * return -1 on failure. | ||
457 | */ | ||
458 | int group_send_audio(Group_Chats *g_c, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
459 | unsigned int sample_rate) | ||
460 | { | ||
461 | //TODO use channels and sample_rate arguments. | ||
462 | Group_AV *group_av = group_get_object(g_c, groupnumber); | ||
463 | |||
464 | if (!group_av) | ||
465 | return -1; | ||
466 | |||
467 | uint8_t encoded[1024]; | ||
468 | int32_t size = opus_encode(group_av->audio_encoder, pcm, samples, encoded, sizeof(encoded)); | ||
469 | |||
470 | if (size <= 0) | ||
471 | return -1; | ||
472 | |||
473 | return send_audio_packet(g_c, groupnumber, encoded, size); | ||
474 | } | ||
diff --git a/toxav/group.h b/toxav/group.h index 1427fe18..3355a447 100644 --- a/toxav/group.h +++ b/toxav/group.h | |||
@@ -30,5 +30,24 @@ | |||
30 | * return group number on success. | 30 | * return group number on success. |
31 | * return -1 on failure. | 31 | * return -1 on failure. |
32 | */ | 32 | */ |
33 | int add_av_groupchat(Group_Chats *g_c); | 33 | int add_av_groupchat(Group_Chats *g_c, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, |
34 | uint8_t, unsigned int, void *), void *userdata); | ||
35 | |||
36 | /* Join a AV group (you need to have been invited first.) | ||
37 | * | ||
38 | * returns group number on success | ||
39 | * returns -1 on failure. | ||
40 | */ | ||
41 | int join_av_groupchat(Group_Chats *g_c, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
42 | void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), | ||
43 | void *userdata); | ||
44 | |||
45 | |||
46 | /* Send audio to the group chat. | ||
47 | * | ||
48 | * return 0 on success. | ||
49 | * return -1 on failure. | ||
50 | */ | ||
51 | int group_send_audio(Group_Chats *g_c, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
52 | unsigned int sample_rate); | ||
34 | 53 | ||
diff --git a/toxav/toxav.c b/toxav/toxav.c index 8549bc05..76e40e89 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include "config.h" | 23 | #include "config.h" |
24 | #endif /* HAVE_CONFIG_H */ | 24 | #endif /* HAVE_CONFIG_H */ |
25 | 25 | ||
26 | #define __TOX_DEFINED__ | ||
27 | typedef struct Messenger Tox; | ||
26 | 28 | ||
27 | #define _GNU_SOURCE /* implicit declaration warning */ | 29 | #define _GNU_SOURCE /* implicit declaration warning */ |
28 | 30 | ||
@@ -30,6 +32,7 @@ | |||
30 | #include "codec.h" | 32 | #include "codec.h" |
31 | #include "msi.h" | 33 | #include "msi.h" |
32 | #include "toxav.h" | 34 | #include "toxav.h" |
35 | #include "group.h" | ||
33 | 36 | ||
34 | #include "../toxcore/logger.h" | 37 | #include "../toxcore/logger.h" |
35 | 38 | ||
@@ -1148,3 +1151,42 @@ end: | |||
1148 | rtp_free_msg(NULL, _msg); | 1151 | rtp_free_msg(NULL, _msg); |
1149 | } | 1152 | } |
1150 | } | 1153 | } |
1154 | |||
1155 | |||
1156 | /* Create a new toxav group. | ||
1157 | * | ||
1158 | * return group number on success. | ||
1159 | * return -1 on failure. | ||
1160 | */ | ||
1161 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, | ||
1162 | uint8_t, unsigned int, void *), void *userdata) | ||
1163 | { | ||
1164 | Messenger *m = tox; | ||
1165 | return add_av_groupchat(m->group_chat_object, audio_callback, userdata); | ||
1166 | } | ||
1167 | |||
1168 | /* Join a AV group (you need to have been invited first.) | ||
1169 | * | ||
1170 | * returns group number on success | ||
1171 | * returns -1 on failure. | ||
1172 | */ | ||
1173 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
1174 | void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), | ||
1175 | void *userdata) | ||
1176 | { | ||
1177 | Messenger *m = tox; | ||
1178 | return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); | ||
1179 | } | ||
1180 | |||
1181 | /* Send audio to the group chat. | ||
1182 | * | ||
1183 | * return 0 on success. | ||
1184 | * return -1 on failure. | ||
1185 | */ | ||
1186 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
1187 | unsigned int sample_rate) | ||
1188 | { | ||
1189 | Messenger *m = tox; | ||
1190 | return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); | ||
1191 | } | ||
1192 | |||
diff --git a/toxav/toxav.h b/toxav/toxav.h index 34c8be5d..db261fd8 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h | |||
@@ -382,6 +382,31 @@ Tox *toxav_get_tox(ToxAv *av); | |||
382 | 382 | ||
383 | int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy ); | 383 | int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy ); |
384 | 384 | ||
385 | |||
386 | /* Create a new toxav group. | ||
387 | * | ||
388 | * return group number on success. | ||
389 | * return -1 on failure. | ||
390 | */ | ||
391 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, | ||
392 | unsigned int, void *), void *userdata); | ||
393 | |||
394 | /* Join a AV group (you need to have been invited first.) | ||
395 | * | ||
396 | * returns group number on success | ||
397 | * returns -1 on failure. | ||
398 | */ | ||
399 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
400 | void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); | ||
401 | |||
402 | /* Send audio to the group chat. | ||
403 | * | ||
404 | * return 0 on success. | ||
405 | * return -1 on failure. | ||
406 | */ | ||
407 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
408 | unsigned int sample_rate); | ||
409 | |||
385 | #ifdef __cplusplus | 410 | #ifdef __cplusplus |
386 | } | 411 | } |
387 | #endif | 412 | #endif |