bool CoreAV::startCall(uint32_t friendNum, bool video) { if (QThread::currentThread() != coreavThread.get()) { if (threadSwitchLock.test_and_set(std::memory_order_acquire)) { qDebug() << "CoreAV::startCall: Backed off of thread-switch lock"; return false; } bool ret; QMetaObject::invokeMethod(this, "startCall", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum), Q_ARG(bool, video)); threadSwitchLock.clear(std::memory_order_release); return ret; } qDebug() << QString("Starting call with %1").arg(friendNum); auto it = calls.find(friendNum); if (it != calls.end()) { qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum); return false; } uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0; if (!toxav_call(toxav, friendNum, Settings::getInstance().getAudioBitrate(), videoBitrate, nullptr)) return false; auto ret = calls.insert(std::make_pair(friendNum, ToxFriendCall(friendNum, video, *this))); ret.first->second.startTimeout(friendNum); return true; }
bool CoreAV::startCall(uint32_t friendNum, bool video) { if (QThread::currentThread() != coreavThread.get()) { if (threadSwitchLock.test_and_set(std::memory_order_acquire)) { qDebug() << "CoreAV::startCall: Backed off of thread-switch lock"; return false; } bool ret; QMetaObject::invokeMethod(this, "startCall", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum), Q_ARG(bool, video)); threadSwitchLock.clear(std::memory_order_release); return ret; } qDebug() << QString("Starting call with %1").arg(friendNum); if (calls.contains(friendNum)) { qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum); return false; } uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0; if (!toxav_call(toxav, friendNum, AUDIO_DEFAULT_BITRATE, videoBitrate, nullptr)) return false; auto call = calls.insert({friendNum, video, *this}); call->startTimeout(); return true; }
/* * Commands from chat_commands.h */ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { const char *error_str; if (argc != 0) { error_str = "Unknown arguments."; goto on_error; } if ( !ASettins.av ) { error_str = "Audio not supported!"; goto on_error; } if (!self->stb->is_online) { error_str = "Friend is offline."; goto on_error; } ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30); if ( error != ErrorNone ) { if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!"; else error_str = "Internal error!"; goto on_error; } line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx); return; on_error: print_err(self, error_str); }
void Cyanide::av_call(int fid) { Friend *f = &friends[fid]; if(f->callstate != 0) notify_error("already in a call", ""); ToxAvCSettings csettings = av_DefaultSettings; toxav_call(toxav, &f->call_index, fid, &csettings, 15); emit signal_friend_callstate(fid, (f->callstate = -1)); }
void Core::startCall(int friendId, bool video) { int callId; ToxAvCSettings cSettings = av_DefaultSettings; cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; if (video) { qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); cSettings.call_type = TypeVideo; toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); calls[callId].videoEnabled=true; } else { qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); cSettings.call_type = TypeAudio; toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME); calls[callId].videoEnabled=false; } }
void Core::startCall(uint32_t friendId, bool video) { int32_t callId; ToxAvCSettings cSettings = av_DefaultSettings; cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; if (video) { qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId); cSettings.call_type = av_TypeVideo; if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0) { calls[callId].videoEnabled=true; } else { qWarning() << QString("Core: Failed to start new video call with %1").arg(friendId); emit avCallFailed(friendId); return; } } else { qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId); cSettings.call_type = av_TypeAudio; if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0) { calls[callId].videoEnabled=false; } else { qWarning() << QString("Core: Failed to start new audio call with %1").arg(friendId); emit avCallFailed(friendId); return; } } }
TERMINATE_SCOPE() /************************************************************************************************* * Other flows */ /* * Call and reject */ { int step = 0; int running = 1; while (running) { tox_do(bootstrap_node); tox_do(Alice); tox_do(Bob); switch ( step ) { case 0: /* Alice */ printf("Alice is calling...\n"); toxav_call(status_control.Alice.av, 0, TypeAudio, 10); step++; break; \ case 1: /* Bob */ if (status_control.Bob.status == Ringing) { printf("Bob rejects...\n"); toxav_reject(status_control.Bob.av, "Who likes D's anyway?"); step++; } break; case 2: /* Wait for Both to have status ended */ if (status_control.Alice.status == Rejected && status_control.Bob.status == Ended) running = 0; break; } c_sleep(20); } printf("\n"); }
/* * Commands from chat_commands.h */ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { TOXAV_ERR_CALL error; const char *error_str; if ( argc != 0 ) { error_str = "Unknown arguments."; goto on_error; } if ( !CallControl.av ) { error_str = "ToxAV not supported!"; goto on_error; } if ( !self->stb->connection ) { error_str = "Friend is offline."; goto on_error; } if ( CallControl.pending_call ) { error_str = "Already a pending call!"; goto on_error; } toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error); if ( error != TOXAV_ERR_CALL_OK ) { if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!"; else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue"; else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_FOUND ) error_str = "Friend number invalid"; else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED ) error_str = "Friend is valid but not currently connected"; else error_str = "Internal error!"; goto on_error; } CallControl.pending_call = true; callback_recv_ringing(self->num); return; on_error: print_err(self, error_str); }
/* * Commands from chat_commands.h */ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { uint8_t msg[MAX_STR_SIZE]; uint8_t *error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } if ( !ASettins.av ) { error_str = "Audio not supported!"; goto on_error; } if (!self->stb->is_online) { error_str = "Friend is offline."; goto on_error; } ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, TypeAudio, 30); if ( error != ErrorNone ) { if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!"; else error_str = "Internal error!"; goto on_error; } snprintf(msg, sizeof(msg), "Calling... idx: %d", self->call_idx); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; on_error: snprintf(msg, sizeof(msg), "%s", error_str); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); }
void do_phone ( av_session_t *_phone ) { INFO("Welcome to tox_phone version: " _USERAGENT "\n" "Usage: \n" "f [pubkey] (add friend)\n" "c [a/v] (type) [friend] (friend id) (calls friend if online)\n" "h (if call is active hang up)\n" "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n" "r (reject incoming call)\n" "q (quit)\n" "================================================================================" ); while ( 1 ) { char _line [ 1500 ]; int _len; if ( -1 == getinput(_line, 1500, &_len) ) { printf(" >> "); fflush(stdout); continue; } if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ) { INFO("Invalid input!"); continue; } switch (_line[0]) { case 'f': { char _id [128]; strncpy(_id, _line + 2, 128); av_add_friend(_phone, _id); } break; case 'c': { ToxAvCallType _ctype; if ( _len < 5 ) { INFO("Invalid input; usage: c a/v [friend]"); break; } else if ( _line[2] == 'a' || _line[2] != 'v' ) { /* default and audio */ _ctype = TypeAudio; } else { /* video */ _ctype = TypeVideo; } char *_end; int _friend = strtol(_line + 4, &_end, 10); if ( *_end ) { INFO("Friend num has to be numerical value"); break; } if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ) { INFO("Already in a call"); break; } else INFO("Calling friend: %d!", _friend); } break; case 'h': { if ( toxav_hangup(_phone->av) == ErrorNoCall ) { INFO("No call!"); break; } else INFO("Hung up..."); } break; case 'a': { ToxAvError rc; if ( _len > 1 && _line[2] == 'v' ) { rc = toxav_answer(_phone->av, TypeVideo); } else rc = toxav_answer(_phone->av, TypeAudio); if ( rc == ErrorInvalidState ) { INFO("No call to answer!"); } } break; case 'r': { if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState ) INFO("No state to cancel!"); else INFO("Call Rejected..."); } break; case 'q': { INFO("Quitting!"); return; } case '\n': { } default: { } break; } } }
void *in_thread_call (void *arg) { #define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args) ACall *this_call = arg; uint64_t start = 0; int step = 0; int call_idx; const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); int16_t sample_payload[frame_size]; randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; register_callbacks(this_call->Caller.av, &status_control); register_callbacks(this_call->Callee.av, arg); /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */ pthread_mutex_lock(&muhmutex); while (call_running[this_call->idx]) { pthread_mutex_unlock(&muhmutex); switch ( step ) { case 0: /* CALLER */ toxav_call(this_call->Caller.av, &call_idx, this_call->Callee.id, &av_DefaultSettings, 10); call_print(call_idx, "Calling ..."); step++; break; case 1: /* CALLEE */ pthread_mutex_lock(&muhmutex); if (this_call->Caller.status == Ringing) { call_print(call_idx, "Callee answers ..."); pthread_mutex_unlock(&muhmutex); toxav_answer(this_call->Callee.av, 0, &av_DefaultSettings); step++; start = time(NULL); pthread_mutex_lock(&muhmutex); } pthread_mutex_unlock(&muhmutex); break; case 2: /* Rtp transmission */ pthread_mutex_lock(&muhmutex); if (this_call->Caller.status == InCall) { /* I think this is okay */ call_print(call_idx, "Sending rtp ..."); pthread_mutex_unlock(&muhmutex); c_sleep(1000); /* We have race condition here */ toxav_prepare_transmission(this_call->Callee.av, 0, 1); toxav_prepare_transmission(this_call->Caller.av, call_idx, 1); int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE, sample_payload, frame_size); if ( payload_size < 0 ) { ck_assert_msg ( 0, "Failed to encode payload" ); } while (time(NULL) - start < 10) { /* 10 seconds */ /* Both send */ toxav_send_audio(this_call->Caller.av, call_idx, prepared_payload, payload_size); toxav_send_audio(this_call->Callee.av, 0, prepared_payload, payload_size); /* Both receive */ int16_t storage[RTP_PAYLOAD_SIZE]; int recved; c_sleep(20); } step++; /* This terminates the loop */ pthread_mutex_lock(&muhmutex); toxav_kill_transmission(this_call->Callee.av, 0); toxav_kill_transmission(this_call->Caller.av, call_idx); pthread_mutex_unlock(&muhmutex); /* Call over CALLER hangs up */ toxav_hangup(this_call->Caller.av, call_idx); call_print(call_idx, "Hanging up ..."); pthread_mutex_lock(&muhmutex); } pthread_mutex_unlock(&muhmutex); break; case 3: /* Wait for Both to have status ended */ pthread_mutex_lock(&muhmutex); if (this_call->Caller.status == Ended) { pthread_mutex_unlock(&muhmutex); c_sleep(1000); /* race condition */ pthread_mutex_lock(&muhmutex); this_call->Callee.status = Ended; call_running[this_call->idx] = 0; } pthread_mutex_unlock(&muhmutex); break; } c_sleep(20); pthread_mutex_lock(&muhmutex); } pthread_mutex_unlock(&muhmutex); call_print(call_idx, "Call ended successfully!"); pthread_exit(NULL); }
static void tox_thread_message(Tox *tox, ToxAv *av, uint64_t time, uint8_t msg, uint16_t param1, uint16_t param2, void *data) { switch(msg) { case TOX_SETNAME: { /* param1: name length * data: name */ tox_set_name(tox, data, param1); break; } case TOX_SETSTATUSMSG: { /* param1: status length * data: status message */ tox_set_status_message(tox, data, param1); break; } case TOX_SETSTATUS: { /* param1: status */ tox_set_user_status(tox, param1); break; } case TOX_ADDFRIEND: { /* param1: length of message * data: friend id + message */ int r; if(!param1) { STRING* default_add_msg = SPTR(DEFAULT_FRIEND_REQUEST_MESSAGE); r = tox_add_friend(tox, data, default_add_msg->str, default_add_msg->length); } else { r = tox_add_friend(tox, data, data + TOX_FRIEND_ADDRESS_SIZE, param1); } if(r < 0) { uint8_t addf_error; switch(r) { case TOX_FAERR_TOOLONG: addf_error = ADDF_TOOLONG; break; case TOX_FAERR_NOMESSAGE: addf_error = ADDF_NOMESSAGE; break; case TOX_FAERR_OWNKEY: addf_error = ADDF_OWNKEY; break; case TOX_FAERR_ALREADYSENT: addf_error = ADDF_ALREADYSENT; break; case TOX_FAERR_BADCHECKSUM: addf_error = ADDF_BADCHECKSUM; break; case TOX_FAERR_SETNEWNOSPAM: addf_error = ADDF_SETNEWNOSPAM; break; case TOX_FAERR_NOMEM: addf_error = ADDF_NOMEM; break; case TOX_FAERR_UNKNOWN: default: addf_error = ADDF_UNKNOWN; break; } postmessage(FRIEND_ADD, 1, addf_error, data); } else { postmessage(FRIEND_ADD, 0, r, data); } break; } case TOX_DELFRIEND: { /* param1: friend # */ tox_del_friend(tox, param1); postmessage(FRIEND_DEL, 0, 0, data); break; } case TOX_ACCEPTFRIEND: { /* data: FRIENDREQ */ FRIENDREQ *req = data; int r = tox_add_friend_norequest(tox, req->id); postmessage(FRIEND_ACCEPT, (r < 0), (r < 0) ? 0 : r, req); break; } case TOX_SENDMESSAGE: { /* param1: friend # * param2: message length * data: message */ log_write(tox, param1, data, param2, 1, LOG_FILE_MSG_TYPE_TEXT); void *p = data; while(param2 > TOX_MAX_MESSAGE_LENGTH) { uint16_t len = TOX_MAX_MESSAGE_LENGTH - utf8_unlen(p + TOX_MAX_MESSAGE_LENGTH); tox_send_message(tox, param1, p, len); param2 -= len; p += len; } tox_send_message(tox, param1, p, param2); free(data); break; } case TOX_SENDACTION: { /* param1: friend # * param2: message length * data: message */ log_write(tox, param1, data, param2, 1, LOG_FILE_MSG_TYPE_ACTION); void *p = data; while(param2 > TOX_MAX_MESSAGE_LENGTH) { uint16_t len = TOX_MAX_MESSAGE_LENGTH - utf8_unlen(p + TOX_MAX_MESSAGE_LENGTH); tox_send_action(tox, param1, p, len); param2 -= len; p += len; } tox_send_action(tox, param1, p, param2); free(data); break; } case TOX_SENDMESSAGEGROUP: { /* param1: group # * param2: message length * data: message */ tox_group_message_send(tox, param1, data, param2); free(data); break; } case TOX_SENDACTIONGROUP: { /* param1: group # * param2: message length * data: message */ tox_group_action_send(tox, param1, data, param2); free(data); } case TOX_SET_TYPING: { /* param1: friend # */ // Check if user has switched to another friend window chat. // Take care not to react on obsolete data from old Tox instance. _Bool need_resetting = (typing_state.tox == tox) && (typing_state.friendnumber != param1) && (typing_state.sent_value); if(need_resetting) { // Tell previous friend that he's betrayed. tox_set_user_is_typing(tox, typing_state.friendnumber, 0); // Mark that new friend doesn't know that we're typing yet. typing_state.sent_value = 0; } // Mark us as typing to this friend at the moment. // utox_thread_work_for_typing_notifications() will // send a notification if it deems necessary. typing_state.tox = tox; typing_state.friendnumber = param1; typing_state.time = time; //debug("Set typing state for friend (%d): %d\n", typing_state.friendnumber, typing_state.sent_value); break; } case TOX_CALL: { /* param1: friend # */ int32_t id; toxav_call(av, &id, param1, &av_DefaultSettings, 10); postmessage(FRIEND_CALL_STATUS, param1, id, (void*)CALL_RINGING); break; } case TOX_CALL_VIDEO: { /* param1: friend # */ ToxAvCSettings settings = av_DefaultSettings; settings.call_type = av_TypeVideo; settings.max_video_width = max_video_width; settings.max_video_height = max_video_height; int32_t id; toxav_call(av, &id, param1, &settings, 10); postmessage(FRIEND_CALL_STATUS, param1, id, (void*)CALL_RINGING_VIDEO); break; } case TOX_CALL_VIDEO_ON: { /* param1: friend # * param2: call # */ ToxAvCSettings settings = av_DefaultSettings; settings.call_type = av_TypeVideo; settings.max_video_width = max_video_width; settings.max_video_height = max_video_height; toxav_change_settings(av, param2, &settings); postmessage(FRIEND_CALL_START_VIDEO, param1, param2, NULL); break; } case TOX_CALL_VIDEO_OFF: { /* param1: friend # * param2: call # */ toxav_change_settings(av, param2, &av_DefaultSettings); postmessage(FRIEND_CALL_STOP_VIDEO, param1, param2, NULL); break; } case TOX_ACCEPTCALL: { /* param1: call # */ ToxAvCSettings settings = av_DefaultSettings; if(param2) { settings.call_type = av_TypeVideo; settings.max_video_width = max_video_width; settings.max_video_height = max_video_height; } toxav_answer(av, param1, &settings); break; } case TOX_HANGUP: { /* param1: call # */ toxav_hangup(av, param1); break; } case TOX_NEWGROUP: { /* */ int g = -1; if (param1) { g = toxav_add_av_groupchat(tox, &callback_av_group_audio, NULL); } else { g = tox_add_groupchat(tox); } if(g != -1) { postmessage(GROUP_ADD, g, 0, tox); } break; } case TOX_LEAVEGROUP: { /* param1: group # */ tox_del_groupchat(tox, param1); break; } case TOX_GROUPINVITE: { /* param1: group # * param2: friend # */ tox_invite_friend(tox, param2, param1); break; } case TOX_GROUPCHANGETOPIC: { /* param1: group # * param2: topic length * data: topic */ tox_group_set_title(tox, param1, data, param2); postmessage(GROUP_TITLE, param1, param2, data); break; } case TOX_GROUP_AUDIO_START:{ /* param1: group # */ postmessage(GROUP_AUDIO_START, param1, 0, NULL); break; } case TOX_GROUP_AUDIO_END:{ /* param1: group # */ postmessage(GROUP_AUDIO_END, param1, 0, NULL); break; } case TOX_SENDFILES: { /* param1: friend # * param2: offset of first file name in data * data: file names */ if(param2 == 0xFFFF) { //paths with line breaks uint8_t *name = data, *p = data, *s = name; while(*p) { _Bool end = 1; while(*p) { if(*p == '\n') { *p = 0; end = 0; break; } if(*p == '/' || *p == '\\') { s = p + 1; } p++; } if(strcmp2(name, "file://") == 0) { name += 7; } utox_transfer_start_file(tox, param1, name, s, p - s); p++; s = name = p; if(end) { break; } } } else { //windows path list uint8_t *name = data; _Bool multifile = (name[param2 - 1] == 0); if(!multifile) { utox_transfer_start_file(tox, param1, data, data + param2, strlen(data) - param2); } else { uint8_t *p = name + param2; name += param2 - 1; if(*(name - 1) != '\\') { *name++ = '\\'; } while(*p) { int len = strlen((char*)p) + 1; memmove(name, p, len); p += len; utox_transfer_start_file(tox, param1, data, name, len - 1); } } } free(data); break; } case TOX_SEND_INLINE: { /* param1: friend id data: pointer to a TOX_SEND_INLINE_MSG struct */ struct TOX_SEND_INLINE_MSG *tsim = data; utox_transfer_start_memory(tox, param1, tsim->image->png_data, tsim->image_size); free(tsim); break; } case TOX_ACCEPTFILE: { /* param1: friend # * param2: file # * data: path to write file */ FILE_T *ft = &friend[param1].incoming[param2]; ft->data = fopen(data, "wb"); if(!ft->data) { free(data); break; } ft->path = data; ft->status = FT_SEND; tox_file_send_control(tox, param1, 1, param2, TOX_FILECONTROL_ACCEPT, NULL, 0); postmessage(FRIEND_FILE_IN_STATUS, param1, param2, (void*)FILE_OK); break; } case TOX_FILE_IN_CANCEL: { /* param1: friend # * param2: file # */ FILE_T *ft = &friend[param1].incoming[param2]; if(ft->data) { if(ft->inline_png) { free(ft->data); } else { fclose(ft->data); free(ft->path); } } ft->status = FT_NONE; tox_file_send_control(tox, param1, 1, param2, TOX_FILECONTROL_KILL, NULL, 0); postmessage(FRIEND_FILE_IN_STATUS, param1, param2, (void*)FILE_KILLED); break; } case TOX_FILE_OUT_CANCEL: { /* param1: friend # * param2: file # */ FILE_T *ft = &friend[param1].outgoing[param2]; ft->status = FT_KILL; tox_file_send_control(tox, param1, 0, param2, TOX_FILECONTROL_KILL, NULL, 0); postmessage(FRIEND_FILE_OUT_STATUS, param1, param2, (void*)FILE_KILLED); break; } case TOX_FILE_IN_PAUSE: { /* param1: friend # * param2: file # */ tox_file_send_control(tox, param1, 1, param2, TOX_FILECONTROL_PAUSE, NULL, 0); postmessage(FRIEND_FILE_IN_STATUS, param1, param2, (void*)FILE_PAUSED); break; } case TOX_FILE_OUT_PAUSE: { /* param1: friend # * param2: file # */ FILE_T *ft = &friend[param1].outgoing[param2]; ft->status = FT_PAUSE; tox_file_send_control(tox, param1, 0, param2, TOX_FILECONTROL_PAUSE, NULL, 0); postmessage(FRIEND_FILE_OUT_STATUS, param1, param2, (void*)FILE_PAUSED); break; } case TOX_FILE_IN_RESUME: { /* param1: friend # * param2: file # */ tox_file_send_control(tox, param1, 1, param2, TOX_FILECONTROL_ACCEPT, NULL, 0); postmessage(FRIEND_FILE_IN_STATUS, param1, param2, (void*)FILE_OK); break; } case TOX_FILE_OUT_RESUME: { /* param1: friend # * param2: file # */ FILE_T *ft = &friend[param1].outgoing[param2]; ft->status = FT_SEND; tox_file_send_control(tox, param1, 0, param2, TOX_FILECONTROL_ACCEPT, NULL, 0); postmessage(FRIEND_FILE_OUT_STATUS, param1, param2, (void*)FILE_OK); break; } } }
static void *call_thread(void *pd) { ToxAV *AliceAV = ((thread_data *) pd)->AliceAV; ToxAV *BobAV = ((thread_data *) pd)->BobAV; CallControl *AliceCC = ((thread_data *) pd)->AliceCC; CallControl *BobCC = ((thread_data *) pd)->BobCC; uint32_t friend_number = ((thread_data *) pd)->friend_number; memset(AliceCC, 0, sizeof(CallControl)); memset(BobCC, 0, sizeof(CallControl)); { /* Call */ TOXAV_ERR_CALL rc; toxav_call(AliceAV, friend_number, 48, 3000, &rc); if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } while (!BobCC->incoming) { c_sleep(10); } { /* Answer */ TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 8, 500, &rc); if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } c_sleep(30); int16_t *PCM = (int16_t *)calloc(960, sizeof(int16_t)); uint8_t *video_y = (uint8_t *)calloc(800 * 600, sizeof(uint8_t)); uint8_t *video_u = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t)); uint8_t *video_v = (uint8_t *)calloc(800 * 600 / 4, sizeof(uint8_t)); time_t start_time = time(NULL); while (time(NULL) - start_time < 4) { toxav_iterate(AliceAV); toxav_iterate(BobAV); toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL); toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL); toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL); toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL); c_sleep(10); } { /* Hangup */ TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc); if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d %p %p\n", rc, (void *)AliceAV, (void *)BobAV); } } c_sleep(30); free(PCM); free(video_y); free(video_u); free(video_v); printf("Closing thread\n"); pthread_exit(NULL); }