Changed Video app to resize streams

git-svn-id: http://bigbluebutton.googlecode.com/svn/trunk@2394 af16638f-c34d-0410-8cfa-b39d5352b314
This commit is contained in:
Denis Zgonjanin 2009-09-06 21:18:56 +00:00
parent 2b82ed313e
commit 00778a2e41
8 changed files with 172 additions and 554 deletions

View File

@ -1,13 +0,0 @@
package org.bigbluebutton.app.video;
import org.red5.server.api.IScope;
import xugglerutils.JoinedStream;
public class JoinedStreamFactory {
public JoinedStream createStream(String room, IScope scope){
return new JoinedStream(scope, room, VideoAppConstants.JOINED_WIDTH,
VideoAppConstants.JOINED_HEIGHT, VideoAppConstants.JOINED_FRAME_RATE);
}
}

View File

@ -11,4 +11,6 @@ public class VideoAppConstants {
public static final int TILE_WIDTH = 160;
public static final int TILE_HEIGHT = 160;
public static final String TILE_PREFIX = "small";
}

View File

@ -1,34 +1,27 @@
package org.bigbluebutton.app.video;
import java.util.ArrayList;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IBandwidthConfigure;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IServerStream;
import org.red5.server.api.stream.IStreamCapableConnection;
import org.red5.server.api.stream.support.SimpleConnectionBWConfig;
import org.slf4j.Logger;
import xugglerutils.JoinedStream;
import xugglerutils.StreamDownsizer;
public class VideoApplication extends MultiThreadedApplicationAdapter {
private static Logger log = Red5LoggerFactory.getLogger(VideoApplication.class, "video");
private VideoTranscoder videoTranscoder = new VideoTranscoder();
private IScope appScope;
private IServerStream serverStream;
private ArrayList<JoinedStream> joinedStreams;
@Override
public boolean appStart(IScope app) {
super.appStart(app);
log.info("video appStart");
joinedStreams = new ArrayList<JoinedStream>();
appScope = app;
return true;
}
@ -54,30 +47,28 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
@Override
public void appDisconnect(IConnection conn) {
log.info("oflaDemo appDisconnect");
if (appScope == conn.getScope() && serverStream != null) {
serverStream.close();
}
super.appDisconnect(conn);
}
/**
* Called on publish: NetStream.publish("streamname", "live")
*/
@Override
public void streamPublishStart(IBroadcastStream stream){
super.streamPublishStart(stream);
StreamDownsizer smallStream = new StreamDownsizer(stream);
public void streamPublishStart(IBroadcastStream stream) {
log.debug("streamPublishStart: {}; {}", stream, stream.getPublishedName());
super.streamPublishStart(stream);
videoTranscoder.startTranscodingStream(stream,
Red5.getConnectionLocal().getScope());
}
@Override
public boolean roomStart(IScope room){
boolean ret = super.roomStart(room);
JoinedStream videoStream = new JoinedStream(room, room.getName(),
VideoAppConstants.JOINED_WIDTH, VideoAppConstants.JOINED_HEIGHT, VideoAppConstants.JOINED_FRAME_RATE);
joinedStreams.add(videoStream);
Thread videoThread = new Thread(videoStream, room.getName());
videoThread.start();
return ret;
public void streamBroadcastClose(IBroadcastStream stream) {
log.debug("streamBroadcastClose: {}; {}", stream, stream.getPublishedName());
videoTranscoder.stopTranscodingStream(stream,
Red5.getConnectionLocal().getScope());
super.streamBroadcastClose(stream);
}
}

View File

@ -0,0 +1,152 @@
package org.bigbluebutton.app.video;
import java.util.HashMap;
import java.util.Map;
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 org.slf4j.Logger;
import com.xuggle.red5.IVideoPictureListener;
import com.xuggle.red5.Transcoder;
import com.xuggle.red5.VideoPictureListener;
import com.xuggle.red5.io.BroadcastStream;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.ISimpleMediaFile;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.SimpleMediaFile;
public class VideoTranscoder {
final private Logger log = Red5LoggerFactory.getLogger(this.getClass());
final private Map<String, BroadcastStream> mOutputStreams = new HashMap<String, BroadcastStream>();
final private Map<String, Transcoder> mTranscoders = new HashMap<String, Transcoder>();
final private IVideoPictureListener pictureListener = new VideoPictureListener(){
public IVideoPicture preEncode(IVideoPicture picture){
IVideoPicture outPicture = IVideoPicture.make(picture.getPixelType(),
VideoAppConstants.TILE_WIDTH, VideoAppConstants.TILE_HEIGHT);
IVideoResampler resampler = IVideoResampler.make(VideoAppConstants.TILE_WIDTH,
VideoAppConstants.TILE_HEIGHT, picture.getPixelType(),
picture.getWidth(), picture.getHeight(), picture.getPixelType());
resampler.resample(outPicture, picture);
return outPicture;
}
};
public VideoTranscoder(){
}
/**
* 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(VideoAppConstants.TILE_PREFIX))
{
log.debug("Not making a copy of a copy: {}", aStream.getPublishedName());
return;
}
log.debug("Making transcoded version of: {}", aStream.getPublishedName());
/*
* Now, we need to set up the output stream we want to broadcast to.
* Turns out aaffmpeg-red5 provides one of those.
*/
String outputName = VideoAppConstants.TILE_PREFIX+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.setHasAudio(true);
outputStreamInfo.setAudioBitRate(32000);
outputStreamInfo.setAudioChannels(1);
outputStreamInfo.setAudioSampleRate(22050);
outputStreamInfo.setAudioCodec(ICodec.ID.CODEC_ID_MP3);
outputStreamInfo.setHasVideo(true);
// Unfortunately the Trans-coder needs to know the width and height
// you want to output as; even if you don't know yet.
outputStreamInfo.setVideoWidth(320);
outputStreamInfo.setVideoHeight(240);
outputStreamInfo.setVideoBitRate(320000);
outputStreamInfo.setVideoCodec(ICodec.ID.CODEC_ID_FLV1);
outputStreamInfo.setVideoGlobalQuality(0);
/**
* And finally, let's create out transcoder
*/
Transcoder transcoder = new Transcoder(aStream,
outputStream, outputStreamInfo,
null, null, pictureListener);
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();
}
}
}

View File

@ -1,71 +0,0 @@
package xugglerutils;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import org.bigbluebutton.app.video.VideoAppConstants;
public class CheckerboardProcessor {
private ArrayList<IImageProvider> imageProviders;
private BufferedImage image;
private Graphics2D graphics;
public CheckerboardProcessor(){
imageProviders = new ArrayList<IImageProvider>();
this.image = new BufferedImage(VideoAppConstants.JOINED_WIDTH,
VideoAppConstants.JOINED_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
this.graphics = this.image.createGraphics();
}
public BufferedImage getImage(){
return this.image;
}
public void registerListener(IImageProvider provider){
this.imageProviders.add(provider);
}
public void updateImage(){
for (int i = 0; i<imageProviders.size(); i++){
appendImage(imageProviders.get(i).getImage(), i);
}
}
public void appendImage(BufferedImage image, int position){
int x = 0;
int y = 0;
if (position == 0){
x=0;
y=0;
} else if (position == 1){
x=VideoAppConstants.TILE_WIDTH;
y=0;
} else if (position == 2){
x=VideoAppConstants.TILE_WIDTH*2;
y=0;
} else if (position == 3){
x=0;
y=VideoAppConstants.TILE_HEIGHT;
} else if (position == 4){
x=VideoAppConstants.TILE_WIDTH;
y=VideoAppConstants.TILE_HEIGHT;
} else if (position == 5){
x=VideoAppConstants.TILE_WIDTH*2;
y=VideoAppConstants.TILE_HEIGHT;
} else if (position == 6){
x=0;
y=VideoAppConstants.TILE_HEIGHT*2;
} else if (position == 7){
x=VideoAppConstants.TILE_WIDTH;
y=VideoAppConstants.TILE_HEIGHT*2;
} else if (position == 8){
y=VideoAppConstants.TILE_HEIGHT*2;
x=VideoAppConstants.TILE_WIDTH*2;
}
graphics.drawImage(image, x, y, null);
}
}

View File

@ -1,7 +0,0 @@
package xugglerutils;
import java.awt.image.BufferedImage;
public interface IImageProvider {
public BufferedImage getImage();
}

View File

@ -1,214 +0,0 @@
package xugglerutils;
import java.awt.image.BufferedImage;
import org.bigbluebutton.app.video.VideoAppConstants;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IContext;
import org.red5.server.api.IScope;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.stream.BroadcastScope;
import org.red5.server.stream.IBroadcastScope;
import org.red5.server.stream.IProviderService;
import org.slf4j.Logger;
import com.xuggle.red5.io.BroadcastStream;
import com.xuggle.red5.io.IRTMPEventIOHandler;
import com.xuggle.red5.io.Red5HandlerFactory;
import com.xuggle.red5.io.Red5Message;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.ISimpleMediaFile;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.SimpleMediaFile;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
public class JoinedStream implements Runnable {
final private Logger log = Red5LoggerFactory.getLogger(JoinedStream.class, "deskshare");
private IStreamCoder outStreamCoder;
private BroadcastStream broadcastStream;
private IContainer outContainer;
private IStream outStream;
private CheckerboardProcessor imageProcessor;
private static final Red5HandlerFactory factory = Red5HandlerFactory.getFactory();
private final IRTMPEventIOHandler outputHandler;
private boolean keepStreaming = true;
private long timestamp = 0, frameNumber = 0;
private int width, height, frameRate, timestampBase;
private int encodingWidth, encodingHeight;
private String outStreamName;
private IScope scope;
public JoinedStream(IScope scope, String streamName, int width, int height, int frameRate){
this.scope = scope;
this.outStreamName = streamName;
this.width = width;
this.height = height;
this.frameRate = frameRate;
this.timestampBase = 1000000 / this.frameRate;
this.imageProcessor = new CheckerboardProcessor();
outputHandler = new IRTMPEventIOHandler(){
public Red5Message read() throws InterruptedException{
return null;
}
public void write(Red5Message message) throws InterruptedException{
log.debug("Handler writing message to stream " + outStreamName);
try{
IRTMPEvent event = message.getData();
if (event != null){
broadcastStream.dispatchEvent(event);
event.release();
}
} finally{
}
}
};
}
public void run(){
startPublishing(scope);
while(keepStreaming){
encodePicture(imageProcessor.getImage());
}
}
/**
* Starts outputting captured video to a red5 stream
* @param aScope
*/
synchronized private void startPublishing(IScope aScope){
log.debug("started publishing stream in " + aScope.getName());
broadcastStream = new BroadcastStream(outStreamName);
broadcastStream.setPublishedName(outStreamName);
broadcastStream.setScope(aScope);
IContext context = aScope.getContext();
IProviderService providerService = (IProviderService) context.getBean(IProviderService.BEAN_NAME);
if (providerService.registerBroadcastStream(aScope, outStreamName, broadcastStream)){
IBroadcastScope bScope = (BroadcastScope) providerService.getLiveProviderInput(aScope, outStreamName, true);
bScope.setAttribute(IBroadcastScope.STREAM_ATTRIBUTE, broadcastStream);
} else{
log.error("could not register broadcast stream");
throw new RuntimeException("could not register broadcast stream");
}
broadcastStream.start();
}
private void encodePicture(BufferedImage screen) {
IPacket packet = IPacket.make();
IConverter converter = null;
IVideoResampler resampler = IVideoResampler.make(encodingWidth, encodingHeight, IPixelFormat.Type.YUV420P,
width, height, IPixelFormat.Type.BGR24);
try{
converter = ConverterFactory.createConverter(screen, IPixelFormat.Type.BGR24);
} catch(UnsupportedOperationException e){
log.error("could not create converter");
}
IVideoPicture inFrame = converter.toPicture(screen, timestamp);
IVideoPicture outFrame = IVideoPicture.make(IPixelFormat.Type.YUV420P, encodingWidth, encodingHeight);
resampler.resample(outFrame, inFrame);
log.debug(outFrame.getPixelType().toString());
timestamp += timestampBase;
frameNumber ++;
outFrame.setQuality(0);
int retval = outStreamCoder.encodeVideo(packet, outFrame, 0);
if (retval < 0)
throw new RuntimeException("could not encode video");
if (packet.isComplete()){
retval = outContainer.writePacket(packet, false);
if (retval < 0)
throw new RuntimeException("could not save packet to container");
}
outFrame.delete();
outFrame = null;
converter = null;
}
/**
* Sets up Xuggler containers & streams
*/
private void setupStreams(){
log.debug("Setting up streams: {}", broadcastStream.getName());
String outputURL = Red5HandlerFactory.DEFAULT_PROTOCOL +":"+ broadcastStream.getName();
ICodec videoCodec = ICodec.findEncodingCodec(ICodec.ID.CODEC_ID_FLV1);
ISimpleMediaFile outInfo = new SimpleMediaFile();
outInfo.setURL(outputURL);
outInfo.setHasVideo(true);
outInfo.setHasAudio(false);
outInfo.setVideoWidth(encodingWidth);
outInfo.setVideoHeight(encodingHeight);
outInfo.setVideoBitRate(VideoAppConstants.BIT_RATE);
outInfo.setVideoPixelFormat(IPixelFormat.Type.YUV420P);
outInfo.setVideoNumPicturesInGroupOfPictures(VideoAppConstants.NUM_PICTURES_IN_GROUP);
outInfo.setVideoCodec(ICodec.ID.CODEC_ID_FLV1);
outInfo.setVideoGlobalQuality(0);
factory.registerStream(outputHandler, outInfo);
outContainer = IContainer.make();
IContainerFormat outFormat = IContainerFormat.make();
outFormat.setOutputFormat("flv", outputURL, null);
int retval = outContainer.open(outputURL, IContainer.Type.WRITE, outFormat);
if (retval <0){
log.error("could not open output container");
throw new RuntimeException("could not open output file");
}
log.debug("Output container is open.");
outStream = outContainer.addNewStream(0);
outStreamCoder = outStream.getStreamCoder();
outStreamCoder.setNumPicturesInGroupOfPictures(VideoAppConstants.NUM_PICTURES_IN_GROUP);
outStreamCoder.setCodec(videoCodec);
outStreamCoder.setBitRate(VideoAppConstants.BIT_RATE);
outStreamCoder.setBitRateTolerance(VideoAppConstants.BIT_RATE_TOLERANCE);
outStreamCoder.setPixelType(IPixelFormat.Type.YUV420P);
outStreamCoder.setHeight(encodingHeight);
outStreamCoder.setWidth(encodingWidth);
outStreamCoder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, true);
outStreamCoder.setGlobalQuality(0);
IRational frameRate = IRational.make(this.frameRate,1);
outStreamCoder.setFrameRate(frameRate);
IRational timeBase = IRational.make(frameRate.getDenominator(), frameRate.getNumerator());
outStreamCoder.setTimeBase(timeBase);
frameRate = null;
retval = outStreamCoder.open();
if (retval <0)
throw new RuntimeException("could not open input decoder");
retval = outContainer.writeHeader();
if (retval <0) {
log.error("could not write file header");
throw new RuntimeException("could not write file header");
}
}
public IScope getScope() {
return scope;
}
}

View File

@ -1,222 +0,0 @@
package xugglerutils;
import java.awt.image.BufferedImage;
import org.apache.mina.core.buffer.IoBuffer;
import org.bigbluebutton.app.video.VideoAppConstants;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IStreamListener;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.VideoData;
import org.red5.server.api.stream.IStreamPacket;
import org.slf4j.Logger;
import com.xuggle.red5.IPacketListener;
import com.xuggle.red5.IVideoPictureListener;
import com.xuggle.red5.io.Red5HandlerFactory;
import com.xuggle.red5.io.Red5Message;
import com.xuggle.red5.io.Red5StreamingQueue;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.ISimpleMediaFile;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.SimpleMediaFile;
public class StreamDownsizer implements IImageProvider, Runnable {
final private Logger log = Red5LoggerFactory.getLogger(this.getClass());
final static private Red5HandlerFactory red5Factory = Red5HandlerFactory.getFactory();
private IBroadcastStream inputStream;
private IStreamListener inputListener;
private Red5StreamingQueue inputQueue;
private String inputURL;
private IContainer inContainer;
private IStreamCoder inCoder;
private IVideoResampler resampler;
private int videoStreamId;
private boolean keepRunning = true;
public StreamDownsizer(IBroadcastStream inputStream){
this.inputStream = inputStream;
this.inputQueue = new Red5StreamingQueue();
this.videoStreamId = -1;
inputListener = new IStreamListener(){
public void packetReceived(IBroadcastStream aStream, IStreamPacket aPacket)
{
try {
IoBuffer buf = aPacket.getData();
if (buf != null)
buf.rewind();
if (buf==null || buf.remaining()==0)
{
log.debug("skipping empty packet with no data");
return;
}
if (aPacket instanceof VideoData)
{
Red5Message.Type type = Red5Message.Type.INTERFRAME;
VideoData dataPacket = (VideoData)aPacket;
switch(dataPacket.getFrameType())
{
case DISPOSABLE_INTERFRAME:
type = Red5Message.Type.DISPOSABLE_INTERFRAME;
break;
case INTERFRAME:
type = Red5Message.Type.INTERFRAME;
break;
case KEYFRAME:
case UNKNOWN:
type = Red5Message.Type.KEY_FRAME;
break;
}
if (type != Red5Message.Type.DISPOSABLE_INTERFRAME) // The FFMPEG FLV decoder doesn't handle disposable frames
{
log.debug(" adding packet type: {}; ts: {}; on stream: {}",
new Object[]{dataPacket.getFrameType(), aPacket.getTimestamp(), aStream.getPublishedName()});
inputQueue.put(new Red5Message(type, dataPacket));
}
} else if (aPacket instanceof IRTMPEvent)
{
log.debug(" adding packet type: {}; ts: {}; on stream: {}",
new Object[]{"OTHER", aPacket.getTimestamp(), aStream.getPublishedName()});
Red5Message.Type type = Red5Message.Type.OTHER;
IRTMPEvent dataPacket = (IRTMPEvent)aPacket;
inputQueue.put(new Red5Message(type, dataPacket));
}
else
{
log.debug("dropping packet type: {}; ts: {}; on stream: {}",
new Object[]{"UNKNOWN", aPacket.getTimestamp(), aStream.getPublishedName()});
}
} catch (InterruptedException ex)
{
log.error("exception: {}", ex);
} finally {
}
}
};
}
private void openContainer()
{
try {
// set out thread name
String threadName = "Transcoder["+inputStream.getPublishedName()+"]";
log.debug("Changing thread name: {}; to {};", Thread.currentThread().getName(), threadName);
Thread.currentThread().setName(threadName);
int retval = -1;
// First let's setup our input URL
{
// Register a new listener; should hopefully start getting audio packets immediately
log.debug("Adding packet listener to stream: {}", inputStream.getPublishedName());
inputStream.addStreamListener(inputListener);
// Tell AAFFMPEG about our new input URL; we use the unique Red5 names for the url
inputURL = Red5HandlerFactory.DEFAULT_PROTOCOL+":"+inputStream.getName();
ISimpleMediaFile inputInfo = new SimpleMediaFile();
inputInfo.setURL(inputURL);
red5Factory.registerStream(inputQueue, inputInfo);
inContainer = IContainer.make();
// NOTE: This will block until we get the later of the first audio if it has audio, or first video
// if it has video
log.debug("About to open input url: {}", inputURL);
IContainerFormat inFormat = IContainerFormat.make();
inFormat.setInputFormat("flv"); // set the input format to avoid FFMPEG probing
retval = inContainer.open(inputURL, IContainer.Type.READ, inFormat, true, false);
if (retval < 0)
{
throw new RuntimeException("Could not open input: " + inputURL);
}
}
} finally {
}
}
@Override
public BufferedImage getImage() {
// TODO Auto-generated method stub
return null;
}
public void stop()
{
try
{
inputQueue.put(new Red5Message(Red5Message.Type.END_STREAM, null));
}
catch (InterruptedException e)
{
log.error("exception: {}", e);
}
keepRunning = false;
}
@Override
public void run() {
// TODO Auto-generated method stub
try
{
openContainer();
decode();
}
catch (Throwable e)
{
log.error("uncaught exception: " + e.getMessage());
e.printStackTrace();
}
finally
{
//closeContainer();
}
}
public void decode(){
IPacket packet = IPacket.make();
while(keepRunning){
int ret = inContainer.readNextPacket(packet);
if (ret<0) {
log.debug("Container empty, stopping stream");
keepRunning = false;
}
IVideoPicture resizedVideoPicture = resizeVideoPicture(packet);
}
}
private IVideoPicture resizeVideoPicture(IPacket packet){
IVideoPicture videoPicture = IVideoPicture.make(inCoder.getPixelType(),
inCoder.getWidth(), inCoder.getHeight());
IVideoPicture resampledPicture = IVideoPicture.make(inCoder.getPixelType(),
VideoAppConstants.TILE_WIDTH, VideoAppConstants.TILE_HEIGHT);
IVideoResampler resampler = IVideoResampler.make(VideoAppConstants.TILE_WIDTH, VideoAppConstants.TILE_HEIGHT,
inCoder.getPixelType(), inCoder.getWidth(), inCoder.getHeight(), inCoder.getPixelType());
resampler.resample(resampledPicture, videoPicture);
return videoPicture;
}
}