Update dependencies
This commit is contained in:
parent
e918a2f94c
commit
aea17aa7cb
@ -94,27 +94,27 @@ android {
|
||||
dependencies {
|
||||
implementation project(":libpdfviewer:app")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.core:core-ktx:1.10.0'
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
def lifecycle_version = "2.6.0"
|
||||
def lifecycle_version = "2.6.1"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
|
||||
|
||||
implementation "androidx.sqlite:sqlite-ktx:2.3.0"
|
||||
implementation "androidx.sqlite:sqlite-ktx:2.3.1"
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation "com.github.bumptech.glide:glide:4.13.2"
|
||||
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||
|
||||
def exoplayer_version = "2.18.4"
|
||||
def exoplayer_version = "2.18.5"
|
||||
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
||||
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
|
||||
|
||||
implementation "androidx.concurrent:concurrent-futures:1.1.0"
|
||||
|
||||
def camerax_version = "1.3.0-alpha04"
|
||||
def camerax_version = "1.3.0-alpha05"
|
||||
implementation "androidx.camera:camera-camera2:$camerax_version"
|
||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||
implementation "androidx.camera:camera-view:$camerax_version"
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 43de737624ceeb1c41012d2ea4f0d5dcba8a19e5
|
||||
Subproject commit 445b26395bf94e3295d12aa46c8c15d5d63cab95
|
@ -1 +1 @@
|
||||
Subproject commit 27232cbdb7257be13a12545b71fa32ee193cb11b
|
||||
Subproject commit 79f9a10e35847e46f8563941345355f15f2dba7c
|
@ -29,7 +29,7 @@ import static androidx.camera.video.VideoRecordEvent.Finalize.VideoRecordError;
|
||||
import static androidx.camera.video.internal.DebugUtils.readableUs;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioEncoderConfig;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioMimeInfo;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioSourceSettings;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioSettings;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
@ -56,7 +56,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.camera.core.AspectRatio;
|
||||
import androidx.camera.core.Logger;
|
||||
import androidx.camera.core.SurfaceRequest;
|
||||
import androidx.camera.core.impl.CamcorderProfileProxy;
|
||||
import androidx.camera.core.impl.MutableStateObservable;
|
||||
import androidx.camera.core.impl.Observable;
|
||||
import androidx.camera.core.impl.StateObservable;
|
||||
@ -69,8 +68,11 @@ import androidx.camera.core.impl.utils.futures.Futures;
|
||||
import androidx.camera.core.internal.utils.ArrayRingBuffer;
|
||||
import androidx.camera.core.internal.utils.RingBuffer;
|
||||
import androidx.camera.video.StreamInfo.StreamState;
|
||||
import androidx.camera.video.internal.AudioSource;
|
||||
import androidx.camera.video.internal.AudioSourceAccessException;
|
||||
import androidx.camera.video.internal.VideoValidatedEncoderProfilesProxy;
|
||||
import androidx.camera.video.internal.audio.AudioSettings;
|
||||
import androidx.camera.video.internal.audio.AudioSource;
|
||||
import androidx.camera.video.internal.audio.AudioSourceAccessException;
|
||||
import androidx.camera.video.internal.compat.Api26Impl;
|
||||
import androidx.camera.video.internal.compat.quirk.DeactivateEncoderSurfaceBeforeStopEncoderQuirk;
|
||||
import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
|
||||
import androidx.camera.video.internal.compat.quirk.EncoderNotUsePersistentInputSurfaceQuirk;
|
||||
@ -342,7 +344,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
boolean mInProgressRecordingStopping = false;
|
||||
private SurfaceRequest.TransformationInfo mSurfaceTransformationInfo = null;
|
||||
private CamcorderProfileProxy mResolvedCamcorderProfile = null;
|
||||
private VideoValidatedEncoderProfilesProxy mResolvedEncoderProfiles = null;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
final List<ListenableFuture<Void>> mEncodingFutures = new ArrayList<>();
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
@ -452,7 +454,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
onSurfaceRequested(request, Timebase.UPTIME);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
public void onSurfaceRequested(@NonNull SurfaceRequest request, @NonNull Timebase timebase) {
|
||||
@ -466,7 +467,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
mSequentialExecutor.execute(() -> onSurfaceRequestedInternal(request, timebase));
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
@NonNull
|
||||
@ -474,7 +474,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
return mMediaSpec;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
@NonNull
|
||||
@ -482,7 +481,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
return mStreamInfo;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
public void onSourceStateChanged(@NonNull SourceState newState) {
|
||||
@ -950,17 +948,17 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
surfaceRequest.setTransformationInfoListener(mSequentialExecutor,
|
||||
(transformationInfo) -> mSurfaceTransformationInfo = transformationInfo);
|
||||
Size surfaceSize = surfaceRequest.getResolution();
|
||||
// Fetch and cache nearest camcorder profile, if one exists.
|
||||
// Fetch and cache nearest encoder profiles, if one exists.
|
||||
VideoCapabilities capabilities =
|
||||
VideoCapabilities.from(surfaceRequest.getCamera().getCameraInfo());
|
||||
Quality highestSupportedQuality = capabilities.findHighestSupportedQualityFor(surfaceSize);
|
||||
Logger.d(TAG, "Using supported quality of " + highestSupportedQuality
|
||||
+ " for surface size " + surfaceSize);
|
||||
if (highestSupportedQuality != Quality.NONE) {
|
||||
mResolvedCamcorderProfile = capabilities.getProfile(highestSupportedQuality);
|
||||
if (mResolvedCamcorderProfile == null) {
|
||||
mResolvedEncoderProfiles = capabilities.getProfiles(highestSupportedQuality);
|
||||
if (mResolvedEncoderProfiles == null) {
|
||||
throw new AssertionError("Camera advertised available quality but did not "
|
||||
+ "produce CamcorderProfile for advertised quality.");
|
||||
+ "produce EncoderProfiles for advertised quality.");
|
||||
}
|
||||
}
|
||||
setupVideo(surfaceRequest, videoSourceTimebase);
|
||||
@ -980,7 +978,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
ListenableFuture<Encoder> configureFuture =
|
||||
videoEncoderSession.configure(request, timebase, mediaSpec,
|
||||
mResolvedCamcorderProfile);
|
||||
mResolvedEncoderProfiles);
|
||||
mVideoEncoderSession = videoEncoderSession;
|
||||
Futures.addCallback(configureFuture, new FutureCallback<Encoder>() {
|
||||
@Override
|
||||
@ -1146,23 +1144,23 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
throws AudioSourceAccessException, InvalidConfigException {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
// Resolve the audio mime info
|
||||
MimeInfo audioMimeInfo = resolveAudioMimeInfo(mediaSpec, mResolvedCamcorderProfile);
|
||||
MimeInfo audioMimeInfo = resolveAudioMimeInfo(mediaSpec, mResolvedEncoderProfiles);
|
||||
Timebase audioSourceTimebase = Timebase.UPTIME;
|
||||
|
||||
// Select and create the audio source
|
||||
AudioSource.Settings audioSourceSettings =
|
||||
resolveAudioSourceSettings(audioMimeInfo, mediaSpec.getAudioSpec());
|
||||
AudioSettings audioSettings =
|
||||
resolveAudioSettings(audioMimeInfo, mediaSpec.getAudioSpec());
|
||||
if (mAudioSource != null) {
|
||||
releaseCurrentAudioSource();
|
||||
}
|
||||
// TODO: set audioSourceTimebase to AudioSource. Currently AudioSource hard code
|
||||
// AudioTimestamp.TIMEBASE_MONOTONIC.
|
||||
mAudioSource = setupAudioSource(recordingToStart, audioSourceSettings);
|
||||
mAudioSource = setupAudioSource(recordingToStart, audioSettings);
|
||||
Logger.d(TAG, String.format("Set up new audio source: 0x%x", mAudioSource.hashCode()));
|
||||
|
||||
// Select and create the audio encoder
|
||||
AudioEncoderConfig audioEncoderConfig = resolveAudioEncoderConfig(audioMimeInfo,
|
||||
audioSourceTimebase, audioSourceSettings, mediaSpec.getAudioSpec());
|
||||
audioSourceTimebase, audioSettings, mediaSpec.getAudioSpec());
|
||||
mAudioEncoder = mAudioEncoderFactory.createEncoder(mExecutor, audioEncoderConfig);
|
||||
|
||||
// Connect the audio source to the audio encoder
|
||||
@ -1176,10 +1174,9 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
@NonNull
|
||||
private AudioSource setupAudioSource(@NonNull RecordingRecord recordingToStart,
|
||||
@NonNull AudioSource.Settings audioSourceSettings)
|
||||
@NonNull AudioSettings audioSettings)
|
||||
throws AudioSourceAccessException {
|
||||
return recordingToStart.performOneTimeAudioSourceCreation(audioSourceSettings,
|
||||
AUDIO_EXECUTOR);
|
||||
return recordingToStart.performOneTimeAudioSourceCreation(audioSettings, AUDIO_EXECUTOR);
|
||||
}
|
||||
|
||||
private void releaseCurrentAudioSource() {
|
||||
@ -1286,7 +1283,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
int muxerOutputFormat =
|
||||
mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO
|
||||
? supportedMuxerFormatOrDefaultFrom(mResolvedCamcorderProfile,
|
||||
? supportedMuxerFormatOrDefaultFrom(mResolvedEncoderProfiles,
|
||||
MediaSpec.outputFormatToMuxerFormat(
|
||||
MEDIA_SPEC_DEFAULT.getOutputFormat()))
|
||||
: MediaSpec.outputFormatToMuxerFormat(mediaSpec.getOutputFormat());
|
||||
@ -1535,7 +1532,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
mAudioSource.setAudioSourceCallback(mSequentialExecutor,
|
||||
new AudioSource.AudioSourceCallback() {
|
||||
@Override
|
||||
public void onSilenced(boolean silenced) {
|
||||
public void onSilenceStateChanged(boolean silenced) {
|
||||
if (mIsAudioSourceSilenced != silenced) {
|
||||
mIsAudioSourceSilenced = silenced;
|
||||
mAudioErrorCause = silenced ? new IllegalStateException(
|
||||
@ -2460,9 +2457,9 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
}
|
||||
|
||||
private static int supportedMuxerFormatOrDefaultFrom(
|
||||
@Nullable CamcorderProfileProxy profileProxy, int defaultMuxerFormat) {
|
||||
if (profileProxy != null) {
|
||||
switch (profileProxy.getFileFormat()) {
|
||||
@Nullable VideoValidatedEncoderProfilesProxy profilesProxy, int defaultMuxerFormat) {
|
||||
if (profilesProxy != null) {
|
||||
switch (profilesProxy.getRecommendedFileFormat()) {
|
||||
case MediaRecorder.OutputFormat.MPEG_4:
|
||||
return android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
|
||||
case MediaRecorder.OutputFormat.WEBM:
|
||||
@ -2576,7 +2573,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
@NonNull
|
||||
@Override
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
public AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
public AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor executor)
|
||||
throws AudioSourceAccessException {
|
||||
// Context will only be held in local scope of the supplier so it will
|
||||
@ -2593,7 +2590,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
@NonNull
|
||||
@Override
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
public AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
public AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor executor)
|
||||
throws AudioSourceAccessException {
|
||||
// Do not set (or retain) context on other API levels
|
||||
@ -2708,7 +2705,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
@NonNull
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
AudioSource performOneTimeAudioSourceCreation(
|
||||
@NonNull AudioSource.Settings settings, @NonNull Executor audioSourceExecutor)
|
||||
@NonNull AudioSettings settings, @NonNull Executor audioSourceExecutor)
|
||||
throws AudioSourceAccessException {
|
||||
if (!hasAudioEnabled()) {
|
||||
throw new AssertionError("Recording does not have audio enabled. Unable to create"
|
||||
@ -2822,7 +2819,7 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
private interface AudioSourceSupplier {
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
@NonNull
|
||||
AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor audioSourceExecutor) throws AudioSourceAccessException;
|
||||
}
|
||||
}
|
||||
@ -2972,7 +2969,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@NonNull
|
||||
Builder setVideoEncoderFactory(@NonNull EncoderFactory videoEncoderFactory) {
|
||||
@ -2980,7 +2976,6 @@ public final class SucklessRecorder implements VideoOutput {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@NonNull
|
||||
Builder setAudioEncoderFactory(@NonNull EncoderFactory audioEncoderFactory) {
|
||||
|
@ -210,7 +210,6 @@ public final class SucklessRecording implements AutoCloseable {
|
||||
* {@link PendingRecording#start(Executor, Consumer)}. Once the active recording is
|
||||
* stopped, a {@link VideoRecordEvent.Finalize} event will be sent to the listener.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RestrictTo(LIBRARY_GROUP)
|
||||
public boolean isClosed() {
|
||||
|
@ -25,6 +25,7 @@ import static androidx.camera.video.internal.encoder.SucklessEncoderImpl.Interna
|
||||
import static androidx.camera.video.internal.encoder.SucklessEncoderImpl.InternalState.RELEASED;
|
||||
import static androidx.camera.video.internal.encoder.SucklessEncoderImpl.InternalState.STARTED;
|
||||
import static androidx.camera.video.internal.encoder.SucklessEncoderImpl.InternalState.STOPPING;
|
||||
import static androidx.core.util.Preconditions.checkState;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@ -241,10 +242,14 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
mMediaFormat = encoderConfig.toMediaFormat();
|
||||
Logger.d(mTag, "mMediaFormat = " + mMediaFormat);
|
||||
mMediaCodec = mEncoderFinder.findEncoder(mMediaFormat);
|
||||
clampVideoBitrateIfNotSupported(mMediaCodec.getCodecInfo(), mMediaFormat);
|
||||
Logger.i(mTag, "Selected encoder: " + mMediaCodec.getName());
|
||||
mEncoderInfo = createEncoderInfo(mIsVideoEncoder, mMediaCodec.getCodecInfo(),
|
||||
encoderConfig.getMimeType());
|
||||
if (mIsVideoEncoder) {
|
||||
VideoEncoderInfo videoEncoderInfo = (VideoEncoderInfo) mEncoderInfo;
|
||||
clampVideoBitrateIfNotSupported(videoEncoderInfo, mMediaFormat);
|
||||
}
|
||||
|
||||
try {
|
||||
reset();
|
||||
} catch (MediaCodec.CodecException e) {
|
||||
@ -263,41 +268,22 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* If video bitrate in MediaFormat is not supported by supplied MediaCodecInfo,
|
||||
* clamp bitrate in MediaFormat
|
||||
* Clamps the video bitrate in MediaFormat if the video bitrate is not supported by the
|
||||
* supplied VideoEncoderInfo.
|
||||
*
|
||||
* @param mediaCodecInfo MediaCodecInfo object
|
||||
* @param mediaFormat MediaFormat object
|
||||
* @param videoEncoderInfo VideoEncoderInfo object
|
||||
* @param mediaFormat MediaFormat object
|
||||
*/
|
||||
private void clampVideoBitrateIfNotSupported(@NonNull MediaCodecInfo mediaCodecInfo,
|
||||
private void clampVideoBitrateIfNotSupported(@NonNull VideoEncoderInfo videoEncoderInfo,
|
||||
@NonNull MediaFormat mediaFormat) {
|
||||
|
||||
if (!mediaCodecInfo.isEncoder() || !mIsVideoEncoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
|
||||
MediaCodecInfo.CodecCapabilities caps = mediaCodecInfo.getCapabilitiesForType(mime);
|
||||
Preconditions.checkArgument(caps != null,
|
||||
"MIME type is not supported");
|
||||
|
||||
if (mediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
|
||||
// We only handle video bitrate issues at this moment.
|
||||
MediaCodecInfo.VideoCapabilities videoCaps = caps.getVideoCapabilities();
|
||||
Preconditions.checkArgument(videoCaps != null,
|
||||
"Not video codec");
|
||||
|
||||
int origBitrate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);
|
||||
int newBitrate = videoCaps.getBitrateRange().clamp(origBitrate);
|
||||
if (origBitrate != newBitrate) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, newBitrate);
|
||||
Logger.d(mTag, "updated bitrate from " + origBitrate
|
||||
+ " to " + newBitrate);
|
||||
}
|
||||
checkState(mIsVideoEncoder);
|
||||
if (mediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
|
||||
int origBitrate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);
|
||||
int newBitrate = videoEncoderInfo.getSupportedBitrateRange().clamp(origBitrate);
|
||||
if (origBitrate != newBitrate) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, newBitrate);
|
||||
Logger.d(mTag, "updated bitrate from " + origBitrate + " to " + newBitrate);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.w(mTag, "Unexpected error while validating video bitrate", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,7 +388,7 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
mLastDataStopTimestamp = null;
|
||||
|
||||
final Range<Long> pauseRange = mActivePauseResumeTimeRanges.removeLast();
|
||||
Preconditions.checkState(
|
||||
checkState(
|
||||
pauseRange != null && pauseRange.getUpper() == NO_LIMIT_LONG,
|
||||
"There should be a \"pause\" before \"resume\"");
|
||||
final long pauseTimeUs = pauseRange.getLower();
|
||||
@ -1205,7 +1191,7 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
|
||||
// If adjusted time <= last sent time, the buffer should have been detected and
|
||||
// dropped in checkBufferInfo().
|
||||
Preconditions.checkState(adjustedTimeUs > mLastSentAdjustedTimeUs);
|
||||
checkState(adjustedTimeUs > mLastSentAdjustedTimeUs);
|
||||
if (DEBUG) {
|
||||
Logger.d(mTag, "Adjust bufferInfo.presentationTimeUs to "
|
||||
+ DebugUtils.readableUs(adjustedTimeUs));
|
||||
@ -1614,7 +1600,7 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
private void cancelInputBuffer(@NonNull ListenableFuture<InputBuffer> inputBufferFuture) {
|
||||
if (!inputBufferFuture.cancel(true)) {
|
||||
// Not able to cancel the future, need to cancel the input buffer as possible.
|
||||
Preconditions.checkState(inputBufferFuture.isDone());
|
||||
checkState(inputBufferFuture.isDone());
|
||||
try {
|
||||
inputBufferFuture.get().cancel();
|
||||
} catch (ExecutionException | InterruptedException | CancellationException e) {
|
||||
@ -1688,3 +1674,5 @@ public class SucklessEncoderImpl implements Encoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
20
app/src/main/java/androidx/camera/video/originals/README.md
Normal file
20
app/src/main/java/androidx/camera/video/originals/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Update the modified CameraX files to a new upstream version:
|
||||
|
||||
Create the `new` folder if needed:
|
||||
```
|
||||
mkdir -p new
|
||||
```
|
||||
|
||||
Put new CameraX files from upstream in the `new` folder.
|
||||
|
||||
Perform the 3 way merge:
|
||||
```
|
||||
./merge.sh
|
||||
```
|
||||
|
||||
If new files are created in the current directory, they contains conflicts. Resolve them then move them to the right location.
|
||||
|
||||
Finally, update the base:
|
||||
```
|
||||
./update.sh
|
||||
```
|
@ -25,6 +25,7 @@ import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.P
|
||||
import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.RELEASED;
|
||||
import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.STARTED;
|
||||
import static androidx.camera.video.internal.encoder.EncoderImpl.InternalState.STOPPING;
|
||||
import static androidx.core.util.Preconditions.checkState;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@ -240,10 +241,14 @@ public class EncoderImpl implements Encoder {
|
||||
mMediaFormat = encoderConfig.toMediaFormat();
|
||||
Logger.d(mTag, "mMediaFormat = " + mMediaFormat);
|
||||
mMediaCodec = mEncoderFinder.findEncoder(mMediaFormat);
|
||||
clampVideoBitrateIfNotSupported(mMediaCodec.getCodecInfo(), mMediaFormat);
|
||||
Logger.i(mTag, "Selected encoder: " + mMediaCodec.getName());
|
||||
mEncoderInfo = createEncoderInfo(mIsVideoEncoder, mMediaCodec.getCodecInfo(),
|
||||
encoderConfig.getMimeType());
|
||||
if (mIsVideoEncoder) {
|
||||
VideoEncoderInfo videoEncoderInfo = (VideoEncoderInfo) mEncoderInfo;
|
||||
clampVideoBitrateIfNotSupported(videoEncoderInfo, mMediaFormat);
|
||||
}
|
||||
|
||||
try {
|
||||
reset();
|
||||
} catch (MediaCodec.CodecException e) {
|
||||
@ -262,41 +267,22 @@ public class EncoderImpl implements Encoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* If video bitrate in MediaFormat is not supported by supplied MediaCodecInfo,
|
||||
* clamp bitrate in MediaFormat
|
||||
* Clamps the video bitrate in MediaFormat if the video bitrate is not supported by the
|
||||
* supplied VideoEncoderInfo.
|
||||
*
|
||||
* @param mediaCodecInfo MediaCodecInfo object
|
||||
* @param mediaFormat MediaFormat object
|
||||
* @param videoEncoderInfo VideoEncoderInfo object
|
||||
* @param mediaFormat MediaFormat object
|
||||
*/
|
||||
private void clampVideoBitrateIfNotSupported(@NonNull MediaCodecInfo mediaCodecInfo,
|
||||
private void clampVideoBitrateIfNotSupported(@NonNull VideoEncoderInfo videoEncoderInfo,
|
||||
@NonNull MediaFormat mediaFormat) {
|
||||
|
||||
if (!mediaCodecInfo.isEncoder() || !mIsVideoEncoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
|
||||
MediaCodecInfo.CodecCapabilities caps = mediaCodecInfo.getCapabilitiesForType(mime);
|
||||
Preconditions.checkArgument(caps != null,
|
||||
"MIME type is not supported");
|
||||
|
||||
if (mediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
|
||||
// We only handle video bitrate issues at this moment.
|
||||
MediaCodecInfo.VideoCapabilities videoCaps = caps.getVideoCapabilities();
|
||||
Preconditions.checkArgument(videoCaps != null,
|
||||
"Not video codec");
|
||||
|
||||
int origBitrate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);
|
||||
int newBitrate = videoCaps.getBitrateRange().clamp(origBitrate);
|
||||
if (origBitrate != newBitrate) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, newBitrate);
|
||||
Logger.d(mTag, "updated bitrate from " + origBitrate
|
||||
+ " to " + newBitrate);
|
||||
}
|
||||
checkState(mIsVideoEncoder);
|
||||
if (mediaFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
|
||||
int origBitrate = mediaFormat.getInteger(MediaFormat.KEY_BIT_RATE);
|
||||
int newBitrate = videoEncoderInfo.getSupportedBitrateRange().clamp(origBitrate);
|
||||
if (origBitrate != newBitrate) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, newBitrate);
|
||||
Logger.d(mTag, "updated bitrate from " + origBitrate + " to " + newBitrate);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logger.w(mTag, "Unexpected error while validating video bitrate", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,7 +387,7 @@ public class EncoderImpl implements Encoder {
|
||||
mLastDataStopTimestamp = null;
|
||||
|
||||
final Range<Long> pauseRange = mActivePauseResumeTimeRanges.removeLast();
|
||||
Preconditions.checkState(
|
||||
checkState(
|
||||
pauseRange != null && pauseRange.getUpper() == NO_LIMIT_LONG,
|
||||
"There should be a \"pause\" before \"resume\"");
|
||||
final long pauseTimeUs = pauseRange.getLower();
|
||||
@ -1204,7 +1190,7 @@ public class EncoderImpl implements Encoder {
|
||||
|
||||
// If adjusted time <= last sent time, the buffer should have been detected and
|
||||
// dropped in checkBufferInfo().
|
||||
Preconditions.checkState(adjustedTimeUs > mLastSentAdjustedTimeUs);
|
||||
checkState(adjustedTimeUs > mLastSentAdjustedTimeUs);
|
||||
if (DEBUG) {
|
||||
Logger.d(mTag, "Adjust bufferInfo.presentationTimeUs to "
|
||||
+ DebugUtils.readableUs(adjustedTimeUs));
|
||||
@ -1622,7 +1608,7 @@ public class EncoderImpl implements Encoder {
|
||||
private void cancelInputBuffer(@NonNull ListenableFuture<InputBuffer> inputBufferFuture) {
|
||||
if (!inputBufferFuture.cancel(true)) {
|
||||
// Not able to cancel the future, need to cancel the input buffer as possible.
|
||||
Preconditions.checkState(inputBufferFuture.isDone());
|
||||
checkState(inputBufferFuture.isDone());
|
||||
try {
|
||||
inputBufferFuture.get().cancel();
|
||||
} catch (ExecutionException | InterruptedException | CancellationException e) {
|
||||
@ -1697,3 +1683,4 @@ public class EncoderImpl implements Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ import java.util.concurrent.Executor;
|
||||
* <p>A pending recording allows for configuration of a recording before it is started. Once a
|
||||
* pending recording is started with {@link #start(Executor, Consumer)}, any changes to the pending
|
||||
* recording will not affect the actual recording; any modifications to the recording will need
|
||||
* to occur through the controls of the {@link SucklessRecording} class returned by
|
||||
* to occur through the controls of the {@link Recording} class returned by
|
||||
* {@link #start(Executor, Consumer)}.
|
||||
*
|
||||
* <p>A pending recording can be created using one of the {@link Recorder} methods for starting a
|
||||
@ -106,7 +106,7 @@ public final class PendingRecording {
|
||||
* Enables audio to be recorded for this recording.
|
||||
*
|
||||
* <p>This method must be called prior to {@link #start(Executor, Consumer)} to enable audio
|
||||
* in the recording. If this method is not called, the {@link SucklessRecording} generated by
|
||||
* in the recording. If this method is not called, the {@link Recording} generated by
|
||||
* {@link #start(Executor, Consumer)} will not contain audio, and
|
||||
* {@link AudioStats#getAudioState()} will always return
|
||||
* {@link AudioStats#AUDIO_STATE_DISABLED} for all {@link RecordingStats} send to the listener
|
||||
@ -124,7 +124,7 @@ public final class PendingRecording {
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
@NonNull
|
||||
public SucklessPendingRecording withAudioEnabled() {
|
||||
public PendingRecording withAudioEnabled() {
|
||||
// Check permissions and throw a security exception if RECORD_AUDIO is not granted.
|
||||
if (PermissionChecker.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO)
|
||||
== PermissionChecker.PERMISSION_DENIED) {
|
||||
@ -143,9 +143,9 @@ public final class PendingRecording {
|
||||
* <p>Only a single recording can be active at a time, so if another recording is active,
|
||||
* this will throw an {@link IllegalStateException}.
|
||||
*
|
||||
* <p>If there are no errors starting the recording, the returned {@link SucklessRecording}
|
||||
* can be used to {@link SucklessRecording#pause() pause}, {@link SucklessRecording#resume() resume},
|
||||
* or {@link SucklessRecording#stop() stop} the recording.
|
||||
* <p>If there are no errors starting the recording, the returned {@link Recording}
|
||||
* can be used to {@link Recording#pause() pause}, {@link Recording#resume() resume},
|
||||
* or {@link Recording#stop() stop} the recording.
|
||||
*
|
||||
* <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
|
||||
* be the first event sent to the provided event listener.
|
||||
@ -153,9 +153,9 @@ public final class PendingRecording {
|
||||
* <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
|
||||
* will be the first event sent to the provided listener, and information about the error can
|
||||
* be found in that event's {@link VideoRecordEvent.Finalize#getError()} method. The returned
|
||||
* {@link SucklessRecording} will be in a finalized state, and all controls will be no-ops.
|
||||
* {@link Recording} will be in a finalized state, and all controls will be no-ops.
|
||||
*
|
||||
* <p>If the returned {@link SucklessRecording} is garbage collected, the recording will be
|
||||
* <p>If the returned {@link Recording} is garbage collected, the recording will be
|
||||
* automatically stopped. A reference to the active recording must be maintained as long as
|
||||
* the recording needs to be active.
|
||||
*
|
||||
@ -166,7 +166,7 @@ public final class PendingRecording {
|
||||
*/
|
||||
@NonNull
|
||||
@CheckResult
|
||||
public SucklessRecording start(
|
||||
public Recording start(
|
||||
@NonNull Executor listenerExecutor,
|
||||
@NonNull Consumer<VideoRecordEvent> listener) {
|
||||
Preconditions.checkNotNull(listenerExecutor, "Listener Executor can't be null.");
|
@ -29,7 +29,7 @@ import static androidx.camera.video.VideoRecordEvent.Finalize.VideoRecordError;
|
||||
import static androidx.camera.video.internal.DebugUtils.readableUs;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioEncoderConfig;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioMimeInfo;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioSourceSettings;
|
||||
import static androidx.camera.video.internal.config.AudioConfigUtil.resolveAudioSettings;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
@ -59,7 +59,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.camera.core.AspectRatio;
|
||||
import androidx.camera.core.Logger;
|
||||
import androidx.camera.core.SurfaceRequest;
|
||||
import androidx.camera.core.impl.CamcorderProfileProxy;
|
||||
import androidx.camera.core.impl.MutableStateObservable;
|
||||
import androidx.camera.core.impl.Observable;
|
||||
import androidx.camera.core.impl.StateObservable;
|
||||
@ -72,8 +71,10 @@ import androidx.camera.core.impl.utils.futures.Futures;
|
||||
import androidx.camera.core.internal.utils.ArrayRingBuffer;
|
||||
import androidx.camera.core.internal.utils.RingBuffer;
|
||||
import androidx.camera.video.StreamInfo.StreamState;
|
||||
import androidx.camera.video.internal.AudioSource;
|
||||
import androidx.camera.video.internal.AudioSourceAccessException;
|
||||
import androidx.camera.video.internal.VideoValidatedEncoderProfilesProxy;
|
||||
import androidx.camera.video.internal.audio.AudioSettings;
|
||||
import androidx.camera.video.internal.audio.AudioSource;
|
||||
import androidx.camera.video.internal.audio.AudioSourceAccessException;
|
||||
import androidx.camera.video.internal.compat.Api26Impl;
|
||||
import androidx.camera.video.internal.compat.quirk.DeactivateEncoderSurfaceBeforeStopEncoderQuirk;
|
||||
import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
|
||||
@ -346,7 +347,7 @@ public final class Recorder implements VideoOutput {
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
boolean mInProgressRecordingStopping = false;
|
||||
private SurfaceRequest.TransformationInfo mSurfaceTransformationInfo = null;
|
||||
private CamcorderProfileProxy mResolvedCamcorderProfile = null;
|
||||
private VideoValidatedEncoderProfilesProxy mResolvedEncoderProfiles = null;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
final List<ListenableFuture<Void>> mEncodingFutures = new ArrayList<>();
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
|
||||
@ -456,7 +457,6 @@ public final class Recorder implements VideoOutput {
|
||||
onSurfaceRequested(request, Timebase.UPTIME);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
public void onSurfaceRequested(@NonNull SurfaceRequest request, @NonNull Timebase timebase) {
|
||||
@ -470,7 +470,6 @@ public final class Recorder implements VideoOutput {
|
||||
mSequentialExecutor.execute(() -> onSurfaceRequestedInternal(request, timebase));
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
@NonNull
|
||||
@ -478,7 +477,6 @@ public final class Recorder implements VideoOutput {
|
||||
return mMediaSpec;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
@NonNull
|
||||
@ -486,7 +484,6 @@ public final class Recorder implements VideoOutput {
|
||||
return mStreamInfo;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@Override
|
||||
public void onSourceStateChanged(@NonNull SourceState newState) {
|
||||
@ -1041,17 +1038,17 @@ public final class Recorder implements VideoOutput {
|
||||
surfaceRequest.setTransformationInfoListener(mSequentialExecutor,
|
||||
(transformationInfo) -> mSurfaceTransformationInfo = transformationInfo);
|
||||
Size surfaceSize = surfaceRequest.getResolution();
|
||||
// Fetch and cache nearest camcorder profile, if one exists.
|
||||
// Fetch and cache nearest encoder profiles, if one exists.
|
||||
VideoCapabilities capabilities =
|
||||
VideoCapabilities.from(surfaceRequest.getCamera().getCameraInfo());
|
||||
Quality highestSupportedQuality = capabilities.findHighestSupportedQualityFor(surfaceSize);
|
||||
Logger.d(TAG, "Using supported quality of " + highestSupportedQuality
|
||||
+ " for surface size " + surfaceSize);
|
||||
if (highestSupportedQuality != Quality.NONE) {
|
||||
mResolvedCamcorderProfile = capabilities.getProfile(highestSupportedQuality);
|
||||
if (mResolvedCamcorderProfile == null) {
|
||||
mResolvedEncoderProfiles = capabilities.getProfiles(highestSupportedQuality);
|
||||
if (mResolvedEncoderProfiles == null) {
|
||||
throw new AssertionError("Camera advertised available quality but did not "
|
||||
+ "produce CamcorderProfile for advertised quality.");
|
||||
+ "produce EncoderProfiles for advertised quality.");
|
||||
}
|
||||
}
|
||||
setupVideo(surfaceRequest, videoSourceTimebase);
|
||||
@ -1071,7 +1068,7 @@ public final class Recorder implements VideoOutput {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
ListenableFuture<Encoder> configureFuture =
|
||||
videoEncoderSession.configure(request, timebase, mediaSpec,
|
||||
mResolvedCamcorderProfile);
|
||||
mResolvedEncoderProfiles);
|
||||
mVideoEncoderSession = videoEncoderSession;
|
||||
Futures.addCallback(configureFuture, new FutureCallback<Encoder>() {
|
||||
@Override
|
||||
@ -1237,23 +1234,23 @@ public final class Recorder implements VideoOutput {
|
||||
throws AudioSourceAccessException, InvalidConfigException {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
// Resolve the audio mime info
|
||||
MimeInfo audioMimeInfo = resolveAudioMimeInfo(mediaSpec, mResolvedCamcorderProfile);
|
||||
MimeInfo audioMimeInfo = resolveAudioMimeInfo(mediaSpec, mResolvedEncoderProfiles);
|
||||
Timebase audioSourceTimebase = Timebase.UPTIME;
|
||||
|
||||
// Select and create the audio source
|
||||
AudioSource.Settings audioSourceSettings =
|
||||
resolveAudioSourceSettings(audioMimeInfo, mediaSpec.getAudioSpec());
|
||||
AudioSettings audioSettings =
|
||||
resolveAudioSettings(audioMimeInfo, mediaSpec.getAudioSpec());
|
||||
if (mAudioSource != null) {
|
||||
releaseCurrentAudioSource();
|
||||
}
|
||||
// TODO: set audioSourceTimebase to AudioSource. Currently AudioSource hard code
|
||||
// AudioTimestamp.TIMEBASE_MONOTONIC.
|
||||
mAudioSource = setupAudioSource(recordingToStart, audioSourceSettings);
|
||||
mAudioSource = setupAudioSource(recordingToStart, audioSettings);
|
||||
Logger.d(TAG, String.format("Set up new audio source: 0x%x", mAudioSource.hashCode()));
|
||||
|
||||
// Select and create the audio encoder
|
||||
AudioEncoderConfig audioEncoderConfig = resolveAudioEncoderConfig(audioMimeInfo,
|
||||
audioSourceTimebase, audioSourceSettings, mediaSpec.getAudioSpec());
|
||||
audioSourceTimebase, audioSettings, mediaSpec.getAudioSpec());
|
||||
mAudioEncoder = mAudioEncoderFactory.createEncoder(mExecutor, audioEncoderConfig);
|
||||
|
||||
// Connect the audio source to the audio encoder
|
||||
@ -1267,10 +1264,9 @@ public final class Recorder implements VideoOutput {
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
@NonNull
|
||||
private AudioSource setupAudioSource(@NonNull RecordingRecord recordingToStart,
|
||||
@NonNull AudioSource.Settings audioSourceSettings)
|
||||
@NonNull AudioSettings audioSettings)
|
||||
throws AudioSourceAccessException {
|
||||
return recordingToStart.performOneTimeAudioSourceCreation(audioSourceSettings,
|
||||
AUDIO_EXECUTOR);
|
||||
return recordingToStart.performOneTimeAudioSourceCreation(audioSettings, AUDIO_EXECUTOR);
|
||||
}
|
||||
|
||||
private void releaseCurrentAudioSource() {
|
||||
@ -1377,7 +1373,7 @@ public final class Recorder implements VideoOutput {
|
||||
MediaSpec mediaSpec = getObservableData(mMediaSpec);
|
||||
int muxerOutputFormat =
|
||||
mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO
|
||||
? supportedMuxerFormatOrDefaultFrom(mResolvedCamcorderProfile,
|
||||
? supportedMuxerFormatOrDefaultFrom(mResolvedEncoderProfiles,
|
||||
MediaSpec.outputFormatToMuxerFormat(
|
||||
MEDIA_SPEC_DEFAULT.getOutputFormat()))
|
||||
: MediaSpec.outputFormatToMuxerFormat(mediaSpec.getOutputFormat());
|
||||
@ -1641,7 +1637,7 @@ public final class Recorder implements VideoOutput {
|
||||
mAudioSource.setAudioSourceCallback(mSequentialExecutor,
|
||||
new AudioSource.AudioSourceCallback() {
|
||||
@Override
|
||||
public void onSilenced(boolean silenced) {
|
||||
public void onSilenceStateChanged(boolean silenced) {
|
||||
if (mIsAudioSourceSilenced != silenced) {
|
||||
mIsAudioSourceSilenced = silenced;
|
||||
mAudioErrorCause = silenced ? new IllegalStateException(
|
||||
@ -2566,9 +2562,9 @@ public final class Recorder implements VideoOutput {
|
||||
}
|
||||
|
||||
private static int supportedMuxerFormatOrDefaultFrom(
|
||||
@Nullable CamcorderProfileProxy profileProxy, int defaultMuxerFormat) {
|
||||
if (profileProxy != null) {
|
||||
switch (profileProxy.getFileFormat()) {
|
||||
@Nullable VideoValidatedEncoderProfilesProxy profilesProxy, int defaultMuxerFormat) {
|
||||
if (profilesProxy != null) {
|
||||
switch (profilesProxy.getRecommendedFileFormat()) {
|
||||
case MediaRecorder.OutputFormat.MPEG_4:
|
||||
return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
|
||||
case MediaRecorder.OutputFormat.WEBM:
|
||||
@ -2738,7 +2734,7 @@ public final class Recorder implements VideoOutput {
|
||||
@NonNull
|
||||
@Override
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
public AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
public AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor executor)
|
||||
throws AudioSourceAccessException {
|
||||
// Context will only be held in local scope of the supplier so it will
|
||||
@ -2755,7 +2751,7 @@ public final class Recorder implements VideoOutput {
|
||||
@NonNull
|
||||
@Override
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
public AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
public AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor executor)
|
||||
throws AudioSourceAccessException {
|
||||
// Do not set (or retain) context on other API levels
|
||||
@ -2870,7 +2866,7 @@ public final class Recorder implements VideoOutput {
|
||||
@NonNull
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
AudioSource performOneTimeAudioSourceCreation(
|
||||
@NonNull AudioSource.Settings settings, @NonNull Executor audioSourceExecutor)
|
||||
@NonNull AudioSettings settings, @NonNull Executor audioSourceExecutor)
|
||||
throws AudioSourceAccessException {
|
||||
if (!hasAudioEnabled()) {
|
||||
throw new AssertionError("Recording does not have audio enabled. Unable to create"
|
||||
@ -2984,7 +2980,7 @@ public final class Recorder implements VideoOutput {
|
||||
private interface AudioSourceSupplier {
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
@NonNull
|
||||
AudioSource get(@NonNull AudioSource.Settings settings,
|
||||
AudioSource get(@NonNull AudioSettings settings,
|
||||
@NonNull Executor audioSourceExecutor) throws AudioSourceAccessException;
|
||||
}
|
||||
}
|
||||
@ -3134,7 +3130,6 @@ public final class Recorder implements VideoOutput {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@NonNull
|
||||
Builder setVideoEncoderFactory(@NonNull EncoderFactory videoEncoderFactory) {
|
||||
@ -3142,7 +3137,6 @@ public final class Recorder implements VideoOutput {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
@NonNull
|
||||
Builder setAudioEncoderFactory(@NonNull EncoderFactory audioEncoderFactory) {
|
@ -69,22 +69,22 @@ public final class Recording implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link SucklessRecording} from a {@link PendingRecording} and recording ID.
|
||||
* Creates an {@link Recording} from a {@link PendingRecording} and recording ID.
|
||||
*
|
||||
* <p>The recording ID is expected to be unique to the recorder that generated the pending
|
||||
* recording.
|
||||
*/
|
||||
@NonNull
|
||||
static SucklessRecording from(@NonNull SucklessPendingRecording pendingRecording, long recordingId) {
|
||||
static Recording from(@NonNull PendingRecording pendingRecording, long recordingId) {
|
||||
Preconditions.checkNotNull(pendingRecording, "The given PendingRecording cannot be null.");
|
||||
return new SucklessRecording(pendingRecording.getRecorder(),
|
||||
return new Recording(pendingRecording.getRecorder(),
|
||||
recordingId,
|
||||
pendingRecording.getOutputOptions(),
|
||||
/*finalizedOnCreation=*/false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link SucklessRecording} from a {@link PendingRecording} and recording ID in a
|
||||
* Creates an {@link Recording} from a {@link PendingRecording} and recording ID in a
|
||||
* finalized state.
|
||||
*
|
||||
* <p>This can be used if there was an error setting up the active recording and it would not
|
||||
@ -94,10 +94,10 @@ public final class Recording implements AutoCloseable {
|
||||
* recording.
|
||||
*/
|
||||
@NonNull
|
||||
static SucklessRecording createFinalizedFrom(@NonNull SucklessPendingRecording pendingRecording,
|
||||
long recordingId) {
|
||||
static Recording createFinalizedFrom(@NonNull PendingRecording pendingRecording,
|
||||
long recordingId) {
|
||||
Preconditions.checkNotNull(pendingRecording, "The given PendingRecording cannot be null.");
|
||||
return new SucklessRecording(pendingRecording.getRecorder(),
|
||||
return new Recording(pendingRecording.getRecorder(),
|
||||
recordingId,
|
||||
pendingRecording.getOutputOptions(),
|
||||
/*finalizedOnCreation=*/true);
|
||||
@ -207,7 +207,6 @@ public final class Recording implements AutoCloseable {
|
||||
* {@link PendingRecording#start(Executor, Consumer)}. Once the active recording is
|
||||
* stopped, a {@link VideoRecordEvent.Finalize} event will be sent to the listener.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RestrictTo(LIBRARY_GROUP)
|
||||
public boolean isClosed() {
|
6
app/src/main/java/androidx/camera/video/originals/merge.sh
Executable file
6
app/src/main/java/androidx/camera/video/originals/merge.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
for i in "PendingRecording" "Recording" "Recorder"; do
|
||||
diff3 -m ../Suckless$i.java base/$i.java new/$i.java > Suckless$i.java && mv Suckless$i.java ..
|
||||
done
|
||||
diff3 -m ../internal/encoder/SucklessEncoderImpl.java base/EncoderImpl.java new/EncoderImpl.java > SucklessEncoderImpl.java && mv SucklessEncoderImpl.java ../internal/encoder/SucklessEncoderImpl.java
|
3
app/src/main/java/androidx/camera/video/originals/update.sh
Executable file
3
app/src/main/java/androidx/camera/video/originals/update.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -r base && mv new base
|
Loading…
Reference in New Issue
Block a user