From e9cba8c6ec43e9f4f6e0350e0bc45661a08febf7 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 2 Mar 2018 17:48:11 +0100 Subject: [PATCH] Add VideoCodec option (#174) * Add VideoCodec options * Fix tests --- README.md | 12 +++++ .../cameraview/CameraViewTest.java | 9 ++++ .../otaliastudios/cameraview/MapperTest.java | 36 ++++++++++++++ .../com/otaliastudios/cameraview/Camera1.java | 2 +- .../cameraview/CameraController.java | 9 ++++ .../otaliastudios/cameraview/CameraView.java | 28 +++++++++++ .../com/otaliastudios/cameraview/Mapper.java | 11 +++++ .../otaliastudios/cameraview/VideoCodec.java | 49 +++++++++++++++++++ cameraview/src/main/res/values/attrs.xml | 6 +++ 9 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 cameraview/src/androidTest/java/com/otaliastudios/cameraview/MapperTest.java create mode 100644 cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java diff --git a/README.md b/README.md index 2e3703f3..246f30d4 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,7 @@ Most camera parameters can be controlled through XML attributes or linked method app:cameraCropOutput="false" app:cameraJpegQuality="100" app:cameraVideoQuality="480p" + app:cameraVideoCodec="deviceDefault" app:cameraWhiteBalance="auto" app:cameraHdr="off" app:cameraAudio="on" @@ -416,6 +417,7 @@ Most camera parameters can be controlled through XML attributes or linked method |[`cameraCropOutput`](#cameracropoutput)|`setCropOutput()`|`true` `false`|`false`| |[`cameraJpegQuality`](#camerajpegquality)|`setJpegQuality()`|`0 < n <= 100`|`100`| |[`cameraVideoQuality`](#cameravideoquality)|`setVideoQuality()`|`lowest` `highest` `maxQvga` `max480p` `max720p` `max1080p` `max2160p`|`max480p`| +|[`cameraVideoCodec`](#cameravideocodec)|`setVideoCodec()`|`deviceDefault` `h263` `h264`|`deviceDefault`| |[`cameraWhiteBalance`](#camerawhitebalance)|`setWhiteBalance()`|`auto` `incandescent` `fluorescent` `daylight` `cloudy`|`auto`| |[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`| |[`cameraAudio`](#cameraaudio)|`setAudio()`|`off` `on`|`on`| @@ -504,6 +506,16 @@ cameraView.setVideoQuality(VideoQuality.MAX_1080P); cameraView.setVideoQuality(VideoQuality.MAX_2160P); ``` +#### cameraVideoCodec + +Sets the encoder for video recordings. + +```java +cameraView.setVideoCodec(VideoCodec.DEVICE_DEFAULT); +cameraView.setVideoCodec(VideoCodec.H_263); +cameraView.setVideoCodec(VideoCodec.H_264); +``` + #### cameraWhiteBalance Sets the desired white balance for the current session. diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 818cc67d..7c539bf6 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -93,6 +93,7 @@ public class CameraViewTest extends BaseTest { assertEquals(cameraView.getHdr(), Hdr.DEFAULT); assertEquals(cameraView.getAudio(), Audio.DEFAULT); assertEquals(cameraView.getVideoQuality(), VideoQuality.DEFAULT); + assertEquals(cameraView.getVideoCodec(), VideoCodec.DEFAULT); assertEquals(cameraView.getLocation(), null); assertEquals(cameraView.getExposureCorrection(), 0f, 0f); assertEquals(cameraView.getZoom(), 0f, 0f); @@ -560,6 +561,14 @@ public class CameraViewTest extends BaseTest { assertEquals(cameraView.getVideoQuality(), VideoQuality.LOWEST); } + @Test + public void testVideoCodec() { + cameraView.set(VideoCodec.H_263); + assertEquals(cameraView.getVideoCodec(), VideoCodec.H_263); + cameraView.set(VideoCodec.H_264); + assertEquals(cameraView.getVideoCodec(), VideoCodec.H_264); + } + @Test public void testPictureSizeSelector() { SizeSelector source = SizeSelectors.minHeight(50); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MapperTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MapperTest.java new file mode 100644 index 00000000..6a61c36d --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MapperTest.java @@ -0,0 +1,36 @@ +package com.otaliastudios.cameraview; + + +import android.hardware.Camera; +import android.media.MediaRecorder; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MapperTest extends BaseTest { + + private Mapper mapper = new Mapper() { + T map(Flash flash) { return null; } + T map(Facing facing) { return null; } + T map(WhiteBalance whiteBalance) { return null; } + T map(Hdr hdr) { return null; } + Flash unmapFlash(T cameraConstant) { return null; } + Facing unmapFacing(T cameraConstant) { return null; } + WhiteBalance unmapWhiteBalance(T cameraConstant) { return null; } + Hdr unmapHdr(T cameraConstant) { return null; } + }; + + @Test + public void testMap() { + assertEquals(mapper.map(VideoCodec.DEVICE_DEFAULT), MediaRecorder.VideoEncoder.DEFAULT); + assertEquals(mapper.map(VideoCodec.H_263), MediaRecorder.VideoEncoder.H263); + assertEquals(mapper.map(VideoCodec.H_264), MediaRecorder.VideoEncoder.H264); + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index 8a00af0a..6ff4347a 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -688,7 +688,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mMediaRecorder.setOutputFormat(profile.fileFormat); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); - mMediaRecorder.setVideoEncoder(profile.videoCodec); + mMediaRecorder.setVideoEncoder(mMapper.map(mVideoCodec)); mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate); if (mAudio == Audio.ON) { mMediaRecorder.setAudioChannels(profile.audioChannels); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index 5dd2350e..3fb41e8a 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -39,6 +39,7 @@ abstract class CameraController implements protected Flash mFlash; protected WhiteBalance mWhiteBalance; protected VideoQuality mVideoQuality; + protected VideoCodec mVideoCodec; protected SessionType mSessionType; protected Hdr mHdr; protected Location mLocation; @@ -285,6 +286,10 @@ abstract class CameraController implements mVideoMaxDuration = videoMaxDurationMillis; } + final void setVideoCodec(VideoCodec codec) { + mVideoCodec = codec; + } + //endregion @@ -362,6 +367,10 @@ abstract class CameraController implements return mVideoQuality; } + final VideoCodec getVideoCodec() { + return mVideoCodec; + } + final long getVideoMaxSize() { return mVideoMaxSize; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 9e0705e1..da30b4f5 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -107,6 +107,7 @@ public class CameraView extends FrameLayout { 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())); + VideoCodec codec = VideoCodec.fromValue(a.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value())); long videoMaxSize = (long) a.getFloat(R.styleable.CameraView_cameraVideoMaxSize, 0); int videoMaxDuration = a.getInteger(R.styleable.CameraView_cameraVideoMaxDuration, 0); @@ -181,6 +182,7 @@ public class CameraView extends FrameLayout { setHdr(hdr); setAudio(audio); setPictureSize(selector); + setVideoCodec(codec); setVideoMaxSize(videoMaxSize); setVideoMaxDuration(videoMaxDuration); @@ -652,6 +654,8 @@ public class CameraView extends FrameLayout { setVideoQuality((VideoQuality) control); } else if (control instanceof WhiteBalance) { setWhiteBalance((WhiteBalance) control); + } else if (control instanceof VideoCodec) { + setVideoCodec((VideoCodec) control); } } @@ -1375,6 +1379,30 @@ public class CameraView extends FrameLayout { } + /** + * Sets the encoder for video recordings. + * Defaults to {@link VideoCodec#DEVICE_DEFAULT}. + * + * @see VideoCodec#DEVICE_DEFAULT + * @see VideoCodec#H_263 + * @see VideoCodec#H_264 + * + * @param codec requested video codec + */ + public void setVideoCodec(VideoCodec codec) { + mCameraController.setVideoCodec(codec); + } + + + /** + * Gets the current encoder for video recordings. + * @return the current video codec + */ + public VideoCodec getVideoCodec() { + return mCameraController.getVideoCodec(); + } + + /** * Sets the maximum size in bytes for recorded video files. * Once this size is reached, the recording will automatically stop. diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java index 54ec3a93..1b507016 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java @@ -1,6 +1,7 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; +import android.media.MediaRecorder; import android.os.Build; import java.util.HashMap; @@ -16,6 +17,16 @@ abstract class Mapper { abstract WhiteBalance unmapWhiteBalance(T cameraConstant); abstract Hdr unmapHdr(T cameraConstant); + int map(VideoCodec codec) { + switch (codec) { + case DEVICE_DEFAULT: return MediaRecorder.VideoEncoder.DEFAULT; + case H_263: return MediaRecorder.VideoEncoder.H263; + case H_264: return MediaRecorder.VideoEncoder.H264; + default: return MediaRecorder.VideoEncoder.DEFAULT; + } + } + + static class Mapper1 extends Mapper { private static final HashMap FLASH = new HashMap<>(); diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java b/cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java new file mode 100644 index 00000000..0b46611b --- /dev/null +++ b/cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java @@ -0,0 +1,49 @@ +package com.otaliastudios.cameraview; + + +/** + * Constants for selecting the encoder of video recordings. + * https://developer.android.com/guide/topics/media/media-formats.html#video-formats + * + * @see CameraView#setVideoCodec(VideoCodec) + */ +public enum VideoCodec implements Control { + + + /** + * Let the device choose its codec. + */ + DEVICE_DEFAULT(0), + + /** + * The H.263 codec. + */ + H_263(1), + + /** + * The H.264 codec. + */ + H_264(2); + + static final VideoCodec DEFAULT = DEVICE_DEFAULT; + + private int value; + + VideoCodec(int value) { + this.value = value; + } + + int value() { + return value; + } + + static VideoCodec fromValue(int value) { + VideoCodec[] list = VideoCodec.values(); + for (VideoCodec 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 f38db8ea..53abcd38 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -114,6 +114,12 @@ + + + + + +