Introduces Android API level linting, fixes all current API lint errors.

This CL attempts to annotate accesses on >16 API levels using as
small scopes as possible. The TargetApi notations mean "yes, I know
I'm accessing a higher API and I take responsibility for gating the
call on Android API level". The Encoder/Decoder classes are annotated
on the whole class, but they're only accessed through JNI; we should
annotate on method level otherwise and preferably on private methods.

This patch also fixes some compiler-level deprecation warnings (i.e.
-Xlint:deprecation), but probably not all of them.

BUG=webrtc:5063
R=henrika@webrtc.org, kjellander@webrtc.org, magjed@webrtc.org

Review URL: https://codereview.webrtc.org/1412673008 .

Cr-Commit-Position: refs/heads/master@{#10624}
This commit is contained in:
Patrik Höglund
2015-11-12 17:36:48 +01:00
parent 56a34df928
commit 68876f990e
14 changed files with 159 additions and 75 deletions

View File

@ -27,7 +27,9 @@
package org.webrtc;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
@ -45,6 +47,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
@TargetApi(21)
public class Camera2Enumerator implements CameraEnumerationAndroid.Enumerator {
private final static String TAG = "Camera2Enumerator";
private final static double NANO_SECONDS_PER_SECOND = 1.0e9;

View File

@ -29,7 +29,6 @@ package org.webrtc;
import static java.lang.Math.abs;
import static java.lang.Math.ceil;
import android.hardware.Camera;
import android.graphics.ImageFormat;
import org.json.JSONArray;
@ -127,8 +126,8 @@ public class CameraEnumerationAndroid {
// Returns device names that can be used to create a new VideoCapturerAndroid.
public static String[] getDeviceNames() {
String[] names = new String[Camera.getNumberOfCameras()];
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
String[] names = new String[android.hardware.Camera.getNumberOfCameras()];
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
names[i] = getDeviceName(i);
}
return names;
@ -136,22 +135,22 @@ public class CameraEnumerationAndroid {
// Returns number of cameras on device.
public static int getDeviceCount() {
return Camera.getNumberOfCameras();
return android.hardware.Camera.getNumberOfCameras();
}
// Returns the name of the camera with camera index. Returns null if the
// camera can not be used.
public static String getDeviceName(int index) {
Camera.CameraInfo info = new Camera.CameraInfo();
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
try {
Camera.getCameraInfo(index, info);
android.hardware.Camera.getCameraInfo(index, info);
} catch (Exception e) {
Logging.e(TAG, "getCameraInfo failed on index " + index,e);
return null;
}
String facing =
(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
(info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
return "Camera " + index + ", Facing " + facing
+ ", Orientation " + info.orientation;
}
@ -159,13 +158,13 @@ public class CameraEnumerationAndroid {
// Returns the name of the front facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfFrontFacingDevice() {
return getNameOfDevice(Camera.CameraInfo.CAMERA_FACING_FRONT);
return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
}
// Returns the name of the back facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfBackFacingDevice() {
return getNameOfDevice(Camera.CameraInfo.CAMERA_FACING_BACK);
return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK);
}
public static String getSupportedFormatsAsJson(int id) throws JSONException {
@ -194,7 +193,8 @@ public class CameraEnumerationAndroid {
}
}
public static int[] getFramerateRange(Camera.Parameters parameters, final int framerate) {
public static int[] getFramerateRange(android.hardware.Camera.Parameters parameters,
final int framerate) {
List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
if (listFpsRange.isEmpty()) {
Logging.w(TAG, "No supported preview fps range");
@ -203,27 +203,28 @@ public class CameraEnumerationAndroid {
return Collections.min(listFpsRange,
new ClosestComparator<int[]>() {
@Override int diff(int[] range) {
return abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX])
+ abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
return abs(framerate - range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX])
+ abs(framerate - range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
});
}
public static Camera.Size getClosestSupportedSize(
List<Camera.Size> supportedSizes, final int requestedWidth, final int requestedHeight) {
public static android.hardware.Camera.Size getClosestSupportedSize(
List<android.hardware.Camera.Size> supportedSizes, final int requestedWidth,
final int requestedHeight) {
return Collections.min(supportedSizes,
new ClosestComparator<Camera.Size>() {
@Override int diff(Camera.Size size) {
new ClosestComparator<android.hardware.Camera.Size>() {
@Override int diff(android.hardware.Camera.Size size) {
return abs(requestedWidth - size.width) + abs(requestedHeight - size.height);
}
});
}
private static String getNameOfDevice(int facing) {
final Camera.CameraInfo info = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
final android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
try {
Camera.getCameraInfo(i, info);
android.hardware.Camera.getCameraInfo(i, info);
if (info.facing == facing) {
return getDeviceName(i);
}

View File

@ -27,7 +27,6 @@
package org.webrtc;
import android.hardware.Camera;
import android.os.SystemClock;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
@ -60,11 +59,11 @@ public class CameraEnumerator implements CameraEnumerationAndroid.Enumerator {
private List<CaptureFormat> enumerateFormats(int cameraId) {
Logging.d(TAG, "Get supported formats for camera index " + cameraId + ".");
final long startTimeMs = SystemClock.elapsedRealtime();
final Camera.Parameters parameters;
Camera camera = null;
final android.hardware.Camera.Parameters parameters;
android.hardware.Camera camera = null;
try {
Logging.d(TAG, "Opening camera with index " + cameraId);
camera = Camera.open(cameraId);
camera = android.hardware.Camera.open(cameraId);
parameters = camera.getParameters();
} catch (RuntimeException e) {
Logging.e(TAG, "Open camera failed on camera index " + cameraId, e);
@ -84,10 +83,10 @@ public class CameraEnumerator implements CameraEnumerationAndroid.Enumerator {
// getSupportedPreviewFpsRange() returns a sorted list. Take the fps range
// corresponding to the highest fps.
final int[] range = listFpsRange.get(listFpsRange.size() - 1);
minFps = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
maxFps = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
minFps = range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
maxFps = range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
}
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
for (android.hardware.Camera.Size size : parameters.getSupportedPreviewSizes()) {
formatList.add(new CaptureFormat(size.width, size.height, minFps, maxFps));
}
} catch (Exception e) {

View File

@ -29,8 +29,6 @@ package org.webrtc;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
@ -68,20 +66,21 @@ import javax.microedition.khronos.egl.EGL10;
// camera thread. The internal *OnCameraThread() methods must check |camera| for null to check if
// the camera has been stopped.
@SuppressWarnings("deprecation")
public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallback,
public class VideoCapturerAndroid extends VideoCapturer implements
android.hardware.Camera.PreviewCallback,
SurfaceTextureHelper.OnTextureFrameAvailableListener {
private final static String TAG = "VideoCapturerAndroid";
private final static int CAMERA_OBSERVER_PERIOD_MS = 2000;
private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 6000;
private Camera camera; // Only non-null while capturing.
private android.hardware.Camera camera; // Only non-null while capturing.
private HandlerThread cameraThread;
private final Handler cameraThreadHandler;
private Context applicationContext;
// Synchronization lock for |id|.
private final Object cameraIdLock = new Object();
private int id;
private Camera.CameraInfo info;
private android.hardware.Camera.CameraInfo info;
private final FramePool videoBuffers;
private final CameraStatistics cameraStatistics;
// Remember the requested format in case we want to switch cameras.
@ -108,10 +107,10 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
private int openCameraAttempts;
// Camera error callback.
private final Camera.ErrorCallback cameraErrorCallback =
new Camera.ErrorCallback() {
private final android.hardware.Camera.ErrorCallback cameraErrorCallback =
new android.hardware.Camera.ErrorCallback() {
@Override
public void onError(int error, Camera camera) {
public void onError(int error, android.hardware.Camera camera) {
String errorMessage;
if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
errorMessage = "Camera server died!";
@ -261,7 +260,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// Switch camera to the next valid camera id. This can only be called while
// the camera is running.
public void switchCamera(final CameraSwitchHandler handler) {
if (Camera.getNumberOfCameras() < 2) {
if (android.hardware.Camera.getNumberOfCameras() < 2) {
if (handler != null) {
handler.onCameraSwitchError("No camera to switch to.");
}
@ -292,7 +291,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
pendingCameraSwitch = false;
}
if (handler != null) {
handler.onCameraSwitchDone(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT);
handler.onCameraSwitchDone(
info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
}
}
});
@ -375,13 +375,13 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// found. If |deviceName| is empty, the first available device is used.
private static int lookupDeviceName(String deviceName) {
Logging.d(TAG, "lookupDeviceName: " + deviceName);
if (deviceName == null || Camera.getNumberOfCameras() == 0) {
if (deviceName == null || android.hardware.Camera.getNumberOfCameras() == 0) {
return -1;
}
if (deviceName.isEmpty()) {
return 0;
}
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
if (deviceName.equals(CameraEnumerationAndroid.getDeviceName(i))) {
return i;
}
@ -461,9 +461,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
if (eventsHandler != null) {
eventsHandler.onCameraOpening(id);
}
camera = Camera.open(id);
info = new Camera.CameraInfo();
Camera.getCameraInfo(id, info);
camera = android.hardware.Camera.open(id);
info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(id, info);
}
} catch (RuntimeException e) {
openCameraAttempts++;
@ -525,14 +525,15 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
requestedFramerate = framerate;
// Find closest supported format for |width| x |height| @ |framerate|.
final Camera.Parameters parameters = camera.getParameters();
final android.hardware.Camera.Parameters parameters = camera.getParameters();
final int[] range = CameraEnumerationAndroid.getFramerateRange(parameters, framerate * 1000);
final Camera.Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize(
parameters.getSupportedPreviewSizes(), width, height);
final android.hardware.Camera.Size previewSize =
CameraEnumerationAndroid.getClosestSupportedSize(
parameters.getSupportedPreviewSizes(), width, height);
final CaptureFormat captureFormat = new CaptureFormat(
previewSize.width, previewSize.height,
range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
// Check if we are already using this capture format, then we don't need to do anything.
if (captureFormat.equals(this.captureFormat)) {
@ -554,8 +555,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
parameters.setPreviewFormat(captureFormat.imageFormat);
// Picture size is for taking pictures and not for preview/video, but we need to set it anyway
// as a workaround for an aspect ratio problem on Nexus 7.
final Camera.Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize(
parameters.getSupportedPictureSizes(), width, height);
final android.hardware.Camera.Size pictureSize =
CameraEnumerationAndroid.getClosestSupportedSize(
parameters.getSupportedPictureSizes(), width, height);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
// Temporarily stop preview if it's already running.
@ -572,8 +574,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
this.captureFormat = captureFormat;
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
if (focusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
camera.setParameters(parameters);
@ -637,7 +639,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
Logging.d(TAG, "switchCameraOnCameraThread");
stopCaptureOnCameraThread();
synchronized (cameraIdLock) {
id = (id + 1) % Camera.getNumberOfCameras();
id = (id + 1) % android.hardware.Camera.getNumberOfCameras();
}
dropNextFrame = true;
startCaptureOnCameraThread(requestedWidth, requestedHeight, requestedFramerate, frameObserver,
@ -699,7 +701,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
private int getFrameOrientation() {
int rotation = getDeviceOrientation();
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
rotation = 360 - rotation;
}
return (info.orientation + rotation) % 360;
@ -707,7 +709,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// Called on cameraThread so must not "synchronized".
@Override
public void onPreviewFrame(byte[] data, Camera callbackCamera) {
public void onPreviewFrame(byte[] data, android.hardware.Camera callbackCamera) {
checkIsOnCameraThread();
if (camera == null) {
return;
@ -752,7 +754,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
}
int rotation = getFrameOrientation();
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) {
// Undo the mirror that the OS "helps" us with.
// http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
transformMatrix =
@ -784,7 +786,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// keeping the buffers alive and for finding the corresponding ByteBuffer given a timestamp.
private final Map<Long, ByteBuffer> pendingBuffers = new HashMap<Long, ByteBuffer>();
private int frameSize = 0;
private Camera camera;
private android.hardware.Camera camera;
public FramePool(Thread thread) {
this.thread = thread;
@ -797,7 +799,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
}
// Discards previous queued buffers and adds new callback buffers to camera.
public void queueCameraBuffers(int frameSize, Camera camera) {
public void queueCameraBuffers(int frameSize, android.hardware.Camera camera) {
checkIsOnValidThread();
this.camera = camera;
this.frameSize = frameSize;

View File

@ -27,6 +27,7 @@
package org.webrtc;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
@ -48,6 +49,7 @@ import java.util.concurrent.TimeUnit;
// Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder.
// This class is an implementation detail of the Java PeerConnection API.
@SuppressWarnings("deprecation")
public class MediaCodecVideoDecoder {
// This class is constructed, operated, and destroyed by its C++ incarnation,
// so the class and its methods have non-public visibility. The API this

View File

@ -27,6 +27,7 @@
package org.webrtc;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo;
@ -44,6 +45,8 @@ import java.util.concurrent.CountDownLatch;
// Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder.
// This class is an implementation detail of the Java PeerConnection API.
@TargetApi(19)
@SuppressWarnings("deprecation")
public class MediaCodecVideoEncoder {
// This class is constructed, operated, and destroyed by its C++ incarnation,
// so the class and its methods have non-public visibility. The API this

View File

@ -227,6 +227,12 @@
'libjingle_peerconnection_so',
],
'variables': {
# Designate as Chromium code and point to our lint settings to
# enable linting of the WebRTC code (this is the only way to make
# lint_action invoke the Android linter).
'android_manifest_path': '<(webrtc_root)/build/android/AndroidManifest.xml',
'suppressions_file': '<(webrtc_root)/build/android/suppressions.xml',
'chromium_code': 1,
'java_in_dir': 'app/webrtc/java',
'webrtc_base_dir': '<(webrtc_root)/base',
'webrtc_modules_dir': '<(webrtc_root)/modules',

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is a dummy manifest which is required by:
1. aapt when generating R.java in java.gypi:
Nothing in the manifest is used, but it is still required by aapt.
2. lint: [min|target]SdkVersion are required by lint and should
be kept up-to-date.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dummy.package">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />
</manifest>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<!-- These lint settings is for the Android linter that gets run by
lint_action.gypi on compile of WebRTC java code. All WebRTC java code
should lint cleanly for the issues below. -->
<!-- TODO(phoglund): make work with suppress.py or remove printout referring
to suppress.py. -->
<issue id="NewApi"></issue>
<issue id="Locale" severity="ignore"/>
<issue id="SdCardPath" severity="ignore"/>
<issue id="UseValueOf" severity="ignore"/>
<issue id="InlinedApi" severity="ignore"/>
<issue id="DefaultLocale" severity="ignore"/>
<issue id="Assert" severity="ignore"/>
<issue id="UseSparseArrays" severity="ignore"/>
<!-- These are just from the dummy AndroidManifest.xml we use for linting.
It's in the same directory as this file. -->
<issue id="MissingApplicationIcon" severity="ignore"/>
<issue id="AllowBackup" severity="ignore"/>
<issue id="MissingVersion" severity="ignore"/>
</lint>

View File

@ -7,7 +7,7 @@
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

View File

@ -10,6 +10,7 @@
package org.webrtc.voiceengine;
import android.annotation.TargetApi;
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.AudioEffect;
import android.media.audiofx.AudioEffect.Descriptor;
@ -119,6 +120,7 @@ class WebRtcAudioEffects {
// Returns true if the platform AEC should be excluded based on its UUID.
// AudioEffect.queryEffects() can throw IllegalStateException.
@TargetApi(18)
private static boolean isAcousticEchoCancelerExcludedByUUID() {
for (Descriptor d : AudioEffect.queryEffects()) {
if (d.type.equals(AudioEffect.EFFECT_TYPE_AEC) &&
@ -131,6 +133,7 @@ class WebRtcAudioEffects {
// Returns true if the platform AGC should be excluded based on its UUID.
// AudioEffect.queryEffects() can throw IllegalStateException.
@TargetApi(18)
private static boolean isAutomaticGainControlExcludedByUUID() {
for (Descriptor d : AudioEffect.queryEffects()) {
if (d.type.equals(AudioEffect.EFFECT_TYPE_AGC) &&
@ -143,6 +146,7 @@ class WebRtcAudioEffects {
// Returns true if the platform NS should be excluded based on its UUID.
// AudioEffect.queryEffects() can throw IllegalStateException.
@TargetApi(18)
private static boolean isNoiseSuppressorExcludedByUUID() {
for (Descriptor d : AudioEffect.queryEffects()) {
if (d.type.equals(AudioEffect.EFFECT_TYPE_NS) &&
@ -368,7 +372,11 @@ class WebRtcAudioEffects {
// AudioEffect.Descriptor array that are actually not available on the device.
// As an example: Samsung Galaxy S6 includes an AGC in the descriptor but
// AutomaticGainControl.isAvailable() returns false.
@TargetApi(18)
private boolean effectTypeIsVoIP(UUID type) {
if (!WebRtcAudioUtils.runningOnJellyBeanMR2OrHigher())
return false;
return (AudioEffect.EFFECT_TYPE_AEC.equals(type)
&& isAcousticEchoCancelerSupported())
|| (AudioEffect.EFFECT_TYPE_AGC.equals(type)

View File

@ -10,6 +10,7 @@
package org.webrtc.voiceengine;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
@ -189,20 +190,26 @@ public class WebRtcAudioManager {
// No overrides available. Deliver best possible estimate based on default
// Android AudioManager APIs.
final int sampleRateHz;
if (!WebRtcAudioUtils.runningOnJellyBeanMR1OrHigher()) {
sampleRateHz = WebRtcAudioUtils.getDefaultSampleRateHz();
if (WebRtcAudioUtils.runningOnJellyBeanMR1OrHigher()) {
sampleRateHz = getSampleRateOnJellyBeanMR10OrHigher();
} else {
String sampleRateString = audioManager.getProperty(
AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
sampleRateHz = (sampleRateString == null)
? WebRtcAudioUtils.getDefaultSampleRateHz()
: Integer.parseInt(sampleRateString);
sampleRateHz = WebRtcAudioUtils.getDefaultSampleRateHz();
}
Logging.d(TAG, "Sample rate is set to " + sampleRateHz + " Hz");
return sampleRateHz;
}
@TargetApi(17)
private int getSampleRateOnJellyBeanMR10OrHigher() {
String sampleRateString = audioManager.getProperty(
AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
return (sampleRateString == null)
? WebRtcAudioUtils.getDefaultSampleRateHz()
: Integer.parseInt(sampleRateString);
}
// Returns the native output buffer size for low-latency output streams.
@TargetApi(17)
private int getLowLatencyOutputFramesPerBuffer() {
assertTrue(isLowLatencyOutputSupported());
if (!WebRtcAudioUtils.runningOnJellyBeanMR1OrHigher()) {

View File

@ -13,6 +13,7 @@ package org.webrtc.voiceengine;
import java.lang.Thread;
import java.nio.ByteBuffer;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
@ -90,13 +91,9 @@ class WebRtcAudioTrack {
assertTrue(sizeInBytes <= byteBuffer.remaining());
int bytesWritten = 0;
if (WebRtcAudioUtils.runningOnLollipopOrHigher()) {
bytesWritten = audioTrack.write(byteBuffer,
sizeInBytes,
AudioTrack.WRITE_BLOCKING);
bytesWritten = writeOnLollipop(audioTrack, byteBuffer, sizeInBytes);
} else {
bytesWritten = audioTrack.write(byteBuffer.array(),
byteBuffer.arrayOffset(),
sizeInBytes);
bytesWritten = writePreLollipop(audioTrack, byteBuffer, sizeInBytes);
}
if (bytesWritten != sizeInBytes) {
Logging.e(TAG, "AudioTrack.write failed: " + bytesWritten);
@ -123,6 +120,15 @@ class WebRtcAudioTrack {
audioTrack.flush();
}
@TargetApi(21)
private int writeOnLollipop(AudioTrack audioTrack, ByteBuffer byteBuffer, int sizeInBytes) {
return audioTrack.write(byteBuffer, sizeInBytes, AudioTrack.WRITE_BLOCKING);
}
private int writePreLollipop(AudioTrack audioTrack, ByteBuffer byteBuffer, int sizeInBytes) {
return audioTrack.write(byteBuffer.array(), byteBuffer.arrayOffset(), sizeInBytes);
}
public void joinThread() {
keepAlive = false;
while (isAlive()) {
@ -224,16 +230,21 @@ class WebRtcAudioTrack {
private boolean setStreamVolume(int volume) {
Logging.d(TAG, "setStreamVolume(" + volume + ")");
assertTrue(audioManager != null);
if (WebRtcAudioUtils.runningOnLollipopOrHigher()) {
if (audioManager.isVolumeFixed()) {
Logging.e(TAG, "The device implements a fixed volume policy.");
return false;
}
if (isVolumeFixed()) {
Logging.e(TAG, "The device implements a fixed volume policy.");
return false;
}
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, volume, 0);
return true;
}
@TargetApi(21)
private boolean isVolumeFixed() {
if (!WebRtcAudioUtils.runningOnLollipopOrHigher())
return false;
return audioManager.isVolumeFixed();
}
/** Get current volume level for a phone call audio stream. */
private int getStreamVolume() {
Logging.d(TAG, "getStreamVolume");

View File

@ -144,6 +144,11 @@ public final class WebRtcAudioUtils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public static boolean runningOnJellyBeanMR2OrHigher() {
// July 24, 2013: Android 4.3. API Level 18.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
}
public static boolean runningOnLollipopOrHigher() {
// API Level 21.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;