#include #include #include // Socket stuff #ifndef _WIN32 #ifdef INET6 #include #endif #include #include #include #else #include #endif #include "talk/session/phone/mediaengine.h" #include "talk/session/phone/portaudiomediaengine.h" // Engine settings #define ENGINE_BUFFER_SIZE 2048 // PortAudio settings #define FRAMES_PER_BUFFER 256 #define SAMPLE_RATE 1 // Speex settings //#define SPEEX_QUALITY 8 // ORTP settings #define MAX_RTP_SIZE 1500 // From mediastreamer // ----------------------------------------------------------------------------- static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *channel_p ) { PortAudioMediaChannel* channel = (PortAudioMediaChannel*) channel_p; channel->readOutput((float*) outputBuffer, framesPerBuffer); channel->writeInput((float*) inputBuffer, framesPerBuffer); return 0; } // ----------------------------------------------------------------------------- PortAudioMediaChannel::PortAudioMediaChannel() : mute_(false), play_(false), stream_(NULL), out_buffer_(NULL), in_buffer_(NULL), speex_frame_(NULL) { // Initialize buffers out_buffer_ = new float[ENGINE_BUFFER_SIZE]; out_buffer_read_ = out_buffer_write_ = (float*) out_buffer_; out_buffer_end_ = (float*) out_buffer_ + ENGINE_BUFFER_SIZE; in_buffer_ = new float[ENGINE_BUFFER_SIZE]; in_buffer_read_ = in_buffer_write_ = (float*) in_buffer_; in_buffer_end_ = (float*) in_buffer_ + ENGINE_BUFFER_SIZE; // Initialize PortAudio int err = Pa_OpenDefaultStream(&stream_, 1, 1, paFloat32, SAMPLE_RATE, FRAMES_PER_BUFFER, 0, portAudioCallback, this ); if (err != paNoError) fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err)); // Initialize Speex speex_bits_init(&speex_bits_); speex_enc_state_ = speex_encoder_init(&speex_nb_mode); speex_dec_state_ = speex_decoder_init(&speex_nb_mode); speex_decoder_ctl(speex_dec_state_, SPEEX_GET_FRAME_SIZE, &speex_frame_size_); speex_frame_ = new float[speex_frame_size_]; // int quality = SPEEX_QUALITY; // speex_encoder_ctl(state, SPEEX_SET_QUALITY, &quality); // Initialize ORTP socket struct sockaddr_in sockaddr; sockaddr.sin_family = AF_INET; sockaddr.sin_addr.s_addr = INADDR_ANY; sockaddr.sin_port = htons(3000); rtp_socket_ = socket(PF_INET, SOCK_DGRAM, 0); fcntl(rtp_socket_, F_SETFL, 0, O_NONBLOCK); bind (rtp_socket_,(struct sockaddr*)&sockaddr, sizeof(sockaddr)); // Initialize ORTP Session rtp_session_ = rtp_session_new(RTP_SESSION_SENDRECV); rtp_session_max_buf_size_set(rtp_session_, MAX_RTP_SIZE); rtp_session_set_profile(rtp_session_, &av_profile); rtp_session_set_local_addr(rtp_session_, "127.0.0.1", 2000); rtp_session_set_remote_addr(rtp_session_, "127.0.0.1", 3000); rtp_session_set_scheduling_mode(rtp_session_, 0); rtp_session_set_blocking_mode(rtp_session_, 0); rtp_session_set_payload_type(rtp_session_, 110); rtp_session_set_jitter_compensation(rtp_session_, 250); rtp_session_enable_adaptive_jitter_compensation(rtp_session_, TRUE); rtp_timestamp_ = 0; //rtp_session_signal_connect(rtp_session_, "telephone-event", (RtpCallback) ortpTelephoneCallback,this); } PortAudioMediaChannel::~PortAudioMediaChannel() { if (stream_) { Pa_CloseStream(stream_); } // Clean up other allocated pointers close(rtp_socket_); } void PortAudioMediaChannel::SetCodec(const char *codec) { if (strcmp(codec, "speex")) printf("Unsupported codec: %s\n", codec); } void PortAudioMediaChannel::OnPacketReceived(const void *data, int len) { struct sockaddr_in sockaddr; sockaddr.sin_family = AF_INET; struct hostent *host = gethostbyname("localhost"); memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length); sockaddr.sin_port = htons(2000); char buf[2048]; memcpy(buf, data, len); // Pass packet on to ORTP if (play_) { sendto(rtp_socket_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); } } void PortAudioMediaChannel::SetPlayout(bool playout) { if (!stream_) return; if (play_ && !playout) { int err = Pa_StopStream(stream_); if (err != paNoError) { fprintf(stderr, "Error stopping PortAudio stream: %s\n", Pa_GetErrorText(err)); return; } play_ = false; } else if (!play_ && playout) { int err = Pa_StartStream(stream_); if (err != paNoError) { fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err)); return; } play_ = true; } } void PortAudioMediaChannel::SetSend(bool send) { mute_ = !send; } float PortAudioMediaChannel::GetCurrentQuality() { return 0; } int PortAudioMediaChannel::GetOutputLevel() { return 0; } void PortAudioMediaChannel::readOutput(float* buf, int len) { //readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); // Receive a packet (if there is one) mblk_t *mp; mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); while (mp != NULL) { gint in_len = mp->b_cont->b_wptr-mp->b_cont->b_rptr; // Decode speex stream speex_bits_read_from(&speex_bits_,mp->b_cont->b_rptr, in_len); speex_decode(speex_dec_state_, &speex_bits_, speex_frame_); writeBuffer(out_buffer_, out_buffer_read_, &out_buffer_write_, out_buffer_end_, speex_frame_, speex_frame_size_); rtp_timestamp_++; mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_); } // Read output readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len); } void PortAudioMediaChannel::writeInput(float* buf, int len) { //writeBuffer(in_buffer_, in_buffer_read_, &in_buffer_write_, in_buffer_end_, buf, len); } void PortAudioMediaChannel::readBuffer(float* buffer, float** buffer_read_p, float*buffer_write, float* buffer_end, float* target_buffer, int target_len) { float *end, *tmp, *buffer_read = *buffer_read_p; int remaining; // First phase tmp = buffer_read + target_len; if (buffer_write < buffer_read && tmp > buffer_end) { end = buffer_end; remaining = tmp - buffer_end; } else { end = (tmp > buffer_write ? buffer_write : tmp); remaining = 0; } while (buffer_read < end) { *target_buffer++ = *buffer_read++; } // Second phase if (remaining > 0) { buffer_read = buffer; tmp = buffer_read + remaining; end = (tmp > buffer_write ? buffer_write : tmp); while (buffer_read < end) { *target_buffer++ = *buffer_read++; } } // Finish up *buffer_read_p = buffer_read; } void PortAudioMediaChannel::writeBuffer(float* buffer, float* buffer_read, float**buffer_write_p, float* buffer_end, float* source_buffer, int source_len) { float *end, *tmp, *buffer_write = *buffer_write_p; int remaining; // First phase tmp = buffer_write + source_len; if (buffer_write > buffer_read) { if (tmp > buffer_end) { end = buffer_end; remaining = tmp - buffer_end; } else { end = tmp; remaining = 0; } } else { if (tmp > buffer_read) { printf("Warning: Dropping frame(s)\n"); end = buffer_read; remaining = 0; } else { end = tmp; remaining = 0; } } while (buffer_write < end) { *buffer_write++ = *source_buffer++; } // Second phase if (remaining > 0) { buffer_write = buffer; tmp = buffer_write + remaining; if (tmp > buffer_read) { printf("Warning: Dropping frame(s)\n"); end = buffer_read; } else { end = tmp; } while (buffer_write < end) { *buffer_write++ = *source_buffer++; } } // Finish up *buffer_write_p = buffer_write; } // ----------------------------------------------------------------------------- PortAudioMediaEngine::PortAudioMediaEngine() { } PortAudioMediaEngine::~PortAudioMediaEngine() { Pa_Terminate(); } bool PortAudioMediaEngine::Init() { ortp_init(); int err = Pa_Initialize(); if (err != paNoError) { fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err)); return false; } // Speex rtp_profile_set_payload(&av_profile, 110, &speex_wb); codecs_.push_back(Codec(110, "speex", 8)); return true; } void PortAudioMediaEngine::Terminate() { } cricket::MediaChannel* PortAudioMediaEngine::CreateChannel() { return new PortAudioMediaChannel(); } int PortAudioMediaEngine::SetAudioOptions(int options) { } int PortAudioMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { } int PortAudioMediaEngine::GetInputLevel() { }