Android: Update MediaCodecVideoDecoder to output VideoFrames

Bug: webrtc:9181
Change-Id: I7eba15167536e453956c511a056143b039f52b92
Reviewed-on: https://webrtc-review.googlesource.com/71664
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22988}
This commit is contained in:
Magnus Jedvert
2018-04-23 11:29:05 +02:00
committed by Commit Bot
parent 9b20677c4e
commit b9ac121598
2 changed files with 38 additions and 57 deletions

View File

@ -30,6 +30,8 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.VideoFrame;
// Java-side of peerconnection.cc:MediaCodecVideoDecoder. // Java-side of peerconnection.cc:MediaCodecVideoDecoder.
// This class is an implementation detail of the Java PeerConnection API. // This class is an implementation detail of the Java PeerConnection API.
@ -320,16 +322,16 @@ public class MediaCodecVideoDecoder {
} }
} }
// Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer output. // Pass null in |eglContext| to configure the codec for ByteBuffer output.
@CalledByNativeUnchecked @CalledByNativeUnchecked
private boolean initDecode(VideoCodecType type, int width, int height, private boolean initDecode(
@Nullable SurfaceTextureHelper surfaceTextureHelper) { VideoCodecType type, int width, int height, @Nullable EglBase.Context eglContext) {
if (mediaCodecThread != null) { if (mediaCodecThread != null) {
throw new RuntimeException("initDecode: Forgot to release()?"); throw new RuntimeException("initDecode: Forgot to release()?");
} }
String mime = null; String mime = null;
useSurface = (surfaceTextureHelper != null); useSurface = (eglContext != null);
String[] supportedCodecPrefixes = null; String[] supportedCodecPrefixes = null;
if (type == VideoCodecType.VIDEO_CODEC_VP8) { if (type == VideoCodecType.VIDEO_CODEC_VP8) {
mime = VP8_MIME_TYPE; mime = VP8_MIME_TYPE;
@ -359,9 +361,14 @@ public class MediaCodecVideoDecoder {
stride = width; stride = width;
sliceHeight = height; sliceHeight = height;
if (useSurface && surfaceTextureHelper != null) { if (useSurface) {
textureListener = new TextureListener(surfaceTextureHelper); @Nullable
surface = new Surface(surfaceTextureHelper.getSurfaceTexture()); final SurfaceTextureHelper surfaceTextureHelper =
SurfaceTextureHelper.create("Decoder SurfaceTextureHelper", eglContext);
if (surfaceTextureHelper != null) {
textureListener = new TextureListener(surfaceTextureHelper);
surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
}
} }
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
@ -567,8 +574,7 @@ public class MediaCodecVideoDecoder {
// Helper struct for dequeueTextureBuffer() below. // Helper struct for dequeueTextureBuffer() below.
private static class DecodedTextureBuffer { private static class DecodedTextureBuffer {
private final int textureID; private final VideoFrame.Buffer videoFrameBuffer;
private final float[] transformMatrix;
// Presentation timestamp returned in dequeueOutputBuffer call. // Presentation timestamp returned in dequeueOutputBuffer call.
private final long presentationTimeStampMs; private final long presentationTimeStampMs;
// C++ inputImage._timeStamp value for output frame. // C++ inputImage._timeStamp value for output frame.
@ -585,11 +591,9 @@ public class MediaCodecVideoDecoder {
// A DecodedTextureBuffer with zero |textureID| has special meaning and represents a frame // A DecodedTextureBuffer with zero |textureID| has special meaning and represents a frame
// that was dropped. // that was dropped.
public DecodedTextureBuffer(int textureID, float[] transformMatrix, public DecodedTextureBuffer(VideoFrame.Buffer videoFrameBuffer, long presentationTimeStampMs,
long presentationTimeStampMs, long timeStampMs, long ntpTimeStampMs, long decodeTimeMs, long timeStampMs, long ntpTimeStampMs, long decodeTimeMs, long frameDelay) {
long frameDelay) { this.videoFrameBuffer = videoFrameBuffer;
this.textureID = textureID;
this.transformMatrix = transformMatrix;
this.presentationTimeStampMs = presentationTimeStampMs; this.presentationTimeStampMs = presentationTimeStampMs;
this.timeStampMs = timeStampMs; this.timeStampMs = timeStampMs;
this.ntpTimeStampMs = ntpTimeStampMs; this.ntpTimeStampMs = ntpTimeStampMs;
@ -598,13 +602,8 @@ public class MediaCodecVideoDecoder {
} }
@CalledByNative("DecodedTextureBuffer") @CalledByNative("DecodedTextureBuffer")
int getTextureId() { VideoFrame.Buffer getVideoFrameBuffer() {
return textureID; return videoFrameBuffer;
}
@CalledByNative("DecodedTextureBuffer")
float[] getTransformMatrix() {
return transformMatrix;
} }
@CalledByNative("DecodedTextureBuffer") @CalledByNative("DecodedTextureBuffer")
@ -634,8 +633,7 @@ public class MediaCodecVideoDecoder {
} }
// Poll based texture listener. // Poll based texture listener.
private static class TextureListener private class TextureListener implements SurfaceTextureHelper.OnTextureFrameAvailableListener {
implements SurfaceTextureHelper.OnTextureFrameAvailableListener {
private final SurfaceTextureHelper surfaceTextureHelper; private final SurfaceTextureHelper surfaceTextureHelper;
// |newFrameLock| is used to synchronize arrival of new frames with wait()/notifyAll(). // |newFrameLock| is used to synchronize arrival of new frames with wait()/notifyAll().
private final Object newFrameLock = new Object(); private final Object newFrameLock = new Object();
@ -674,9 +672,10 @@ public class MediaCodecVideoDecoder {
throw new IllegalStateException("Already holding a texture."); throw new IllegalStateException("Already holding a texture.");
} }
// |timestampNs| is always zero on some Android versions. // |timestampNs| is always zero on some Android versions.
renderedBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix, final VideoFrame.Buffer buffer = surfaceTextureHelper.createTextureBuffer(
bufferToRender.presentationTimeStampMs, bufferToRender.timeStampMs, width, height, RendererCommon.convertMatrixToAndroidGraphicsMatrix(transformMatrix));
bufferToRender.ntpTimeStampMs, bufferToRender.decodeTimeMs, renderedBuffer = new DecodedTextureBuffer(buffer, bufferToRender.presentationTimeStampMs,
bufferToRender.timeStampMs, bufferToRender.ntpTimeStampMs, bufferToRender.decodeTimeMs,
SystemClock.elapsedRealtime() - bufferToRender.endDecodeTimeMs); SystemClock.elapsedRealtime() - bufferToRender.endDecodeTimeMs);
bufferToRender = null; bufferToRender = null;
newFrameLock.notifyAll(); newFrameLock.notifyAll();
@ -709,10 +708,11 @@ public class MediaCodecVideoDecoder {
surfaceTextureHelper.stopListening(); surfaceTextureHelper.stopListening();
synchronized (newFrameLock) { synchronized (newFrameLock) {
if (renderedBuffer != null) { if (renderedBuffer != null) {
surfaceTextureHelper.returnTextureFrame(); renderedBuffer.getVideoFrameBuffer().release();
renderedBuffer = null; renderedBuffer = null;
} }
} }
surfaceTextureHelper.dispose();
} }
} }
@ -844,8 +844,9 @@ public class MediaCodecVideoDecoder {
} }
mediaCodec.releaseOutputBuffer(droppedFrame.index, false /* render */); mediaCodec.releaseOutputBuffer(droppedFrame.index, false /* render */);
return new DecodedTextureBuffer(0, null, droppedFrame.presentationTimeStampMs, return new DecodedTextureBuffer(null /* videoFrameBuffer */,
droppedFrame.timeStampMs, droppedFrame.ntpTimeStampMs, droppedFrame.decodeTimeMs, droppedFrame.presentationTimeStampMs, droppedFrame.timeStampMs,
droppedFrame.ntpTimeStampMs, droppedFrame.decodeTimeMs,
SystemClock.elapsedRealtime() - droppedFrame.endDecodeTimeMs); SystemClock.elapsedRealtime() - droppedFrame.endDecodeTimeMs);
} }
return null; return null;

View File

@ -112,7 +112,6 @@ class MediaCodecVideoDecoder : public VideoDecoder, public rtc::MessageHandler {
bool use_surface_; bool use_surface_;
VideoCodec codec_; VideoCodec codec_;
I420BufferPool decoded_frame_pool_; I420BufferPool decoded_frame_pool_;
rtc::scoped_refptr<SurfaceTextureHelper> surface_texture_helper_;
DecodedImageCallback* callback_; DecodedImageCallback* callback_;
int frames_received_; // Number of frames received by decoder. int frames_received_; // Number of frames received by decoder.
int frames_decoded_; // Number of frames decoded by decoder. int frames_decoded_; // Number of frames decoded by decoder.
@ -224,26 +223,12 @@ int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() {
ResetVariables(); ResetVariables();
if (use_surface_) {
surface_texture_helper_ = SurfaceTextureHelper::create(
jni, "Decoder SurfaceTextureHelper",
JavaParamRef<jobject>(render_egl_context_));
if (!surface_texture_helper_) {
ALOGE << "Couldn't create SurfaceTextureHelper - fallback to SW codec";
sw_fallback_required_ = true;
return WEBRTC_VIDEO_CODEC_ERROR;
}
}
ScopedJavaLocalRef<jobject> j_video_codec_enum = ScopedJavaLocalRef<jobject> j_video_codec_enum =
Java_VideoCodecType_fromNativeIndex(jni, codecType_); Java_VideoCodecType_fromNativeIndex(jni, codecType_);
jobject j_surface_texture_helper = jobject j_egl_context = use_surface_ ? render_egl_context_ : nullptr;
use_surface_
? surface_texture_helper_->GetJavaSurfaceTextureHelper().obj()
: nullptr;
bool success = Java_MediaCodecVideoDecoder_initDecode( bool success = Java_MediaCodecVideoDecoder_initDecode(
jni, j_media_codec_video_decoder_, j_video_codec_enum, codec_.width, jni, j_media_codec_video_decoder_, j_video_codec_enum, codec_.width,
codec_.height, JavaParamRef<jobject>(j_surface_texture_helper)); codec_.height, JavaParamRef<jobject>(j_egl_context));
if (CheckException(jni) || !success) { if (CheckException(jni) || !success) {
ALOGE << "Codec initialization error - fallback to SW codec."; ALOGE << "Codec initialization error - fallback to SW codec.";
@ -325,7 +310,6 @@ int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() {
ScopedLocalRefFrame local_ref_frame(jni); ScopedLocalRefFrame local_ref_frame(jni);
input_buffers_.clear(); input_buffers_.clear();
Java_MediaCodecVideoDecoder_release(jni, j_media_codec_video_decoder_); Java_MediaCodecVideoDecoder_release(jni, j_media_codec_video_decoder_);
surface_texture_helper_ = nullptr;
inited_ = false; inited_ = false;
rtc::MessageQueueManager::Clear(this); rtc::MessageQueueManager::Clear(this);
if (CheckException(jni)) { if (CheckException(jni)) {
@ -604,18 +588,14 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs(
decode_time_ms = decode_time_ms =
Java_DecodedTextureBuffer_getDecodeTimeMs(jni, j_decoder_output_buffer); Java_DecodedTextureBuffer_getDecodeTimeMs(jni, j_decoder_output_buffer);
const int texture_id = ScopedJavaLocalRef<jobject> j_video_frame_buffer =
Java_DecodedTextureBuffer_getTextureId(jni, j_decoder_output_buffer); Java_DecodedTextureBuffer_getVideoFrameBuffer(jni,
if (texture_id != 0) { // |texture_id| == 0 represents a dropped frame. j_decoder_output_buffer);
ScopedJavaLocalRef<jfloatArray> j_transform_matrix = // |video_frame_buffer| == null represents a dropped frame.
Java_DecodedTextureBuffer_getTransformMatrix(jni, if (!j_video_frame_buffer.is_null()) {
j_decoder_output_buffer);
frame_delayed_ms = Java_DecodedTextureBuffer_getFrameDelayMs( frame_delayed_ms = Java_DecodedTextureBuffer_getFrameDelayMs(
jni, j_decoder_output_buffer); jni, j_decoder_output_buffer);
frame_buffer = AndroidVideoBuffer::Adopt(jni, j_video_frame_buffer);
// Create VideoFrameBuffer with native texture handle.
frame_buffer = surface_texture_helper_->CreateTextureFrame(
width, height, NativeHandleImpl(jni, texture_id, j_transform_matrix));
} else { } else {
EnableFrameLogOnWarning(); EnableFrameLogOnWarning();
} }