Add capability to set audio codec to encode video files with. (#861)

* Add capability to set audio codec to encode video files with.

* Cleanup

* Handle audio codec in SnapshotVideoRecorder.java

* Remove two AMR codecs and VORBIS, replace if with switch

* All chosen codecs in SnapshotVideoRecorder.java should use "audio/mp4a-latm". Setting DEVICE_DEFAULT to use it's actual value would change the behaviour of the class.

* Match style of video codec switch block above.
pull/806/merge
Ryan McClarnon 5 years ago committed by GitHub
parent edbade24a9
commit 4364ec7c3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  2. 19
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java
  3. 3
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/options/Camera1OptionsTest.java
  4. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  5. 31
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java
  7. 63
      cameraview/src/main/java/com/otaliastudios/cameraview/controls/AudioCodec.java
  8. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/controls/ControlParser.java
  9. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraBaseEngine.java
  10. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  11. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  12. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  13. 7
      cameraview/src/main/res/values/attrs.xml
  14. 4
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  15. 6
      demo/src/main/java/com/otaliastudios/cameraview/demo/Option.java
  16. 2
      demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
  17. 5
      demo/src/main/res/layout/activity_video_preview.xml
  18. 18
      docs/_docs/controls.md

@ -19,6 +19,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.ControlParser; import com.otaliastudios.cameraview.controls.ControlParser;
import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
@ -165,6 +166,7 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.getHdr(), controls.getHdr()); assertEquals(cameraView.getHdr(), controls.getHdr());
assertEquals(cameraView.getAudio(), controls.getAudio()); assertEquals(cameraView.getAudio(), controls.getAudio());
assertEquals(cameraView.getVideoCodec(), controls.getVideoCodec()); assertEquals(cameraView.getVideoCodec(), controls.getVideoCodec());
assertEquals(cameraView.getAudioCodec(), controls.getAudioCodec());
assertEquals(cameraView.getPictureFormat(), controls.getPictureFormat()); assertEquals(cameraView.getPictureFormat(), controls.getPictureFormat());
//noinspection SimplifiableJUnitAssertion //noinspection SimplifiableJUnitAssertion
assertEquals(cameraView.getLocation(), null); assertEquals(cameraView.getLocation(), null);
@ -773,6 +775,19 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.get(Audio.class), Audio.STEREO); assertEquals(cameraView.get(Audio.class), Audio.STEREO);
} }
@Test
public void testAudioCodec() {
cameraView.set(AudioCodec.DEVICE_DEFAULT);
assertEquals(cameraView.get(AudioCodec.class), AudioCodec.DEVICE_DEFAULT);
cameraView.set(AudioCodec.AAC);
assertEquals(cameraView.get(AudioCodec.class), AudioCodec.AAC);
cameraView.set(AudioCodec.HE_AAC);
assertEquals(cameraView.get(AudioCodec.class), AudioCodec.HE_AAC);
cameraView.set(AudioCodec.AAC_ELD);
assertEquals(cameraView.get(AudioCodec.class), AudioCodec.AAC_ELD);
}
@Test @Test
public void testVideoCodec() { public void testVideoCodec() {
cameraView.set(VideoCodec.H_263); cameraView.set(VideoCodec.H_263);

@ -4,6 +4,7 @@ package com.otaliastudios.cameraview;
import android.location.Location; import android.location.Location;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
@ -32,7 +33,8 @@ public class VideoResultTest extends BaseTest {
File file = Mockito.mock(File.class); File file = Mockito.mock(File.class);
int rotation = 90; int rotation = 90;
Size size = new Size(20, 120); Size size = new Size(20, 120);
VideoCodec codec = VideoCodec.H_263; VideoCodec videoCodec = VideoCodec.H_263;
AudioCodec audioCodec = AudioCodec.DEVICE_DEFAULT;
Location location = Mockito.mock(Location.class); Location location = Mockito.mock(Location.class);
boolean isSnapshot = true; boolean isSnapshot = true;
int maxDuration = 1234; int maxDuration = 1234;
@ -47,7 +49,8 @@ public class VideoResultTest extends BaseTest {
stub.file = file; stub.file = file;
stub.rotation = rotation; stub.rotation = rotation;
stub.size = size; stub.size = size;
stub.videoCodec = codec; stub.videoCodec = videoCodec;
stub.audioCodec = audioCodec;
stub.location = location; stub.location = location;
stub.isSnapshot = isSnapshot; stub.isSnapshot = isSnapshot;
stub.maxDuration = maxDuration; stub.maxDuration = maxDuration;
@ -63,7 +66,8 @@ public class VideoResultTest extends BaseTest {
assertEquals(result.getFile(), file); assertEquals(result.getFile(), file);
assertEquals(result.getRotation(), rotation); assertEquals(result.getRotation(), rotation);
assertEquals(result.getSize(), size); assertEquals(result.getSize(), size);
assertEquals(result.getVideoCodec(), codec); assertEquals(result.getVideoCodec(), videoCodec);
assertEquals(result.getAudioCodec(), audioCodec);
assertEquals(result.getLocation(), location); assertEquals(result.getLocation(), location);
assertEquals(result.isSnapshot(), isSnapshot); assertEquals(result.isSnapshot(), isSnapshot);
assertEquals(result.getMaxSize(), maxFileSize); assertEquals(result.getMaxSize(), maxFileSize);
@ -81,7 +85,8 @@ public class VideoResultTest extends BaseTest {
FileDescriptor fileDescriptor = FileDescriptor.in; FileDescriptor fileDescriptor = FileDescriptor.in;
int rotation = 90; int rotation = 90;
Size size = new Size(20, 120); Size size = new Size(20, 120);
VideoCodec codec = VideoCodec.H_263; VideoCodec videoCodec = VideoCodec.H_263;
AudioCodec audioCodec = AudioCodec.DEVICE_DEFAULT;
Location location = Mockito.mock(Location.class); Location location = Mockito.mock(Location.class);
boolean isSnapshot = true; boolean isSnapshot = true;
int maxDuration = 1234; int maxDuration = 1234;
@ -96,7 +101,8 @@ public class VideoResultTest extends BaseTest {
stub.fileDescriptor = fileDescriptor; stub.fileDescriptor = fileDescriptor;
stub.rotation = rotation; stub.rotation = rotation;
stub.size = size; stub.size = size;
stub.videoCodec = codec; stub.videoCodec = videoCodec;
stub.audioCodec = audioCodec;
stub.location = location; stub.location = location;
stub.isSnapshot = isSnapshot; stub.isSnapshot = isSnapshot;
stub.maxDuration = maxDuration; stub.maxDuration = maxDuration;
@ -112,7 +118,8 @@ public class VideoResultTest extends BaseTest {
assertEquals(result.getFileDescriptor(), fileDescriptor); assertEquals(result.getFileDescriptor(), fileDescriptor);
assertEquals(result.getRotation(), rotation); assertEquals(result.getRotation(), rotation);
assertEquals(result.getSize(), size); assertEquals(result.getSize(), size);
assertEquals(result.getVideoCodec(), codec); assertEquals(result.getVideoCodec(), videoCodec);
assertEquals(result.getAudioCodec(), audioCodec);
assertEquals(result.getLocation(), location); assertEquals(result.getLocation(), location);
assertEquals(result.isSnapshot(), isSnapshot); assertEquals(result.isSnapshot(), isSnapshot);
assertEquals(result.getMaxSize(), maxFileSize); assertEquals(result.getMaxSize(), maxFileSize);

@ -7,6 +7,7 @@ import android.hardware.Camera;
import com.otaliastudios.cameraview.BaseTest; import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.PictureFormat; import com.otaliastudios.cameraview.controls.PictureFormat;
@ -244,10 +245,12 @@ public class Camera1OptionsTest extends BaseTest {
Collection<Grid> grids = o.getSupportedControls(Grid.class); Collection<Grid> grids = o.getSupportedControls(Grid.class);
Collection<VideoCodec> video = o.getSupportedControls(VideoCodec.class); Collection<VideoCodec> video = o.getSupportedControls(VideoCodec.class);
Collection<AudioCodec> audioCodecs = o.getSupportedControls(AudioCodec.class);
Collection<Mode> sessions = o.getSupportedControls(Mode.class); Collection<Mode> sessions = o.getSupportedControls(Mode.class);
Collection<Audio> audio = o.getSupportedControls(Audio.class); Collection<Audio> audio = o.getSupportedControls(Audio.class);
assertEquals(grids.size(), Grid.values().length); assertEquals(grids.size(), Grid.values().length);
assertEquals(video.size(), VideoCodec.values().length); assertEquals(video.size(), VideoCodec.values().length);
assertEquals(audioCodecs.size(), AudioCodec.values().length);
assertEquals(sessions.size(), Mode.values().length); assertEquals(sessions.size(), Mode.values().length);
assertEquals(audio.size(), Audio.values().length); assertEquals(audio.size(), Audio.values().length);
} }

@ -4,6 +4,7 @@ package com.otaliastudios.cameraview;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Control; import com.otaliastudios.cameraview.controls.Control;
import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
@ -105,6 +106,8 @@ public abstract class CameraOptions {
return (Collection<T>) Arrays.asList(Mode.values()); return (Collection<T>) Arrays.asList(Mode.values());
} else if (controlClass.equals(VideoCodec.class)) { } else if (controlClass.equals(VideoCodec.class)) {
return (Collection<T>) Arrays.asList(VideoCodec.values()); return (Collection<T>) Arrays.asList(VideoCodec.values());
} else if (controlClass.equals(AudioCodec.class)) {
return (Collection<T>) Arrays.asList(AudioCodec.values());
} else if (controlClass.equals(WhiteBalance.class)) { } else if (controlClass.equals(WhiteBalance.class)) {
return (Collection<T>) getSupportedWhiteBalance(); return (Collection<T>) getSupportedWhiteBalance();
} else if (controlClass.equals(Engine.class)) { } else if (controlClass.equals(Engine.class)) {

@ -33,6 +33,7 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Control; import com.otaliastudios.cameraview.controls.Control;
import com.otaliastudios.cameraview.controls.ControlParser; import com.otaliastudios.cameraview.controls.ControlParser;
import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Engine;
@ -268,6 +269,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setHdr(controls.getHdr()); setHdr(controls.getHdr());
setAudio(controls.getAudio()); setAudio(controls.getAudio());
setAudioBitRate(audioBitRate); setAudioBitRate(audioBitRate);
setAudioCodec(controls.getAudioCodec());
setPictureSize(sizeSelectors.getPictureSizeSelector()); setPictureSize(sizeSelectors.getPictureSizeSelector());
setPictureMetering(pictureMetering); setPictureMetering(pictureMetering);
setPictureSnapshotMetering(pictureSnapshotMetering); setPictureSnapshotMetering(pictureSnapshotMetering);
@ -906,6 +908,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setWhiteBalance((WhiteBalance) control); setWhiteBalance((WhiteBalance) control);
} else if (control instanceof VideoCodec) { } else if (control instanceof VideoCodec) {
setVideoCodec((VideoCodec) control); setVideoCodec((VideoCodec) control);
} else if (control instanceof AudioCodec) {
setAudioCodec((AudioCodec) control);
} else if (control instanceof Preview) { } else if (control instanceof Preview) {
setPreview((Preview) control); setPreview((Preview) control);
} else if (control instanceof Engine) { } else if (control instanceof Engine) {
@ -942,6 +946,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return (T) getWhiteBalance(); return (T) getWhiteBalance();
} else if (controlClass == VideoCodec.class) { } else if (controlClass == VideoCodec.class) {
return (T) getVideoCodec(); return (T) getVideoCodec();
} else if (controlClass == AudioCodec.class) {
return (T) getAudioCodec();
} else if (controlClass == Preview.class) { } else if (controlClass == Preview.class) {
return (T) getPreview(); return (T) getPreview();
} else if (controlClass == Engine.class) { } else if (controlClass == Engine.class) {
@ -1014,6 +1020,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setHdr(oldEngine.getHdr()); setHdr(oldEngine.getHdr());
setAudio(oldEngine.getAudio()); setAudio(oldEngine.getAudio());
setAudioBitRate(oldEngine.getAudioBitRate()); setAudioBitRate(oldEngine.getAudioBitRate());
setAudioCodec(oldEngine.getAudioCodec());
setPictureSize(oldEngine.getPictureSizeSelector()); setPictureSize(oldEngine.getPictureSizeSelector());
setPictureFormat(oldEngine.getPictureFormat()); setPictureFormat(oldEngine.getPictureFormat());
setVideoSize(oldEngine.getVideoSizeSelector()); setVideoSize(oldEngine.getVideoSizeSelector());
@ -1643,6 +1650,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraEngine.getAudioBitRate(); return mCameraEngine.getAudioBitRate();
} }
/**
* Sets the encoder for audio recordings.
* Defaults to {@link AudioCodec#DEVICE_DEFAULT}.
*
* @see AudioCodec#DEVICE_DEFAULT
* @see AudioCodec#AAC
* @see AudioCodec#HE_AAC
* @see AudioCodec#AAC_ELD
*
* @param codec requested audio codec
*/
public void setAudioCodec(@NonNull AudioCodec codec) {
mCameraEngine.setAudioCodec(codec);
}
/**
* Gets the current encoder for audio recordings.
* @return the current audio codec
*/
@NonNull
public AudioCodec getAudioCodec() {
return mCameraEngine.getAudioCodec();
}
/** /**
* Adds a {@link CameraListener} instance to be notified of all * Adds a {@link CameraListener} instance to be notified of all
* interesting events that happen during the camera lifecycle. * interesting events that happen during the camera lifecycle.

@ -3,6 +3,7 @@ package com.otaliastudios.cameraview;
import android.location.Location; import android.location.Location;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
@ -34,6 +35,7 @@ public class VideoResult {
public FileDescriptor fileDescriptor; public FileDescriptor fileDescriptor;
public Facing facing; public Facing facing;
public VideoCodec videoCodec; public VideoCodec videoCodec;
public AudioCodec audioCodec;
public Audio audio; public Audio audio;
public long maxSize; public long maxSize;
public int maxDuration; public int maxDuration;
@ -60,6 +62,7 @@ public class VideoResult {
private final FileDescriptor fileDescriptor; private final FileDescriptor fileDescriptor;
private final Facing facing; private final Facing facing;
private final VideoCodec videoCodec; private final VideoCodec videoCodec;
private final AudioCodec audioCodec;
private final Audio audio; private final Audio audio;
private final long maxSize; private final long maxSize;
private final int maxDuration; private final int maxDuration;
@ -77,6 +80,7 @@ public class VideoResult {
fileDescriptor = builder.fileDescriptor; fileDescriptor = builder.fileDescriptor;
facing = builder.facing; facing = builder.facing;
videoCodec = builder.videoCodec; videoCodec = builder.videoCodec;
audioCodec = builder.audioCodec;
audio = builder.audio; audio = builder.audio;
maxSize = builder.maxSize; maxSize = builder.maxSize;
maxDuration = builder.maxDuration; maxDuration = builder.maxDuration;
@ -173,6 +177,16 @@ public class VideoResult {
return videoCodec; return videoCodec;
} }
/**
* Returns the codec that was used to encode the audio frames.
*
* @return the audio codec
*/
@NonNull
public AudioCodec getAudioCodec() {
return audioCodec;
}
/** /**
* Returns the max file size in bytes that was set before recording, * Returns the max file size in bytes that was set before recording,
* or 0 if no constraint was set. * or 0 if no constraint was set.

@ -0,0 +1,63 @@
package com.otaliastudios.cameraview.controls;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.CameraView;
/**
* Constants for selecting the encoder of audio recordings.
* https://developer.android.com/guide/topics/media/media-formats.html#audio-formats
*
* @see CameraView#setAudioCodec(AudioCodec)
*/
public enum AudioCodec implements Control {
/**
* Let the device choose its codec.
*/
DEVICE_DEFAULT(0),
/**
* The AAC codec.
*/
AAC(1),
/**
* The HE_AAC codec.
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
HE_AAC(2),
/**
* The AAC_ELD codec.
*/
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
AAC_ELD(3);
static final AudioCodec DEFAULT = DEVICE_DEFAULT;
private int value;
AudioCodec(int value) {
this.value = value;
}
int value() {
return value;
}
@NonNull
static AudioCodec fromValue(int value) {
AudioCodec[] list = AudioCodec.values();
for (AudioCodec action : list) {
if (action.value() == value) {
return action;
}
}
return DEFAULT;
}
}

@ -22,6 +22,7 @@ public class ControlParser {
private int hdr; private int hdr;
private int audio; private int audio;
private int videoCodec; private int videoCodec;
private int audioCodec;
private int engine; private int engine;
private int pictureFormat; private int pictureFormat;
@ -38,6 +39,8 @@ public class ControlParser {
audio = array.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value()); audio = array.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value());
videoCodec = array.getInteger(R.styleable.CameraView_cameraVideoCodec, videoCodec = array.getInteger(R.styleable.CameraView_cameraVideoCodec,
VideoCodec.DEFAULT.value()); VideoCodec.DEFAULT.value());
audioCodec = array.getInteger(R.styleable.CameraView_cameraAudioCodec,
AudioCodec.DEFAULT.value());
engine = array.getInteger(R.styleable.CameraView_cameraEngine, Engine.DEFAULT.value()); engine = array.getInteger(R.styleable.CameraView_cameraEngine, Engine.DEFAULT.value());
pictureFormat = array.getInteger(R.styleable.CameraView_cameraPictureFormat, pictureFormat = array.getInteger(R.styleable.CameraView_cameraPictureFormat,
PictureFormat.DEFAULT.value()); PictureFormat.DEFAULT.value());
@ -84,6 +87,11 @@ public class ControlParser {
return Audio.fromValue(audio); return Audio.fromValue(audio);
} }
@NonNull
public AudioCodec getAudioCodec() {
return AudioCodec.fromValue(audioCodec);
}
@NonNull @NonNull
public VideoCodec getVideoCodec() { public VideoCodec getVideoCodec() {
return VideoCodec.fromValue(videoCodec); return VideoCodec.fromValue(videoCodec);

@ -16,6 +16,7 @@ import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Hdr;
@ -61,6 +62,7 @@ public abstract class CameraBaseEngine extends CameraEngine {
@SuppressWarnings("WeakerAccess") protected Flash mFlash; @SuppressWarnings("WeakerAccess") protected Flash mFlash;
@SuppressWarnings("WeakerAccess") protected WhiteBalance mWhiteBalance; @SuppressWarnings("WeakerAccess") protected WhiteBalance mWhiteBalance;
@SuppressWarnings("WeakerAccess") protected VideoCodec mVideoCodec; @SuppressWarnings("WeakerAccess") protected VideoCodec mVideoCodec;
@SuppressWarnings("WeakerAccess") protected AudioCodec mAudioCodec;
@SuppressWarnings("WeakerAccess") protected Hdr mHdr; @SuppressWarnings("WeakerAccess") protected Hdr mHdr;
@SuppressWarnings("WeakerAccess") protected PictureFormat mPictureFormat; @SuppressWarnings("WeakerAccess") protected PictureFormat mPictureFormat;
@SuppressWarnings("WeakerAccess") protected Location mLocation; @SuppressWarnings("WeakerAccess") protected Location mLocation;
@ -243,6 +245,17 @@ public abstract class CameraBaseEngine extends CameraEngine {
return mVideoBitRate; return mVideoBitRate;
} }
@Override
public final void setAudioCodec(@NonNull AudioCodec codec) {
mAudioCodec = codec;
}
@NonNull
@Override
public final AudioCodec getAudioCodec() {
return mAudioCodec;
}
@Override @Override
public final void setAudioBitRate(int audioBitRate) { public final void setAudioBitRate(int audioBitRate) {
mAudioBitRate = audioBitRate; mAudioBitRate = audioBitRate;
@ -581,6 +594,7 @@ public abstract class CameraBaseEngine extends CameraEngine {
} }
stub.isSnapshot = false; stub.isSnapshot = false;
stub.videoCodec = mVideoCodec; stub.videoCodec = mVideoCodec;
stub.audioCodec = mAudioCodec;
stub.location = mLocation; stub.location = mLocation;
stub.facing = mFacing; stub.facing = mFacing;
stub.audio = mAudio; stub.audio = mAudio;
@ -608,6 +622,7 @@ public abstract class CameraBaseEngine extends CameraEngine {
stub.file = file; stub.file = file;
stub.isSnapshot = true; stub.isSnapshot = true;
stub.videoCodec = mVideoCodec; stub.videoCodec = mVideoCodec;
stub.audioCodec = mAudioCodec;
stub.location = mLocation; stub.location = mLocation;
stub.facing = mFacing; stub.facing = mFacing;
stub.videoBitRate = mVideoBitRate; stub.videoBitRate = mVideoBitRate;

@ -17,6 +17,7 @@ import com.otaliastudios.cameraview.CameraException;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.PictureFormat; import com.otaliastudios.cameraview.controls.PictureFormat;
import com.otaliastudios.cameraview.engine.orchestrator.CameraOrchestrator; import com.otaliastudios.cameraview.engine.orchestrator.CameraOrchestrator;
import com.otaliastudios.cameraview.engine.orchestrator.CameraState; import com.otaliastudios.cameraview.engine.orchestrator.CameraState;
@ -637,6 +638,9 @@ public abstract class CameraEngine implements
public abstract void setAudioBitRate(int audioBitRate); public abstract void setAudioBitRate(int audioBitRate);
public abstract int getAudioBitRate(); public abstract int getAudioBitRate();
public abstract void setAudioCodec(@NonNull AudioCodec codec);
@NonNull public abstract AudioCodec getAudioCodec();
public abstract void setSnapshotMaxWidth(int maxWidth); public abstract void setSnapshotMaxWidth(int maxWidth);
public abstract int getSnapshotMaxWidth(); public abstract int getSnapshotMaxWidth();

@ -2,10 +2,12 @@ package com.otaliastudios.cameraview.video;
import android.media.CamcorderProfile; import android.media.CamcorderProfile;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Build;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.internal.DeviceEncoders; import com.otaliastudios.cameraview.internal.DeviceEncoders;
import com.otaliastudios.cameraview.internal.CamcorderProfiles; import com.otaliastudios.cameraview.internal.CamcorderProfiles;
@ -13,6 +15,7 @@ import com.otaliastudios.cameraview.size.Size;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/** /**
* A {@link VideoRecorder} that uses {@link android.media.MediaRecorder} APIs. * A {@link VideoRecorder} that uses {@link android.media.MediaRecorder} APIs.
@ -105,6 +108,16 @@ public abstract class FullVideoRecorder extends VideoRecorder {
mProfile.videoCodec = MediaRecorder.VideoEncoder.H263; mProfile.videoCodec = MediaRecorder.VideoEncoder.H263;
mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4; // should work mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4; // should work
} }
// Set audio codec if the user has specified a specific codec.
if (stub.audioCodec == AudioCodec.AAC) {
mProfile.audioCodec = MediaRecorder.AudioEncoder.AAC;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& stub.audioCodec == AudioCodec.HE_AAC) {
mProfile.audioCodec = MediaRecorder.AudioEncoder.HE_AAC;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& stub.audioCodec == AudioCodec.AAC_ELD) {
mProfile.audioCodec = MediaRecorder.AudioEncoder.AAC_ELD;
}
mMediaRecorder.setOutputFormat(mProfile.fileFormat); mMediaRecorder.setOutputFormat(mProfile.fileFormat);
// 4. Update the VideoResult stub with information from the profile, if the // 4. Update the VideoResult stub with information from the profile, if the

@ -1,10 +1,13 @@
package com.otaliastudios.cameraview.video; package com.otaliastudios.cameraview.video;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.opengl.EGL14; import android.opengl.EGL14;
import android.os.Build; import android.os.Build;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.controls.AudioCodec;
import com.otaliastudios.cameraview.internal.DeviceEncoders; import com.otaliastudios.cameraview.internal.DeviceEncoders;
import com.otaliastudios.cameraview.overlay.Overlay; import com.otaliastudios.cameraview.overlay.Overlay;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
@ -141,7 +144,14 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
case H_264: videoType = "video/avc"; break; // MediaFormat.MIMETYPE_VIDEO_AVC: case H_264: videoType = "video/avc"; break; // MediaFormat.MIMETYPE_VIDEO_AVC:
case DEVICE_DEFAULT: videoType = "video/avc"; break; case DEVICE_DEFAULT: videoType = "video/avc"; break;
} }
String audioType = "audio/mp4a-latm"; String audioType = "";
switch (mResult.audioCodec) {
case AAC:
case HE_AAC:
case AAC_ELD: audioType = "audio/mp4a-latm"; break; // MediaFormat.MIMETYPE_AUDIO_AAC:
case DEVICE_DEFAULT: audioType = "audio/mp4a-latm"; break;
}
TextureConfig videoConfig = new TextureConfig(); TextureConfig videoConfig = new TextureConfig();
AudioConfig audioConfig = new AudioConfig(); AudioConfig audioConfig = new AudioConfig();

@ -141,6 +141,13 @@
<enum name="h264" value="2" /> <enum name="h264" value="2" />
</attr> </attr>
<attr name="cameraAudioCodec" format="enum">
<enum name="deviceDefault" value="0" />
<enum name="aac" value="1" />
<enum name="heAac" value="2" />
<enum name="aacEld" value="3" />
</attr>
<attr name="cameraAutoFocusResetDelay" format="integer|reference"/> <attr name="cameraAutoFocusResetDelay" format="integer|reference"/>
<attr name="cameraAutoFocusMarker" format="string|reference"/> <attr name="cameraAutoFocusMarker" format="string|reference"/>

@ -123,7 +123,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
new Option.PictureMetering(), new Option.PictureSnapshotMetering(), new Option.PictureMetering(), new Option.PictureSnapshotMetering(),
new Option.PictureFormat(), new Option.PictureFormat(),
// Video recording // Video recording
new Option.PreviewFrameRate(), new Option.VideoCodec(), new Option.Audio(), new Option.PreviewFrameRate(), new Option.VideoCodec(), new Option.Audio(), new Option.AudioCodec(),
// Gestures // Gestures
new Option.Pinch(), new Option.HorizontalScroll(), new Option.VerticalScroll(), new Option.Pinch(), new Option.HorizontalScroll(), new Option.VerticalScroll(),
new Option.Tap(), new Option.LongTap(), new Option.Tap(), new Option.LongTap(),
@ -144,7 +144,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
// Some controls // Some controls
false, false, false, false, false, true, false, false, false, false, false, true,
// Video recording // Video recording
false, false, true, false, false, false, true,
// Gestures // Gestures
false, false, false, false, true, false, false, false, false, true,
// Watermarks // Watermarks

@ -308,6 +308,12 @@ public abstract class Option<T> {
} }
} }
public static class AudioCodec extends ControlOption<com.otaliastudios.cameraview.controls.AudioCodec> {
public AudioCodec() {
super(com.otaliastudios.cameraview.controls.AudioCodec.class, "Audio Codec");
}
}
public static class Audio extends ControlOption<com.otaliastudios.cameraview.controls.Audio> { public static class Audio extends ControlOption<com.otaliastudios.cameraview.controls.Audio> {
public Audio() { public Audio() {
super(com.otaliastudios.cameraview.controls.Audio.class, "Audio"); super(com.otaliastudios.cameraview.controls.Audio.class, "Audio");

@ -59,6 +59,7 @@ public class VideoPreviewActivity extends AppCompatActivity {
final MessageView audio = findViewById(R.id.audio); final MessageView audio = findViewById(R.id.audio);
final MessageView audioBitRate = findViewById(R.id.audioBitRate); final MessageView audioBitRate = findViewById(R.id.audioBitRate);
final MessageView videoCodec = findViewById(R.id.videoCodec); final MessageView videoCodec = findViewById(R.id.videoCodec);
final MessageView audioCodec = findViewById(R.id.audioCodec);
final MessageView videoBitRate = findViewById(R.id.videoBitRate); final MessageView videoBitRate = findViewById(R.id.videoBitRate);
final MessageView videoFrameRate = findViewById(R.id.videoFrameRate); final MessageView videoFrameRate = findViewById(R.id.videoFrameRate);
@ -69,6 +70,7 @@ public class VideoPreviewActivity extends AppCompatActivity {
audio.setTitleAndMessage("Audio", result.getAudio().name()); audio.setTitleAndMessage("Audio", result.getAudio().name());
audioBitRate.setTitleAndMessage("Audio bit rate", result.getAudioBitRate() + " bits per sec."); audioBitRate.setTitleAndMessage("Audio bit rate", result.getAudioBitRate() + " bits per sec.");
videoCodec.setTitleAndMessage("VideoCodec", result.getVideoCodec().name()); videoCodec.setTitleAndMessage("VideoCodec", result.getVideoCodec().name());
audioCodec.setTitleAndMessage("AudioCodec", result.getAudioCodec().name());
videoBitRate.setTitleAndMessage("Video bit rate", result.getVideoBitRate() + " bits per sec."); videoBitRate.setTitleAndMessage("Video bit rate", result.getVideoBitRate() + " bits per sec.");
videoFrameRate.setTitleAndMessage("Video frame rate", result.getVideoFrameRate() + " fps"); videoFrameRate.setTitleAndMessage("Video frame rate", result.getVideoFrameRate() + " fps");
MediaController controller = new MediaController(this); MediaController controller = new MediaController(this);

@ -39,6 +39,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/audioCodec"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView <com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/videoCodec" android:id="@+id/videoCodec"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -70,6 +70,24 @@ cameraView.setVideoCodec(VideoCodec.H_263);
cameraView.setVideoCodec(VideoCodec.H_264); cameraView.setVideoCodec(VideoCodec.H_264);
``` ```
##### cameraAudioCodec
Sets the audio encoder for video recordings. Defaults to `DEVICE_DEFAULT`,
which should typically be AAC.
The available values are exposed through the `CameraOptions` object.
`AudioCodec.HE_AAC` and `AudioCodec.AAC_ELD` require at least JellyBean.
The library will safely fall back to device default if the min API requirements
are not met.
```java
cameraView.setAudioCodec(AudioCodec.DEVICE_DEFAULT);
cameraView.setAudioCodec(AudioCodec.AAC);
cameraView.setAudioCodec(AudioCodec.HE_AAC);
cameraView.setAudioCodec(AudioCodec.AAC_ELD);
```
##### cameraWhiteBalance ##### cameraWhiteBalance
Sets the desired white balance for the current session. Sets the desired white balance for the current session.

Loading…
Cancel
Save