diff --git a/README.md b/README.md
index 9c58db39..3f139f44 100644
--- a/README.md
+++ b/README.md
@@ -295,7 +295,8 @@ Most camera parameters can be controlled through XML attributes or linked method
app:cameraJpegQuality="100"
app:cameraVideoQuality="480p"
app:cameraWhiteBalance="auto"
- app:cameraHdr="off" />
+ app:cameraHdr="off"
+ app:cameraAudio="on"/>
```
|XML Attribute|Method|Values|Default Value|
@@ -309,6 +310,7 @@ Most camera parameters can be controlled through XML attributes or linked method
|[`cameraVideoQuality`](#cameravideoquality)|`setVideoQuality()`|`lowest` `highest` `maxQvga` `max480p` `max720p` `max1080p` `max2160p`|`max480p`|
|[`cameraWhiteBalance`](#camerawhitebalance)|`setWhiteBalance()`|`auto` `incandescent` `fluorescent` `daylight` `cloudy`|`auto`|
|[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`|
+|[`cameraAudio`](#cameraaudio)|`setAudio()`|`off` `on`|`on`|
#### cameraSessionType
@@ -404,6 +406,15 @@ cameraView.setHdr(Hdr.OFF);
cameraView.setHdr(Hdr.ON);
```
+#### cameraAudio
+
+Turns on or off audio stream.
+
+```java
+cameraView.setAudio(Audio.OFF);
+cameraView.setAudio(Audio.ON);
+```
+
## Other APIs
Other APIs not mentioned above are provided, and are well documented and commented in code.
@@ -435,15 +446,15 @@ Take also a look at public methods in `CameraUtils`, `CameraOptions`, `ExtraProp
`CameraView` needs two permissions:
- `android.permission.CAMERA` : required for capturing pictures and videos
-- `android.permission.RECORD_AUDIO` : required for capturing videos
+- `android.permission.RECORD_AUDIO` : required for capturing videos with `Audio.ON` (the default)
-You can handle permissions yourself and then call `CameraView.start()` once they are acquired. If they are not, `CameraView` will request permissions to the user based on the `sessionType` that was set. In that case, you can restart the camera if you have a successful response from `onRequestPermissionResults()`.
+You can handle permissions yourself and then call `CameraView.start()` once they are acquired. If they are not, `CameraView` will request permissions to the user based on whether they are needed. In that case, you can restart the camera if you have a successful response from `onRequestPermissionResults()`.
## Manifest file
The library manifest file is not strict and only asks for camera permissions. This means that:
-- If you wish to record videos, you should also add `android.permission.RECORD_AUDIO` to required permissions
+- If you wish to record videos with `Audio.ON` (the default), you should also add `android.permission.RECORD_AUDIO` to required permissions
```xml
diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
index ac856644..82e819f5 100644
--- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
+++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
@@ -49,7 +49,7 @@ public class CameraViewTest extends BaseTest {
}
@Override
- protected boolean checkPermissions(SessionType sessionType) {
+ protected boolean checkPermissions(SessionType sessionType, Audio audio) {
return hasPermissions;
}
};
diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
index 5ab2ff9d..8aeda053 100644
--- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
+++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java
@@ -81,6 +81,11 @@ public class MockCameraController extends CameraController {
mHdr = hdr;
}
+ @Override
+ void setAudio(Audio audio) {
+ mAudio = audio;
+ }
+
@Override
void setLocation(Location location) {
mLocation = location;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
index ed76dd0c..a1975322 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
@@ -308,6 +308,17 @@ class Camera1 extends CameraController {
return false;
}
+
+ @Override
+ void setAudio(Audio audio) {
+ if (mAudio != audio) {
+ if (mIsCapturingVideo) {
+ LOG.w("Changing audio mode while recording. Changes will take place starting from next video");
+ }
+ mAudio = audio;
+ }
+ }
+
@Override
void setFlash(Flash flash) {
Flash old = mFlash;
@@ -617,13 +628,25 @@ class Camera1 extends CameraController {
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+
+ CamcorderProfile profile = getCamcorderProfile(mVideoQuality);
+ if (mAudio == Audio.ON) {
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+ mMediaRecorder.setProfile(profile);
+ } else {
+ // Set all values contained in profile except audio settings
+ mMediaRecorder.setOutputFormat(profile.fileFormat);
+ mMediaRecorder.setVideoEncoder(profile.videoCodec);
+ mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
+ mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
+ mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
+ }
+
if (mLocation != null) {
mMediaRecorder.setLocation((float) mLocation.getLatitude(),
(float) mLocation.getLongitude());
}
- mMediaRecorder.setProfile(getCamcorderProfile(mVideoQuality));
mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());
mMediaRecorder.setOrientationHint(computeExifRotation());
// Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
index 531a2139..575e59a0 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
@@ -81,6 +81,11 @@ class Camera2 extends CameraController {
}
+ @Override
+ void setAudio(Audio audio) {
+
+ }
+
@Override
void setLocation(Location location) {
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
index c12f7f9d..a55cdea1 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
@@ -29,6 +29,7 @@ abstract class CameraController implements Preview.SurfaceCallback {
protected SessionType mSessionType;
protected Hdr mHdr;
protected Location mLocation;
+ protected Audio mAudio;
protected Size mCaptureSize;
protected Size mPreviewSize;
@@ -204,6 +205,8 @@ abstract class CameraController implements Preview.SurfaceCallback {
// If closed, keep. If opened, check supported and apply.
abstract void setLocation(Location location);
+ abstract void setAudio(Audio audio);
+
// Throw if capturing. If in video session, recompute capture size, and, if needed, preview size.
abstract void setVideoQuality(VideoQuality videoQuality);
@@ -266,6 +269,10 @@ abstract class CameraController implements Preview.SurfaceCallback {
return mLocation;
}
+ final Audio getAudio() {
+ return mAudio;
+ }
+
final Size getCaptureSize() {
return mCaptureSize;
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
index de7d5b1a..bfc95b99 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
@@ -100,6 +100,7 @@ public class CameraView extends FrameLayout {
VideoQuality videoQuality = VideoQuality.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoQuality, VideoQuality.DEFAULT.value()));
SessionType sessionType = SessionType.fromValue(a.getInteger(R.styleable.CameraView_cameraSessionType, SessionType.DEFAULT.value()));
Hdr hdr = Hdr.fromValue(a.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value()));
+ Audio audio = Audio.fromValue(a.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value()));
// Gestures
GestureAction tapGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureTap, GestureAction.DEFAULT_TAP.value()));
@@ -138,6 +139,7 @@ public class CameraView extends FrameLayout {
setWhiteBalance(whiteBalance);
setGrid(grid);
setHdr(hdr);
+ setAudio(audio);
// Apply gestures
mapGesture(Gesture.TAP, tapGesture);
@@ -488,7 +490,7 @@ public class CameraView extends FrameLayout {
public void start() {
if (!isEnabled()) return;
- if (checkPermissions(getSessionType())) {
+ if (checkPermissions(getSessionType(), getAudio())) {
// Update display orientation for current CameraController
mOrientationHelper.enable(getContext());
mCameraController.start();
@@ -502,31 +504,21 @@ public class CameraView extends FrameLayout {
* @return true if we can go on, false otherwise.
*/
@SuppressLint("NewApi")
- protected boolean checkPermissions(SessionType sessionType) {
- checkPermissionsManifestOrThrow(sessionType);
- boolean api23 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- int cameraCheck, audioCheck;
- if (!api23) {
- cameraCheck = PackageManager.PERMISSION_GRANTED;
- audioCheck = PackageManager.PERMISSION_GRANTED;
- } else {
- cameraCheck = getContext().checkSelfPermission(Manifest.permission.CAMERA);
- audioCheck = getContext().checkSelfPermission(Manifest.permission.RECORD_AUDIO);
- }
- switch (sessionType) {
- case VIDEO:
- if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(true, true);
- return false;
- }
- break;
+ protected boolean checkPermissions(SessionType sessionType, Audio audio) {
+ checkPermissionsManifestOrThrow(sessionType, audio);
+ // Manifest is OK at this point. Let's check runtime permissions.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
- case PICTURE:
- if (cameraCheck != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(true, false);
- return false;
- }
- break;
+ Context c = getContext();
+ boolean needsCamera = true;
+ boolean needsAudio = sessionType == SessionType.VIDEO && audio == Audio.ON;
+
+ needsCamera = needsCamera && c.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED;
+ needsAudio = needsAudio && c.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED;
+
+ if (needsCamera || needsAudio) {
+ requestPermissions(needsCamera, needsAudio);
+ return false;
}
return true;
}
@@ -537,8 +529,8 @@ public class CameraView extends FrameLayout {
* If the developer did not add this to its manifest, throw and fire warnings.
* (Hoping this is not caught elsewhere... we should test).
*/
- private void checkPermissionsManifestOrThrow(SessionType sessionType) {
- if (sessionType == SessionType.VIDEO) {
+ private void checkPermissionsManifestOrThrow(SessionType sessionType, Audio audio) {
+ if (sessionType == SessionType.VIDEO && audio == Audio.ON) {
try {
PackageManager manager = getContext().getPackageManager();
PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS);
@@ -866,6 +858,43 @@ public class CameraView extends FrameLayout {
}
+ /**
+ * Controls the audio mode.
+ *
+ * @see Audio#OFF
+ * @see Audio#ON
+ *
+ * @param audio desired audio value
+ */
+ public void setAudio(Audio audio) {
+
+ if (audio == getAudio() || isStopped()) {
+ // Check did took place, or will happen on start().
+ mCameraController.setAudio(audio);
+
+ } else if (checkPermissions(getSessionType(), audio)) {
+ // Camera is running. Pass.
+ mCameraController.setAudio(audio);
+
+ } else {
+ // This means that the audio permission is being asked.
+ // Stop the camera so it can be restarted by the developer onPermissionResult.
+ // Developer must also set the audio value again...
+ // Not ideal but good for now.
+ stop();
+ }
+ }
+
+
+ /**
+ * Gets the current audio value.
+ * @return the current audio value
+ */
+ public Audio getAudio() {
+ return mCameraController.getAudio();
+ }
+
+
/**
* Starts an autofocus process at the given coordinates, with respect
* to the view width and height.
@@ -897,7 +926,7 @@ public class CameraView extends FrameLayout {
// Check did took place, or will happen on start().
mCameraController.setSessionType(sessionType);
- } else if (checkPermissions(sessionType)) {
+ } else if (checkPermissions(sessionType, getAudio())) {
// Camera is running. CameraImpl setSessionType will do the trick.
mCameraController.setSessionType(sessionType);
diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java b/cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java
new file mode 100644
index 00000000..e1f4f6af
--- /dev/null
+++ b/cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java
@@ -0,0 +1,42 @@
+package com.otaliastudios.cameraview;
+
+
+/**
+ * Audio values indicate whether to record audio stream when record video.
+ *
+ * @see CameraView#setAudio(Audio)
+ */
+public enum Audio {
+
+ /**
+ * No Audio.
+ */
+ OFF(0),
+
+ /**
+ * With Audio.
+ */
+ ON(1);
+
+ final static Audio DEFAULT = ON;
+
+ private int value;
+
+ Audio(int value) {
+ this.value = value;
+ }
+
+ int value() {
+ return value;
+ }
+
+ static Audio fromValue(int value) {
+ Audio[] list = Audio.values();
+ for (Audio action : list) {
+ if (action.value() == value) {
+ return action;
+ }
+ }
+ return null;
+ }
+}
diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml
index b2f41d80..30050a1e 100644
--- a/cameraview/src/main/res/values/attrs.xml
+++ b/cameraview/src/main/res/values/attrs.xml
@@ -91,6 +91,11 @@
+
+
+
+
+