testProgs/testWAVAudioStreamer.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // Copyright (c) 1996-2012, Live Networks, Inc.  All rights reserved
00017 // A test program that streams a WAV audio file via RTP/RTCP
00018 // main program
00019 
00020 #include "liveMedia.hh"
00021 #include "GroupsockHelper.hh"
00022 
00023 #include "BasicUsageEnvironment.hh"
00024 
00025 // To convert 16-bit samples to 8-bit u-law ("u" is the Greek letter "mu")
00026 // encoding, before streaming, uncomment the following line:
00027 //#define CONVERT_TO_ULAW 1
00028 
00029 UsageEnvironment* env;
00030 
00031 void play(); // forward
00032 
00033 int main(int argc, char** argv) {
00034   // Begin by setting up our usage environment:
00035   TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00036   env = BasicUsageEnvironment::createNew(*scheduler);
00037 
00038   play();
00039 
00040   env->taskScheduler().doEventLoop(); // does not return
00041   return 0; // only to prevent compiler warnings
00042 }
00043 
00044 char const* inputFileName = "test.wav";
00045 
00046 void afterPlaying(void* clientData); // forward
00047 
00048 // A structure to hold the state of the current session.
00049 // It is used in the "afterPlaying()" function to clean up the session.
00050 struct sessionState_t {
00051   FramedSource* source;
00052   RTPSink* sink;
00053   RTCPInstance* rtcpInstance;
00054   Groupsock* rtpGroupsock;
00055   Groupsock* rtcpGroupsock;
00056   RTSPServer* rtspServer;
00057 } sessionState;
00058 
00059 void play() {
00060   // Open the file as a 'WAV' file:
00061   WAVAudioFileSource* pcmSource
00062     = WAVAudioFileSource::createNew(*env, inputFileName);
00063   if (pcmSource == NULL) {
00064     *env << "Unable to open file \"" << inputFileName
00065          << "\" as a WAV audio file source: "
00066          << env->getResultMsg() << "\n";
00067     exit(1);
00068   }
00069 
00070   // Get attributes of the audio source:
00071   unsigned char const bitsPerSample = pcmSource->bitsPerSample();
00072   if (bitsPerSample != 8 && bitsPerSample !=  16) {
00073     *env << "The input file contains " << bitsPerSample
00074          << " bit-per-sample audio, which we don't handle\n";
00075     exit(1);
00076   }
00077   sessionState.source = pcmSource;
00078   unsigned const samplingFrequency = pcmSource->samplingFrequency();
00079   unsigned char const numChannels = pcmSource->numChannels();
00080   unsigned bitsPerSecond
00081     = samplingFrequency*bitsPerSample*numChannels;
00082   *env << "Audio source parameters:\n\t" << samplingFrequency << " Hz, ";
00083   *env << bitsPerSample << " bits-per-sample, ";
00084   *env << numChannels << " channels => ";
00085   *env << bitsPerSecond << " bits-per-second\n";
00086 
00087   // Add in any filter necessary to transform the data prior to streaming.
00088   // (This is where any audio compression would get added.)
00089   char const* mimeType;
00090   unsigned char payloadFormatCode;
00091   if (bitsPerSample == 16) {
00092 #ifdef CONVERT_TO_ULAW
00093     // Add a filter that converts from raw 16-bit PCM audio (in little-endian order)
00094     // to 8-bit u-law audio:
00095     sessionState.source
00096       = uLawFromPCMAudioSource::createNew(*env, pcmSource, 1/*little-endian*/);
00097     if (sessionState.source == NULL) {
00098       *env << "Unable to create a u-law filter from the PCM audio source: "
00099            << env->getResultMsg() << "\n";
00100       exit(1);
00101     }
00102     bitsPerSecond /= 2;
00103     mimeType = "PCMU";
00104     if (samplingFrequency == 8000 && numChannels == 1) {
00105       payloadFormatCode = 0; // a static RTP payload type
00106     } else {
00107       payloadFormatCode = 96; // a dynamic RTP payload type
00108     }
00109     *env << "Converting to 8-bit u-law audio for streaming => "
00110          << bitsPerSecond << " bits-per-second\n";
00111 #else
00112     // The 16-bit samples in WAV files are in little-endian order.
00113     // Add a filter that converts them to network (i.e., big-endian) order:
00114     sessionState.source = EndianSwap16::createNew(*env, pcmSource);
00115     if (sessionState.source == NULL) {
00116       *env << "Unable to create a little->bit-endian order filter from the PCM audio source: "
00117            << env->getResultMsg() << "\n";
00118       exit(1);
00119     }
00120     mimeType = "L16";
00121     if (samplingFrequency == 44100 && numChannels == 2) {
00122       payloadFormatCode = 10; // a static RTP payload type
00123     } else if (samplingFrequency == 44100 && numChannels == 1) {
00124       payloadFormatCode = 11; // a static RTP payload type
00125     } else {
00126       payloadFormatCode = 96; // a dynamic RTP payload type
00127     }
00128     *env << "Converting to network byte order for streaming\n";
00129 #endif
00130   } else { // bitsPerSample == 8
00131     // Don't do any transformation; send the 8-bit PCM data 'as is':
00132     mimeType = "L8";
00133     payloadFormatCode = 96; // a dynamic RTP payload type
00134   }
00135 
00136   // Create 'groupsocks' for RTP and RTCP:
00137   struct in_addr destinationAddress;
00138   destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
00139   // Note: This is a multicast address.  If you wish instead to stream
00140   // using unicast, then you should use the "testOnDemandRTSPServer"
00141   // test program - not this test program - as a model.
00142 
00143   const unsigned short rtpPortNum = 2222;
00144   const unsigned short rtcpPortNum = rtpPortNum+1;
00145   const unsigned char ttl = 255;
00146 
00147   const Port rtpPort(rtpPortNum);
00148   const Port rtcpPort(rtcpPortNum);
00149 
00150   sessionState.rtpGroupsock
00151     = new Groupsock(*env, destinationAddress, rtpPort, ttl);
00152   sessionState.rtpGroupsock->multicastSendOnly(); // we're a SSM source
00153   sessionState.rtcpGroupsock
00154     = new Groupsock(*env, destinationAddress, rtcpPort, ttl);
00155   sessionState.rtcpGroupsock->multicastSendOnly(); // we're a SSM source
00156 
00157   // Create an appropriate audio RTP sink (using "SimpleRTPSink")
00158   // from the RTP 'groupsock':
00159   sessionState.sink
00160     = SimpleRTPSink::createNew(*env, sessionState.rtpGroupsock,
00161                                payloadFormatCode, samplingFrequency,
00162                                "audio", mimeType, numChannels);
00163 
00164   // Create (and start) a 'RTCP instance' for this RTP sink:
00165   const unsigned estimatedSessionBandwidth = bitsPerSecond/1000;
00166       // in kbps; for RTCP b/w share
00167   const unsigned maxCNAMElen = 100;
00168   unsigned char CNAME[maxCNAMElen+1];
00169   gethostname((char*)CNAME, maxCNAMElen);
00170   CNAME[maxCNAMElen] = '\0'; // just in case
00171   sessionState.rtcpInstance
00172     = RTCPInstance::createNew(*env, sessionState.rtcpGroupsock,
00173                               estimatedSessionBandwidth, CNAME,
00174                               sessionState.sink, NULL /* we're a server */,
00175                               True /* we're a SSM source*/);
00176   // Note: This starts RTCP running automatically
00177 
00178   // Create and start a RTSP server to serve this stream:
00179   sessionState.rtspServer = RTSPServer::createNew(*env, 8554);
00180   if (sessionState.rtspServer == NULL) {
00181     *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
00182     exit(1);
00183   }
00184   ServerMediaSession* sms
00185     = ServerMediaSession::createNew(*env, "testStream", inputFileName,
00186            "Session streamed by \"testWAVAudiotreamer\"", True/*SSM*/);
00187   sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstance));
00188   sessionState.rtspServer->addServerMediaSession(sms);
00189 
00190   char* url = sessionState.rtspServer->rtspURL(sms);
00191   *env << "Play this stream using the URL \"" << url << "\"\n";
00192   delete[] url;
00193 
00194   // Finally, start the streaming:
00195   *env << "Beginning streaming...\n";
00196   sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
00197 }
00198 
00199 
00200 void afterPlaying(void* /*clientData*/) {
00201   *env << "...done streaming\n";
00202 
00203   // End by closing the media:
00204   Medium::close(sessionState.rtspServer);
00205   Medium::close(sessionState.rtcpInstance);
00206   Medium::close(sessionState.sink);
00207   delete sessionState.rtpGroupsock;
00208   Medium::close(sessionState.source);
00209   delete sessionState.rtcpGroupsock;
00210 
00211   // We're done:
00212   exit(0);
00213 }

Generated on Thu May 17 07:11:48 2012 for live by  doxygen 1.5.2