Commit ae47a138 by olly Committed by Oliver Woodman

Delete unused surfacecapturer package

PiperOrigin-RevId: 366131005
parent af926deb
/*
* Copyright (C) 2018 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 com.google.android.exoplayer2.video.surfacecapturer;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.view.PixelCopy;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.util.EGLSurfaceTexture;
/**
* A {@link SurfaceCapturer} implementation that uses {@link PixelCopy} APIs to perform image copy
* from a {@link SurfaceTexture} into a {@link Bitmap}.
*/
@RequiresApi(24)
/* package */ final class PixelCopySurfaceCapturerV24 extends SurfaceCapturer
implements EGLSurfaceTexture.TextureImageListener, PixelCopy.OnPixelCopyFinishedListener {
/** Exception to be thrown if there is some problem capturing images from the surface. */
public static final class SurfaceCapturerException extends Exception {
/**
* One of the {@link PixelCopy} {@code ERROR_*} values return from the {@link
* PixelCopy#request(Surface, Bitmap, PixelCopy.OnPixelCopyFinishedListener, Handler)}
*/
public final int errorCode;
/**
* Constructs a new instance.
*
* @param message The error message.
* @param errorCode The error code.
*/
public SurfaceCapturerException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
}
private final EGLSurfaceTexture eglSurfaceTexture;
private final Handler handler;
private final Surface decoderSurface;
@Nullable private Bitmap bitmap;
@SuppressWarnings("nullness")
/* package */ PixelCopySurfaceCapturerV24(
Callback callback, int outputWidth, int outputHeight, Handler imageRenderingHandler) {
super(callback, outputWidth, outputHeight);
this.handler = imageRenderingHandler;
eglSurfaceTexture = new EGLSurfaceTexture(imageRenderingHandler, /* callback= */ this);
eglSurfaceTexture.init(EGLSurfaceTexture.SECURE_MODE_NONE);
decoderSurface = new Surface(eglSurfaceTexture.getSurfaceTexture());
}
@Override
public Surface getSurface() {
return decoderSurface;
}
@Override
public void release() {
eglSurfaceTexture.release();
decoderSurface.release();
}
/** @see SurfaceTexture#setDefaultBufferSize(int, int) */
public void setDefaultSurfaceTextureBufferSize(int width, int height) {
eglSurfaceTexture.getSurfaceTexture().setDefaultBufferSize(width, height);
}
// TextureImageListener
@Override
public void onFrameAvailable() {
bitmap = Bitmap.createBitmap(getOutputWidth(), getOutputHeight(), Bitmap.Config.ARGB_8888);
PixelCopy.request(decoderSurface, bitmap, this, handler);
}
// OnPixelCopyFinishedListener
@Override
public void onPixelCopyFinished(int copyResult) {
Callback callback = getCallback();
if (copyResult == PixelCopy.SUCCESS && bitmap != null) {
callback.onSurfaceCaptured(bitmap);
} else {
callback.onSurfaceCaptureError(
new SurfaceCapturerException("Couldn't copy image from surface", copyResult));
}
}
}
/*
* Copyright (C) 2018 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 com.google.android.exoplayer2.video.surfacecapturer;
import android.content.Context;
import android.media.MediaCodec;
import android.view.Surface;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import java.nio.ByteBuffer;
/**
* Decodes and renders video using {@link MediaCodec}.
*
* <p>This video renderer will only render the first frame after position reset (seeking), or after
* being re-enabled.
*/
public class SingleFrameMediaCodecVideoRenderer extends MediaCodecVideoRenderer {
private static final String TAG = "SingleFrameMediaCodecVideoRenderer";
private boolean hasRenderedFirstFrame;
@Nullable private Surface surface;
public SingleFrameMediaCodecVideoRenderer(
Context context, MediaCodecSelector mediaCodecSelector) {
super(context, mediaCodecSelector);
}
@Override
public String getName() {
return TAG;
}
@Override
public void handleMessage(int messageType, @Nullable Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) {
this.surface = (Surface) message;
}
super.handleMessage(messageType, message);
}
@Override
protected void onEnabled(boolean joining, boolean mayRenderStartOfStream)
throws ExoPlaybackException {
hasRenderedFirstFrame = false;
super.onEnabled(joining, mayRenderStartOfStream);
}
@Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
hasRenderedFirstFrame = false;
super.onPositionReset(positionUs, joining);
}
@Override
protected boolean processOutputBuffer(
long positionUs,
long elapsedRealtimeUs,
@Nullable MediaCodecAdapter codec,
@Nullable ByteBuffer buffer,
int bufferIndex,
int bufferFlags,
int sampleCount,
long bufferPresentationTimeUs,
boolean isDecodeOnlyBuffer,
boolean isLastBuffer,
Format format)
throws ExoPlaybackException {
Assertions.checkNotNull(codec); // Can not render video without codec
long presentationTimeUs = bufferPresentationTimeUs - getOutputStreamOffsetUs();
if (isDecodeOnlyBuffer && !isLastBuffer) {
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
return true;
}
if (surface == null || hasRenderedFirstFrame) {
return false;
}
hasRenderedFirstFrame = true;
if (Util.SDK_INT >= 21) {
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, System.nanoTime());
} else {
renderOutputBuffer(codec, bufferIndex, presentationTimeUs);
}
return true;
}
}
/*
* Copyright (C) 2018 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 com.google.android.exoplayer2.video.surfacecapturer;
import android.graphics.Bitmap;
import android.view.Surface;
/**
* A surface capturer, which captures image drawn into its surface as bitmaps.
*
* <p>It constructs a {@link Surface}, which can be used as the output surface for an image producer
* to draw images to. As images are being drawn into this surface, this capturer will capture these
* images, and return them via {@link Callback}. The output images will have a fixed frame size of
* (width, height), and any image drawn into the surface will be stretched to fit this frame size.
*/
public abstract class SurfaceCapturer {
/** The callback to be notified of the image capturing result. */
public interface Callback {
/**
* Called when the surface capturer has been able to capture its surface into a {@link Bitmap}.
* This will happen whenever the producer updates the image on the wrapped surface.
*/
void onSurfaceCaptured(Bitmap bitmap);
/** Called when the surface capturer couldn't capture its surface due to an error. */
void onSurfaceCaptureError(Exception e);
}
/** The callback to be notified of the image capturing result. */
private final Callback callback;
/** The width of the output images. */
private final int outputWidth;
/** The height of the output images. */
private final int outputHeight;
/**
* Constructs a new instance.
*
* @param callback See {@link #callback}.
* @param outputWidth See {@link #outputWidth}.
* @param outputHeight See {@link #outputHeight}.
*/
protected SurfaceCapturer(Callback callback, int outputWidth, int outputHeight) {
this.callback = callback;
this.outputWidth = outputWidth;
this.outputHeight = outputHeight;
}
/** Returns the callback to be notified of the image capturing result. */
protected Callback getCallback() {
return callback;
}
/** Returns the width of the output images. */
public int getOutputWidth() {
return outputWidth;
}
/** Returns the height of the output images. */
public int getOutputHeight() {
return outputHeight;
}
/** Returns a {@link Surface} that image producers (camera, video codec etc...) can draw to. */
public abstract Surface getSurface();
/** Releases all kept resources. This instance cannot be used after this call. */
public abstract void release();
}
/*
* Copyright (C) 2018 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 com.google.android.exoplayer2.video.surfacecapturer;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/**
* A capturer that can capture the output of a video {@link SingleFrameMediaCodecVideoRenderer} into
* bitmaps.
*
* <p>Start by setting the output size via {@link #setOutputSize(int, int)}. The capturer will
* create a surface and set this up as the output for the video renderer.
*
* <p>Once the surface setup is done, the capturer will call {@link Callback#onOutputSizeSet(int,
* int)}. After this call, the capturer will capture all images rendered by the {@link Renderer},
* and deliver the captured bitmaps via {@link Callback#onSurfaceCaptured(Bitmap)}, or failure via
* {@link Callback#onSurfaceCaptureError(Exception)}. You can change the output image size at any
* time by calling {@link #setOutputSize(int, int)}.
*
* <p>When this capturer is no longer needed, you need to call {@link #release()} to release all
* resources it is holding. After this call returns, no callback will be called anymore.
*/
public final class VideoRendererOutputCapturer implements Handler.Callback {
/** The callback to be notified of the video image capturing result. */
public interface Callback extends SurfaceCapturer.Callback {
/** Called when output surface has been set properly. */
void onOutputSizeSet(int width, int height);
}
private static final int MSG_SET_OUTPUT = 1;
private static final int MSG_RELEASE = 2;
private final HandlerThread handlerThread;
private final Handler handler;
private final ExoPlayer exoPlayer;
private final EventDispatcher eventDispatcher;
private final Renderer renderer;
@Nullable private SurfaceCapturer surfaceCapturer;
private volatile boolean released;
/**
* Constructs a new instance.
*
* @param callback The callback to be notified of image capturing result.
* @param callbackHandler The {@link Handler} that the callback will be called on.
* @param videoRenderer A {@link SingleFrameMediaCodecVideoRenderer} that will be used to render
* video frames, which this capturer will capture.
* @param exoPlayer The {@link ExoPlayer} instance that is using the video renderer.
*/
public VideoRendererOutputCapturer(
Callback callback,
Handler callbackHandler,
SingleFrameMediaCodecVideoRenderer videoRenderer,
ExoPlayer exoPlayer) {
this.renderer = Assertions.checkNotNull(videoRenderer);
this.exoPlayer = Assertions.checkNotNull(exoPlayer);
this.eventDispatcher = new EventDispatcher(callbackHandler, callback);
// Use a separate thread to handle all operations in this class, because bitmap copying may take
// time and should not be handled on callbackHandler (which maybe run on main thread).
handlerThread = new HandlerThread("ExoPlayer:VideoRendererOutputCapturer");
handlerThread.start();
handler = Util.createHandler(handlerThread.getLooper(), /* callback= */ this);
}
/**
* Sets the size of the video renderer surface's with and height.
*
* <p>This call is performed asynchronously. Only after the {@code callback} receives a call to
* {@link Callback#onOutputSizeSet(int, int)}, the output frames will conform to the new size.
* Output frames before the callback will still conform to previous size.
*
* @param width The target width of the output frame.
* @param height The target height of the output frame.
*/
public void setOutputSize(int width, int height) {
handler.obtainMessage(MSG_SET_OUTPUT, width, height).sendToTarget();
}
/** Releases all kept resources. This instance cannot be used after this call. */
public synchronized void release() {
if (released) {
return;
}
// Some long running or waiting operations may run on the handler thread, so we try to
// interrupt the thread to end these operations quickly.
handlerThread.interrupt();
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessage(MSG_RELEASE);
boolean wasInterrupted = false;
while (!released) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
if (wasInterrupted) {
// Restore the interrupted status.
Thread.currentThread().interrupt();
}
}
// Handler.Callback
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SET_OUTPUT:
handleSetOutput(/* width= */ message.arg1, /* height= */ message.arg2);
return true;
case MSG_RELEASE:
handleRelease();
return true;
default:
return false;
}
}
// Internal methods
private void handleSetOutput(int width, int height) {
if (surfaceCapturer == null
|| surfaceCapturer.getOutputWidth() != width
|| surfaceCapturer.getOutputHeight() != height) {
updateSurfaceCapturer(width, height);
}
eventDispatcher.onOutputSizeSet(width, height);
}
private void updateSurfaceCapturer(int width, int height) {
SurfaceCapturer oldSurfaceCapturer = surfaceCapturer;
if (oldSurfaceCapturer != null) {
blockingSetRendererSurface(/* surface= */ null);
oldSurfaceCapturer.release();
}
surfaceCapturer = createSurfaceCapturer(width, height);
blockingSetRendererSurface(surfaceCapturer.getSurface());
}
private SurfaceCapturer createSurfaceCapturer(int width, int height) {
if (Util.SDK_INT >= 24) {
return createSurfaceCapturerV24(width, height);
} else {
// TODO: Use different SurfaceCapturer based on API level, flags etc...
throw new UnsupportedOperationException(
"Creating Surface Capturer is not supported for API < 24 yet");
}
}
@RequiresApi(24)
private SurfaceCapturer createSurfaceCapturerV24(int width, int height) {
return new PixelCopySurfaceCapturerV24(eventDispatcher, width, height, handler);
}
private void blockingSetRendererSurface(@Nullable Surface surface) {
try {
exoPlayer
.createMessage(renderer)
.setType(Renderer.MSG_SET_SURFACE)
.setPayload(surface)
.send()
.blockUntilDelivered();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void handleRelease() {
eventDispatcher.release();
handler.removeCallbacksAndMessages(null);
if (surfaceCapturer != null) {
surfaceCapturer.release();
}
handlerThread.quit();
synchronized (this) {
released = true;
notifyAll();
}
}
/** Dispatches {@link Callback} events using a callback handler. */
private static final class EventDispatcher implements Callback {
private final Handler callbackHandler;
private final Callback callback;
private volatile boolean released;
private EventDispatcher(Handler callbackHandler, Callback callback) {
this.callbackHandler = callbackHandler;
this.callback = callback;
}
@Override
public void onOutputSizeSet(int width, int height) {
callbackHandler.post(
() -> {
if (released) {
return;
}
callback.onOutputSizeSet(width, height);
});
}
@Override
public void onSurfaceCaptured(Bitmap bitmap) {
callbackHandler.post(
() -> {
if (released) {
return;
}
callback.onSurfaceCaptured(bitmap);
});
}
@Override
public void onSurfaceCaptureError(Exception exception) {
callbackHandler.post(
() -> {
if (released) {
return;
}
callback.onSurfaceCaptureError(exception);
});
}
/** Releases this event dispatcher. No event will be dispatched after this call. */
public void release() {
released = true;
}
}
}
/*
* Copyright (C) 2019 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.
*/
@NonNullApi
package com.google.android.exoplayer2.video.surfacecapturer;
import com.google.android.exoplayer2.util.NonNullApi;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment