public final class

Recording

extends java.lang.Object

implements java.lang.AutoCloseable

 java.lang.Object

↳androidx.camera.video.Recording

Gradle dependencies

compile group: 'androidx.camera', name: 'camera-video', version: '1.5.0-alpha01'

  • groupId: androidx.camera
  • artifactId: camera-video
  • version: 1.5.0-alpha01

Artifact androidx.camera:camera-video:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Provides controls for the currently active recording.

An active recording is created by starting a pending recording with PendingRecording.start(Executor, Consumer). If there are no errors starting the recording, upon creation, an active recording will provide controls to pause, resume or stop a recording. If errors occur while starting the recording, the active recording will be instantiated in a finalized state, and all controls will be no-ops. The state of the recording can be observed by the video record event listener provided to PendingRecording.start(Executor, Consumer) when starting the recording.

Either Recording.stop() or Recording.close() can be called when it is desired to stop the recording. If Recording.stop() or Recording.close() are not called on this object before it is no longer referenced, it will be automatically stopped at a future point in time when the object is garbage collected, and no new recordings can be started from the same Recorder that generated the object until that occurs.

Summary

Methods
public voidclose()

Close this recording.

protected voidfinalize()

public booleanisClosed()

Returns whether the recording is closed.

public booleanisPersistent()

Returns whether this recording is a persistent recording.

public voidmute(boolean muted)

Mutes or un-mutes the current recording.

public voidpause()

Pauses the current recording if active.

public voidresume()

Resumes the current recording if paused.

public voidstop()

Stops the recording, as if calling Recording.close().

from java.lang.Objectclone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public boolean isPersistent()

Returns whether this recording is a persistent recording.

A persistent recording will only be stopped by explicitly calling of Recording.stop() and will ignore the lifecycle events or source state changes. Users are responsible of stopping a persistent recording.

Returns:

true if the recording is a persistent recording, otherwise false.

public void pause()

Pauses the current recording if active.

Successful pausing of a recording will generate a VideoRecordEvent.Pause event which will be sent to the listener passed to PendingRecording.start(Executor, Consumer).

If the recording has already been paused or has been finalized internally, this is a no-op.

public void resume()

Resumes the current recording if paused.

Successful resuming of a recording will generate a VideoRecordEvent.Resume event which will be sent to the listener passed to PendingRecording.start(Executor, Consumer).

If the recording is active or has been finalized internally, this is a no-op.

public void stop()

Stops the recording, as if calling Recording.close().

This method is equivalent to calling Recording.close().

public void mute(boolean muted)

Mutes or un-mutes the current recording.

The output file will contain an audio track even the whole recording is muted. Create a recording without calling PendingRecording.withAudioEnabled() to record a file with no audio track. To set the initial mute state of the recording, use PendingRecording.

Muting or unmuting a recording that isn't created PendingRecording.withAudioEnabled() with audio enabled is no-op.

Parameters:

muted: mutes the recording if true, un-mutes otherwise.

public void close()

Close this recording.

Once Recording.stop() or close() called, all methods for controlling the state of this recording besides Recording.stop() or close() will throw an java.lang.IllegalStateException.

Once an active recording has been closed, the next recording can be started with PendingRecording.start(Executor, Consumer).

This method is idempotent; if the recording has already been closed or has been finalized internally, calling Recording.stop() or close() is a no-op.

This method is invoked automatically on active recording instances managed by the try-with-resources statement.

protected void finalize()

public boolean isClosed()

Returns whether the recording is closed.

The returned value does not reflect the state of the recording; it only reflects whether Recording.stop() or Recording.close() was called on this object.

The state of the recording should be checked from the listener passed to PendingRecording.start(Executor, Consumer). Once the active recording is stopped, a VideoRecordEvent.Finalize event will be sent to the listener.

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.video;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.camera.core.impl.utils.CloseGuardHelper;
import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Provides controls for the currently active recording.
 *
 * <p>An active recording is created by starting a pending recording with
 * {@link PendingRecording#start(Executor, Consumer)}. If there are no errors starting the
 * recording, upon creation, an active recording will provide controls to pause, resume or stop a
 * recording. If errors occur while starting the recording, the active recording will be
 * instantiated in a {@link VideoRecordEvent.Finalize finalized} state, and all controls will be
 * no-ops. The state of the recording can be observed by the video record event listener provided
 * to {@link PendingRecording#start(Executor, Consumer)} when starting the recording.
 *
 * <p>Either {@link #stop()} or {@link #close()} can be called when it is desired to
 * stop the recording. If {@link #stop()} or {@link #close()} are not called on this object
 * before it is no longer referenced, it will be automatically stopped at a future point in time
 * when the object is garbage collected, and no new recordings can be started from the same
 * {@link Recorder} that generated the object until that occurs.
 */
public final class Recording implements AutoCloseable {

    // Indicates the recording has been explicitly stopped by users.
    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
    private final Recorder mRecorder;
    private final long mRecordingId;
    private final OutputOptions mOutputOptions;
    private final boolean mIsPersistent;
    private final CloseGuardHelper mCloseGuard = CloseGuardHelper.create();

    Recording(@NonNull Recorder recorder, long recordingId, @NonNull OutputOptions options,
            boolean isPersistent, boolean finalizedOnCreation) {
        mRecorder = recorder;
        mRecordingId = recordingId;
        mOutputOptions = options;
        mIsPersistent = isPersistent;

        if (finalizedOnCreation) {
            mIsClosed.set(true);
        } else {
            mCloseGuard.open("stop");
        }
    }

    /**
     * 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 Recording from(@NonNull PendingRecording pendingRecording, long recordingId) {
        Preconditions.checkNotNull(pendingRecording, "The given PendingRecording cannot be null.");
        return new Recording(pendingRecording.getRecorder(),
                recordingId,
                pendingRecording.getOutputOptions(),
                pendingRecording.isPersistent(),
                /*finalizedOnCreation=*/false);
    }

    /**
     * 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
     * be able to be started.
     *
     * <p>The recording ID is expected to be unique to the recorder that generated the pending
     * recording.
     */
    @NonNull
    static Recording createFinalizedFrom(@NonNull PendingRecording pendingRecording,
            long recordingId) {
        Preconditions.checkNotNull(pendingRecording, "The given PendingRecording cannot be null.");
        return new Recording(pendingRecording.getRecorder(),
                recordingId,
                pendingRecording.getOutputOptions(),
                pendingRecording.isPersistent(),
                /*finalizedOnCreation=*/true);
    }

    @NonNull
    OutputOptions getOutputOptions() {
        return mOutputOptions;
    }

    /**
     * Returns whether this recording is a persistent recording.
     *
     * <p>A persistent recording will only be stopped by explicitly calling of
     * {@link Recording#stop()} and will ignore the lifecycle events or source state changes.
     * Users are responsible of stopping a persistent recording.
     *
     * @return {@code true} if the recording is a persistent recording, otherwise {@code false}.
     */
    @ExperimentalPersistentRecording
    public boolean isPersistent() {
        return mIsPersistent;
    }

    /**
     * Pauses the current recording if active.
     *
     * <p>Successful pausing of a recording will generate a {@link VideoRecordEvent.Pause} event
     * which will be sent to the listener passed to
     * {@link PendingRecording#start(Executor, Consumer)}.
     *
     * <p>If the recording has already been paused or has been finalized internally, this is a
     * no-op.
     *
     * @throws IllegalStateException if the recording has been stopped with
     * {@link #close()} or {@link #stop()}.
     */
    public void pause() {
        if (mIsClosed.get()) {
            throw new IllegalStateException("The recording has been stopped.");
        }
        mRecorder.pause(this);
    }

    /**
     * Resumes the current recording if paused.
     *
     * <p>Successful resuming of a recording will generate a {@link VideoRecordEvent.Resume} event
     * which will be sent to the listener passed to
     * {@link PendingRecording#start(Executor, Consumer)}.
     *
     * <p>If the recording is active or has been finalized internally, this is a no-op.
     *
     * @throws IllegalStateException if the recording has been stopped with
     * {@link #close()} or {@link #stop()}.
     */
    public void resume() {
        if (mIsClosed.get()) {
            throw new IllegalStateException("The recording has been stopped.");
        }
        mRecorder.resume(this);
    }

    /**
     * Stops the recording, as if calling {@link #close()}.
     *
     * <p>This method is equivalent to calling {@link #close()}.
     */
    public void stop() {
        close();
    }

    /**
     * Mutes or un-mutes the current recording.
     *
     * <p>The output file will contain an audio track even the whole recording is muted. Create a
     * recording without calling {@link PendingRecording#withAudioEnabled()} to record a file
     * with no audio track. To set the initial mute state of the recording, use
     * {@link PendingRecording#withAudioEnabled(boolean)}.
     *
     * <p>Muting or unmuting a recording that isn't created
     * {@link PendingRecording#withAudioEnabled()} with audio enabled is no-op.
     *
     * @param muted mutes the recording if {@code true}, un-mutes otherwise.
     */
    public void mute(boolean muted) {
        if (mIsClosed.get()) {
            throw new IllegalStateException("The recording has been stopped.");
        }
        mRecorder.mute(this, muted);
    }

    /**
     * Close this recording.
     *
     * <p>Once {@link #stop()} or {@code close()} called, all methods for controlling the state of
     * this recording besides {@link #stop()} or {@code close()} will throw an
     * {@link IllegalStateException}.
     *
     * <p>Once an active recording has been closed, the next recording can be started with
     * {@link PendingRecording#start(Executor, Consumer)}.
     *
     * <p>This method is idempotent; if the recording has already been closed or has been
     * finalized internally, calling {@link #stop()} or {@code close()} is a no-op.
     *
     * <p>This method is invoked automatically on active recording instances managed by the {@code
     * try-with-resources} statement.
     */
    @Override
    public void close() {
        stopWithError(VideoRecordEvent.Finalize.ERROR_NONE, /*errorCause=*/ null);
    }

    @Override
    @SuppressWarnings("GenericException") // super.finalize() throws Throwable
    protected void finalize() throws Throwable {
        try {
            mCloseGuard.warnIfOpen();
            stopWithError(VideoRecordEvent.Finalize.ERROR_RECORDING_GARBAGE_COLLECTED,
                    new RuntimeException("Recording stopped due to being garbage collected."));
        } finally {
            super.finalize();
        }
    }

    /** Returns the recording ID which is unique to the recorder that generated this recording. */
    long getRecordingId() {
        return mRecordingId;
    }

    /**
     * Returns whether the recording is closed.
     *
     * <p>The returned value does not reflect the state of the recording; it only reflects
     * whether {@link #stop()} or {@link #close()} was called on this object.
     *
     * <p>The state of the recording should be checked from the listener passed to
     * {@link PendingRecording#start(Executor, Consumer)}. Once the active recording is
     * stopped, a {@link VideoRecordEvent.Finalize} event will be sent to the listener.
     *
     */
    @RestrictTo(LIBRARY_GROUP)
    public boolean isClosed() {
        return mIsClosed.get();
    }

    private void stopWithError(@VideoRecordEvent.Finalize.VideoRecordError int error,
            @Nullable Throwable errorCause) {
        mCloseGuard.close();
        if (mIsClosed.getAndSet(true)) {
            return;
        }
        mRecorder.stop(this, error, errorCause);
    }
}