[Live-devel] Transcoder Question
Susanne Lefvert
lefvert at mcs.anl.gov
Mon Jan 31 09:57:49 PST 2005
Hello,
I am trying to develop an audio transcoder using the live.com streaming
media library. The goal is to receive L16 RTP audio streams and transcode
them on the fly to PCMU audio, then forward the new streams to a different
multicast address. I found the uLawFromPCMAudioSource that already exists
in the library and tried to use that filter for the transformation. It
seems to work if I transmit audio using the RAT player -> my_transcoder ->
QuickTime, however, RAT -> transcoder -> RAT fails. I have attached my
code, if you have any suggestions what might go wrong, please let me
know. Also, I am planning on adding down-sample code so I can transform
L16 - 16kHz -> PCMU - 8KHz. Would a new filter be the appropriate place
for me to add that?
Thanks,
Susanne
-------------- next part --------------
/*
An audio transcoder that can translate L16-8K-Mono to u-law-8K-Mono. It also mixes
several audio streams into one stream that can be played back in for example Apple
QuickTime Player. The code uses the live.com streaming media library (www.live.com/liveMedia)
Author: Susanne Lefvert
*/
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
FramedSource* source;
RTPSource* rtpSource;
RTCPInstance* rtcpInstanceReceive;
RTCPInstance* rtcpInstanceTransmit;
RTPSink* sink;
Groupsock* rtpGroupsock;
Groupsock* rtcpGroupsock;
RTSPServer* rtspServer;
} sessionState;
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
if (argc != 5){
*env << "\n Usage: L16ToPCMUTranscoder.cpp fromAddress fromPort toAddress toPort\n\n fromAddress/fromPort transmits L16-8K-Mono audio (rtp payload 122) \n toAddress/toPort receives PCMu-8K-Mono (rtp payload 0) \n\n";
exit(1);
}
unsigned const samplingFrequency = 8000;
unsigned char const numChannels = 1;
// *********** SOURCE ****************
// Create 'groupsocks' for RTP and RTCP:
char* fromAddr = argv[1];
const unsigned short fromPort = atoi(argv[2]);
const unsigned short rtcpFromPort = fromPort + 1;
const unsigned char ttlSource = 15;
char* mimeType = "L16";
unsigned char rtpPayloadFormat = 122; //Receive L16-8K-Mono
unsigned char const bitsPerSample = 16;
unsigned bitsPerSecond = samplingFrequency*bitsPerSample*numChannels;
struct in_addr sessionAddress;
sessionAddress.s_addr = our_inet_addr(fromAddr);
const Port rtpPortSource(fromPort);
const Port rtcpPortSource(rtcpFromPort);
Groupsock rtpGroupsock(*env, sessionAddress, rtpPortSource, ttlSource);
Groupsock rtcpGroupsock(*env, sessionAddress, rtcpPortSource, ttlSource);
RTPSource* rtpSource
= SimpleRTPSource::createNew(*env, &rtpGroupsock, rtpPayloadFormat,
samplingFrequency, mimeType, 1, true);
sessionState.rtpSource = rtpSource;
// Create (and start) a 'RTCP instance' for the RTP source:
const unsigned estimatedSessionBandwidth = 130; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstanceReceive
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
NULL /* we're a client */, rtpSource);
// Note: This starts RTCP running automatically
// ************** FILTER ****************
// Add a filter that converts from raw 16-bit linear PCM audio (in little-endian order)
// to 8-bit u-law audio:
sessionState.source = uLawFromPCMAudioSource::createNew(*env, rtpSource, 1/*little-endian*/);
if (sessionState.source == NULL) {
*env << "Unable to create a u-law filter from the PCM audio source: "
<< env->getResultMsg() << "\n";
exit(1);
}
bitsPerSecond /= 2;
mimeType = "PCMU";
if (samplingFrequency == 8000 && numChannels == 1) {
*env << "Convert to u-law 8kHz with payloadformat 0";
rtpPayloadFormat = 0; // a static RTP payload type
} else {
*env << "dynamic payload format 96\n";
rtpPayloadFormat = 96; // a dynamic RTP payload type
}
// ************** SINK ********************
// Get attributes of the audio source:
if (bitsPerSample != 8 && bitsPerSample != 16) {
*env << "The input file contains " << bitsPerSample
<< " bit-per-sample audio, which we don't handle\n";
exit(1);
}
char* toAddr = argv[3];
const unsigned short toPort = atoi(argv[4]);
const unsigned short rtcpToPort = toPort + 1;
const unsigned char ttlSink = 15;
const Port rtpPortSink(toPort);
const Port rtcpPortSink(rtcpToPort);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(toAddr);
sessionState.rtpGroupsock
= new Groupsock(*env, destinationAddress, rtpPortSink, ttlSink);
//sessionState.rtpGroupsock->multicastSendOnly(); // we're a SSM source
sessionState.rtcpGroupsock
= new Groupsock(*env, destinationAddress, rtcpPortSink, ttlSink);
// sessionState.rtcpGroupsock->multicastSendOnly(); // we're a SSM source
// Create an appropriate audio RTP sink (using "SimpleRTPSink")
// from the RTP 'groupsock':
sessionState.sink
= SimpleRTPSink::createNew(*env, sessionState.rtpGroupsock,
rtpPayloadFormat, samplingFrequency,
"audio", mimeType, 1);
// Create (and start) a 'RTCP instance' for this RTP sink:
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstanceTransmit
= RTCPInstance::createNew(*env, sessionState.rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
sessionState.sink, NULL /* we're a server */,
False);//True /* we're a SSM source*/);
// Note: This starts RTCP running automatically
// Create and start a RTSP server to serve this stream:
sessionState.rtspServer = RTSPServer::createNew(*env, 7070);
if (sessionState.rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "Transcoder", "L16-8K-Mono -> PCMu-8K-Mono",
"Session streamed by \"L16ToPCMUTranscoder\"", False);//True/*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstanceTransmit));
sessionState.rtspServer->addServerMediaSession(sms);
// ************ START RECEIVING AND SENDING *************
// Finally, start receiving the multicast stream:
*env << "\nBegin receiving multicast stream at address "<< fromAddr << " port "<< fromPort<<"\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
// Start streaming received rtp packets
char* url = sessionState.rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
*env << "Or tune in via multicast address " <<toAddr<<"/"<<toPort<<"\n";
delete[] url;
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done receiving\n";
// End by closing the media:
Medium::close(sessionState.rtcpInstanceReceive); // Note: Sends a RTCP BYE
Medium::close(sessionState.rtcpInstanceTransmit); // Note: Sends a RTCP BYE
Medium::close(sessionState.sink);
Medium::close(sessionState.source);
Medium::close(sessionState.rtpSource);
Medium::close(sessionState.rtspServer);
}
More information about the live-devel
mailing list