You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cpp

332 lines
8.2 KiB

#include <portaudio.h>
#include <ortp.h>
#include <speex/speex.h>
// Socket stuff
#ifndef _WIN32
#ifdef INET6
#include <netdb.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#else
#include <winsock32.h>
#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()
{
}