Compare commits

..
15 Commits
Author SHA1 Message Date
Cameron Gutman 34f72544d8 Increment version 2014-11-25 14:56:40 -08:00
Cameron Gutman d839ea9781 Increase deadzone on triggers to Xinput defaults and add special handling of the Nexus Player Controller and Nexus Remote 2014-11-25 14:54:36 -08:00
Cameron Gutman 2b7f13fdbb Increase max frame time to improve accuracy of latency counter 2014-11-25 13:34:00 -08:00
Cameron Gutman 7557a3a4ae Don't capture the back button on remotes 2014-11-25 11:16:47 -08:00
Cameron Gutman fcecba484f Fix a crash caught by Monkey 2014-11-25 02:05:24 -08:00
Cameron Gutman fa85a0a0bd Improve CPU decoder frame latency when rendering speed is less than decoding speed 2014-11-25 02:04:51 -08:00
Cameron Gutman dc64bfeba2 Slightly reduce max packet size in an attempt to cut packet losses 2014-11-25 01:05:55 -08:00
Cameron Gutman 871b73c48d Fix PC duplication issue when multiple machines report the same remote IP address 2014-11-24 20:10:02 -08:00
Cameron Gutman 5dcff91d27 Only grab Fire TV remotes if a gamepad isn't attached 2014-11-24 18:43:08 -08:00
Cameron Gutman 0041fc1dab Fix broken de-duplication of computers 2014-11-24 18:25:58 -08:00
Cameron Gutman 314242ab08 Update to Ion with fixes for SSLContext and self-signed certificates 2014-11-24 18:10:23 -08:00
Cameron Gutman 09e8ddfd74 Use the bitstream restrictions fixup on Broadcom VideoCore IV devices 2014-11-24 18:03:47 -08:00
Cameron Gutman 444c4602c1 Update libraries. Seems to improve image caching behavior with Ion. 2014-11-23 23:39:20 -08:00
Cameron Gutman 5b6eac7140 Update build.gradle for re-release 2014-11-23 02:07:03 -08:00
Cameron Gutman 7cdd184197 Fix null pointer exception on ATV emulator 2014-11-23 02:06:49 -08:00
14 changed files with 135 additions and 64 deletions

No files matched your search

+5 -5
View File
@@ -101,18 +101,18 @@
</content> </content>
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="okhttp-2.1.0-RC1" level="project" />
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.51" level="project" /> <orderEntry type="library" exported="" name="bcprov-jdk15on-1.51" level="project" />
<orderEntry type="library" exported="" name="gson-2.3.1" level="project" /> <orderEntry type="library" exported="" name="gson-2.3.1" level="project" />
<orderEntry type="library" exported="" name="androidasync-1.3.7" level="project" /> <orderEntry type="library" exported="" name="support-v4-r7" level="project" />
<orderEntry type="library" exported="" name="jmdns-fixed" level="project" /> <orderEntry type="library" exported="" name="jmdns-fixed" level="project" />
<orderEntry type="library" exported="" name="jcodec-0.1.6-3" level="project" />
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.51" level="project" /> <orderEntry type="library" exported="" name="bcpkix-jdk15on-1.51" level="project" />
<orderEntry type="library" exported="" name="tinyrtsp" level="project" /> <orderEntry type="library" exported="" name="tinyrtsp" level="project" />
<orderEntry type="library" exported="" name="okhttp-2.1.0" level="project" />
<orderEntry type="library" exported="" name="limelight-common" level="project" /> <orderEntry type="library" exported="" name="limelight-common" level="project" />
<orderEntry type="library" exported="" name="support-v4-r6" level="project" />
<orderEntry type="library" exported="" name="okio-1.0.1" level="project" /> <orderEntry type="library" exported="" name="okio-1.0.1" level="project" />
<orderEntry type="library" exported="" name="ion-1.3.7" level="project" /> <orderEntry type="library" exported="" name="androidasync-e1dfb4" level="project" />
<orderEntry type="library" exported="" name="jcodec-0.1.9" level="project" />
<orderEntry type="library" exported="" name="ion-2f46fa" level="project" />
</component> </component>
</module> </module>
+16 -10
View File
@@ -5,14 +5,14 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion 21 compileSdkVersion 21
buildToolsVersion "21.0.2" buildToolsVersion "21.1.1"
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 21 targetSdkVersion 21
versionName "3.0" versionName "3.0.1"
versionCode = 45 versionCode = 47
} }
productFlavors { productFlavors {
@@ -62,13 +62,19 @@ android {
} }
dependencies { dependencies {
compile group: 'org.jcodec', name: 'jcodec', version: '0.1.6-3' compile group: 'org.jcodec', name: 'jcodec', version: '+'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.51' compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '+'
compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.51' compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '+'
compile group: 'com.google.android', name: 'support-v4', version:'r6' compile group: 'com.google.android', name: 'support-v4', version:'+'
compile group: 'com.koushikdutta.ion', name: 'ion', version:'1.3.7'
compile group: 'com.squareup.okhttp', name: 'okhttp', version:'2.1.0-RC1' // FIXME: Pending resolution of issue #346 using custom build
compile group: 'com.squareup.okio', name:'okio', version:'1.0.1' //compile group: 'com.koushikdutta.ion', name: 'ion', version:'+'
compile group: 'com.google.code.gson', name: 'gson', version:'+'
compile files('libs/androidasync-e1dfb4.jar')
compile files('libs/ion-2f46fa.jar')
compile group: 'com.squareup.okhttp', name: 'okhttp', version:'+'
compile group: 'com.squareup.okio', name:'okio', version:'+'
compile files('libs/jmdns-fixed.jar') compile files('libs/jmdns-fixed.jar')
compile files('libs/limelight-common.jar') compile files('libs/limelight-common.jar')
compile files('libs/tinyrtsp.jar') compile files('libs/tinyrtsp.jar')
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -170,7 +170,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
.enableAdaptiveResolution((decoderRenderer.getCapabilities() & .enableAdaptiveResolution((decoderRenderer.getCapabilities() &
VideoDecoderRenderer.CAPABILITY_ADAPTIVE_RESOLUTION) != 0) VideoDecoderRenderer.CAPABILITY_ADAPTIVE_RESOLUTION) != 0)
.enableLocalAudioPlayback(prefConfig.playHostAudio) .enableLocalAudioPlayback(prefConfig.playHostAudio)
.setMaxPacketSize(remote ? 1024 : 1392) .setMaxPacketSize(remote ? 1024 : 1292)
.build(); .build();
// Initialize the connection // Initialize the connection
@@ -47,6 +47,7 @@ public class ControllerHandler {
private NvConnection conn; private NvConnection conn;
private double stickDeadzone; private double stickDeadzone;
private final ControllerMapping defaultMapping = new ControllerMapping(); private final ControllerMapping defaultMapping = new ControllerMapping();
private boolean hasGameController;
public ControllerHandler(NvConnection conn, int deadzonePercentage) { public ControllerHandler(NvConnection conn, int deadzonePercentage) {
this.conn = conn; this.conn = conn;
@@ -55,6 +56,20 @@ public class ControllerHandler {
// is required for controller batching support to work. // is required for controller batching support to work.
deadzonePercentage = 10; deadzonePercentage = 10;
int[] ids = InputDevice.getDeviceIds();
for (int i = 0; i < ids.length; i++) {
InputDevice dev = InputDevice.getDevice(ids[i]);
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 ||
(dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) {
// This looks like a gamepad, but we'll check X and Y to be sure
if (getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_X) != null &&
getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Y) != null) {
// This is a gamepad
hasGameController = true;
}
}
}
// 1% is the lowest possible deadzone we support // 1% is the lowest possible deadzone we support
if (deadzonePercentage <= 0) { if (deadzonePercentage <= 0) {
deadzonePercentage = 1; deadzonePercentage = 1;
@@ -94,6 +109,12 @@ public class ControllerHandler {
mapping.leftStickXAxis = MotionEvent.AXIS_X; mapping.leftStickXAxis = MotionEvent.AXIS_X;
mapping.leftStickYAxis = MotionEvent.AXIS_Y; mapping.leftStickYAxis = MotionEvent.AXIS_Y;
if (getMotionRangeForJoystickAxis(dev, mapping.leftStickXAxis) != null &&
getMotionRangeForJoystickAxis(dev, mapping.leftStickYAxis) != null) {
// This is a gamepad
hasGameController = true;
mapping.hasJoystickAxes = true;
}
InputDevice.MotionRange leftTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_LTRIGGER); InputDevice.MotionRange leftTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_LTRIGGER);
InputDevice.MotionRange rightTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RTRIGGER); InputDevice.MotionRange rightTriggerRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RTRIGGER);
@@ -182,27 +203,43 @@ public class ControllerHandler {
// It's important to have a valid deadzone so controller packet batching works properly // It's important to have a valid deadzone so controller packet batching works properly
mapping.triggerDeadzone = Math.max(Math.abs(ltRange.getFlat()), Math.abs(rtRange.getFlat())); mapping.triggerDeadzone = Math.max(Math.abs(ltRange.getFlat()), Math.abs(rtRange.getFlat()));
// For triggers without (valid) deadzones, we'll use 10% // For triggers without (valid) deadzones, we'll use 13% (around XInput's default)
if (mapping.triggerDeadzone <= 0.02 || if (mapping.triggerDeadzone < 0.13f ||
mapping.triggerDeadzone > 0.30) mapping.triggerDeadzone > 0.30f)
{ {
mapping.triggerDeadzone = 0.1f; mapping.triggerDeadzone = 0.13f;
} }
} }
// For the Nexus Player (and probably other ATV devices), we should if (devName != null) {
// use the back button as start since it doesn't have a start/menu button // For the Nexus Player (and probably other ATV devices), we should
// on the controller // use the back button as start since it doesn't have a start/menu button
if (devName != null && devName.contains("ASUS Gamepad")) { // on the controller
// We can only do this check on KitKat or higher, but it doesn't matter since ATV if (devName.contains("ASUS Gamepad")) {
// is Android 5.0 anyway // We can only do this check on KitKat or higher, but it doesn't matter since ATV
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { // is Android 5.0 anyway
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
if (!hasStartKey[0] && !hasStartKey[1]) { boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
mapping.backIsStart = true; if (!hasStartKey[0] && !hasStartKey[1]) {
mapping.backIsStart = true;
}
}
// The ASUS Gamepad has triggers that sit far forward and are prone to false presses
// so we increase the deadzone on them to minimize this
mapping.triggerDeadzone = 0.30f;
}
// Classify this device as a remote by name
else if (devName.contains("Fire TV Remote") || devName.contains("Nexus Remote")) {
// It's only a remote if it doesn't any sticks
if (!mapping.hasJoystickAxes) {
mapping.isRemote = true;
} }
} }
} }
LimeLog.info("Analog stick deadzone: "+mapping.leftStickDeadzoneRadius+" "+mapping.rightStickDeadzoneRadius);
LimeLog.info("Trigger deadzone: "+mapping.triggerDeadzone);
return mapping; return mapping;
} }
@@ -232,9 +269,18 @@ public class ControllerHandler {
conn.sendControllerInput(inputMap, leftTrigger, rightTrigger, conn.sendControllerInput(inputMap, leftTrigger, rightTrigger,
leftStickX, leftStickY, rightStickX, rightStickY); leftStickX, leftStickY, rightStickX, rightStickY);
} }
private static int handleRemapping(ControllerMapping mapping, KeyEvent event) { // Return a valid keycode, 0 to consume, or -1 to not consume the event
if (mapping.isDualShock4) { // Device MAY BE NULL
private int handleRemapping(ControllerMapping mapping, KeyEvent event) {
// For remotes, don't capture the back button
if (mapping.isRemote) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
return -1;
}
}
if (mapping.isDualShock4) {
switch (event.getKeyCode()) { switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BUTTON_Y: case KeyEvent.KEYCODE_BUTTON_Y:
return KeyEvent.KEYCODE_BUTTON_L1; return KeyEvent.KEYCODE_BUTTON_L1;
@@ -718,5 +764,7 @@ public class ControllerHandler {
public boolean isDualShock4; public boolean isDualShock4;
public boolean isXboxController; public boolean isXboxController;
public boolean backIsStart; public boolean backIsStart;
public boolean isRemote;
public boolean hasJoystickAxes;
} }
} }
@@ -141,7 +141,9 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
throw new IllegalStateException("AVC decoder initialization failure: "+err); throw new IllegalStateException("AVC decoder initialization failure: "+err);
} }
AvcDecoder.setRenderTarget(sh.getSurface()); if (!AvcDecoder.setRenderTarget(sh.getSurface())) {
return false;
}
decoderBuffer = ByteBuffer.allocate(DECODER_BUFFER_SIZE + AvcDecoder.getInputPaddingSize()); decoderBuffer = ByteBuffer.allocate(DECODER_BUFFER_SIZE + AvcDecoder.getInputPaddingSize());
@@ -252,7 +254,7 @@ public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
// Add delta time to the totals (excluding probable outliers) // Add delta time to the totals (excluding probable outliers)
long delta = timeAfterDecode - decodeUnit.getReceiveTimestamp(); long delta = timeAfterDecode - decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) { if (delta >= 0 && delta < 1000) {
totalTimeMs += delta; totalTimeMs += delta;
totalFrames++; totalFrames++;
} }
@@ -273,7 +273,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
// Add delta time to the totals (excluding probable outliers) // Add delta time to the totals (excluding probable outliers)
long delta = System.currentTimeMillis()-(presentationTimeUs/1000); long delta = System.currentTimeMillis()-(presentationTimeUs/1000);
if (delta >= 0 && delta < 300) { if (delta >= 0 && delta < 1000) {
decoderTimeMs += delta; decoderTimeMs += delta;
totalTimeMs += delta; totalTimeMs += delta;
} }
@@ -371,7 +371,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) { private void submitDecodeUnit(DecodeUnit decodeUnit, ByteBuffer buf, int inputBufferIndex) {
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
long delta = currentTime-decodeUnit.getReceiveTimestamp(); long delta = currentTime-decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) { if (delta >= 0 && delta < 1000) {
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp(); totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
totalFrames++; totalFrames++;
} }
@@ -44,6 +44,7 @@ public class MediaCodecHelper {
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.mtk"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.mtk");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.brcm");
baselineProfileHackPrefixes = new LinkedList<String>(); baselineProfileHackPrefixes = new LinkedList<String>();
baselineProfileHackPrefixes.add("omx.intel"); baselineProfileHackPrefixes.add("omx.intel");
@@ -244,9 +244,12 @@ public class ComputerManagerService extends Service {
for (PollingTuple tuple : pollingTuples) { for (PollingTuple tuple : pollingTuples) {
// Check if this is the same computer // Check if this is the same computer
if (tuple.computer == details || if (tuple.computer == details ||
tuple.computer.localIp.equals(details.localIp) || // If there's no name on one of these computers, compare with the local IP
tuple.computer.remoteIp.equals(details.remoteIp) || ((details.name.isEmpty() || tuple.computer.name.isEmpty()) &&
tuple.computer.name.equals(details.name)) { tuple.computer.localIp.equals(details.localIp)) ||
// If there is a name on both computers, compare with name
((!details.name.isEmpty() && !tuple.computer.name.isEmpty()) &&
tuple.computer.name.equals(details.name))) {
// Start a polling thread if polling is active // Start a polling thread if polling is active
if (pollingActive && tuple.thread == null) { if (pollingActive && tuple.thread == null) {
@@ -329,12 +332,22 @@ public class ComputerManagerService extends Service {
} }
} }
private ComputerDetails tryPollIp(InetAddress ipAddr) { private ComputerDetails tryPollIp(ComputerDetails details, InetAddress ipAddr) {
try { try {
NvHTTP http = new NvHTTP(ipAddr, idManager.getUniqueId(), NvHTTP http = new NvHTTP(ipAddr, idManager.getUniqueId(),
null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); null, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
return http.getComputerDetails(); ComputerDetails newDetails = http.getComputerDetails();
// Check if this is the PC we expected
if (details.uuid != null && newDetails.uuid != null &&
!details.uuid.equals(newDetails.uuid)) {
// We got the wrong PC!
LimeLog.info("Polling returned the wrong PC!");
return null;
}
return newDetails;
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
@@ -344,19 +357,19 @@ public class ComputerManagerService extends Service {
ComputerDetails polledDetails; ComputerDetails polledDetails;
if (localFirst) { if (localFirst) {
polledDetails = tryPollIp(details.localIp); polledDetails = tryPollIp(details, details.localIp);
} }
else { else {
polledDetails = tryPollIp(details.remoteIp); polledDetails = tryPollIp(details, details.remoteIp);
} }
if (polledDetails == null && !details.localIp.equals(details.remoteIp)) { if (polledDetails == null && !details.localIp.equals(details.remoteIp)) {
// Failed, so let's try the fallback // Failed, so let's try the fallback
if (!localFirst) { if (!localFirst) {
polledDetails = tryPollIp(details.localIp); polledDetails = tryPollIp(details, details.localIp);
} }
else { else {
polledDetails = tryPollIp(details.remoteIp); polledDetails = tryPollIp(details, details.remoteIp);
} }
// The fallback poll worked // The fallback poll worked
@@ -414,8 +427,8 @@ public class ComputerManagerService extends Service {
} }
for (ComputerDetails computer : dbManager.getAllComputers()) { for (ComputerDetails computer : dbManager.getAllComputers()) {
// Add this computer without a thread // Add tuples for each computer
pollingTuples.add(new PollingTuple(computer, null)); addTuple(computer);
} }
releaseLocalDatabaseReference(); releaseLocalDatabaseReference();
@@ -142,8 +142,9 @@ public class AddComputerManually extends Activity {
@Override @Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_ACTION_DONE || if (actionId == EditorInfo.IME_ACTION_DONE ||
keyEvent.getAction() == KeyEvent.ACTION_DOWN && (keyEvent != null &&
keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER) { keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if (hostText.getText().length() == 0) { if (hostText.getText().length() == 0) {
Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show(); Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show();
return true; return true;
+12 -12
View File
@@ -69,9 +69,6 @@ int nv_avc_init(int width, int height, int perf_lvl, int thread_count) {
return -1; return -1;
} }
// Show frames even before a reference frame
decoder_ctx->flags2 |= CODEC_FLAG2_SHOW_ALL;
if (perf_lvl & DISABLE_LOOP_FILTER) { if (perf_lvl & DISABLE_LOOP_FILTER) {
// Skip the loop filter for performance reasons // Skip the loop filter for performance reasons
decoder_ctx->skip_loop_filter = AVDISCARD_ALL; decoder_ctx->skip_loop_filter = AVDISCARD_ALL;
@@ -370,17 +367,20 @@ int nv_avc_decode(unsigned char* indata, int inlen) {
// Only copy the picture at the end of decoding the packet // Only copy the picture at the end of decoding the packet
if (got_pic) { if (got_pic) {
// Clone the current decode frame outside of the mutex
AVFrame* new_frame = av_frame_clone(dec_frame);
AVFrame* old_frame;
// Swap it in under lock
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
old_frame = yuv_frame;
// Only clone this frame if the last frame was taken. yuv_frame = new_frame;
// This saves on extra copies for frames that don't get
// rendered.
if (yuv_frame == NULL) {
// Clone a new frame
yuv_frame = av_frame_clone(dec_frame);
}
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
// Free the old frame outside of the mutex
if (old_frame != NULL) {
av_frame_free(&old_frame);
}
} }
return err < 0 ? err : 0; return err < 0 ? err : 0;
+2 -2
View File
@@ -4,12 +4,12 @@ This file serves to document some of the decoder errata when using MediaCodec ha
- Affected decoders: TI OMAP4, Allwinner A20 - Affected decoders: TI OMAP4, Allwinner A20
2. Some decoders have a huge per-frame latency with the unmodified SPS sent from NVENC. Setting max_dec_frame_buffering fixes this latency issue. 2. Some decoders have a huge per-frame latency with the unmodified SPS sent from NVENC. Setting max_dec_frame_buffering fixes this latency issue.
- Affected decoders: NVIDIA Tegra 3 and 4 - Affected decoders: NVIDIA Tegra 3 and 4, Broadcom VideoCore IV
3. Some decoders strictly require that you pass BUFFER_FLAG_CODEC_CONFIG and crash upon the IDR frame if you don't 3. Some decoders strictly require that you pass BUFFER_FLAG_CODEC_CONFIG and crash upon the IDR frame if you don't
- Affected decoders: TI OMAP4 - Affected decoders: TI OMAP4
4. Some decoders require num_ref_frames=1 and max_dec_frame_buffering=1 to avoid crashing on SPS or first I-frame 4. Some decoders require num_ref_frames=1 and max_dec_frame_buffering=1 to avoid crashing on SPS on first I-frame
- Affected decoders: Qualcomm in GS3 on 4.3+, Exynos 4 at 1080p only - Affected decoders: Qualcomm in GS3 on 4.3+, Exynos 4 at 1080p only
5. Some decoders will hang if max_dec_frame_buffering is not present 5. Some decoders will hang if max_dec_frame_buffering is not present