pull/696/head
Mattia Iavarone 6 years ago
parent cbde71fd96
commit 658f91b4fa
  1. 2
      .github/workflows/emulator_script.sh
  2. 67
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java
  3. 32
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  4. 35
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java
  5. 9
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/AudioConfig.java

@ -1,4 +1,4 @@
#!/usr/bin/env bash
adb logcat -c
adb logcat CameraOrchestrator:I CameraEngine:I CameraView:I CameraCallbacks:I CameraIntegrationTest:I MediaEncoderEngine:I MediaEncoder:I AudioMediaEncoder:I VideoMediaEncoder:I TextureMediaEncoder:I VideoRecorder:I FullVideoRecorder:I SnapshotVideoRecorder:I MessageQueue:W WorkerHandler:I MPEG4Writer:I *:E -v color &
adb logcat CameraOrchestrator:I CameraEngine:I CameraView:I CameraCallbacks:I CameraIntegrationTest:I MediaEncoderEngine:I MediaEncoder:I AudioMediaEncoder:I VideoMediaEncoder:I TextureMediaEncoder:I VideoRecorder:I FullVideoRecorder:I SnapshotVideoRecorder:I MessageQueue:W WorkerHandler:I DeviceEncoders:I MPEG4Writer:I *:E -v color &
./gradlew cameraview:connectedCheck

@ -1,8 +1,11 @@
package com.otaliastudios.cameraview.internal;
import android.annotation.SuppressLint;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;
import androidx.annotation.NonNull;
@ -49,8 +52,9 @@ import java.util.List;
* - MediaCodecList (https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/MediaCodecList.cpp#322)
*
* To be fair, what {@link android.media.MediaRecorder} does is actually choose the first one
* that configures itself without errors. We currently do not offer this option here.
* TODO add a tryConfigure() step, that throws AudioException/VideoException ?
* that configures itself without errors. We offer this option through
* {@link #tryConfigureVideo(String, Size, int, int)} and
* {@link #tryConfigureAudio(String, int, int, int)}.
*
* 2. {@link #MODE_PREFER_HARDWARE}
*
@ -350,4 +354,63 @@ public class DeviceEncoders {
}
}
@SuppressLint("NewApi")
public void tryConfigureVideo(@NonNull String mimeType,
@NonNull Size size,
int frameRate,
int bitRate) {
if (mVideoEncoder != null) {
MediaCodec codec = null;
try {
MediaFormat format = MediaFormat.createVideoFormat(mimeType, size.getWidth(),
size.getHeight());
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
codec = MediaCodec.createByCodecName(mVideoEncoder.getName());
codec.configure(format, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (Exception e) {
throw new VideoException("Failed to configure video codec: " + e.getMessage());
} finally {
if (codec != null) {
try {
codec.release();
} catch (Exception ignore) {}
}
}
}
}
@SuppressLint("NewApi")
public void tryConfigureAudio(@NonNull String mimeType,
int bitRate,
int sampleRate,
int channels) {
if (mAudioEncoder != null) {
MediaCodec codec = null;
try {
final MediaFormat format = MediaFormat.createAudioFormat(mimeType, sampleRate,
channels);
int channelMask = channels == 2 ? AudioFormat.CHANNEL_IN_STEREO
: AudioFormat.CHANNEL_IN_MONO;
format.setInteger(MediaFormat.KEY_CHANNEL_MASK, channelMask);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
codec = MediaCodec.createByCodecName(mAudioEncoder.getName());
codec.configure(format, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (Exception e) {
throw new AudioException("Failed to configure video audio: " + e.getMessage());
} finally {
if (codec != null) {
try {
codec.release();
} catch (Exception ignore) {}
}
}
}
}
}

@ -84,9 +84,15 @@ public abstract class FullVideoRecorder extends VideoRecorder {
// 2. Set the video and audio sources.
applyVideoSource(stub, mMediaRecorder);
boolean hasAudio = stub.audio == Audio.ON
|| stub.audio == Audio.MONO
|| stub.audio == Audio.STEREO;
int audioChannels = 0;
if (stub.audio == Audio.ON) {
audioChannels = mProfile.audioChannels;
} else if (stub.audio == Audio.MONO) {
audioChannels = 1;
} else if (stub.audio == Audio.STEREO) {
audioChannels = 2;
}
boolean hasAudio = audioChannels > 0;
if (hasAudio) {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
}
@ -156,13 +162,23 @@ public abstract class FullVideoRecorder extends VideoRecorder {
try {
newVideoSize = encoders.getSupportedVideoSize(stub.size);
newVideoBitRate = encoders.getSupportedVideoBitRate(stub.videoBitRate);
newAudioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate);
newVideoFrameRate = encoders.getSupportedVideoFrameRate(newVideoSize,
stub.videoFrameRate);
encoders.tryConfigureVideo(videoType, newVideoSize, newVideoFrameRate,
newVideoBitRate);
if (hasAudio) {
newAudioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate);
encoders.tryConfigureAudio(audioType, newAudioBitRate,
mProfile.audioSampleRate, audioChannels);
}
encodersFound = true;
} catch (DeviceEncoders.VideoException videoException) {
LOG.i("prepareMediaRecorder:", "Got VideoException:",
videoException.getMessage());
videoEncoderOffset++;
} catch (DeviceEncoders.AudioException audioException) {
LOG.i("prepareMediaRecorder:", "Got AudioException:",
audioException.getMessage());
audioEncoderOffset++;
}
}
@ -185,13 +201,7 @@ public abstract class FullVideoRecorder extends VideoRecorder {
// 6B. Configure MediaRecorder from stub and from profile (audio)
if (hasAudio) {
if (stub.audio == Audio.ON) {
mMediaRecorder.setAudioChannels(mProfile.audioChannels);
} else if (stub.audio == Audio.MONO) {
mMediaRecorder.setAudioChannels(1);
} else if (stub.audio == Audio.STEREO) {
mMediaRecorder.setAudioChannels(2);
}
mMediaRecorder.setAudioChannels(audioChannels);
mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);
mMediaRecorder.setAudioEncoder(mProfile.audioCodec);
mMediaRecorder.setAudioEncodingBitRate(stub.audioBitRate);

@ -146,6 +146,19 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
case DEVICE_DEFAULT: videoType = "video/avc"; break;
}
String audioType = "audio/mp4a-latm";
TextureConfig videoConfig = new TextureConfig();
AudioConfig audioConfig = new AudioConfig();
// See if we have audio
int audioChannels = 0;
if (mResult.audio == Audio.ON) {
audioChannels = audioConfig.channels;
} else if (mResult.audio == Audio.MONO) {
audioChannels = 1;
} else if (mResult.audio == Audio.STEREO) {
audioChannels = 2;
}
boolean hasAudio = audioChannels > 0;
// Check the availability of values
Size newVideoSize = null;
@ -157,18 +170,30 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
boolean encodersFound = false;
DeviceEncoders deviceEncoders = null;
while (!encodersFound) {
LOG.i("Checking DeviceEncoders...",
"videoOffset:", videoEncoderOffset,
"audioOffset:", audioEncoderOffset);
deviceEncoders = new DeviceEncoders(DeviceEncoders.MODE_PREFER_HARDWARE,
videoType, audioType, videoEncoderOffset, audioEncoderOffset);
try {
newVideoSize = deviceEncoders.getSupportedVideoSize(mResult.size);
newVideoBitRate = deviceEncoders.getSupportedVideoBitRate(mResult.videoBitRate);
newAudioBitRate = deviceEncoders.getSupportedAudioBitRate(mResult.audioBitRate);
newVideoFrameRate = deviceEncoders.getSupportedVideoFrameRate(newVideoSize,
mResult.videoFrameRate);
deviceEncoders.tryConfigureVideo(videoType, newVideoSize, newVideoFrameRate,
newVideoBitRate);
if (hasAudio) {
newAudioBitRate = deviceEncoders
.getSupportedAudioBitRate(mResult.audioBitRate);
deviceEncoders.tryConfigureAudio(audioType, newAudioBitRate,
audioConfig.samplingFrequency, audioChannels);
}
encodersFound = true;
} catch (DeviceEncoders.VideoException videoException) {
LOG.i("Got VideoException:", videoException.getMessage());
videoEncoderOffset++;
} catch (DeviceEncoders.AudioException audioException) {
LOG.i("Got AudioException:", audioException.getMessage());
audioEncoderOffset++;
}
}
@ -178,7 +203,6 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
mResult.videoFrameRate = newVideoFrameRate;
// Video
TextureConfig videoConfig = new TextureConfig();
videoConfig.width = mResult.size.getWidth();
videoConfig.height = mResult.size.getHeight();
videoConfig.bitRate = mResult.videoBitRate;
@ -206,12 +230,9 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
// Audio
AudioMediaEncoder audioEncoder = null;
if (mResult.audio == Audio.ON || mResult.audio == Audio.MONO
|| mResult.audio == Audio.STEREO) {
AudioConfig audioConfig = new AudioConfig();
if (hasAudio) {
audioConfig.bitRate = mResult.audioBitRate;
if (mResult.audio == Audio.MONO) audioConfig.channels = 1;
if (mResult.audio == Audio.STEREO) audioConfig.channels = 2;
audioConfig.channels = audioChannels;
audioConfig.encoder = deviceEncoders.getAudioEncoder();
audioEncoder = new AudioMediaEncoder(audioConfig);
}

@ -16,11 +16,11 @@ public class AudioConfig {
public int channels = 1;
public String encoder;
public String mimeType = "audio/mp4a-latm";
public int samplingFrequency = 44100; // samples/sec
// Not configurable options (for now)
final int encoding = AudioFormat.ENCODING_PCM_16BIT; // Determines the sampleSizePerChannel
// The 44.1KHz frequency is the only setting guaranteed to be available on all devices.
final int samplingFrequency = 44100; // samples/sec
// If sampleSizePerChannel changes, review noise introduction
final int sampleSizePerChannel = 2; // byte/sample/channel [16bit].
final int byteRatePerChannel = samplingFrequency * sampleSizePerChannel; // byte/sec/channel
@ -28,10 +28,11 @@ public class AudioConfig {
@NonNull
AudioConfig copy() {
AudioConfig config = new AudioConfig();
config.bitRate = this.bitRate;
config.channels = this.channels;
config.encoder = this.encoder;
config.bitRate = bitRate;
config.channels = channels;
config.encoder = encoder;
config.mimeType = mimeType;
config.samplingFrequency = samplingFrequency;
return config;
}

Loading…
Cancel
Save