/** * @brief Receive decoded video packet. * * @param av Handler. * @param output Storage. * @return int * @retval 0 Success. * @retval ToxAvError On Error. */ inline__ int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output) { if ( !output ) return ErrorInternal; if (cii(call_index, av->msi_session)) return ErrorNoCall; uint8_t packet [RTP_PAYLOAD_SIZE]; int recved_size = 0; int rc; CallSpecific *call = &av->calls[call_index]; do { recved_size = toxav_recv_rtp_payload(av, call_index, TypeVideo, packet); if (recved_size > 0 && ( rc = vpx_codec_decode(&call->cs->v_decoder, packet, recved_size, NULL, 0) ) != VPX_CODEC_OK) { LOGGER_ERROR("Error decoding video: %s\n", vpx_codec_err_to_string(rc)); return ErrorInternal; } } while (recved_size > 0); vpx_codec_iter_t iter = NULL; vpx_image_t *img; img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); *output = img; return 0; }
/** * @brief Receive decoded audio frame. * * @param av Handler. * @param frame_size The size of dest in frames/samples (one frame/sample is 16 bits or 2 bytes * and corresponds to one sample of audio.) * @param dest Destination of the raw audio (16 bit signed pcm with AUDIO_CHANNELS channels). * Make sure it has enough space for frame_size frames/samples. * @return int * @retval >=0 Size of received data in frames/samples. * @retval ToxAvError On error. */ inline__ int toxav_recv_audio ( ToxAv *av, int32_t call_index, int frame_size, int16_t *dest ) { if ( !dest ) return ErrorInternal; if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; uint8_t packet [RTP_PAYLOAD_SIZE]; int recved_size = toxav_recv_rtp_payload(av, call_index, TypeAudio, packet); if ( recved_size == ErrorAudioPacketLost ) { int dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1); if ( dec_size < 0 ) { LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); return ErrorInternal; } else return dec_size; } else if ( recved_size ) { int dec_size = opus_decode(call->cs->audio_decoder, packet, recved_size, dest, frame_size, 0); if ( dec_size < 0 ) { LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); return ErrorInternal; } else return dec_size; } else { return 0; /* Nothing received */ } }
/** * @brief Send audio frame. * * @param av Handler. * @param data The audio data encoded with toxav_prepare_audio_frame(). * @param size Its size in number of bytes. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) { if (size > MAX_CRYPTO_DATA_SIZE) return ErrorInternal; if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } int rc = toxav_send_rtp_payload(av, call_index, TypeAudio, data, size); pthread_mutex_unlock(&call->mutex); return rc; }
/** * @brief Encode audio frame * * @param av Handler * @param dest dest * @param dest_max Max dest size * @param frame The frame * @param frame_size The frame size * @return int * @retval ToxAvError On error. * @retval >0 On success */ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, int frame_size) { if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); pthread_mutex_unlock(&call->mutex); if (rc < 0) { LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); return ErrorInternal; } return rc; }
/** * @brief Call this at the end of the transmission. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) { if (cii(call_index, av->msi_session)) return ErrorNoCall; CallSpecific *call = &av->calls[call_index]; if ( call->crtps[audio_index] && -1 == rtp_terminate_session(call->crtps[audio_index], av->messenger) ) { LOGGER_ERROR("Error while terminating audio RTP session!\n"); return ErrorTerminatingAudioRtp; } if ( call->crtps[video_index] && -1 == rtp_terminate_session(call->crtps[video_index], av->messenger) ) { LOGGER_ERROR("Error while terminating video RTP session!\n"); return ErrorTerminatingVideoRtp; } call->crtps[audio_index] = NULL; call->crtps[video_index] = NULL; if ( call->j_buf ) { terminate_queue(call->j_buf); call->j_buf = NULL; LOGGER_DEBUG("Terminated j queue"); } else LOGGER_DEBUG("No j queue"); if ( call->cs ) { codec_terminate_session(call->cs); call->cs = NULL; LOGGER_DEBUG("Terminated codec session"); } else LOGGER_DEBUG("No codec session"); return ErrorNone; }
/** * @brief Encode video frame * * @param av Handler * @param dest Where to * @param dest_max Max size * @param input What to encode * @return int * @retval ToxAvError On error. * @retval >0 On success */ inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) { if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h); int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); if ( rc != VPX_CODEC_OK) { LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); return ErrorInternal; } ++call->cs->frame_counter; vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; int copied = 0; while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { if ( copied + pkt->data.frame.sz > dest_max ) return ErrorPacketTooLarge; memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); copied += pkt->data.frame.sz; } } return copied; }
/** * @brief Get id of peer participating in conversation * * @param av Handler * @param peer peer index * @return int * @retval ToxAvError No peer id */ ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) return av_CallNonExistant; return av->msi_session->calls[call_index]->state; }
/** * @brief Cancel outgoing request. * * @param av Handler. * @param reason Optional reason. * @param peer_id peer friend_id * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } return msi_cancel(av->msi_session, call_index, peer_id, reason); }
/** * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_stop_call ( ToxAv *av, int32_t call_index ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } return msi_stopcall(av->msi_session, call_index); }
/** * @brief Get id of peer participating in conversation * * @param av Handler * @param peer peer index * @return int * @retval ToxAvError No peer id */ int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) { if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) return ErrorInternal; return av->msi_session->calls[call_index]->peers[peer]; }
/** * @brief Notify peer that we are changing call type * * @param av Handler. * @return int * @param call_type Change to... * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); }
/** * @brief Get peer transmission type. It can either be audio or video. * * @param av Handler. * @param peer The peer * @return int * @retval ToxAvCallType On success. * @retval ToxAvError On error. */ int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) { if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) return ErrorInternal; *dest = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); return ErrorNone; }
/** * @brief Must be call before any RTP transmission occurs. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video ) { if ( !av->msi_session || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { LOGGER_ERROR("Error while starting audio RTP session: invalid call!\n"); return ErrorInternal; } CallSpecific *call = &av->calls[call_index]; call->crtps[audio_index] = rtp_init_session( type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0], av->msi_session->calls[call_index]->key_peer, av->msi_session->calls[call_index]->key_local, av->msi_session->calls[call_index]->nonce_peer, av->msi_session->calls[call_index]->nonce_local); if ( !call->crtps[audio_index] ) { LOGGER_ERROR("Error while starting audio RTP session!\n"); return ErrorStartingAudioRtp; } if ( support_video ) { call->crtps[video_index] = rtp_init_session ( type_video, av->messenger, av->msi_session->calls[call_index]->peers[0], av->msi_session->calls[call_index]->key_peer, av->msi_session->calls[call_index]->key_local, av->msi_session->calls[call_index]->nonce_peer, av->msi_session->calls[call_index]->nonce_local); if ( !call->crtps[video_index] ) { LOGGER_ERROR("Error while starting video RTP session!\n"); return ErrorStartingVideoRtp; } } if ( !(call->j_buf = create_queue(codec_settings->jbuf_capacity)) ) return ErrorInternal; call->cs = codec_init_session(codec_settings->audio_bitrate, codec_settings->audio_frame_duration, codec_settings->audio_sample_rate, codec_settings->audio_channels, codec_settings->video_width, codec_settings->video_height, codec_settings->video_bitrate); return call->cs ? ErrorNone : ErrorInternal; }
/** * @brief Send audio frame. * * @param av Handler. * @param frame The frame (raw 16 bit signed pcm with AUDIO_CHANNELS channels audio.) * @param frame_size Its size in number of frames/samples (one frame/sample is 16 bits or 2 bytes) * frame size should be AUDIO_FRAME_SIZE. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) { if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } return toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size); }
/** * @brief Send RTP payload. * * @param av Handler. * @param type Type of payload. * @param payload The payload. * @param length Size of it. * @return int * @retval 0 Success. * @retval -1 Failure. */ inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, uint16_t length ) { if (cii(call_index, av->msi_session)) return ErrorNoCall; if ( av->calls[call_index].crtps[type - TypeAudio] ) return rtp_send_msg ( av->calls[call_index].crtps[type - TypeAudio], av->msi_session->messenger_handle, payload, length ); else return -1; }
/** * @brief Answer incomming call. * * @param av Handler. * @param call_type Answer with... * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_answer ( ToxAv *av, int32_t call_index, ToxAvCallType call_type ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } if ( av->msi_session->calls[call_index]->state != call_starting ) { return ErrorInvalidState; } return msi_answer(av->msi_session, call_index, call_type); }
/** * @brief Reject incomming call. * * @param av Handler. * @param reason Optional reason. Set NULL if none. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } if ( av->msi_session->calls[call_index]->state != call_starting ) { return ErrorInvalidState; } return msi_reject(av->msi_session, call_index, reason); }
/** * @brief Answer incomming call. * * @param av Handler. * @param call_type Answer with... * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } if ( av->msi_session->calls[call_index]->state != call_starting ) { return ErrorInvalidState; } return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); }
/** * @brief Hangup active call. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_hangup ( ToxAv *av, int32_t call_index ) { if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { return ErrorNoCall; } if ( av->msi_session->calls[call_index]->state != call_active ) { return ErrorInvalidState; } return msi_hangup(av->msi_session, call_index); }
/** * @brief Encode audio frame * * @param av Handler * @param dest dest * @param dest_max Max dest size * @param frame The frame * @param frame_size The frame size * @return int * @retval ToxAvError On error. * @retval >0 On success */ inline__ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, int frame_size) { if (cii(call_index, av->msi_session)) return ErrorNoCall; int32_t rc = opus_encode(av->calls[call_index].cs->audio_encoder, frame, frame_size, dest, dest_max); if (rc < 0) { LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); return ErrorInternal; } return rc; }
/** * @brief Receive RTP payload. * * @param av Handler. * @param type Type of the payload. * @param dest Storage. * @return int * @retval ToxAvError On Error. * @retval >=0 Size of received payload. */ inline__ int toxav_recv_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, uint8_t *dest ) { if ( !dest ) return ErrorInternal; if (cii(call_index, av->msi_session)) return ErrorNoCall; CallSpecific *call = &av->calls[call_index]; if ( !call->crtps[type - TypeAudio] ) return ErrorNoRtpSession; RTPMessage *message; if ( type == TypeAudio ) { do { message = rtp_recv_msg(call->crtps[audio_index]); if (message) { /* push the packet into the queue */ queue(call->j_buf, message); } } while (message); int success = 0; message = dequeue(call->j_buf, &success); if ( success == 2) return ErrorAudioPacketLost; } else { message = rtp_recv_msg(call->crtps[video_index]); } if ( message ) { memcpy(dest, message->data, message->length); int length = message->length; rtp_free_msg(NULL, message); return length; } return 0; }
/** * @brief Encode and send video packet. * * @param av Handler. * @param input The packet. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) { if (cii(call_index, av->msi_session)) { LOGGER_WARNING("Invalid call index: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } int rc = toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); pthread_mutex_unlock(&call->mutex); return rc; }
/** * @brief Receive decoded video packet. * * @param av Handler. * @param output Storage. * @return int * @retval 0 Success. * @retval ToxAvError On Error. */ inline__ int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output) { if ( !output ) return ErrorInternal; if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } uint8_t packet [RTP_PAYLOAD_SIZE]; CallSpecific *call = &av->calls[call_index]; int recved_size; while ((recved_size = toxav_recv_rtp_payload(av, call_index, TypeVideo, packet)) > 0) { if (recved_size < VIDEOFRAME_HEADER_SIZE) { continue; } uint8_t i = packet[0] - call->frame_id; if (i == 0) { /* piece of current frame */ } else if (i > 0 && i < 128) { /* recieved a piece of a frame ahead, flush current frame and start reading this new frame */ int rc = vpx_codec_decode(&call->cs->v_decoder, call->frame_buf, call->frame_limit, NULL, 0); call->frame_id = packet[0]; memset(call->frame_buf, 0, call->frame_limit); call->frame_limit = 0; if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Error decoding video: %u %s\n", i, vpx_codec_err_to_string(rc)); } } else { /* old packet, dont read */ LOGGER_DEBUG("Old packet: %u\n", i); continue; } if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure /* packet out of buffer range */ continue; } LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, recved_size - VIDEOFRAME_HEADER_SIZE); uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; if (limit > call->frame_limit) { call->frame_limit = limit; LOGGER_DEBUG("Limit: %u\n", call->frame_limit); } } vpx_codec_iter_t iter = NULL; vpx_image_t *img; img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); *output = img; return 0; }
/** * @brief Call this at the end of the transmission. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) { if (cii(call_index, av->msi_session)) { LOGGER_WARNING("Invalid call index: %d", call_index); return ErrorNoCall; } CallSpecific *call = &av->calls[call_index]; pthread_mutex_lock(&call->mutex); if (!call->call_active) { pthread_mutex_unlock(&call->mutex); LOGGER_WARNING("Action on inactive call: %d", call_index); return ErrorNoCall; } call->call_active = 0; rtp_terminate_session(call->crtps[audio_index], av->messenger); call->crtps[audio_index] = NULL; rtp_terminate_session(call->crtps[video_index], av->messenger); call->crtps[video_index] = NULL; terminate_queue(call->j_buf); call->j_buf = NULL; int i; DECODE_PACKET *p; call->exit = 1; pthread_mutex_lock(&call->decode_cond_mutex); pthread_cond_signal(&call->decode_cond); pthread_cond_wait(&call->decode_cond, &call->decode_cond_mutex); pthread_mutex_unlock(&call->decode_cond_mutex); pthread_mutex_destroy(&call->decode_cond_mutex); pthread_cond_destroy(&call->decode_cond); for (i = 0; i != VIDEO_DECODE_QUEUE_SIZE; i++) { p = call->video_decode_queue[i]; call->video_decode_queue[i] = NULL; if (p) { free(p); } } for (i = 0; i != AUDIO_DECODE_QUEUE_SIZE; i++) { p = call->audio_decode_queue[i]; call->audio_decode_queue[i] = NULL; if (p) { free(p); } } codec_terminate_session(call->cs); call->cs = NULL; pthread_mutex_unlock(&call->mutex); pthread_mutex_destroy(&call->mutex); memset(call, 0, sizeof(CallSpecific)); return ErrorNone; }
/** * @brief Must be call before any RTP transmission occurs. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, uint32_t jbuf_capacity, uint32_t VAD_treshold, int support_video ) { if ( !av->msi_session || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer || av->calls[call_index].call_active) { LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); return ErrorInternal; } CallSpecific *call = &av->calls[call_index]; call->crtps[audio_index] = rtp_init_session(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); if ( !call->crtps[audio_index] ) { LOGGER_ERROR("Error while starting audio RTP session!\n"); return ErrorInternal; } call->crtps[audio_index]->call_index = call_index; call->crtps[audio_index]->av = av; if ( support_video ) { call->crtps[video_index] = rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); if ( !call->crtps[video_index] ) { LOGGER_ERROR("Error while starting video RTP session!\n"); goto error; } call->crtps[video_index]->call_index = call_index; call->crtps[video_index]->av = av; call->frame_limit = 0; call->frame_id = 0; call->frame_outid = 0; call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1); if (!call->frame_buf) { LOGGER_WARNING("Frame buffer allocation failed!"); goto error; } } if ( !(call->j_buf = create_queue(jbuf_capacity)) ) { LOGGER_WARNING("Jitter buffer creaton failed!"); goto error; } ToxAvCSettings csettings_peer = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[0]); ToxAvCSettings csettings_local = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_local); LOGGER_DEBUG( "Type: %u \n" "Video bitrate: %u \n" "Video height: %u \n" "Video width: %u \n" "Audio bitrate: %u \n" "Audio framedur: %u \n" "Audio sample rate: %u \n" "Audio channels: %u \n", csettings_peer.call_type, csettings_peer.video_bitrate, csettings_peer.max_video_height, csettings_peer.max_video_width, csettings_peer.audio_bitrate, csettings_peer.audio_frame_duration, csettings_peer.audio_sample_rate, csettings_peer.audio_channels ); if ( (call->cs = codec_init_session(csettings_local.audio_bitrate, csettings_local.audio_frame_duration, csettings_local.audio_sample_rate, csettings_local.audio_channels, csettings_peer.audio_channels, VAD_treshold, csettings_local.max_video_width, csettings_local.max_video_height, csettings_local.video_bitrate) )) { if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; //todo: add error checks pthread_mutex_init(&call->decode_cond_mutex, NULL); pthread_cond_init(&call->decode_cond, NULL); void **arg = malloc(2 * sizeof(void *)); arg[0] = av; arg[1] = call; pthread_t temp; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 1 << 18); pthread_create(&temp, &attr, toxav_decoding, arg); pthread_attr_destroy(&attr); LOGGER_WARNING("Got here"); call->call_active = 1; return ErrorNone; } error: rtp_terminate_session(call->crtps[audio_index], av->messenger); rtp_terminate_session(call->crtps[video_index], av->messenger); free(call->frame_buf); terminate_queue(call->j_buf); codec_terminate_session(call->cs); return ErrorInternal; }
/** * @brief Send audio frame. * * @param av Handler. * @param frame The frame (raw 16 bit signed pcm with AUDIO_CHANNELS channels audio.) * @param frame_size Its size in number of frames/samples (one frame/sample is 16 bits or 2 bytes) * frame size should be AUDIO_FRAME_SIZE. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) { if (cii(call_index, av->msi_session)) return ErrorNoCall; return toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size); }
/** * @brief Must be call before any RTP transmission occurs. * * @param av Handler. * @return int * @retval 0 Success. * @retval ToxAvError On error. */ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video ) { if ( !av->msi_session || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] || av->calls[call_index].call_active) { LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); return ErrorInternal; } CallSpecific *call = &av->calls[call_index]; call->crtps[audio_index] = rtp_init_session(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); if ( !call->crtps[audio_index] ) { LOGGER_ERROR("Error while starting audio RTP session!\n"); return ErrorStartingAudioRtp; } if ( support_video ) { call->crtps[video_index] = rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); if ( !call->crtps[video_index] ) { LOGGER_ERROR("Error while starting video RTP session!\n"); rtp_terminate_session(call->crtps[audio_index], av->messenger); return ErrorStartingVideoRtp; } call->frame_limit = 0; call->frame_id = 0; call->frame_outid = 0; call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1); if (!call->frame_buf) { rtp_terminate_session(call->crtps[audio_index], av->messenger); rtp_terminate_session(call->crtps[video_index], av->messenger); LOGGER_WARNING("Frame buffer allocation failed!"); return ErrorInternal; } } if ( !(call->j_buf = create_queue(codec_settings->jbuf_capacity)) ) { rtp_terminate_session(call->crtps[audio_index], av->messenger); rtp_terminate_session(call->crtps[video_index], av->messenger); free(call->frame_buf); LOGGER_WARNING("Jitter buffer creaton failed!"); return ErrorInternal; } if ( (call->cs = codec_init_session(codec_settings->audio_bitrate, codec_settings->audio_frame_duration, codec_settings->audio_sample_rate, codec_settings->audio_channels, codec_settings->audio_VAD_tolerance, codec_settings->video_width, codec_settings->video_height, codec_settings->video_bitrate) )) { call->call_active = 1; return ErrorNone; } rtp_terminate_session(call->crtps[audio_index], av->messenger); rtp_terminate_session(call->crtps[video_index], av->messenger); free(call->frame_buf); terminate_queue(call->j_buf); return ErrorInternal; }