diff --git a/src/com/limelight/Limelight.java b/src/com/limelight/Limelight.java index a807d13..d5847ab 100644 --- a/src/com/limelight/Limelight.java +++ b/src/com/limelight/Limelight.java @@ -101,12 +101,12 @@ public class Limelight implements NvConnectionListener { /* * Creates a connection to the host and starts up the stream. */ - private void startUpFake(StreamConfiguration streamConfig) { + private void startUpFake(StreamConfiguration streamConfig, String videoFile) { conn = new NvConnection(host, this, streamConfig, PlatformBinding.getCryptoProvider()); conn.start(PlatformBinding.getDeviceName(), null, VideoDecoderRenderer.FLAG_PREFER_QUALITY, new FakeAudioRenderer(), - new FakeVideoRenderer()); + new FakeVideoRenderer(videoFile)); } /** @@ -176,6 +176,7 @@ public class Limelight implements NvConnectionListener { boolean tests = true; String mapping = null; String audio = "default"; + String video = null; Level debug = Level.SEVERE; for (int i = 0; i < args.length; i++) { @@ -256,6 +257,14 @@ public class Limelight implements NvConnectionListener { } } else if (args[i].equals("-fake")) { fake = true; + } else if (args[i].equals("-out")) { + if (i + 1 < args.length) { + video = args[i+1]; + i++; + } else { + System.out.println("Syntax error: output file expected after -out"); + System.exit(3); + } } else if (args[i].equals("-notest")) { tests = false; } else if (args[i].equals("-v")) { @@ -314,7 +323,7 @@ public class Limelight implements NvConnectionListener { Limelight limelight = new Limelight(host); if (!pair) if (fake) - limelight.startUpFake(streamConfig); + limelight.startUpFake(streamConfig, video); else limelight.startUp(streamConfig, inputs, mapping, audio, tests); else diff --git a/src/com/limelight/binding/audio/FakeAudioRenderer.java b/src/com/limelight/binding/audio/FakeAudioRenderer.java index 02433dc..6e56a46 100644 --- a/src/com/limelight/binding/audio/FakeAudioRenderer.java +++ b/src/com/limelight/binding/audio/FakeAudioRenderer.java @@ -7,13 +7,25 @@ import com.limelight.nvstream.av.audio.AudioRenderer; * @author Iwan Timmer */ public class FakeAudioRenderer implements AudioRenderer { + + private int dataSize; + private long last; @Override public void streamInitialized(int channelCount, int sampleRate) { + System.out.println("Fake " + channelCount + " channel " + sampleRate + " samplerate audio output"); + last = System.currentTimeMillis(); } @Override public void playDecodedAudio(byte[] audioData, int offset, int length) { + if (System.currentTimeMillis()>last+2000) { + int bitrate = (dataSize/2)/1024; + System.out.println("Audio " + bitrate + "kB/s"); + dataSize = 0; + last = System.currentTimeMillis(); + } + dataSize += length; } @Override diff --git a/src/com/limelight/binding/video/FakeVideoRenderer.java b/src/com/limelight/binding/video/FakeVideoRenderer.java index ac0b127..0625b60 100644 --- a/src/com/limelight/binding/video/FakeVideoRenderer.java +++ b/src/com/limelight/binding/video/FakeVideoRenderer.java @@ -1,7 +1,14 @@ package com.limelight.binding.video; +import com.limelight.LimeLog; +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.DecodeUnit; import com.limelight.nvstream.av.video.VideoDecoderRenderer; import com.limelight.nvstream.av.video.VideoDepacketizer; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; /** * Implementation of a video decoder and renderer. @@ -12,18 +19,34 @@ public class FakeVideoRenderer implements VideoDecoderRenderer { private Thread thread; private boolean running; - @Override - public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) { + private int dataSize; + private long last; + + private OutputStream out; + + public FakeVideoRenderer(String videoFile) { + try { + if (videoFile!=null) + out = new FileOutputStream(videoFile); + } catch (FileNotFoundException e) { + LimeLog.severe(e.getMessage()); + } } + @Override + public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) { + System.out.println("Fake " + width + "x" + height + " " + redrawRate + "fps video output"); + } + @Override public void start(final VideoDepacketizer depacketizer) { + last = System.currentTimeMillis(); thread = new Thread(new Runnable() { @Override public void run() { while (running) { try { - depacketizer.takeNextDecodeUnit(); + decodeUnit(depacketizer.takeNextDecodeUnit()); } catch (InterruptedException ex) { } } } @@ -36,12 +59,46 @@ public class FakeVideoRenderer implements VideoDecoderRenderer { public void stop() { running = false; thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException ex) { + LimeLog.severe(ex.getMessage()); + } + + try { + if (out!=null) + out.close(); + } catch (IOException e) { + LimeLog.severe(e.getMessage()); + } } @Override public void release() { } + public boolean decodeUnit(DecodeUnit decodeUnit) { + if (System.currentTimeMillis()>last+2000) { + int bitrate = (dataSize/2)/1024; + System.out.println("Video " + bitrate + "kB/s"); + dataSize = 0; + last = System.currentTimeMillis(); + } + dataSize += decodeUnit.getDataLength(); + + if (out!=null) { + try { + for (ByteBufferDescriptor buf:decodeUnit.getBufferList()) + out.write(buf.data, buf.offset, buf.length); + } catch (IOException e) { + LimeLog.severe(e.getMessage()); + return false; + } + } + + return true; + } + @Override public int getCapabilities() { return 0;