summaryrefslogtreecommitdiff
path: root/toxav/toxav.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r--toxav/toxav.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/toxav/toxav.c b/toxav/toxav.c
new file mode 100644
index 00000000..f704bc6b
--- /dev/null
+++ b/toxav/toxav.c
@@ -0,0 +1,551 @@
1/** toxav.c
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif /* HAVE_CONFIG_H */
27
28#include "media.h"
29#include "rtp.h"
30#include "msi.h"
31
32#include <stdlib.h>
33#include <string.h>
34#include <assert.h>
35
36#include "toxav.h"
37
38#define inline__ inline __attribute__((always_inline))
39
40static const uint8_t audio_index = 0, video_index = 1;
41
42
43typedef enum {
44 ts_closing,
45 ts_running,
46 ts_closed
47
48} ThreadState;
49
50typedef struct _ToxAv {
51 Messenger *messenger;
52
53 MSISession *msi_session; /** Main msi session */
54
55 RTPSession *rtp_sessions[2]; /* Audio is first and video is second */
56
57 struct jitter_buffer *j_buf;
58 CodecState *cs;
59
60 void *agent_handler;
61} ToxAv;
62
63/**
64 * @brief Start new A/V session. There can only be one session at the time. If you register more
65 * it will result in undefined behaviour.
66 *
67 * @param messenger The messenger handle.
68 * @param useragent The agent handling A/V session (i.e. phone).
69 * @param ua_name Useragent name.
70 * @param video_width Width of video frame.
71 * @param video_height Height of video frame.
72 * @return ToxAv*
73 * @retval NULL On error.
74 */
75ToxAv *toxav_new( Tox *messenger, void *useragent, const char *ua_name , uint16_t video_width, uint16_t video_height)
76{
77 ToxAv *av = calloc ( sizeof(ToxAv), 1);
78
79 if (av == NULL)
80 return NULL;
81
82 av->messenger = (Messenger *)messenger;
83
84 av->msi_session = msi_init_session(av->messenger, (const unsigned char *) ua_name );
85 av->msi_session->agent_handler = av;
86
87 av->rtp_sessions[0] = av->rtp_sessions [1] = NULL;
88
89 /* NOTE: This should be user defined or? */
90 av->j_buf = create_queue(20);
91
92 av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width,
93 video_height, VIDEO_BITRATE);
94
95 av->agent_handler = useragent;
96
97 return av;
98}
99
100/**
101 * @brief Remove A/V session.
102 *
103 * @param av Handler.
104 * @return void
105 */
106void toxav_kill ( ToxAv *av )
107{
108 msi_terminate_session(av->msi_session);
109
110 if ( av->rtp_sessions[audio_index] ) {
111 rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle);
112 }
113
114 if ( av->rtp_sessions[video_index] ) {
115 rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle);
116 }
117
118 codec_terminate_session(av->cs);
119
120 free(av);
121}
122
123/**
124 * @brief Register callback for call state.
125 *
126 * @param callback The callback
127 * @param id One of the ToxAvCallbackID values
128 * @return void
129 */
130void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id )
131{
132 msi_register_callback((MSICallback)callback, (MSICallbackID) id);
133}
134
135/**
136 * @brief Call user. Use its friend_id.
137 *
138 * @param av Handler.
139 * @param user The user.
140 * @param call_type Call type.
141 * @param ringing_seconds Ringing timeout.
142 * @return int
143 * @retval 0 Success.
144 * @retval ToxAvError On error.
145 */
146int toxav_call (ToxAv *av, int user, ToxAvCallType call_type, int ringing_seconds )
147{
148 if ( av->msi_session->call ) {
149 return ErrorAlreadyInCall;
150 }
151
152 return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user);
153}
154
155/**
156 * @brief Hangup active call.
157 *
158 * @param av Handler.
159 * @return int
160 * @retval 0 Success.
161 * @retval ToxAvError On error.
162 */
163int toxav_hangup ( ToxAv *av )
164{
165 if ( !av->msi_session->call ) {
166 return ErrorNoCall;
167 }
168
169 if ( av->msi_session->call->state != call_active ) {
170 return ErrorInvalidState;
171 }
172
173 return msi_hangup(av->msi_session);
174}
175
176/**
177 * @brief Answer incomming call.
178 *
179 * @param av Handler.
180 * @param call_type Answer with...
181 * @return int
182 * @retval 0 Success.
183 * @retval ToxAvError On error.
184 */
185int toxav_answer ( ToxAv *av, ToxAvCallType call_type )
186{
187 if ( !av->msi_session->call ) {
188 return ErrorNoCall;
189 }
190
191 if ( av->msi_session->call->state != call_starting ) {
192 return ErrorInvalidState;
193 }
194
195 return msi_answer(av->msi_session, call_type);
196}
197
198/**
199 * @brief Reject incomming call.
200 *
201 * @param av Handler.
202 * @param reason Optional reason. Set NULL if none.
203 * @return int
204 * @retval 0 Success.
205 * @retval ToxAvError On error.
206 */
207int toxav_reject ( ToxAv *av, const char *reason )
208{
209 if ( !av->msi_session->call ) {
210 return ErrorNoCall;
211 }
212
213 if ( av->msi_session->call->state != call_starting ) {
214 return ErrorInvalidState;
215 }
216
217 return msi_reject(av->msi_session, (const uint8_t *) reason);
218}
219
220/**
221 * @brief Cancel outgoing request.
222 *
223 * @param av Handler.
224 * @param reason Optional reason.
225 * @return int
226 * @retval 0 Success.
227 * @retval ToxAvError On error.
228 */
229int toxav_cancel ( ToxAv *av, const char *reason )
230{
231 if ( !av->msi_session->call ) {
232 return ErrorNoCall;
233 }
234
235 return msi_cancel(av->msi_session, 0, (const uint8_t *)reason);
236}
237
238/**
239 * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
240 *
241 * @param av Handler.
242 * @return int
243 * @retval 0 Success.
244 * @retval ToxAvError On error.
245 */
246int toxav_stop_call ( ToxAv *av )
247{
248 if ( !av->msi_session->call ) {
249 return ErrorNoCall;
250 }
251
252 return msi_stopcall(av->msi_session);
253}
254
255/**
256 * @brief Must be call before any RTP transmission occurs.
257 *
258 * @param av Handler.
259 * @return int
260 * @retval 0 Success.
261 * @retval ToxAvError On error.
262 */
263int toxav_prepare_transmission ( ToxAv *av )
264{
265 assert(av->msi_session);
266
267 if ( !av->msi_session || !av->msi_session->call ) {
268 return ErrorNoCall;
269 }
270
271 av->rtp_sessions[audio_index] = rtp_init_session(
272 type_audio,
273 av->messenger,
274 av->msi_session->call->peers[0],
275 av->msi_session->call->key_peer,
276 av->msi_session->call->key_local,
277 av->msi_session->call->nonce_peer,
278 av->msi_session->call->nonce_local
279 );
280
281
282 if ( !av->rtp_sessions[audio_index] ) {
283 fprintf(stderr, "Error while starting audio RTP session!\n");
284 return ErrorStartingAudioRtp;
285 }
286
287 av->rtp_sessions[video_index] = rtp_init_session (
288 type_video,
289 av->messenger,
290 av->msi_session->call->peers[0],
291 av->msi_session->call->key_peer,
292 av->msi_session->call->key_local,
293 av->msi_session->call->nonce_peer,
294 av->msi_session->call->nonce_local
295 );
296
297
298 if ( !av->rtp_sessions[video_index] ) {
299 fprintf(stderr, "Error while starting video RTP session!\n");
300 return ErrorStartingVideoRtp;
301 }
302
303 return ErrorNone;
304}
305
306/**
307 * @brief Call this at the end of the transmission.
308 *
309 * @param av Handler.
310 * @return int
311 * @retval 0 Success.
312 * @retval ToxAvError On error.
313 */
314int toxav_kill_transmission ( ToxAv *av )
315{
316 /* Both sessions should be active at any time */
317 if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
318 return ErrorNoTransmission;
319
320
321 if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
322 fprintf(stderr, "Error while terminating audio RTP session!\n");
323 return ErrorTerminatingAudioRtp;
324 }
325
326 if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
327 fprintf(stderr, "Error while terminating video RTP session!\n");
328 return ErrorTerminatingVideoRtp;
329 }
330
331 return ErrorNone;
332}
333
334
335/**
336 * @brief Send RTP payload.
337 *
338 * @param av Handler.
339 * @param type Type of payload.
340 * @param payload The payload.
341 * @param length Size of it.
342 * @return int
343 * @retval 0 Success.
344 * @retval -1 Failure.
345 */
346inline__ int toxav_send_rtp_payload ( ToxAv *av, ToxAvCallType type, const uint8_t *payload, uint16_t length )
347{
348 if ( av->rtp_sessions[type - TypeAudio] )
349 return rtp_send_msg ( av->rtp_sessions[type - TypeAudio], av->msi_session->messenger_handle, payload, length );
350 else return -1;
351}
352
353/**
354 * @brief Receive RTP payload.
355 *
356 * @param av Handler.
357 * @param type Type of the payload.
358 * @param dest Storage.
359 * @return int
360 * @retval ToxAvError On Error.
361 * @retval >=0 Size of received payload.
362 */
363inline__ int toxav_recv_rtp_payload ( ToxAv *av, ToxAvCallType type, uint8_t *dest )
364{
365 if ( !dest ) return ErrorInternal;
366
367 if ( !av->rtp_sessions[type - TypeAudio] ) return ErrorNoRtpSession;
368
369 RTPMessage *message;
370
371 if ( type == TypeAudio ) {
372
373 do {
374 message = rtp_recv_msg(av->rtp_sessions[audio_index]);
375
376 if (message) {
377 /* push the packet into the queue */
378 queue(av->j_buf, message);
379 }
380 } while (message);
381
382 int success = 0;
383 message = dequeue(av->j_buf, &success);
384
385 if ( success == 2) return ErrorAudioPacketLost;
386 } else {
387 message = rtp_recv_msg(av->rtp_sessions[video_index]);
388 }
389
390 if ( message ) {
391 memcpy(dest, message->data, message->length);
392
393 int length = message->length;
394
395 rtp_free_msg(NULL, message);
396
397 return length;
398 }
399
400 return 0;
401}
402
403/**
404 * @brief Receive decoded video packet.
405 *
406 * @param av Handler.
407 * @param output Storage.
408 * @return int
409 * @retval 0 Success.
410 * @retval ToxAvError On Error.
411 */
412inline__ int toxav_recv_video ( ToxAv *av, vpx_image_t **output)
413{
414 if ( !output ) return ErrorInternal;
415
416 uint8_t packet [RTP_PAYLOAD_SIZE];
417 int recved_size = 0;
418
419 do {
420 recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
421
422 if (recved_size > 0) {
423 printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
424 }
425 } while (recved_size > 0);
426
427 vpx_codec_iter_t iter = NULL;
428 vpx_image_t *img;
429 img = vpx_codec_get_frame(&av->cs->v_decoder, &iter);
430
431 if (img == NULL)
432 return ErrorInternal;
433
434 *output = img;
435 return 0;
436}
437
438/**
439 * @brief Encode and send video packet.
440 *
441 * @param av Handler.
442 * @param input The packet.
443 * @return int
444 * @retval 0 Success.
445 * @retval ToxAvError On error.
446 */
447inline__ int toxav_send_video ( ToxAv *av, vpx_image_t *input)
448{
449 if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
450 printf("could not encode video frame\n");
451 return ErrorInternal;
452 }
453
454 ++av->cs->frame_counter;
455
456 vpx_codec_iter_t iter = NULL;
457 const vpx_codec_cx_pkt_t *pkt;
458 int sent = 0;
459
460 while ( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) {
461 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
462 if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1)
463 ++sent;
464 }
465 }
466
467 if (sent > 0)
468 return 0;
469
470 return ErrorInternal;
471}
472
473/**
474 * @brief Receive decoded audio frame.
475 *
476 * @param av Handler.
477 * @param frame_size ...
478 * @param dest Destination of the packet. Make sure it has enough space for
479 * RTP_PAYLOAD_SIZE bytes.
480 * @return int
481 * @retval >=0 Size of received packet.
482 * @retval ToxAvError On error.
483 */
484inline__ int toxav_recv_audio ( ToxAv *av, int frame_size, int16_t *dest )
485{
486 if ( !dest ) return ErrorInternal;
487
488 uint8_t packet [RTP_PAYLOAD_SIZE];
489
490 int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
491
492 if ( recved_size == ErrorAudioPacketLost ) {
493 printf("Lost packet\n");
494 return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
495 } else if ( recved_size ) {
496 return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
497 } else {
498 return 0; /* Nothing received */
499 }
500}
501
502/**
503 * @brief Encode and send audio frame.
504 *
505 * @param av Handler.
506 * @param frame The frame.
507 * @param frame_size It's size.
508 * @return int
509 * @retval 0 Success.
510 * @retval ToxAvError On error.
511 */
512inline__ int toxav_send_audio ( ToxAv *av, const int16_t *frame, int frame_size)
513{
514 uint8_t temp_data[RTP_PAYLOAD_SIZE];
515 int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data));
516
517 if (ret <= 0)
518 return ErrorInternal;
519
520 return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret);
521}
522
523/**
524 * @brief Get peer transmission type. It can either be audio or video.
525 *
526 * @param av Handler.
527 * @param peer The peer
528 * @return int
529 * @retval ToxAvCallType On success.
530 * @retval ToxAvError On error.
531 */
532int toxav_get_peer_transmission_type ( ToxAv *av, int peer )
533{
534 assert(av->msi_session);
535
536 if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
537 return ErrorInternal;
538
539 return av->msi_session->call->type_peer[peer];
540}
541
542/**
543 * @brief Get reference to an object that is handling av session.
544 *
545 * @param av Handler.
546 * @return void*
547 */
548void *toxav_get_agent_handler ( ToxAv *av )
549{
550 return av->agent_handler;
551}