mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-02-16 18:41:00 +00:00
Merge branch 'master' of github.com:limelight-stream/limelight-pc
# By Cameron Gutman (15) and others # Via Cameron Gutman (2) and Diego Waxemberg (1) * 'master' of github.com:limelight-stream/limelight-pc: Update common jar Fix library loading on Windows when not using a JAR Delete the extracted libraries when Limelight terminates Update common jar Write a better native library loader so DLLs are no longer extracted to the current directory on Windows. Suppress log spam from jinput on Windows 8 and higher Rough Draft of Icon Fix settings.lime being created as a directory. See GitHub issue 1 for details. Update limelight-common jar Use the stream's frame rate as the redraw rate instead of always refreshing at 30 FPS Don't print stack traces for InterruptExceptions. Call the NvConnectionListener class to terminate the stream without causing the unexpected termination error dialog. Fix resolution changing on Windows. Improve the resolution selection algorithm. Only use the audio buffer hack on OS X dynamically increase the buffer size if we build up to large of a queue Revert "Try to workaround Java's poor OS X mixer" Try to workaround Java's poor OS X mixer Move audio buffering responsibility onto us and simply fill whatever the runtime gives us Conflicts: src/com/limelight/Limelight.java src/com/limelight/binding/audio/JavaxAudioRenderer.java src/com/limelight/gui/StreamFrame.java
This commit is contained in:
BIN
limelight-pc/icon/Icon Draft/LimeLight.png
Normal file
BIN
limelight-pc/icon/Icon Draft/LimeLight.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 147 KiB |
BIN
limelight-pc/icon/Icon Draft/appstore copy.psd
Normal file
BIN
limelight-pc/icon/Icon Draft/appstore copy.psd
Normal file
Binary file not shown.
1
limelight-pc/icon/Icon Draft/appstore.ai
Normal file
1
limelight-pc/icon/Icon Draft/appstore.ai
Normal file
@@ -0,0 +1 @@
|
||||
%!PS-Adobe-2.0
|
||||
BIN
limelight-pc/icon/Icon Draft/appstore.psd
Normal file
BIN
limelight-pc/icon/Icon Draft/appstore.psd
Normal file
Binary file not shown.
@@ -1,14 +1,12 @@
|
||||
package com.limelight;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import com.limelight.binding.LibraryHelper;
|
||||
import com.limelight.binding.PlatformBinding;
|
||||
import com.limelight.gui.MainFrame;
|
||||
import com.limelight.gui.StreamFrame;
|
||||
@@ -33,7 +31,7 @@ public class Limelight implements NvConnectionListener {
|
||||
private String host;
|
||||
private StreamFrame streamFrame;
|
||||
private NvConnection conn;
|
||||
private boolean connectionFailed;
|
||||
private boolean connectionTerminating;
|
||||
private static JFrame limeFrame;
|
||||
|
||||
/**
|
||||
@@ -189,14 +187,15 @@ public class Limelight implements NvConnectionListener {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
prepareNativeLibraries();
|
||||
} catch (IOException e) {
|
||||
// This is expected to fail when not in a JAR
|
||||
}
|
||||
LibraryHelper.prepareNativeLibraries();
|
||||
|
||||
createFrame();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
connectionTerminating = true;
|
||||
conn.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to specify which stage is starting. Used to update UI.
|
||||
@@ -244,9 +243,11 @@ public class Limelight implements NvConnectionListener {
|
||||
*/
|
||||
@Override
|
||||
public void connectionTerminated(Exception e) {
|
||||
e.printStackTrace();
|
||||
if (!connectionFailed) {
|
||||
connectionFailed = true;
|
||||
if (!(e instanceof InterruptedException)) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!connectionTerminating) {
|
||||
connectionTerminating = true;
|
||||
|
||||
// Kill the connection to the target
|
||||
conn.stop();
|
||||
|
||||
97
src/com/limelight/binding/LibraryHelper.java
Normal file
97
src/com/limelight/binding/LibraryHelper.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.limelight.binding;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class LibraryHelper {
|
||||
private static final HashSet<String> avcDependencies = new HashSet<String>();
|
||||
private static final boolean needsDependencyExtraction;
|
||||
private static final String libraryExtractionFolder;
|
||||
|
||||
private static boolean librariesExtracted = false;
|
||||
|
||||
static {
|
||||
needsDependencyExtraction = System.getProperty("os.name", "").contains("Windows");
|
||||
libraryExtractionFolder = System.getProperty("java.io.tmpdir", ".");
|
||||
|
||||
// FFMPEG libraries
|
||||
avcDependencies.add("avutil-52");
|
||||
avcDependencies.add("swresample-0");
|
||||
avcDependencies.add("swscale-2");
|
||||
avcDependencies.add("avcodec-55");
|
||||
avcDependencies.add("avformat-55");
|
||||
avcDependencies.add("avfilter-3");
|
||||
|
||||
// The AVC JNI library itself
|
||||
avcDependencies.add("nv_avc_dec");
|
||||
|
||||
// Additional Windows dependencies
|
||||
if (System.getProperty("os.name").contains("Windows")) {
|
||||
avcDependencies.add("postproc-52");
|
||||
avcDependencies.add("pthreadVC2");
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadNativeLibrary(String libraryName) {
|
||||
if (librariesExtracted && avcDependencies.contains(libraryName)) {
|
||||
System.load(libraryExtractionFolder + File.separatorChar + System.mapLibraryName(libraryName));
|
||||
}
|
||||
else {
|
||||
System.loadLibrary(libraryName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void prepareNativeLibraries() {
|
||||
if (!needsDependencyExtraction) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (String dependency : avcDependencies) {
|
||||
extractNativeLibrary(dependency);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// This is expected if this code is not running from a JAR
|
||||
return;
|
||||
}
|
||||
|
||||
librariesExtracted = true;
|
||||
}
|
||||
|
||||
private static void extractNativeLibrary(String libraryName) throws IOException {
|
||||
// convert general library name to platform-specific name
|
||||
libraryName = System.mapLibraryName(libraryName);
|
||||
|
||||
InputStream resource = new Object().getClass().getResourceAsStream("/binlib/"+libraryName);
|
||||
if (resource == null) {
|
||||
throw new FileNotFoundException("Unable to find native library in JAR: "+libraryName);
|
||||
}
|
||||
File destination = new File(libraryExtractionFolder+File.separatorChar+libraryName);
|
||||
|
||||
// this will only delete it if it exists, and then create a new file
|
||||
destination.delete();
|
||||
destination.createNewFile();
|
||||
|
||||
// schedule the temporary file to be deleted when the program exits
|
||||
destination.deleteOnExit();
|
||||
|
||||
//this is the janky java 6 way to copy a file
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(destination);
|
||||
int read;
|
||||
byte[] readBuffer = new byte[16384];
|
||||
while ((read = resource.read(readBuffer)) != -1) {
|
||||
fos.write(readBuffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
package com.limelight.binding.audio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
import com.limelight.nvstream.av.ShortBufferDescriptor;
|
||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||
import com.limelight.nvstream.av.audio.OpusDecoder;
|
||||
|
||||
/**
|
||||
* Audio renderer implementation
|
||||
@@ -18,6 +16,15 @@ import com.limelight.nvstream.av.audio.OpusDecoder;
|
||||
public class JavaxAudioRenderer implements AudioRenderer {
|
||||
|
||||
private SourceDataLine soundLine;
|
||||
private SoundBuffer soundBuffer;
|
||||
private byte[] lineBuffer;
|
||||
private int channelCount;
|
||||
private int sampleRate;
|
||||
private boolean reallocateLines;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 0;
|
||||
public static final int STARING_BUFFER_SIZE = 4096;
|
||||
public static final int STAGING_BUFFERS = 3; // 3 complete frames of audio
|
||||
|
||||
/**
|
||||
* Takes some audio data and writes it out to the renderer.
|
||||
@@ -28,13 +35,36 @@ public class JavaxAudioRenderer implements AudioRenderer {
|
||||
@Override
|
||||
public void playDecodedAudio(short[] pcmData, int offset, int length) {
|
||||
if (soundLine != null) {
|
||||
byte[] pcmDataBytes = new byte[length * 2];
|
||||
ByteBuffer.wrap(pcmDataBytes).asShortBuffer().put(pcmData, offset, length);
|
||||
if (soundLine.available() < length) {
|
||||
soundLine.write(pcmDataBytes, 0, soundLine.available());
|
||||
// Queue the decoded samples into the staging sound buffer
|
||||
soundBuffer.queue(new ShortBufferDescriptor(pcmData, offset, length));
|
||||
|
||||
int available = soundLine.available();
|
||||
if (reallocateLines) {
|
||||
// Kinda jank. If the queued is larger than available, we are going to have a delay
|
||||
// so we increase the buffer size
|
||||
if (available < soundBuffer.size()) {
|
||||
System.out.println("buffer too full, buffer size: " + soundLine.getBufferSize());
|
||||
int currentBuffer = soundLine.getBufferSize();
|
||||
soundLine.close();
|
||||
createSoundLine(currentBuffer*2);
|
||||
if (soundLine != null) {
|
||||
available = soundLine.available();
|
||||
System.out.println("creating new line with buffer size: " + soundLine.getBufferSize());
|
||||
}
|
||||
else {
|
||||
available = 0;
|
||||
System.out.println("failed to create sound line");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
soundLine.write(pcmDataBytes, 0, pcmDataBytes.length);
|
||||
|
||||
// If there's space available in the sound line, pull some data out
|
||||
// of the staging buffer and write it to the sound line
|
||||
if (available > 0) {
|
||||
int written = soundBuffer.fill(lineBuffer, 0, available);
|
||||
if (written > 0) {
|
||||
soundLine.write(lineBuffer, 0, written);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,15 +86,51 @@ public class JavaxAudioRenderer implements AudioRenderer {
|
||||
*/
|
||||
@Override
|
||||
public void streamInitialized(int channelCount, int sampleRate) {
|
||||
|
||||
private void createSoundLine(int bufferSize) {
|
||||
AudioFormat audioFormat = new AudioFormat(sampleRate, 16, channelCount, true, true);
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat, OpusDecoder.getMaxOutputShorts());
|
||||
|
||||
DataLine.Info info;
|
||||
|
||||
if (bufferSize == DEFAULT_BUFFER_SIZE) {
|
||||
info = new DataLine.Info(SourceDataLine.class, audioFormat);
|
||||
}
|
||||
else {
|
||||
info = new DataLine.Info(SourceDataLine.class, audioFormat, bufferSize);
|
||||
}
|
||||
|
||||
try {
|
||||
soundLine = (SourceDataLine) AudioSystem.getLine(info);
|
||||
soundLine.open(audioFormat, OpusDecoder.getMaxOutputShorts()*4*2);
|
||||
|
||||
if (bufferSize == DEFAULT_BUFFER_SIZE) {
|
||||
soundLine.open(audioFormat);
|
||||
}
|
||||
else {
|
||||
soundLine.open(audioFormat, bufferSize);
|
||||
}
|
||||
|
||||
soundLine.start();
|
||||
lineBuffer = new byte[soundLine.getBufferSize()];
|
||||
soundBuffer = new SoundBuffer(STAGING_BUFFERS);
|
||||
} catch (LineUnavailableException e) {
|
||||
soundLine = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamInitialized(int channelCount, int sampleRate) {
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
|
||||
// Workaround OS X's bad Java mixer
|
||||
if (System.getProperty("os.name").contains("Mac OS X")) {
|
||||
createSoundLine(STARING_BUFFER_SIZE);
|
||||
reallocateLines = true;
|
||||
}
|
||||
else {
|
||||
createSoundLine(DEFAULT_BUFFER_SIZE);
|
||||
reallocateLines = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
61
src/com/limelight/binding/audio/SoundBuffer.java
Normal file
61
src/com/limelight/binding/audio/SoundBuffer.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package com.limelight.binding.audio;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import com.limelight.nvstream.av.ShortBufferDescriptor;
|
||||
|
||||
public class SoundBuffer {
|
||||
|
||||
private LinkedList<ShortBufferDescriptor> bufferList;
|
||||
private int maxBuffers;
|
||||
|
||||
public SoundBuffer(int maxBuffers) {
|
||||
this.bufferList = new LinkedList<ShortBufferDescriptor>();
|
||||
this.maxBuffers = maxBuffers;
|
||||
}
|
||||
|
||||
public void queue(ShortBufferDescriptor buff) {
|
||||
if (bufferList.size() > maxBuffers) {
|
||||
bufferList.removeFirst();
|
||||
}
|
||||
|
||||
bufferList.addLast(buff);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (ShortBufferDescriptor desc : bufferList) {
|
||||
size += desc.length;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public int fill(byte[] data, int offset, int length) {
|
||||
int filled = 0;
|
||||
|
||||
// Convert offset and length to be relative to shorts
|
||||
offset /= 2;
|
||||
length /= 2;
|
||||
|
||||
ShortBuffer sb = ByteBuffer.wrap(data).asShortBuffer();
|
||||
sb.position(offset);
|
||||
while (length > 0 && !bufferList.isEmpty()) {
|
||||
ShortBufferDescriptor buff = bufferList.getFirst();
|
||||
|
||||
if (buff.length > length) {
|
||||
break;
|
||||
}
|
||||
|
||||
sb.put(buff.data, buff.offset, buff.length);
|
||||
length -= buff.length;
|
||||
filled += buff.length;
|
||||
|
||||
bufferList.removeFirst();
|
||||
}
|
||||
|
||||
// Return bytes instead of shorts
|
||||
return filled * 2;
|
||||
}
|
||||
}
|
||||
@@ -40,23 +40,15 @@ public class SwingCpuDecoderRenderer implements VideoDecoderRenderer {
|
||||
* @param drFlags flags for the decoder and renderer
|
||||
*/
|
||||
@Override
|
||||
public void setup(int width, int height, Object renderTarget, int drFlags) {
|
||||
this.targetFps = 30;
|
||||
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
||||
this.targetFps = redrawRate;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
// Single threaded low latency decode is ideal
|
||||
int avcFlags = AvcDecoder.LOW_LATENCY_DECODE;
|
||||
int threadCount = 1;
|
||||
|
||||
// Hack to work around the bad Java native library loader
|
||||
// which can't resolve native library dependencies
|
||||
if (System.getProperty("os.name").contains("Windows")) {
|
||||
System.loadLibrary("avutil-52");
|
||||
System.loadLibrary("postproc-52");
|
||||
System.loadLibrary("pthreadVC2");
|
||||
}
|
||||
|
||||
|
||||
int err = AvcDecoder.init(width, height, avcFlags, threadCount);
|
||||
if (err != 0) {
|
||||
throw new IllegalStateException("AVC decoder initialization failure: "+err);
|
||||
|
||||
@@ -11,7 +11,9 @@ import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Point;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.swing.Box;
|
||||
@@ -22,6 +24,7 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
|
||||
import com.limelight.Limelight;
|
||||
import com.limelight.input.KeyboardHandler;
|
||||
import com.limelight.input.MouseHandler;
|
||||
import com.limelight.nvstream.NvConnection;
|
||||
@@ -36,12 +39,17 @@ import com.limelight.nvstream.StreamConfiguration;
|
||||
*/
|
||||
public class StreamFrame extends JFrame {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final double DESIRED_ASPECT_RATIO = 16.0/9.0;
|
||||
private static final double ALTERNATE_ASPECT_RATIO = 16.0/10.0;
|
||||
|
||||
private KeyboardHandler keyboard;
|
||||
private MouseHandler mouse;
|
||||
private JProgressBar spinner;
|
||||
private JLabel spinnerLabel;
|
||||
private Cursor noCursor;
|
||||
private Limelight limelight;
|
||||
|
||||
private NvConnection conn;
|
||||
|
||||
/**
|
||||
@@ -60,6 +68,8 @@ public class StreamFrame extends JFrame {
|
||||
hideCursor();
|
||||
}
|
||||
|
||||
public void build(Limelight limelight, NvConnection conn, StreamConfiguration streamConfig, boolean fullscreen) {
|
||||
this.limelight = limelight;
|
||||
/**
|
||||
* Builds the components of this frame with the specified configurations.
|
||||
* @param conn the connection this frame belongs to
|
||||
@@ -85,52 +95,77 @@ public class StreamFrame extends JFrame {
|
||||
this.getRootPane().setBackground(Color.BLACK);
|
||||
|
||||
if (fullscreen) {
|
||||
makeFullScreen();
|
||||
makeFullScreen(streamConfig);
|
||||
}
|
||||
|
||||
hideCursor();
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the best display mode for the system and desired configuration
|
||||
*/
|
||||
private DisplayMode getBestDisplay(DisplayMode[] configs) {
|
||||
Arrays.sort(configs, new Comparator<DisplayMode>() {
|
||||
private ArrayList<DisplayMode> getDisplayModesByAspectRatio(DisplayMode[] configs, double aspectRatio) {
|
||||
ArrayList<DisplayMode> matchingConfigs = new ArrayList<DisplayMode>();
|
||||
|
||||
for (DisplayMode config : configs) {
|
||||
if ((double)config.getWidth()/(double)config.getHeight() == aspectRatio) {
|
||||
matchingConfigs.add(config);
|
||||
}
|
||||
}
|
||||
|
||||
return matchingConfigs;
|
||||
}
|
||||
|
||||
private DisplayMode getBestDisplay(StreamConfiguration targetConfig, DisplayMode[] configs) {
|
||||
int targetDisplaySize = targetConfig.getWidth()*targetConfig.getHeight();
|
||||
|
||||
// Try to match the target aspect ratio
|
||||
ArrayList<DisplayMode> aspectMatchingConfigs = getDisplayModesByAspectRatio(configs, DESIRED_ASPECT_RATIO);
|
||||
if (aspectMatchingConfigs.size() == 0) {
|
||||
// No matches for the target, so try the alternate
|
||||
aspectMatchingConfigs = getDisplayModesByAspectRatio(configs, ALTERNATE_ASPECT_RATIO);
|
||||
if (aspectMatchingConfigs.size() == 0) {
|
||||
// No matches for either, so just use all of them
|
||||
aspectMatchingConfigs = new ArrayList<DisplayMode>(Arrays.asList(configs));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by display size
|
||||
Collections.sort(aspectMatchingConfigs, new Comparator<DisplayMode>() {
|
||||
@Override
|
||||
public int compare(DisplayMode o1, DisplayMode o2) {
|
||||
if (o1.getWidth() > o2.getWidth()) {
|
||||
if (o1.getWidth()*o1.getHeight() > o2.getWidth()*o2.getHeight()) {
|
||||
return -1;
|
||||
} else if (o2.getWidth() > o1.getWidth()) {
|
||||
} else if (o2.getWidth()*o2.getHeight() > o1.getWidth()*o1.getHeight()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Find the aspect-matching config with the closest matching display size
|
||||
DisplayMode bestConfig = null;
|
||||
for (DisplayMode config : configs) {
|
||||
if (config.getWidth() >= getSize().width && config.getHeight() >= getSize().height) {
|
||||
for (DisplayMode config : aspectMatchingConfigs) {
|
||||
if (config.getWidth()*config.getHeight() >= targetDisplaySize) {
|
||||
bestConfig = config;
|
||||
}
|
||||
}
|
||||
if (bestConfig == null) {
|
||||
return configs[0];
|
||||
|
||||
if (bestConfig != null) {
|
||||
System.out.println("Using full-screen display mode "+bestConfig.getWidth()+"x"+bestConfig.getHeight()+
|
||||
" for "+targetConfig.getWidth()+"x"+targetConfig.getHeight()+" stream");
|
||||
}
|
||||
|
||||
return bestConfig;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to make the frame fullscreen
|
||||
*/
|
||||
private void makeFullScreen() {
|
||||
private void makeFullScreen(StreamConfiguration streamConfig) {
|
||||
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||
if (gd.isFullScreenSupported()) {
|
||||
this.setUndecorated(true);
|
||||
gd.setFullScreenWindow(this);
|
||||
|
||||
if (gd.isDisplayChangeSupported()) {
|
||||
DisplayMode config = getBestDisplay(gd.getDisplayModes());
|
||||
DisplayMode config = getBestDisplay(streamConfig, gd.getDisplayModes());
|
||||
if (config != null) {
|
||||
gd.setDisplayMode(config);
|
||||
}
|
||||
@@ -229,7 +264,7 @@ public class StreamFrame extends JFrame {
|
||||
* Stops the stream and destroys the frame
|
||||
*/
|
||||
public void close() {
|
||||
limelight.stop();
|
||||
dispose();
|
||||
conn.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.limelight.input.gamepad;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.LinkedList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.limelight.nvstream.NvConnection;
|
||||
|
||||
@@ -21,6 +23,9 @@ public class GamepadListener {
|
||||
* @return true if it started a thread, false if the thread is already running.
|
||||
*/
|
||||
public static boolean startUp() {
|
||||
// Suppress spam from jinput log warnings in DefaultControllerEnvironment
|
||||
Logger.getLogger(ControllerEnvironment.getDefaultEnvironment().getClass().getName()).setLevel(Level.SEVERE);
|
||||
|
||||
if (listenerThread == null || !listenerThread.isAlive()) {
|
||||
System.out.println("Controller Listener thread starting up");
|
||||
listenerThread = new Thread() {
|
||||
|
||||
50
src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java
Normal file
50
src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.limelight.nvstream.av.video.cpu;
|
||||
|
||||
import com.limelight.binding.LibraryHelper;
|
||||
|
||||
public class AvcDecoder {
|
||||
static {
|
||||
LibraryHelper.loadNativeLibrary("avutil-52");
|
||||
if (System.getProperty("os.name").contains("Windows")) {
|
||||
LibraryHelper.loadNativeLibrary("postproc-52");
|
||||
LibraryHelper.loadNativeLibrary("pthreadVC2");
|
||||
}
|
||||
LibraryHelper.loadNativeLibrary("swresample-0");
|
||||
LibraryHelper.loadNativeLibrary("swscale-2");
|
||||
LibraryHelper.loadNativeLibrary("avcodec-55");
|
||||
LibraryHelper.loadNativeLibrary("avformat-55");
|
||||
LibraryHelper.loadNativeLibrary("avfilter-3");
|
||||
|
||||
LibraryHelper.loadNativeLibrary("nv_avc_dec");
|
||||
}
|
||||
|
||||
/** Disables the deblocking filter at the cost of image quality */
|
||||
public static final int DISABLE_LOOP_FILTER = 0x1;
|
||||
/** Uses the low latency decode flag (disables multithreading) */
|
||||
public static final int LOW_LATENCY_DECODE = 0x2;
|
||||
/** Threads process each slice, rather than each frame */
|
||||
public static final int SLICE_THREADING = 0x4;
|
||||
/** Uses nonstandard speedup tricks */
|
||||
public static final int FAST_DECODE = 0x8;
|
||||
/** Uses bilinear filtering instead of bicubic */
|
||||
public static final int BILINEAR_FILTERING = 0x10;
|
||||
/** Uses a faster bilinear filtering with lower image quality */
|
||||
public static final int FAST_BILINEAR_FILTERING = 0x20;
|
||||
/** Disables color conversion (output is NV21) */
|
||||
public static final int NO_COLOR_CONVERSION = 0x40;
|
||||
|
||||
public static native int init(int width, int height, int perflvl, int threadcount);
|
||||
public static native void destroy();
|
||||
|
||||
// Rendering API when NO_COLOR_CONVERSION == 0
|
||||
public static native boolean setRenderTarget(Object androidSurface);
|
||||
public static native boolean getRgbFrameInt(int[] rgbFrame, int bufferSize);
|
||||
public static native boolean getRgbFrame(byte[] rgbFrame, int bufferSize);
|
||||
public static native boolean redraw();
|
||||
|
||||
// Rendering API when NO_COLOR_CONVERSION == 1
|
||||
public static native boolean getRawFrame(byte[] yuvFrame, int bufferSize);
|
||||
|
||||
public static native int getInputPaddingSize();
|
||||
public static native int decode(byte[] indata, int inoff, int inlen);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class SettingsManager {
|
||||
*/
|
||||
public File getGamepadFile() {
|
||||
if (!settingsDir.exists()) {
|
||||
settingsFile.mkdirs();
|
||||
settingsDir.mkdirs();
|
||||
}
|
||||
|
||||
if (!gamepadFile.exists()) {
|
||||
@@ -74,7 +74,7 @@ public class SettingsManager {
|
||||
*/
|
||||
public File getSettingsFile() {
|
||||
if (!settingsDir.exists()) {
|
||||
settingsFile.mkdirs();
|
||||
settingsDir.mkdirs();
|
||||
}
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
|
||||
Reference in New Issue
Block a user