Instantiate an ImageRenderer
Instantiate an ImageRenderer and add it to the list returned by DefaultRenderersFactory.buildRenderers. Add Renderer.MessageType.MSG_SET_IMAGE_OUTPUT and ExoPlayer.setImageOutput to enable setting a custom ImageRenderer.imageOutput. Add ImageRenderer.handleMessage to process messages sent to the renderer. PiperOrigin-RevId: 581962451
This commit is contained in:
parent
330713f687
commit
2fb5695a82
@ -14,6 +14,10 @@
|
||||
preparing and caching the period, selecting tracks and loading the data
|
||||
on the period. Apps are able to control the preload progress by
|
||||
implementing `PreloadMediaSource.PreloadControl`.
|
||||
* Add `ExoPlayer.setImageOutput` that allows apps to set
|
||||
`ImageRenderer.ImageOutput`.
|
||||
* `DefaultRenderersFactory` now provides an `ImageRenderer` to the player
|
||||
by default with null `ImageOutput` and `ImageDecoder.Factory.DEFAULT`.
|
||||
* Transformer:
|
||||
* Add support for flattening H.265/HEVC SEF slow motion videos.
|
||||
* Track Selection:
|
||||
|
@ -30,6 +30,8 @@ import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.audio.AudioSink;
|
||||
import androidx.media3.exoplayer.audio.DefaultAudioSink;
|
||||
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
||||
import androidx.media3.exoplayer.image.ImageDecoder;
|
||||
import androidx.media3.exoplayer.image.ImageRenderer;
|
||||
import androidx.media3.exoplayer.mediacodec.DefaultMediaCodecAdapterFactory;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector;
|
||||
@ -307,6 +309,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
|
||||
extensionRendererMode,
|
||||
renderersList);
|
||||
buildCameraMotionRenderers(context, extensionRendererMode, renderersList);
|
||||
buildImageRenderers(renderersList);
|
||||
buildMiscellaneousRenderers(context, eventHandler, extensionRendererMode, renderersList);
|
||||
return renderersList.toArray(new Renderer[0]);
|
||||
}
|
||||
@ -594,6 +597,18 @@ public class DefaultRenderersFactory implements RenderersFactory {
|
||||
out.add(new CameraMotionRenderer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds image renderers for use by the player.
|
||||
*
|
||||
* <p>The {@link ImageRenderer} is built with {@code ImageOutput} set to null and {@link
|
||||
* ImageDecoder.Factory} set to {@code ImageDecoder.Factory.DEFAULT} by default.
|
||||
*
|
||||
* @param out An array to which the built renderers should be appended.
|
||||
*/
|
||||
protected void buildImageRenderers(ArrayList<Renderer> out) {
|
||||
out.add(new ImageRenderer(ImageDecoder.Factory.DEFAULT, /* imageOutput= */ null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds any miscellaneous renderers used by the player.
|
||||
*
|
||||
|
@ -56,6 +56,7 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
|
||||
import androidx.media3.exoplayer.audio.AudioSink;
|
||||
import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer;
|
||||
import androidx.media3.exoplayer.image.ImageOutput;
|
||||
import androidx.media3.exoplayer.metadata.MetadataRenderer;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
@ -1821,4 +1822,12 @@ public interface ExoPlayer extends Player {
|
||||
*/
|
||||
@UnstableApi
|
||||
boolean isTunnelingEnabled();
|
||||
|
||||
/**
|
||||
* Sets the {@link ImageOutput} where rendered images will be forwarded.
|
||||
*
|
||||
* @param imageOutput The {@link ImageOutput}.
|
||||
*/
|
||||
@UnstableApi
|
||||
void setImageOutput(ImageOutput imageOutput);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package androidx.media3.exoplayer;
|
||||
|
||||
import static androidx.media3.common.C.TRACK_TYPE_AUDIO;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_IMAGE;
|
||||
import static androidx.media3.common.C.TRACK_TYPE_VIDEO;
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
@ -27,6 +28,7 @@ import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_SESSION_ID;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_AUX_EFFECT_INFO;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_CAMERA_MOTION_LISTENER;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_IMAGE_OUTPUT;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_PREFERRED_AUDIO_DEVICE;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE;
|
||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED;
|
||||
@ -98,6 +100,7 @@ import androidx.media3.exoplayer.analytics.MediaMetricsListener;
|
||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.audio.AudioSink;
|
||||
import androidx.media3.exoplayer.image.ImageOutput;
|
||||
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
||||
import androidx.media3.exoplayer.source.MaskingMediaSource;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
@ -1833,6 +1836,12 @@ import java.util.concurrent.TimeoutException;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageOutput(ImageOutput imageOutput) {
|
||||
verifyApplicationThread();
|
||||
sendRendererMessage(TRACK_TYPE_IMAGE, MSG_SET_IMAGE_OUTPUT, imageOutput);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Calling deprecated methods.
|
||||
/* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
|
||||
this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread;
|
||||
|
@ -33,6 +33,7 @@ import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.image.ImageOutput;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||
import androidx.media3.exoplayer.source.SampleStream;
|
||||
@ -94,9 +95,9 @@ public interface Renderer extends PlayerMessage.Target {
|
||||
* #MSG_SET_SCALING_MODE}, {@link #MSG_SET_CHANGE_FRAME_RATE_STRATEGY}, {@link
|
||||
* #MSG_SET_AUX_EFFECT_INFO}, {@link #MSG_SET_VIDEO_FRAME_METADATA_LISTENER}, {@link
|
||||
* #MSG_SET_CAMERA_MOTION_LISTENER}, {@link #MSG_SET_SKIP_SILENCE_ENABLED}, {@link
|
||||
* #MSG_SET_AUDIO_SESSION_ID}, {@link #MSG_SET_WAKEUP_LISTENER}, {@link #MSG_SET_VIDEO_EFFECTS} or
|
||||
* {@link #MSG_SET_VIDEO_OUTPUT_RESOLUTION}. May also be an app-defined value (see {@link
|
||||
* #MSG_CUSTOM_BASE}).
|
||||
* #MSG_SET_AUDIO_SESSION_ID}, {@link #MSG_SET_WAKEUP_LISTENER}, {@link #MSG_SET_VIDEO_EFFECTS},
|
||||
* {@link #MSG_SET_VIDEO_OUTPUT_RESOLUTION} or {@link #MSG_SET_IMAGE_OUTPUT}. May also be an
|
||||
* app-defined value (see {@link #MSG_CUSTOM_BASE}).
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@ -116,7 +117,8 @@ public interface Renderer extends PlayerMessage.Target {
|
||||
MSG_SET_AUDIO_SESSION_ID,
|
||||
MSG_SET_WAKEUP_LISTENER,
|
||||
MSG_SET_VIDEO_EFFECTS,
|
||||
MSG_SET_VIDEO_OUTPUT_RESOLUTION
|
||||
MSG_SET_VIDEO_OUTPUT_RESOLUTION,
|
||||
MSG_SET_IMAGE_OUTPUT
|
||||
})
|
||||
public @interface MessageType {}
|
||||
|
||||
@ -244,6 +246,12 @@ public interface Renderer extends PlayerMessage.Target {
|
||||
*/
|
||||
int MSG_SET_VIDEO_OUTPUT_RESOLUTION = 14;
|
||||
|
||||
/**
|
||||
* The type of message that can be passed to an image renderer to set a desired image output. The
|
||||
* message payload should be an {@link ImageOutput}.
|
||||
*/
|
||||
int MSG_SET_IMAGE_OUTPUT = 15;
|
||||
|
||||
/**
|
||||
* Applications or extensions may define custom {@code MSG_*} constants that can be passed to
|
||||
* renderers. These custom constants must be greater than or equal to this value.
|
||||
|
@ -51,6 +51,7 @@ import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||
import androidx.media3.exoplayer.image.ImageOutput;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||
@ -1326,6 +1327,12 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
return player.isTunnelingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageOutput(ImageOutput imageOutput) {
|
||||
blockUntilConstructorFinished();
|
||||
player.setImageOutput(imageOutput);
|
||||
}
|
||||
|
||||
/* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) {
|
||||
blockUntilConstructorFinished();
|
||||
player.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread);
|
||||
|
@ -23,6 +23,20 @@ import androidx.media3.common.util.UnstableApi;
|
||||
@UnstableApi
|
||||
public interface ImageOutput {
|
||||
|
||||
/** A no-op implementation of ImageOutput. */
|
||||
ImageOutput NO_OP =
|
||||
new ImageOutput() {
|
||||
@Override
|
||||
public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
// Do nothing.
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when an there is a new image available.
|
||||
*
|
||||
|
@ -84,7 +84,6 @@ public final class ImageRenderer extends BaseRenderer {
|
||||
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 3;
|
||||
|
||||
private final ImageDecoder.Factory decoderFactory;
|
||||
private final ImageOutput imageOutput;
|
||||
private final DecoderInputBuffer flagsOnlyBuffer;
|
||||
private final LongArrayQueue offsetQueue;
|
||||
|
||||
@ -96,6 +95,7 @@ public final class ImageRenderer extends BaseRenderer {
|
||||
private @Nullable ImageDecoder decoder;
|
||||
private @Nullable DecoderInputBuffer inputBuffer;
|
||||
private @Nullable ImageOutputBuffer outputBuffer;
|
||||
private ImageOutput imageOutput;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
@ -103,12 +103,12 @@ public final class ImageRenderer extends BaseRenderer {
|
||||
* @param decoderFactory A {@link ImageDecoder.Factory} that supplies a decoder depending on the
|
||||
* format provided.
|
||||
* @param imageOutput The rendering component to send the {@link Bitmap} and rendering commands
|
||||
* to.
|
||||
* to, or {@code null} if no bitmap output is required.
|
||||
*/
|
||||
public ImageRenderer(ImageDecoder.Factory decoderFactory, ImageOutput imageOutput) {
|
||||
public ImageRenderer(ImageDecoder.Factory decoderFactory, @Nullable ImageOutput imageOutput) {
|
||||
super(C.TRACK_TYPE_IMAGE);
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.imageOutput = imageOutput;
|
||||
this.imageOutput = getImageOutput(imageOutput);
|
||||
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
|
||||
offsetQueue = new LongArrayQueue();
|
||||
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||
@ -223,6 +223,20 @@ public final class ImageRenderer extends BaseRenderer {
|
||||
releaseDecoderResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(@MessageType int messageType, @Nullable Object message)
|
||||
throws ExoPlaybackException {
|
||||
switch (messageType) {
|
||||
case MSG_SET_IMAGE_OUTPUT:
|
||||
@Nullable ImageOutput imageOutput =
|
||||
message instanceof ImageOutput ? (ImageOutput) message : null;
|
||||
setImageOutput(imageOutput);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(messageType, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to dequeue an output buffer from the decoder and, if successful and permitted to,
|
||||
* renders it.
|
||||
@ -371,4 +385,12 @@ public final class ImageRenderer extends BaseRenderer {
|
||||
decoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setImageOutput(@Nullable ImageOutput imageOutput) {
|
||||
this.imageOutput = getImageOutput(imageOutput);
|
||||
}
|
||||
|
||||
private static ImageOutput getImageOutput(@Nullable ImageOutput imageOutput) {
|
||||
return imageOutput == null ? ImageOutput.NO_OP : imageOutput;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import androidx.media3.exoplayer.Renderer;
|
||||
import androidx.media3.exoplayer.SeekParameters;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||
import androidx.media3.exoplayer.image.ImageOutput;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||
@ -406,4 +407,9 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer {
|
||||
public boolean isTunnelingEnabled() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageOutput(ImageOutput imageOutput) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user