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