VideoSink.java
/*
* Copyright 2023 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.media3.exoplayer.video;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.graphics.Bitmap;
import android.view.Surface;
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.VideoSize;
import androidx.media3.common.util.TimestampIterator;
import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.Executor;
/** A sink that consumes decoded video frames. */
@UnstableApi
public interface VideoSink {
/** Thrown by {@link VideoSink} implementations. */
final class VideoSinkException extends Exception {
/**
* The {@link Format} of the frames set to the {@link VideoSink} when this exception occurred.
*/
public final Format format;
/** Creates a new instance. */
public VideoSinkException(Throwable cause, Format format) {
super(cause);
this.format = format;
}
}
/** Listener for {@link VideoSink} events. */
interface Listener {
/** Called when the sink renderers the first frame. */
void onFirstFrameRendered(VideoSink videoSink);
/** Called when the sink dropped a frame. */
void onFrameDropped(VideoSink videoSink);
/**
* Called before a frame is rendered for the first time since setting the surface, and each time
* there's a change in the size, rotation or pixel aspect ratio of the video being rendered.
*/
void onVideoSizeChanged(VideoSink videoSink, VideoSize videoSize);
/** Called when the {@link VideoSink} encountered an error. */
void onError(VideoSink videoSink, VideoSinkException videoSinkException);
/** A no-op listener implementation. */
Listener NO_OP =
new Listener() {
@Override
public void onFirstFrameRendered(VideoSink videoSink) {}
@Override
public void onFrameDropped(VideoSink videoSink) {}
@Override
public void onVideoSizeChanged(VideoSink videoSink, VideoSize videoSize) {}
@Override
public void onError(VideoSink videoSink, VideoSinkException videoSinkException) {}
};
}
/**
* Specifies how the input frames are made available to the video sink. One of {@link
* #INPUT_TYPE_SURFACE} or {@link #INPUT_TYPE_BITMAP}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({INPUT_TYPE_SURFACE, INPUT_TYPE_BITMAP})
@interface InputType {}
/** Input frames come from a {@link #getInputSurface surface}. */
int INPUT_TYPE_SURFACE = 1;
/** Input frames come from a {@link Bitmap}. */
int INPUT_TYPE_BITMAP = 2;
/**
* Sets a {@link Listener} on this sink. Callbacks are triggered on the supplied {@link Executor}.
*
* @param listener The {@link Listener}.
* @param executor The {@link Executor} to dispatch the callbacks.
*/
void setListener(Listener listener, Executor executor);
/**
* Flushes the video sink.
*
* <p>After calling this method, any frames stored inside the video sink are discarded.
*/
void flush();
/** Whether the video sink is able to immediately render media from the current position. */
boolean isReady();
/**
* Whether all queued video frames have been rendered, including the frame marked as last buffer.
*/
boolean isEnded();
/**
* Whether frames could be dropped from the sink's {@linkplain #getInputSurface() input surface}.
*/
boolean isFrameDropAllowedOnInput();
/** Returns the input {@link Surface} where the video sink consumes input frames from. */
Surface getInputSurface();
/** Sets the playback speed. */
void setPlaybackSpeed(@FloatRange(from = 0, fromInclusive = false) float speed);
/**
* Informs the video sink that a new input stream will be queued.
*
* @param inputType The {@link InputType} of the stream.
* @param format The {@link Format} of the stream.
*/
void registerInputStream(@InputType int inputType, Format format);
/**
* Informs the video sink that a frame will be queued to its {@linkplain #getInputSurface() input
* surface}.
*
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
* @param isLastFrame Whether this is the last frame of the video stream.
* @return a release timestamp, in nanoseconds, that should be associated when releasing this
* frame, or {@link C#TIME_UNSET} if the sink was not able to register the frame and the
* caller must try again later.
*/
long registerInputFrame(long framePresentationTimeUs, boolean isLastFrame);
/**
* Provides an input {@link Bitmap} to the video sink.
*
* @param inputBitmap The {@link Bitmap} queued to the video sink.
* @param timestampIterator The times within the current stream that the bitmap should be shown
* at. The timestamps should be monotonically increasing.
* @return Whether the bitmap was queued successfully. A {@code false} value indicates the caller
* must try again later.
*/
boolean queueBitmap(Bitmap inputBitmap, TimestampIterator timestampIterator);
/**
* Incrementally renders processed video frames.
*
* @param positionUs The current playback position, in microseconds.
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
* taken approximately at the time the playback position was {@code positionUs}.
* @throws VideoSinkException If an error occurs during rendering.
*/
void render(long positionUs, long elapsedRealtimeUs) throws VideoSinkException;
}