Revision: 7275
          http://playerstage.svn.sourceforge.net/playerstage/?rev=7275&view=rev
Author:   natepak
Date:     2009-01-16 04:32:09 +0000 (Fri, 16 Jan 2009)

Log Message:
-----------
Added audio_video directory

Modified Paths:
--------------
    code/gazebo/trunk/server/SConscript
    code/gazebo/trunk/server/rendering/UserCamera.cc

Added Paths:
-----------
    code/gazebo/trunk/server/audio_video/
    code/gazebo/trunk/server/audio_video/AudioDecoder.cc
    code/gazebo/trunk/server/audio_video/AudioDecoder.hh
    code/gazebo/trunk/server/audio_video/OpenAL.cc
    code/gazebo/trunk/server/audio_video/OpenAL.hh
    code/gazebo/trunk/server/audio_video/SConscript

Modified: code/gazebo/trunk/server/SConscript
===================================================================
--- code/gazebo/trunk/server/SConscript 2009-01-15 23:15:17 UTC (rev 7274)
+++ code/gazebo/trunk/server/SConscript 2009-01-16 04:32:09 UTC (rev 7275)
@@ -1,7 +1,7 @@
 #Import variables
 Import('env install_prefix sharedObjs headers')
 
-dirs = Split('physics rendering sensors controllers gui')# bindings')
+dirs = Split('physics rendering sensors controllers gui audio_video')# 
bindings')
 
 for subdir in dirs:
   SConscript('%s/SConscript' % subdir)

Added: code/gazebo/trunk/server/audio_video/AudioDecoder.cc
===================================================================
--- code/gazebo/trunk/server/audio_video/AudioDecoder.cc                        
        (rev 0)
+++ code/gazebo/trunk/server/audio_video/AudioDecoder.cc        2009-01-16 
04:32:09 UTC (rev 7275)
@@ -0,0 +1,173 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "AudioDecoder.hh"
+
+bool AudioDecoder::initialized = false;
+
+////////////////////////////////////////////////////////////////////////////////
+/// Constructor
+AudioDecoder::AudioDecoder()
+{
+  this->formatCtx = NULL;
+  this->codecCtx = NULL;
+  this->codec = NULL;
+  this->audioStream = 0;
+
+  // Initialize the ffmpeg library only once
+  if (!initialized)
+  {
+    initialized = true;
+    avcodec_init();
+    avcodec_register_all();
+
+    // Register all formats and codecs
+    av_register_all();
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Destructor
+AudioDecoder::~AudioDecoder()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Decode and audio file
+int AudioDecoder::Decode(uint8_t **outBuffer, unsigned int *outBufferSize)
+{
+  AVPacket packet;
+  char tmpBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
+  int tmpBufsize = 0;
+  int bytesDecoded = 0;
+  unsigned int maxBufferSize = 0;
+
+  if (this->codec == NULL)
+  {
+    printf("Set and audio file before decoding.\n");
+    return -1;
+  }
+
+  if (outBufferSize == NULL)
+  {
+    printf("outBufferSize is NULL!!\n");
+    return -1;
+  }
+
+  *outBufferSize = 0;
+
+  if (*outBuffer)
+  {
+    delete [] *outBuffer;
+    *outBuffer = NULL;
+  }
+
+  // Read the next frame of a stream
+  while (av_read_frame(this->formatCtx, &packet) >=0)
+  {
+    if (packet.stream_index == this->audioStream)
+    {
+      tmpBufsize = sizeof(tmpBuf);
+
+      // Decode the frame
+      bytesDecoded = avcodec_decode_audio2( this->codecCtx, (int16_t*)tmpBuf, 
+          &tmpBufsize, packet.data, packet.size );
+
+      if (bytesDecoded < 0)
+      {
+        printf("Error decoding audio\n");
+        return -1;
+      }
+
+      if (tmpBufsize <= 0)
+      {
+        printf("No data yet\n");
+        return -1;
+      }
+
+      // Resize the audio buffer as necessary
+      if (*outBufferSize + tmpBufsize > maxBufferSize)
+      {
+        maxBufferSize += tmpBufsize * 10;
+        *outBuffer = (uint8_t*)realloc(*outBuffer, 
+                                       maxBufferSize * sizeof(*outBuffer[0]) );
+      }
+
+      memcpy(*outBuffer + *outBufferSize, tmpBuf, tmpBufsize); 
+      *outBufferSize += tmpBufsize;
+    }
+  }
+
+  av_free_packet(&packet);
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Get the sample rate from the latest decoded file
+int AudioDecoder::GetSampleRate()
+{
+  return this->codecCtx->sample_rate;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Helper function to read in an audio file
+int AudioDecoder::SetFile(const std::string &filename)
+{
+  unsigned int i;
+
+  // Open file
+  if (av_open_input_file(&this->formatCtx, filename.c_str(), NULL, 0, NULL) != 
0)
+  {
+    printf("Unable to open input file\n");
+    return -1;
+  }
+
+  // Retrieve some information
+  if (av_find_stream_info(this->formatCtx) < 0)
+  {
+    printf("Unable to find stream info\n");
+    return -1;
+  }
+
+  // Dump information about file onto standard error
+  //dump_format(formatCtx, 0, "dump.txt", false);
+
+  // Find audio stream;
+  this->audioStream = -1;
+  for (i=0; i < this->formatCtx->nb_streams; i++)
+  {
+    if (this->formatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
+    {
+      this->audioStream = i;
+      break;
+    }
+  }
+
+  if (this->audioStream == -1)
+  {
+    printf("Couldn't find audio stream\n");
+    return -1;
+  }
+
+  // Get the audio stream codec
+  this->codecCtx = this->formatCtx->streams[audioStream]->codec;
+
+  // Find a decoder
+  this->codec = avcodec_find_decoder(codecCtx->codec_id);
+
+  if (this->codec == NULL)
+  {
+    perror("couldn't find codec");
+    return -1;
+  }
+
+  if (this->codec->capabilities & CODEC_CAP_TRUNCATED)
+    this->codecCtx->flags |= CODEC_FLAG_TRUNCATED;
+
+  // Open codec
+  if (avcodec_open(this->codecCtx, this->codec) < 0)
+    perror("couldn't open codec");
+
+  return 0;
+}

Added: code/gazebo/trunk/server/audio_video/AudioDecoder.hh
===================================================================
--- code/gazebo/trunk/server/audio_video/AudioDecoder.hh                        
        (rev 0)
+++ code/gazebo/trunk/server/audio_video/AudioDecoder.hh        2009-01-16 
04:32:09 UTC (rev 7275)
@@ -0,0 +1,40 @@
+#ifndef AUDIODECODER_HH
+#define AUDIODECODER_HH
+
+extern "C" {
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+}
+
+#include <string>
+
+class AudioDecoder
+{
+  /// \brief Constructor
+  public: AudioDecoder();
+
+  /// \brief Destructor
+  public: virtual ~AudioDecoder();
+
+  /// \brief Set the file to decode
+  public: int SetFile(const std::string &filename);
+
+  /// \brief Decode and audio file
+  public: int Decode(uint8_t **outBuffer, unsigned int *outBufferSize);
+
+  /// \brief Get the sample rate from the latest decoded file
+  public: int GetSampleRate();
+
+  private: AVFormatContext *formatCtx;
+  private: AVCodecContext *codecCtx;
+
+  // libavcodec audio codec
+  private: AVCodec *codec;
+
+  // Index of the audio stream
+  private: int audioStream;
+
+  private: static bool initialized;
+};
+
+#endif

Added: code/gazebo/trunk/server/audio_video/OpenAL.cc
===================================================================
--- code/gazebo/trunk/server/audio_video/OpenAL.cc                              
(rev 0)
+++ code/gazebo/trunk/server/audio_video/OpenAL.cc      2009-01-16 04:32:09 UTC 
(rev 7275)
@@ -0,0 +1,396 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <AL/alut.h>
+#include <AL/alc.h>
+
+#include "AudioDecoder.hh"
+#include "OpenAL.hh"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+///// Constructor
+OpenAL::OpenAL()
+{
+  this->context = NULL;
+  this->audioDevice = NULL;
+
+  this->pos[0] = this->pos[1] = this->pos[2] = 0.0;
+  this->pos[0] = -10.0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Destructor
+OpenAL::~OpenAL()
+{
+  if (this->context && this->audioDevice)
+  {
+    this->context = alcGetCurrentContext();
+    this->audioDevice = alcGetContextsDevice(this->context);
+    alcMakeContextCurrent(NULL);
+    alcDestroyContext(this->context);
+    alcCloseDevice(this->audioDevice);
+  }
+
+  this->sources.clear();
+  this->buffers.clear();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Initialize
+int OpenAL::Init()
+{
+  // Open the default audio device
+  this->audioDevice = alcOpenDevice("ALSA Software on HDA Intel");
+  if (this->audioDevice == NULL)
+  {
+    printf("Unable to open audio device\n");
+    return -1;
+  }
+
+  this->context = alcCreateContext(this->audioDevice, NULL);
+
+  alcMakeContextCurrent(this->context);
+
+  //Clear error code
+  alGetError();
+
+  // TODO: put in function to set distance model
+  //alDistanceModel(AL_EXPONENT_DISTANCE);
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Create a sound source
+unsigned int OpenAL::CreateSource()
+{
+  unsigned int source;
+
+  //Create 1 source
+  alGenSources(1, &source);
+
+  this->sources.push_back(source);
+
+  return this->sources.size()-1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Create an audio data buffer
+unsigned int OpenAL::CreateBuffer(const std::string &audioFile)
+{
+  unsigned int buffer;
+  uint8_t *dataBuffer = NULL;
+  unsigned int dataBufferSize;
+
+  // Create an audio decoder
+  AudioDecoder audioDecoder;
+
+  // Create and openAL audio buffer
+  alGenBuffers(1, &buffer);
+
+  // Store the openAL buffer
+  this->buffers.push_back(buffer);
+
+  // Set the audio file to decode
+  audioDecoder.SetFile(audioFile);
+  audioDecoder.Decode(&dataBuffer, &dataBufferSize);
+
+  // Fill the openAL data buffer
+  this->SetData(buffer, dataBuffer, dataBufferSize, 
+                audioDecoder.GetSampleRate() );
+
+  if (dataBuffer)
+    delete [] dataBuffer; 
+
+  return this->buffers.size()-1;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+/// Update all the sources and the listener
+void OpenAL::Update()
+{
+  //ALfloat pos[3];
+  ALfloat vel[3];
+
+  //for (int i=0; i< this->sourceCount; i++)
+  {
+
+    //alGetSourcefv(0, AL_VELOCITY, vel);
+    //alGetSourcefv(0, AL_POSITION, pos);
+
+    vel[0] = 0.1;
+    vel[1] = 0.0;
+    vel[2] = 0.0;
+
+    this->pos[0] += 0.01;
+    this->pos[1] = 0;
+    this->pos[2] = 0;
+
+    //printf("p[%f %f %f] v[%f %f %f]\n", pos[0], pos[1], pos[2], vel[0], 
vel[1], vel[2]);
+    this->SetSourcePos(0, this->pos[0], this->pos[1], this->pos[2] );
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Set the data
+int OpenAL::SetData(unsigned int index, uint8_t *data, unsigned int dataSize, 
unsigned int freq)
+{
+  // Clear the error buffer;
+  alGetError();
+
+  printf("Set Data Freq[%d] DataSize[%d]\n",freq, dataSize);
+
+  // Copy raw buffer into AL buffer 0
+  // AL_FORMAT_MONO8, AL_FORMAT_MONO16, AL_FORMAT_STEREO8,
+  // AL_FORMAT_STEREO16
+  alBufferData( this->buffers[0], AL_FORMAT_MONO16, data, dataSize, freq);
+
+  if ( alGetError() != AL_NO_ERROR)
+  {
+    printf("Unable to copy data into openAL buffer\n");
+
+    alDeleteBuffers(1, &this->buffers[index]);
+    return -1;
+  }
+
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// \brief Attach a buffer to a source
+int OpenAL::SetSourceBuffer(unsigned int sourceIndex, unsigned int bufferIndex)
+{
+  if (sourceIndex >= this->sources.size())
+  {
+    std::cerr << "Invalid source index\n";
+    return -1;
+  }
+
+  if (bufferIndex >= this->buffers.size())
+  {
+    std::cerr << "Invalid buffer index\n";
+    return -1;
+  }
+
+  // Buffer data must be set before calling this function
+  
+  // Attach buffer to source
+  alSourcei(this->sources[sourceIndex], AL_BUFFER, this->buffers[bufferIndex] 
);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL SetSourceBuffer Error: [%d]\n" << this->error << "\n";
+    return -1;
+  }
+
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Play the source
+void OpenAL::Play( unsigned int index )
+{
+  // Play the source
+  alSourcePlay( this->sources[index] );
+
+
+  // Wait until finished
+  /*ALint state;
+  do
+  {
+    usleep(100000);
+    alGetSourcei(this->sources[index], AL_SOURCE_STATE, &state);
+  } while ( state == AL_PLAYING);
+  */
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Set the listener position
+int OpenAL::SetListenerPos( float x, float y, float z )
+{
+  // Clear error state
+  alGetError();
+
+  alListener3f(AL_POSITION, x, y, z);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL SetListenerPos Error: [%d]\n" << this->error << "\n";
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Set the listener velocity
+int OpenAL::SetListenerVel( float x, float y, float z )
+{
+  // Clear error state
+  alGetError();
+
+  alListener3f(AL_VELOCITY, x, y, z);
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL SetListenerVel Error: [%d]" << this->error << "\n";
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// Set the listener orientation
+int OpenAL::SetListenerOrient( float cx, float cy, float cz,
+                               float ux, float uy, float uz )
+{
+  ALfloat orient[]={cx, cy, cz, ux, uy, uz};
+
+  // Clear error state
+  alGetError();
+
+  alListenerfv( AL_ORIENTATION, orient );
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL SetListenerOrientation Error: [%d]" << this->error << 
"\n";
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set the position of the source
+int OpenAL::SetSourcePos(unsigned int index, float x, float y, float z)
+{
+  ALfloat p[3] = {x, y, z};
+
+  if (index >= this->sources.size())
+  {
+    std::cerr << "Invalid source index[" << index <<" ]\n";
+    return -1;
+  }
+
+  // Clear error state
+  alGetError();
+
+  alSourcefv( this->sources[index], AL_POSITION, p);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL::SetSourcePos Error: [%d]" << this->error << "\n";
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set the position of the source
+int OpenAL::SetSourceVel(unsigned int index, float x, float y, float z)
+{
+  ALfloat v[3] = {x, y, z};
+
+  if (index >= this->sources.size())
+  {
+    std::cerr << "Invalid source index[" << index <<" ]\n";
+    return -1;
+  }
+
+  // Clear error state
+  alGetError();
+
+  alSourcefv( this->sources[index], AL_VELOCITY, v);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL::SetSourceVel Error: [%d]" << this->error << "\n";
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set the pitch of the source
+int OpenAL::SetSourcePitch(unsigned int index, float p)
+{
+  if (index >= this->sources.size())
+  {
+    std::cerr << "Invalid source index[" << index <<" ]\n";
+    return -1;
+  }
+
+  // clear error state
+  alGetError();
+
+  alSourcef(this->sources[index], AL_PITCH, p);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL::SetSourcePitch Error: [%d]\n" << this->error;
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set the pitch of the source
+int OpenAL::SetSourceGain(unsigned int index, float g)
+{
+  if (index >= this->sources.size())
+  {
+    std::cerr << "Invalid source index[" << index <<" ]\n";
+    return -1;
+  }
+
+  // clear error state
+  alGetError();
+
+  alSourcef(this->sources[index], AL_GAIN, g);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL::SetSourceGain Error: [%d]\n" << this->error;
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Set whether the source loops the audio 
+int OpenAL::SetSourceLoop(unsigned int index, bool state)
+{
+  if (index >= this->sources.size())
+  {
+    std::cerr << "Invalid source index[" << index <<" ]\n";
+    return -1;
+  }
+
+  // clear error state
+  alGetError();
+
+  // Set looping state 
+  alSourcei(this->sources[index], AL_LOOPING, state);
+
+  if ((this->error = alGetError()) != AL_NO_ERROR)
+  {
+    std::cerr << "OpenAL::SetSourceLoop Error: [%d]\n" << this->error << "\n";
+    return -1;
+  }
+
+
+  return 0;
+}

Added: code/gazebo/trunk/server/audio_video/OpenAL.hh
===================================================================
--- code/gazebo/trunk/server/audio_video/OpenAL.hh                              
(rev 0)
+++ code/gazebo/trunk/server/audio_video/OpenAL.hh      2009-01-16 04:32:09 UTC 
(rev 7275)
@@ -0,0 +1,81 @@
+#ifndef OPENAL_HH
+#define OPENAL_HH
+
+#include <AL/al.h>
+#include <AL/alc.h>
+#include <AL/alext.h>
+#include <stdint.h>
+
+#include <deque>
+
+
+class OpenAL
+{
+  /// \brief Constructor
+  public: OpenAL();
+
+  /// \brief Destructor
+  public: virtual ~OpenAL();
+
+  /// \brief Initialize
+  public: int Init();
+
+  /// \brief Update all the sources and the listener
+  public: void Update();
+
+  /// \brief Create a sound source
+  public: unsigned int CreateSource();
+
+  /// \brief Create an audio data buffer
+  public: unsigned int CreateBuffer(const std::string &audioFile);
+
+  /// \brief Set the data
+  public: int SetData(unsigned int index, uint8_t *data, unsigned int 
dataSize, unsigned int freq);
+
+  /// \brief Attach a buffer to a source
+  public: int SetSourceBuffer(unsigned int sourceIndex, unsigned int 
bufferIndex);
+
+  /// \brief Play a sound
+  public: void Play(unsigned int source);
+
+  /// \brief Set the listener position
+  public: int SetListenerPos( float x, float y, float z );
+
+  /// \brief Set the listener velocity
+  public: int SetListenerVel( float x, float y, float z );
+
+  /// \brief Set the listener orientation
+  public: int SetListenerOrient(float cx, float cy, float cz,
+                                      float ux, float uy, float uz);
+
+  /// \brief Set the position of the source
+  public: int SetSourcePos(unsigned int index, float x, float y, float z);
+
+  /// \brief Set the position of the source
+  public: int SetSourceVel(unsigned int index, float x, float y, float z);
+
+  /// \brief Set the pitch of the source
+  public: int SetSourcePitch(unsigned int index, float p);
+
+  /// \brief Set the pitch of the source
+  public: int SetSourceGain(unsigned int index, float g);
+
+  /// \brief Set whether the source loops the audio 
+  public: int SetSourceLoop(unsigned int index, bool state);
+
+  private: ALCcontext *context;
+  private: ALCdevice *audioDevice;
+
+  // OpenAL error code
+  private: ALenum error;
+
+  // Audio sources. 
+  private: std::deque<unsigned int> sources;
+
+  // Audio data buffers
+  private: std::deque<unsigned int> buffers;
+
+  private: ALfloat pos[3];
+};
+
+#endif

Added: code/gazebo/trunk/server/audio_video/SConscript
===================================================================
--- code/gazebo/trunk/server/audio_video/SConscript                             
(rev 0)
+++ code/gazebo/trunk/server/audio_video/SConscript     2009-01-16 04:32:09 UTC 
(rev 7275)
@@ -0,0 +1,41 @@
+#Import variable
+Import('env sharedObjs headers')
+
+parseConfigs = [
+  'pkg-config --cflags --libs libavformat',
+  'pkg-config --cflags --libs libavcodec',
+  'pkg-config --cflags --libs openal',
+  'pkg-config --cflags --libs freealut'
+]
+
+myEnv = env.Clone()
+
+sources = ['AudioDecoder.cc', 
+           'OpenAL.cc'
+           ]
+
+headers.append( 
+          ['server/audio_video/AudioDecoder.hh',
+           'server/audio_video/OpenAL.hh',
+           ] )
+#
+# Parse all the pacakge configurations
+#
+if not myEnv.GetOption('clean'):
+  for cfg in parseConfigs:
+    print "Checking for ["+cfg+"]"
+    try:
+      myEnv.ParseConfig(cfg)
+      print "  Success"
+    except OSError,e:
+      print "Unable to parse config ["+cfg+"]"
+      if cfg.find("OpenAL") >= 0:
+        print "OpenAL not found. 3D audio is disabled."
+        print "  http://connect.creativelabs.com/";
+        Exit(1)
+      if cfg.find("avcodec") >= 0 or cfg.find("avformat") >= 0:
+        print "FFMpeg not found. Audio decoding disabled."
+        print "  http://ffmpeg.mplayerhq.hu/";
+        Exit(1)
+
+myEnv.SharedLibrary('gazeboav', sources)

Modified: code/gazebo/trunk/server/rendering/UserCamera.cc
===================================================================
--- code/gazebo/trunk/server/rendering/UserCamera.cc    2009-01-15 23:15:17 UTC 
(rev 7274)
+++ code/gazebo/trunk/server/rendering/UserCamera.cc    2009-01-16 04:32:09 UTC 
(rev 7275)
@@ -65,7 +65,7 @@
 {
   OgreCamera::LoadCam(node);
 
-  this->SetClipDist(0.1, 100);
+  this->SetClipDist(0.1, 50);
   this->SetFOV( DTOR(60) );
 }
 


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
This SF.net email is sponsored by:
SourcForge Community
SourceForge wants to tell your story.
http://p.sf.net/sfu/sf-spreadtheword
_______________________________________________
Playerstage-commit mailing list
Playerstage-commit@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to