Add UI elements and backend code for manually overriding the default decoder choice. Redraw at the specified refresh rate.

This commit is contained in:
Cameron Gutman
2013-12-26 17:35:53 -05:00
parent 163ee74e98
commit 9d3ee3a0a2
9 changed files with 204 additions and 78 deletions

View File

@@ -33,6 +33,7 @@ public class Connection extends Activity {
private SharedPreferences prefs;
private CheckBox qualityCheckbox;
private RadioButton rbutton720p, rbutton1080p, rbutton30fps, rbutton60fps;
private RadioButton forceSoftDec, autoDec, forceHardDec;
private static final String DEFAULT_HOST = "";
public static final String HOST_KEY = "hostText";
@@ -61,6 +62,9 @@ public class Connection extends Activity {
this.rbutton1080p = (RadioButton) findViewById(R.id.res1080pSelected);
this.rbutton30fps = (RadioButton) findViewById(R.id.rr30Selected);
this.rbutton60fps = (RadioButton) findViewById(R.id.rr60Selected);
this.forceSoftDec = (RadioButton) findViewById(R.id.softwareDec);
this.autoDec = (RadioButton) findViewById(R.id.autoDec);
this.forceHardDec = (RadioButton) findViewById(R.id.hardwareDec);
prefs = getSharedPreferences(Game.PREFS_FILE_NAME, Context.MODE_MULTI_PROCESS);
this.hostText.setText(prefs.getString(Connection.HOST_KEY, Connection.DEFAULT_HOST));
@@ -84,6 +88,24 @@ public class Connection extends Activity {
rbutton30fps.setChecked(false);
}
switch (prefs.getInt(Game.DECODER_PREF_STRING, Game.DEFAULT_DECODER)) {
case Game.FORCE_SOFTWARE_DECODER:
forceSoftDec.setChecked(true);
autoDec.setChecked(false);
forceHardDec.setChecked(false);
break;
case Game.AUTOSELECT_DECODER:
forceSoftDec.setChecked(false);
autoDec.setChecked(true);
forceHardDec.setChecked(false);
break;
case Game.FORCE_HARDWARE_DECODER:
forceSoftDec.setChecked(false);
autoDec.setChecked(false);
forceHardDec.setChecked(true);
break;
}
OnCheckedChangeListener occl = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
@@ -107,12 +129,24 @@ public class Connection extends Activity {
prefs.edit().putInt(Game.WIDTH_PREF_STRING, 1920).
putInt(Game.HEIGHT_PREF_STRING, 1080).commit();
}
else if (buttonView == forceSoftDec) {
prefs.edit().putInt(Game.DECODER_PREF_STRING, Game.FORCE_SOFTWARE_DECODER).commit();
}
else if (buttonView == forceHardDec) {
prefs.edit().putInt(Game.DECODER_PREF_STRING, Game.FORCE_HARDWARE_DECODER).commit();
}
else if (buttonView == autoDec) {
prefs.edit().putInt(Game.DECODER_PREF_STRING, Game.AUTOSELECT_DECODER).commit();
}
}
};
rbutton720p.setOnCheckedChangeListener(occl);
rbutton1080p.setOnCheckedChangeListener(occl);
rbutton30fps.setOnCheckedChangeListener(occl);
rbutton60fps.setOnCheckedChangeListener(occl);
forceSoftDec.setOnCheckedChangeListener(occl);
forceHardDec.setOnCheckedChangeListener(occl);
autoDec.setOnCheckedChangeListener(occl);
this.qualityCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override

View File

@@ -1,6 +1,7 @@
package com.limelight;
import com.limelight.binding.PlatformBinding;
import com.limelight.binding.video.ConfigurableDecoderRenderer;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.NvConnectionListener;
import com.limelight.nvstream.StreamConfiguration;
@@ -52,10 +53,16 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
public static final String WIDTH_PREF_STRING = "Width";
public static final String HEIGHT_PREF_STRING = "Height";
public static final String REFRESH_RATE_PREF_STRING = "RefreshRate";
public static final String DECODER_PREF_STRING = "Decoder";
public static final int DEFAULT_WIDTH = 1280;
public static final int DEFAULT_HEIGHT = 720;
public static final int DEFAULT_REFRESH_RATE = 30;
public static final int DEFAULT_DECODER = 0;
public static final int FORCE_HARDWARE_DECODER = -1;
public static final int AUTOSELECT_DECODER = 0;
public static final int FORCE_SOFTWARE_DECODER = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -91,6 +98,17 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
if (prefs.getBoolean(QUALITY_PREF_STRING, false)) {
drFlags |= VideoDecoderRenderer.FLAG_PREFER_QUALITY;
}
switch (prefs.getInt(Game.DECODER_PREF_STRING, Game.DEFAULT_DECODER)) {
case Game.FORCE_SOFTWARE_DECODER:
drFlags |= VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING;
break;
case Game.AUTOSELECT_DECODER:
break;
case Game.FORCE_HARDWARE_DECODER:
drFlags |= VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING;
break;
}
int width, height, refreshRate;
width = prefs.getInt(WIDTH_PREF_STRING, DEFAULT_WIDTH);
height = prefs.getInt(HEIGHT_PREF_STRING, DEFAULT_HEIGHT);
@@ -104,7 +122,7 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi
conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this,
new StreamConfiguration(width, height, refreshRate));
conn.start(PlatformBinding.getDeviceName(), sv.getHolder(), drFlags,
PlatformBinding.getAudioRenderer(), PlatformBinding.chooseDecoderRenderer());
PlatformBinding.getAudioRenderer(), new ConfigurableDecoderRenderer());
}
private void checkDataConnection()

View File

@@ -1,29 +1,9 @@
package com.limelight.binding;
import android.os.Build;
import com.limelight.binding.audio.AndroidAudioRenderer;
import com.limelight.binding.video.AndroidCpuDecoderRenderer;
import com.limelight.binding.video.MediaCodecDecoderRenderer;
import com.limelight.nvstream.av.audio.AudioRenderer;
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
public class PlatformBinding {
public static VideoDecoderRenderer chooseDecoderRenderer() {
if (Build.HARDWARE.equals("goldfish")) {
// Emulator - don't render video (it's slow!)
return null;
}
else if (MediaCodecDecoderRenderer.findSafeDecoder() != null) {
// Hardware decoding
return new MediaCodecDecoderRenderer();
}
else {
// Software decoding
return new AndroidCpuDecoderRenderer();
}
}
public static String getDeviceName() {
String deviceName = android.os.Build.MODEL;
deviceName = deviceName.replace(" ", "");

View File

@@ -78,8 +78,8 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
}
@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;
int perfLevel = findOptimalPerformanceLevel();
int threadCount;

View File

@@ -0,0 +1,43 @@
package com.limelight.binding.video;
import com.limelight.nvstream.av.DecodeUnit;
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
private VideoDecoderRenderer decoderRenderer;
@Override
public void release() {
decoderRenderer.release();
}
@Override
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 ||
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
MediaCodecDecoderRenderer.findSafeDecoder() != null)) {
decoderRenderer = new MediaCodecDecoderRenderer();
}
else {
decoderRenderer = new AndroidCpuDecoderRenderer();
}
decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags);
}
@Override
public void start() {
decoderRenderer.start();
}
@Override
public void stop() {
decoderRenderer.stop();
}
@Override
public boolean submitDecodeUnit(DecodeUnit du) {
return decoderRenderer.submitDecodeUnit(du);
}
}

View File

@@ -20,6 +20,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
private ByteBuffer[] videoDecoderInputBuffers;
private MediaCodec videoDecoder;
private Thread rendererThread;
private int redrawRate;
public static final List<String> blacklistedDecoderPrefixes;
@@ -71,14 +72,20 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
}
@Override
public void setup(int width, int height, Object renderTarget, int drFlags) {
videoDecoder = MediaCodec.createByCodecName(findSafeDecoder().getName());
MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
this.redrawRate = redrawRate;
MediaCodecInfo safeDecoder = findSafeDecoder();
if (safeDecoder != null) {
videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName());
}
else {
videoDecoder = MediaCodec.createDecoderByType("video/avc");
}
MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
videoDecoder.start();
videoDecoderInputBuffers = videoDecoder.getInputBuffers();
@@ -112,7 +119,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
if (currentTimeUs() >= nextFrameTimeUs) {
render = true;
nextFrameTimeUs = computePresentationTime(60);
nextFrameTimeUs = computePresentationTime(redrawRate);
}
videoDecoder.releaseOutputBuffer(outIndex, render);