takeVideo improvements (#617)

* takeVideo improvements

* Try to fix tests

* Reorder operations in FullVideoRecorder
pull/632/head
Mattia Iavarone 5 years ago committed by GitHub
parent 5b92e81b83
commit 824abbc55a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 66
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java
  2. 12
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  3. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  4. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java
  5. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfiles.java
  6. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/video/Full1VideoRecorder.java
  7. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.java
  8. 115
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java

@ -41,7 +41,7 @@ public class CameraOptions1Test extends BaseTest {
@Test
public void testEmpty() {
CameraOptions o = new CameraOptions(mock(Camera.Parameters.class), false);
CameraOptions o = new CameraOptions(mock(Camera.Parameters.class), 0, false);
assertTrue(o.getSupportedPictureAspectRatios().isEmpty());
assertTrue(o.getSupportedPictureSizes().isEmpty());
assertTrue(o.getSupportedWhiteBalance().isEmpty());
@ -71,7 +71,7 @@ public class CameraOptions1Test extends BaseTest {
);
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Size> supportedSizes = o.getSupportedPictureSizes();
assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) {
@ -90,7 +90,7 @@ public class CameraOptions1Test extends BaseTest {
);
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, true);
CameraOptions o = new CameraOptions(params, 0, true);
Collection<Size> supportedSizes = o.getSupportedPictureSizes();
assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) {
@ -115,7 +115,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedPictureSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<AspectRatio> supportedRatios = o.getSupportedPictureAspectRatios();
assertEquals(supportedRatios.size(), expected.size());
for (AspectRatio ratio : expected) {
@ -126,15 +126,17 @@ public class CameraOptions1Test extends BaseTest {
@Test
public void testVideoSizes() {
// VideoSize are capped by CamcorderProfile.QUALITY_HIGH max size.
// This can be very small on an emulator, so use very small sizes to not hit that value.
List<Camera.Size> sizes = Arrays.asList(
mockCameraSize(100, 200),
mockCameraSize(50, 50),
mockCameraSize(1600, 900),
mockCameraSize(1000, 2000)
mockCameraSize(10, 20),
mockCameraSize(5, 5),
mockCameraSize(16, 9),
mockCameraSize(20, 40)
);
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedVideoSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Size> supportedSizes = o.getSupportedVideoSizes();
assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) {
@ -147,15 +149,15 @@ public class CameraOptions1Test extends BaseTest {
public void testVideoSizesNull() {
// When videoSizes is null, we take the preview sizes.
List<Camera.Size> sizes = Arrays.asList(
mockCameraSize(100, 200),
mockCameraSize(50, 50),
mockCameraSize(1600, 900),
mockCameraSize(1000, 2000)
mockCameraSize(10, 20),
mockCameraSize(5, 5),
mockCameraSize(16, 9),
mockCameraSize(20, 40)
);
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedVideoSizes()).thenReturn(null);
when(params.getSupportedPreviewSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Size> supportedSizes = o.getSupportedVideoSizes();
assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) {
@ -167,14 +169,14 @@ public class CameraOptions1Test extends BaseTest {
@Test
public void testVideoSizesFlip() {
List<Camera.Size> sizes = Arrays.asList(
mockCameraSize(100, 200),
mockCameraSize(50, 50),
mockCameraSize(1600, 900),
mockCameraSize(1000, 2000)
mockCameraSize(10, 20),
mockCameraSize(5, 5),
mockCameraSize(16, 9),
mockCameraSize(20, 40)
);
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedVideoSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, true);
CameraOptions o = new CameraOptions(params, 0, true);
Collection<Size> supportedSizes = o.getSupportedVideoSizes();
assertEquals(supportedSizes.size(), sizes.size());
for (Camera.Size size : sizes) {
@ -186,10 +188,10 @@ public class CameraOptions1Test extends BaseTest {
@Test
public void testVideoAspectRatio() {
List<Camera.Size> sizes = Arrays.asList(
mockCameraSize(100, 200),
mockCameraSize(50, 50),
mockCameraSize(1600, 900),
mockCameraSize(1000, 2000)
mockCameraSize(10, 20),
mockCameraSize(5, 5),
mockCameraSize(16, 9),
mockCameraSize(20, 40)
);
Set<AspectRatio> expected = new HashSet<>();
@ -199,7 +201,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters params = mock(Camera.Parameters.class);
when(params.getSupportedVideoSizes()).thenReturn(sizes);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<AspectRatio> supportedRatios = o.getSupportedVideoAspectRatios();
assertEquals(supportedRatios.size(), expected.size());
for (AspectRatio ratio : expected) {
@ -215,7 +217,7 @@ public class CameraOptions1Test extends BaseTest {
when(params.getMaxExposureCompensation()).thenReturn(0);
when(params.getMinExposureCompensation()).thenReturn(0);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
assertFalse(o.supports(GestureAction.AUTO_FOCUS));
assertTrue(o.supports(GestureAction.TAKE_PICTURE));
assertTrue(o.supports(GestureAction.NONE));
@ -229,7 +231,7 @@ public class CameraOptions1Test extends BaseTest {
public void testAlwaysSupportedControls() {
// Grid, VideoQuality, SessionType and Audio are always supported.
Camera.Parameters params = mock(Camera.Parameters.class);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Grid> grids = o.getSupportedControls(Grid.class);
Collection<VideoCodec> video = o.getSupportedControls(VideoCodec.class);
@ -250,7 +252,7 @@ public class CameraOptions1Test extends BaseTest {
supported.add(cameraInfo.facing);
}
CameraOptions o = new CameraOptions(mock(Camera.Parameters.class), false);
CameraOptions o = new CameraOptions(mock(Camera.Parameters.class), 0, false);
Camera1Mapper m = Camera1Mapper.get();
Collection<Facing> s = o.getSupportedControls(Facing.class);
assertEquals(s.size(), supported.size());
@ -269,7 +271,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters.WHITE_BALANCE_SHADE // Not supported
));
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<WhiteBalance> w = o.getSupportedControls(WhiteBalance.class);
assertEquals(w.size(), 2);
assertTrue(w.contains(WhiteBalance.AUTO));
@ -288,7 +290,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters.FLASH_MODE_RED_EYE // Not supported
));
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Flash> f = o.getSupportedControls(Flash.class);
assertEquals(f.size(), 3);
assertTrue(f.contains(Flash.OFF));
@ -309,7 +311,7 @@ public class CameraOptions1Test extends BaseTest {
Camera.Parameters.SCENE_MODE_FIREWORKS // Not supported
));
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
Collection<Hdr> h = o.getSupportedControls(Hdr.class);
assertEquals(h.size(), 2);
assertTrue(h.contains(Hdr.OFF));
@ -326,7 +328,7 @@ public class CameraOptions1Test extends BaseTest {
when(params.isZoomSupported()).thenReturn(true);
//noinspection ArraysAsListWithZeroOrOneArgument
when(params.getSupportedFocusModes()).thenReturn(Arrays.asList(Camera.Parameters.FOCUS_MODE_AUTO));
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
assertTrue(o.isZoomSupported());
assertTrue(o.isAutoFocusSupported());
}
@ -337,7 +339,7 @@ public class CameraOptions1Test extends BaseTest {
when(params.getMaxExposureCompensation()).thenReturn(10);
when(params.getMinExposureCompensation()).thenReturn(-10);
when(params.getExposureCompensationStep()).thenReturn(0.5f);
CameraOptions o = new CameraOptions(params, false);
CameraOptions o = new CameraOptions(params, 0, false);
assertTrue(o.isExposureCorrectionSupported());
assertEquals(o.getExposureCorrectionMinValue(), -10f * 0.5f, 0f);
assertEquals(o.getExposureCorrectionMaxValue(), 10f * 0.5f, 0f);

@ -62,7 +62,7 @@ public class CameraOptions {
private boolean autoFocusSupported;
public CameraOptions(@NonNull Camera.Parameters params, boolean flipSizes) {
public CameraOptions(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) {
List<String> strings;
Camera1Mapper mapper = Camera1Mapper.get();
@ -127,19 +127,28 @@ public class CameraOptions {
}
// Video Sizes
// As a safety measure, remove Sizes bigger than CamcorderProfile.highest
CamcorderProfile profile = CamcorderProfiles.get(cameraId,
new Size(Integer.MAX_VALUE, Integer.MAX_VALUE));
Size videoMaxSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
List<Camera.Size> vsizes = params.getSupportedVideoSizes();
if (vsizes != null) {
for (Camera.Size size : vsizes) {
if (size.width <= videoMaxSize.getWidth()
&& size.height <= videoMaxSize.getHeight()) {
int width = flipSizes ? size.height : size.width;
int height = flipSizes ? size.width : size.height;
supportedVideoSizes.add(new Size(width, height));
supportedVideoAspectRatio.add(AspectRatio.of(width, height));
}
}
} else {
// StackOverflow threads seems to agree that if getSupportedVideoSizes is null,
// previews can be used.
List<Camera.Size> fallback = params.getSupportedPreviewSizes();
for (Camera.Size size : fallback) {
if (size.width <= videoMaxSize.getWidth()
&& size.height <= videoMaxSize.getHeight()) {
int width = flipSizes ? size.height : size.width;
int height = flipSizes ? size.width : size.height;
supportedVideoSizes.add(new Size(width, height));
@ -147,6 +156,7 @@ public class CameraOptions {
}
}
}
}
// Camera2Engine constructor.
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)

@ -154,8 +154,8 @@ public class Camera1Engine extends CameraEngine implements
// Set parameters that might have been set before the camera was opened.
LOG.i("onStartEngine:", "Applying default parameters.");
Camera.Parameters params = mCamera.getParameters();
mCameraOptions = new CameraOptions(params, getAngles()
.flip(Reference.SENSOR, Reference.VIEW));
mCameraOptions = new CameraOptions(params, mCameraId,
getAngles().flip(Reference.SENSOR, Reference.VIEW));
applyAllParameters(params);
mCamera.setParameters(params);
mCamera.setDisplayOrientation(getAngles().offset(Reference.SENSOR, Reference.VIEW,

@ -221,22 +221,28 @@ public class DeviceEncoders {
int width = size.getWidth();
int height = size.getHeight();
double aspect = (double) width / height;
LOG.i("getSupportedVideoSize - started. width:", width, "height:", height);
// If width is too large, scale down, but keep aspect ratio.
if (mVideoCapabilities.getSupportedWidths().getUpper() < width) {
width = mVideoCapabilities.getSupportedWidths().getUpper();
height = (int) Math.round(width / aspect);
LOG.i("getSupportedVideoSize - exceeds maxWidth! width:", width,
"height:", height);
}
// If height is too large, scale down, but keep aspect ratio.
if (mVideoCapabilities.getSupportedHeights().getUpper() < height) {
height = mVideoCapabilities.getSupportedHeights().getUpper();
width = (int) Math.round(aspect * height);
LOG.i("getSupportedVideoSize - exceeds maxHeight! width:", width,
"height:", height);
}
// Adjust the alignment.
while (width % mVideoCapabilities.getWidthAlignment() != 0) width--;
while (height % mVideoCapabilities.getHeightAlignment() != 0) height--;
LOG.i("getSupportedVideoSize - aligned. width:", width, "height:", height);
// It's still possible that we're BELOW the lower.
if (!mVideoCapabilities.getSupportedWidths().contains(width)) {
@ -256,9 +262,7 @@ public class DeviceEncoders {
" Might be an aspect ratio issue." +
" Desired size:" + new Size(width, height));
}
Size adjusted = new Size(width, height);
LOG.i("getSupportedVideoSize -", "inputSize:", size, "adjustedSize:", adjusted);
return adjusted;
return new Size(width, height);
}
/**

@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.media.CamcorderProfile;
import android.os.Build;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.size.Size;
import java.util.ArrayList;
@ -20,6 +21,9 @@ import androidx.annotation.NonNull;
*/
public class CamcorderProfiles {
private static final String TAG = CamcorderProfiles.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG);
@SuppressLint("UseSparseArrays")
private static Map<Size, Integer> sizeToProfileMap = new HashMap<>();
@ -53,6 +57,7 @@ public class CamcorderProfiles {
int camera1Id = Integer.parseInt(cameraId);
return get(camera1Id, targetSize);
} catch (NumberFormatException e) {
LOG.w("NumberFormatException for Camera2 id:", cameraId);
return CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
}
}

@ -1,6 +1,7 @@
package com.otaliastudios.cameraview.video;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.util.Log;
@ -34,15 +35,18 @@ public class Full1VideoRecorder extends FullVideoRecorder {
}
@Override
protected boolean onPrepareMediaRecorder(@NonNull VideoResult.Stub stub,
protected void applyVideoSource(@NonNull VideoResult.Stub stub,
@NonNull MediaRecorder mediaRecorder) {
mediaRecorder.setCamera(mCamera);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
}
@NonNull
@Override
protected CamcorderProfile getCamcorderProfile(@NonNull VideoResult.Stub stub) {
// Get a profile of quality compatible with the chosen size.
Size size = stub.rotation % 180 != 0 ? stub.size.flip() : stub.size;
mProfile = CamcorderProfiles.get(mCameraId, size);
return super.onPrepareMediaRecorder(stub, mediaRecorder);
return CamcorderProfiles.get(mCameraId, size);
}
@Override

@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Build;
import android.view.Surface;
@ -69,19 +70,22 @@ public class Full2VideoRecorder extends FullVideoRecorder {
action.start(mHolder);
}
@SuppressLint("NewApi")
@Override
protected boolean onPrepareMediaRecorder(@NonNull VideoResult.Stub stub,
protected void applyVideoSource(@NonNull VideoResult.Stub stub,
@NonNull MediaRecorder mediaRecorder) {
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
Size size = stub.rotation % 180 != 0 ? stub.size.flip() : stub.size;
mProfile = CamcorderProfiles.get(mCameraId, size);
}
@NonNull
@Override
protected CamcorderProfile getCamcorderProfile(@NonNull VideoResult.Stub stub) {
// This was an option: get the surface from outside this class, using
// MediaCodec.createPersistentInputSurface(). But it doesn't really help since the
// Camera2 engine refuses a surface that has not been configured, so even with that trick
// we would have to attach the surface to this recorder before creating the CameraSession.
// mediaRecorder.setInputSurface(mInputSurface);
return super.onPrepareMediaRecorder(stub, mediaRecorder);
Size size = stub.rotation % 180 != 0 ? stub.size.flip() : stub.size;
return CamcorderProfiles.get(mCameraId, size);
}
/**

@ -8,6 +8,7 @@ import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Audio;
import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.internal.DeviceEncoders;
import com.otaliastudios.cameraview.internal.utils.CamcorderProfiles;
import com.otaliastudios.cameraview.size.Size;
import androidx.annotation.NonNull;
@ -17,10 +18,9 @@ import androidx.annotation.Nullable;
* A {@link VideoRecorder} that uses {@link android.media.MediaRecorder} APIs.
*
* When started, the media recorder will be prepared in
* {@link #onPrepareMediaRecorder(VideoResult.Stub, MediaRecorder)}.
* Subclasses should override this method and, before calling super(), do two things:
* - set the media recorder VideoSource
* - define {@link #mProfile}
* {@link #prepareMediaRecorder(VideoResult.Stub)}. This will call two abstract methods:
* - {@link #getCamcorderProfile(VideoResult.Stub)}
* - {@link #applyVideoSource(VideoResult.Stub, MediaRecorder)}
*
* Subclasses can also call {@link #prepareMediaRecorder(VideoResult.Stub)} before start happens,
* in which case it will not be prepared twice. This can be used for example to test some
@ -32,7 +32,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
private static final CameraLogger LOG = CameraLogger.create(TAG);
@SuppressWarnings("WeakerAccess") protected MediaRecorder mMediaRecorder;
@SuppressWarnings("WeakerAccess") protected CamcorderProfile mProfile;
private CamcorderProfile mProfile;
private boolean mMediaRecorderPrepared;
@ -40,24 +40,75 @@ public abstract class FullVideoRecorder extends VideoRecorder {
super(listener);
}
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
protected boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub) {
/**
* Subclasses should return an appropriate CamcorderProfile.
* This could be taken from the {@link CamcorderProfiles} utility class based on the
* stub declared size, for instance.
*
* @param stub the stub
* @return the profile
*/
@NonNull
protected abstract CamcorderProfile getCamcorderProfile(@NonNull VideoResult.Stub stub);
/**
* Subclasses should apply a video source to the given recorder.
*
* @param stub the stub
* @param mediaRecorder the recorder
*/
protected abstract void applyVideoSource(@NonNull VideoResult.Stub stub,
@NonNull MediaRecorder mediaRecorder);
@SuppressWarnings("WeakerAccess")
protected final boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub) {
if (mMediaRecorderPrepared) return true;
return onPrepareMediaRecorder(stub, new MediaRecorder());
// We kind of trust the stub size at this point. It's coming from CameraOptions sizes
// and it's clipped to be less than CamcorderProfile's highest available profile.
// However, we still can't trust the developer parameters (e.g. bit rates), and even
// without them, the camera declared sizes can cause crashes in MediaRecorder (#467, #602).
// A possible solution was to prepare without checking DeviceEncoders first, and should it
// fail, prepare again checking them. However, when parameters are wrong, MediaRecorder
// fails on start() instead of prepare() (start failed -19), so this wouldn't be effective.
return prepareMediaRecorder(stub, true);
}
protected boolean onPrepareMediaRecorder(@NonNull VideoResult.Stub stub,
@NonNull MediaRecorder mediaRecorder) {
mMediaRecorder = mediaRecorder;
@SuppressWarnings("SameParameterValue")
private boolean prepareMediaRecorder(@NonNull VideoResult.Stub stub,
boolean applyEncodersConstraints) {
// 1. Create reference and ask for the CamcorderProfile
mMediaRecorder = new MediaRecorder();
mProfile = getCamcorderProfile(stub);
// 2. Set the video and audio sources.
applyVideoSource(stub, mMediaRecorder);
boolean hasAudio = stub.audio == Audio.ON
|| stub.audio == Audio.MONO
|| stub.audio == Audio.STEREO;
if (hasAudio) {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
}
// 3. Set the output format. Before, change the profile data if the user
// has specified a specific codec.
if (stub.videoCodec == VideoCodec.H_264) {
mProfile.videoCodec = MediaRecorder.VideoEncoder.H264;
mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4;
} else if (stub.videoCodec == VideoCodec.H_263) {
mProfile.videoCodec = MediaRecorder.VideoEncoder.H263;
mProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4; // should work
}
mMediaRecorder.setOutputFormat(mProfile.fileFormat);
// Get the audio mime type
// 4. Update the VideoResult stub with information from the profile, if the
// stub values are absent or incomplete
if (stub.videoFrameRate <= 0) stub.videoFrameRate = mProfile.videoFrameRate;
if (stub.videoBitRate <= 0) stub.videoBitRate = mProfile.videoBitRate;
if (stub.audioBitRate <= 0 && hasAudio) stub.audioBitRate = mProfile.audioBitRate;
// 5. Update the VideoResult stub with DeviceEncoders constraints
if (applyEncodersConstraints) {
// A. Get the audio mime type
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1096
// https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
String audioType;
@ -71,17 +122,10 @@ public abstract class FullVideoRecorder extends VideoRecorder {
case MediaRecorder.AudioEncoder.DEFAULT:
default: audioType = "audio/3gpp";
}
// Get the video mime type
// B. Get the video mime type
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libmediaplayerservice/StagefrightRecorder.cpp#1650
// https://github.com/MrAlex94/Waterfox-Old/blob/master/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp
String videoType;
if (stub.videoCodec == VideoCodec.H_264) {
mProfile.videoCodec = MediaRecorder.VideoEncoder.H264;
}
if (stub.videoCodec == VideoCodec.H_263) {
mProfile.videoCodec = MediaRecorder.VideoEncoder.H263;
}
switch (mProfile.videoCodec) {
case MediaRecorder.VideoEncoder.H263: videoType = "video/3gpp"; break;
case MediaRecorder.VideoEncoder.H264: videoType = "video/avc"; break;
@ -91,16 +135,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
case MediaRecorder.VideoEncoder.DEFAULT:
default: videoType = "video/avc";
}
// Merge stub and profile
stub.videoFrameRate = stub.videoFrameRate > 0 ? stub.videoFrameRate
: mProfile.videoFrameRate;
stub.videoBitRate = stub.videoBitRate > 0 ? stub.videoBitRate : mProfile.videoBitRate;
if (hasAudio) {
stub.audioBitRate = stub.audioBitRate > 0 ? stub.audioBitRate : mProfile.audioBitRate;
}
// Check DeviceEncoders support
// C. Check DeviceEncoders support
boolean flip = stub.rotation % 180 != 0;
if (flip) stub.size = stub.size.flip();
Size newVideoSize = null;
@ -111,6 +146,9 @@ public abstract class FullVideoRecorder extends VideoRecorder {
int audioEncoderOffset = 0;
boolean encodersFound = false;
while (!encodersFound) {
LOG.i("prepareMediaRecorder:", "Checking DeviceEncoders...",
"videoOffset:", videoEncoderOffset,
"audioOffset:", audioEncoderOffset);
DeviceEncoders encoders = new DeviceEncoders(DeviceEncoders.MODE_RESPECT_ORDER,
videoType, audioType, videoEncoderOffset, audioEncoderOffset);
try {
@ -126,13 +164,16 @@ public abstract class FullVideoRecorder extends VideoRecorder {
audioEncoderOffset++;
}
}
// D. Apply results
stub.size = newVideoSize;
stub.videoBitRate = newVideoBitRate;
stub.audioBitRate = newAudioBitRate;
stub.videoFrameRate = newVideoFrameRate;
if (flip) stub.size = stub.size.flip();
}
// Set video params
// 6A. Configure MediaRecorder from stub and from profile (video)
boolean flip = stub.rotation % 180 != 0;
mMediaRecorder.setVideoSize(
flip ? stub.size.getHeight() : stub.size.getWidth(),
flip ? stub.size.getWidth() : stub.size.getHeight());
@ -140,7 +181,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setVideoEncoder(mProfile.videoCodec);
mMediaRecorder.setVideoEncodingBitRate(stub.videoBitRate);
// Set audio params
// 6B. Configure MediaRecorder from stub and from profile (audio)
if (hasAudio) {
if (stub.audio == Audio.ON) {
mMediaRecorder.setAudioChannels(mProfile.audioChannels);
@ -154,7 +195,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setAudioEncodingBitRate(stub.audioBitRate);
}
// Set other params
// 7. Set other params
if (stub.location != null) {
mMediaRecorder.setLocation(
(float) stub.location.getLatitude(),
@ -180,7 +221,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
}
});
// Prepare the Recorder
// 8. Prepare the Recorder
try {
mMediaRecorder.prepare();
mMediaRecorderPrepared = true;
@ -220,13 +261,15 @@ public abstract class FullVideoRecorder extends VideoRecorder {
try {
mMediaRecorder.stop();
} catch (Exception e) {
LOG.w("stop:", "Error while closing media recorder.", e);
// This can happen if stopVideo() is called right after takeVideo()
// (in which case we don't care). Or when prepare()/start() have failed for
// some reason and we are not allowed to call stop.
// Make sure we don't override the error if one exists already.
mResult = null;
if (mError == null) mError = e;
if (mError == null) {
LOG.w("stop:", "Error while closing media recorder.", e);
mError = e;
}
}
mMediaRecorder.release();
}

Loading…
Cancel
Save