/*******************************************************************************
* Copyright (c) 2008, 2010 Xuggle Inc. All rights reserved.
*
* This file is part of Xuggle-Xuggler-Red5.
*
* Xuggle-Xuggler-Red5 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Xuggle-Xuggler-Red5 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Xuggle-Xuggler-Red5. If not, see .
*******************************************************************************/
package com.xuggle.red5.demo;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IContext;
import org.red5.server.api.IScope;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.stream.BroadcastScope;
import org.red5.server.stream.IBroadcastScope;
import org.red5.server.stream.IProviderService;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.ISimpleMediaFile;
import com.xuggle.xuggler.SimpleMediaFile;
import com.xuggle.red5.Transcoder;
import com.xuggle.red5.io.BroadcastStream;
/**
* This demo application takes a stream broadcasted from a Flash player and
* decodes, re-samples, and re-encodes the audio on that stream to produce
* 5.5KHZ stero PCM audio. It drops all the video.
*
* To use install the application to a Red5 server and connect to the "audiotranscoder"
* application.
*
*
* YOU MUST MAKE SURE YOU HAVE INSTALLED XUGGLER ON THE MACHINE RUNNING YOUR RED5 SERVER.
*
* See http://www.xuggle.com/xuggler
*
*
* Then publish (you can use the "demos/publisher/publisher.html" application) an audio
* stream with a unique name (e.g. "my_stream").
*
* To hear your transcoded audio stream, connect to the same application and then playback
* a stream that has the same unique name, but with "xuggle_" appended to it (e.g. "xuggle_my_stream").
*
* You should hear the audio you are broadcasting, just re-encoded at the new parameters. Please
* note that while you will hear a latency in this audio, approximately 3-msec is added by our transcoder --
* the rest is coming from your network, from red5, and from the fact that (probably) you've set your
* buffer time on your flash NetStream object to 2 seconds. To see exactly how fast transcoding is,
* check out the performance metrics that this application outputs every few seconds.
*
*/
public class AudioTranscoderDemo
{
final private Logger log = Red5LoggerFactory.getLogger(this.getClass());
final private String mStreamPrefix;
final private Map mOutputStreams = new HashMap();
final private Map mTranscoders = new HashMap();
/**
* Create a new resampler demo object.
* @param streamPrefix The prefix to attach to the name of the stream copies we make.
*/
public AudioTranscoderDemo(String streamPrefix)
{
mStreamPrefix = streamPrefix;
}
/**
* @return the streamPrefix
*/
public String getStreamPrefix()
{
return mStreamPrefix;
}
/**
* Starts transcoding this stream. This method is a no-op if
* this stream is already a stream copy created by this
* transcoder.
* @param aStream The stream to copy.
* @param aScope The application scope.
*/
synchronized public void startTranscodingStream(IBroadcastStream aStream, IScope aScope)
{
log.debug("startTranscodingStream({},{})", aStream.getPublishedName(), aScope.getName());
if (aStream.getPublishedName().startsWith(getStreamPrefix()))
{
log.debug("Not making a copy of a copy: {}", aStream.getPublishedName());
return;
}
log.debug("Making transcoded version of: {}", aStream.getPublishedName());
/*
* First, we create the meta data information about our stream.
*
* Turns out aaffmpeg-red5 provides an object to do that with.
*/
ISimpleMediaFile inputStreamInfo = new SimpleMediaFile();
// now we're going to default to no video, and lots of audio,
// but we're going to ask AAFFMPEG to figure out as much
// data as possible. This means AAFFMPEG will have to look at at
// least ONE audio packet before finishing the IContainer open call.
inputStreamInfo.setHasVideo(false);
inputStreamInfo.setHasAudio(true);
/*
* Now, we need to set up the output stream we want to broadcast to.
* Turns out aaffmpeg-red5 provides one of those.
*/
String outputName = getStreamPrefix()+aStream.getPublishedName();
BroadcastStream outputStream = new BroadcastStream(outputName);
outputStream.setPublishedName(outputName);
outputStream.setScope(aScope);
IContext context = aScope.getContext();
IProviderService providerService = (IProviderService) context
.getBean(IProviderService.BEAN_NAME);
if (providerService.registerBroadcastStream(aScope, outputName,
outputStream))
{
IBroadcastScope bsScope = (BroadcastScope) providerService
.getLiveProviderInput(aScope, outputName, true);
bsScope.setAttribute(IBroadcastScope.STREAM_ATTRIBUTE, outputStream);
}
else
{
log.error("Got a fatal error; could not register broadcast stream");
throw new RuntimeException("fooey!");
}
mOutputStreams.put(aStream.getPublishedName(), outputStream);
outputStream.start();
/**
* Now let's give aaffmpeg-red5 some information about what we want to transcode as.
*/
ISimpleMediaFile outputStreamInfo = new SimpleMediaFile();
outputStreamInfo.setAudioSampleRate(22050/4);
outputStreamInfo.setAudioChannels(2);
outputStreamInfo.setAudioBitRate(32000);
outputStreamInfo.setAudioCodec(ICodec.ID.CODEC_ID_PCM_S16LE);
outputStreamInfo.setHasVideo(false);
/**
* And finally, let's create out transcoder
*/
Transcoder transcoder = new Transcoder(aStream,
outputStream, outputStreamInfo);
Thread transcoderThread = new Thread(transcoder);
transcoderThread.setDaemon(true);
mTranscoders.put(aStream.getPublishedName(), transcoder);
log.debug("Starting transcoding thread for: {}", aStream.getPublishedName());
transcoderThread.start();
}
/**
* Stop transcoding a stream.
* @param aStream The stream to stop transcoding.
* @param aScope The application scope.
*/
synchronized public void stopTranscodingStream(IBroadcastStream aStream, IScope aScope)
{
log.debug("stopTranscodingStream({},{})", aStream.getPublishedName(), aScope.getName());
String inputName = aStream.getPublishedName();
Transcoder transcoder = mTranscoders.get(inputName);
if (transcoder != null)
{
transcoder.stop();
}
BroadcastStream outputStream = mOutputStreams.get(inputName);
if (outputStream != null)
{
outputStream.stop();
}
}
}