Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
9991f146
authored
Sep 03, 2021
by
kimvde
Committed by
Ian Baker
Sep 06, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add Open GL step to Transformer
PiperOrigin-RevId: 394708737
parent
dd19bc89
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
199 additions
and
46 deletions
library/common/src/main/java/com/google/android/exoplayer2/util/GlUtil.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java
library/common/src/main/java/com/google/android/exoplayer2/util/GlUtil.java
View file @
9991f146
...
@@ -223,6 +223,9 @@ public final class GlUtil {
...
@@ -223,6 +223,9 @@ public final class GlUtil {
}
}
}
}
/** Represents an unset texture ID. */
public
static
final
int
TEXTURE_ID_UNSET
=
-
1
;
/** Whether to throw a {@link GlException} in case of an OpenGL error. */
/** Whether to throw a {@link GlException} in case of an OpenGL error. */
public
static
boolean
glAssertionsEnabled
=
false
;
public
static
boolean
glAssertionsEnabled
=
false
;
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java
View file @
9991f146
...
@@ -343,8 +343,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -343,8 +343,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* be available until the previous has been released.
* be available until the previous has been released.
*/
*/
public
void
releaseOutputBuffer
()
{
public
void
releaseOutputBuffer
()
{
releaseOutputBuffer
(
/* render= */
false
);
}
/**
* Releases the current output buffer. If the {@link MediaCodec} was configured with an output
* surface, setting {@code render} to {@code true} will first send the buffer to the output
* surface. The surface will release the buffer back to the codec once it is no longer
* used/displayed.
*
* <p>This should be called after the buffer has been processed. The next output buffer will not
* be available until the previous has been released.
*/
public
void
releaseOutputBuffer
(
boolean
render
)
{
outputBuffer
=
null
;
outputBuffer
=
null
;
codec
.
releaseOutputBuffer
(
outputBufferIndex
,
/* render= */
false
);
codec
.
releaseOutputBuffer
(
outputBufferIndex
,
render
);
outputBufferIndex
=
C
.
INDEX_UNSET
;
outputBufferIndex
=
C
.
INDEX_UNSET
;
}
}
...
@@ -359,18 +372,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -359,18 +372,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
codec
.
release
();
codec
.
release
();
}
}
/** Returns {@code true} if a buffer is successfully obtained, rendered and released. */
public
boolean
maybeDequeueRenderAndReleaseOutputBuffer
()
{
if
(!
maybeDequeueOutputBuffer
())
{
return
false
;
}
codec
.
releaseOutputBuffer
(
outputBufferIndex
,
/* render= */
true
);
outputBuffer
=
null
;
outputBufferIndex
=
C
.
INDEX_UNSET
;
return
true
;
}
/**
/**
* Tries obtaining an output buffer and sets {@link #outputBuffer} to the obtained output buffer.
* Tries obtaining an output buffer and sets {@link #outputBuffer} to the obtained output buffer.
*
*
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java
View file @
9991f146
...
@@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
...
@@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
import
androidx.annotation.RequiresApi
;
import
androidx.annotation.RequiresApi
;
import
com.google.android.exoplayer2.BaseRenderer
;
import
com.google.android.exoplayer2.BaseRenderer
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.RendererCapabilities
;
import
com.google.android.exoplayer2.RendererCapabilities
;
import
com.google.android.exoplayer2.util.MediaClock
;
import
com.google.android.exoplayer2.util.MediaClock
;
...
@@ -75,7 +76,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
...
@@ -75,7 +76,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
}
}
@Override
@Override
protected
final
void
onStarted
()
{
protected
void
onStarted
()
throws
ExoPlaybackException
{
isRendererStarted
=
true
;
isRendererStarted
=
true
;
}
}
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java
View file @
9991f146
...
@@ -17,8 +17,18 @@
...
@@ -17,8 +17,18 @@
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
android.content.Context
;
import
android.graphics.SurfaceTexture
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.opengl.EGL14
;
import
android.opengl.EGLContext
;
import
android.opengl.EGLDisplay
;
import
android.opengl.EGLExt
;
import
android.opengl.EGLSurface
;
import
android.opengl.GLES20
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.RequiresApi
;
import
androidx.annotation.RequiresApi
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
...
@@ -28,34 +38,57 @@ import com.google.android.exoplayer2.FormatHolder;
...
@@ -28,34 +38,57 @@ import com.google.android.exoplayer2.FormatHolder;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.PlaybackException
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.source.SampleStream
;
import
com.google.android.exoplayer2.source.SampleStream
;
import
com.google.android.exoplayer2.util.GlUtil
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
@RequiresApi
(
18
)
@RequiresApi
(
18
)
/* package */
final
class
TransformerTranscodingVideoRenderer
extends
TransformerBaseRenderer
{
/* package */
final
class
TransformerTranscodingVideoRenderer
extends
TransformerBaseRenderer
{
static
{
GlUtil
.
glAssertionsEnabled
=
true
;
}
private
static
final
String
TAG
=
"TransformerTranscodingVideoRenderer"
;
private
static
final
String
TAG
=
"TransformerTranscodingVideoRenderer"
;
private
final
DecoderInputBuffer
decoderInputBuffer
;
private
final
Context
context
;
/** The format the encoder is configured to output, may differ from the actual output format. */
/** The format the encoder is configured to output, may differ from the actual output format. */
private
final
Format
encoderConfigurationOutputFormat
;
private
final
Format
encoderConfigurationOutputFormat
;
private
final
DecoderInputBuffer
decoderInputBuffer
;
private
final
float
[]
decoderTextureTransformMatrix
;
@Nullable
private
EGLDisplay
eglDisplay
;
@Nullable
private
EGLContext
eglContext
;
@Nullable
private
EGLSurface
eglSurface
;
private
int
decoderTextureId
;
@Nullable
private
SurfaceTexture
decoderSurfaceTexture
;
@Nullable
private
Surface
decoderSurface
;
@Nullable
private
MediaCodecAdapterWrapper
decoder
;
@Nullable
private
MediaCodecAdapterWrapper
decoder
;
private
volatile
boolean
isDecoderSurfacePopulated
;
private
boolean
waitingForPopulatedDecoderSurface
;
@Nullable
private
GlUtil
.
Uniform
decoderTextureTransformUniform
;
@Nullable
private
MediaCodecAdapterWrapper
encoder
;
@Nullable
private
MediaCodecAdapterWrapper
encoder
;
private
long
nextEncoderTimeUs
;
/** Whether encoder's actual output format is obtained. */
/** Whether encoder's actual output format is obtained. */
private
boolean
hasEncoderActualOutputFormat
;
private
boolean
hasEncoderActualOutputFormat
;
private
boolean
muxerWrapperTrackEnded
;
private
boolean
muxerWrapperTrackEnded
;
public
TransformerTranscodingVideoRenderer
(
public
TransformerTranscodingVideoRenderer
(
Context
context
,
MuxerWrapper
muxerWrapper
,
MuxerWrapper
muxerWrapper
,
TransformerMediaClock
mediaClock
,
TransformerMediaClock
mediaClock
,
Transformation
transformation
,
Transformation
transformation
,
Format
encoderConfigurationOutputFormat
)
{
Format
encoderConfigurationOutputFormat
)
{
super
(
C
.
TRACK_TYPE_VIDEO
,
muxerWrapper
,
mediaClock
,
transformation
);
super
(
C
.
TRACK_TYPE_VIDEO
,
muxerWrapper
,
mediaClock
,
transformation
);
this
.
context
=
context
;
decoderInputBuffer
=
new
DecoderInputBuffer
(
DecoderInputBuffer
.
BUFFER_REPLACEMENT_MODE_DIRECT
);
this
.
encoderConfigurationOutputFormat
=
encoderConfigurationOutputFormat
;
this
.
encoderConfigurationOutputFormat
=
encoderConfigurationOutputFormat
;
decoderInputBuffer
=
new
DecoderInputBuffer
(
DecoderInputBuffer
.
BUFFER_REPLACEMENT_MODE_DIRECT
);
decoderTextureTransformMatrix
=
new
float
[
16
];
decoderTextureId
=
GlUtil
.
TEXTURE_ID_UNSET
;
}
}
@Override
@Override
...
@@ -64,12 +97,15 @@ import java.nio.ByteBuffer;
...
@@ -64,12 +97,15 @@ import java.nio.ByteBuffer;
}
}
@Override
@Override
public
void
render
(
long
positionUs
,
long
elapsedRealtimeUs
)
throws
ExoPlaybackException
{
protected
void
onStarted
()
throws
ExoPlaybackException
{
if
(!
isRendererStarted
||
isEnded
())
{
super
.
onStarted
();
return
;
ensureEncoderConfigured
();
ensureOpenGlConfigured
();
}
}
if
(!
ensureEncoderConfigured
()
||
!
ensureDecoderConfigured
())
{
@Override
public
void
render
(
long
positionUs
,
long
elapsedRealtimeUs
)
throws
ExoPlaybackException
{
if
(!
isRendererStarted
||
isEnded
()
||
!
ensureDecoderConfigured
())
{
return
;
return
;
}
}
...
@@ -87,10 +123,28 @@ import java.nio.ByteBuffer;
...
@@ -87,10 +123,28 @@ import java.nio.ByteBuffer;
protected
void
onReset
()
{
protected
void
onReset
()
{
decoderInputBuffer
.
clear
();
decoderInputBuffer
.
clear
();
decoderInputBuffer
.
data
=
null
;
decoderInputBuffer
.
data
=
null
;
GlUtil
.
destroyEglContext
(
eglDisplay
,
eglContext
);
eglDisplay
=
null
;
eglContext
=
null
;
eglSurface
=
null
;
if
(
decoderTextureId
!=
GlUtil
.
TEXTURE_ID_UNSET
)
{
GlUtil
.
deleteTexture
(
decoderTextureId
);
}
if
(
decoderSurfaceTexture
!=
null
)
{
decoderSurfaceTexture
.
release
();
decoderSurfaceTexture
=
null
;
}
if
(
decoderSurface
!=
null
)
{
decoderSurface
.
release
();
decoderSurface
=
null
;
}
if
(
decoder
!=
null
)
{
if
(
decoder
!=
null
)
{
decoder
.
release
();
decoder
.
release
();
decoder
=
null
;
decoder
=
null
;
}
}
isDecoderSurfacePopulated
=
false
;
waitingForPopulatedDecoderSurface
=
false
;
decoderTextureTransformUniform
=
null
;
if
(
encoder
!=
null
)
{
if
(
encoder
!=
null
)
{
encoder
.
release
();
encoder
.
release
();
encoder
=
null
;
encoder
=
null
;
...
@@ -99,6 +153,94 @@ import java.nio.ByteBuffer;
...
@@ -99,6 +153,94 @@ import java.nio.ByteBuffer;
muxerWrapperTrackEnded
=
false
;
muxerWrapperTrackEnded
=
false
;
}
}
private
void
ensureEncoderConfigured
()
throws
ExoPlaybackException
{
if
(
encoder
!=
null
)
{
return
;
}
try
{
encoder
=
MediaCodecAdapterWrapper
.
createForVideoEncoding
(
encoderConfigurationOutputFormat
);
}
catch
(
IOException
e
)
{
throw
createRendererException
(
// TODO(claincly): should be "ENCODER_INIT_FAILED"
e
,
checkNotNull
(
this
.
decoder
).
getOutputFormat
(),
PlaybackException
.
ERROR_CODE_DECODER_INIT_FAILED
);
}
}
private
void
ensureOpenGlConfigured
()
{
if
(
eglDisplay
!=
null
)
{
return
;
}
eglDisplay
=
GlUtil
.
createEglDisplay
();
EGLContext
eglContext
;
try
{
eglContext
=
GlUtil
.
createEglContext
(
eglDisplay
);
this
.
eglContext
=
eglContext
;
}
catch
(
GlUtil
.
UnsupportedEglVersionException
e
)
{
throw
new
IllegalStateException
(
"EGL version is unsupported"
,
e
);
}
eglSurface
=
GlUtil
.
getEglSurface
(
eglDisplay
,
checkNotNull
(
checkNotNull
(
encoder
).
getInputSurface
()));
GlUtil
.
focusSurface
(
eglDisplay
,
eglContext
,
eglSurface
,
encoderConfigurationOutputFormat
.
width
,
encoderConfigurationOutputFormat
.
height
);
decoderTextureId
=
GlUtil
.
createExternalTexture
();
String
vertexShaderCode
;
String
fragmentShaderCode
;
try
{
vertexShaderCode
=
GlUtil
.
loadAsset
(
context
,
"shaders/blit_vertex_shader.glsl"
);
fragmentShaderCode
=
GlUtil
.
loadAsset
(
context
,
"shaders/copy_external_fragment_shader.glsl"
);
}
catch
(
IOException
e
)
{
throw
new
IllegalStateException
(
e
);
}
int
copyProgram
=
GlUtil
.
compileProgram
(
vertexShaderCode
,
fragmentShaderCode
);
GLES20
.
glUseProgram
(
copyProgram
);
GlUtil
.
Attribute
[]
copyAttributes
=
GlUtil
.
getAttributes
(
copyProgram
);
checkState
(
copyAttributes
.
length
==
2
,
"Expected program to have two vertex attributes."
);
for
(
GlUtil
.
Attribute
copyAttribute
:
copyAttributes
)
{
if
(
copyAttribute
.
name
.
equals
(
"a_position"
))
{
copyAttribute
.
setBuffer
(
new
float
[]
{
-
1.0f
,
-
1.0f
,
0.0f
,
1.0f
,
1.0f
,
-
1.0f
,
0.0f
,
1.0f
,
-
1.0f
,
1.0f
,
0.0f
,
1.0f
,
1.0f
,
1.0f
,
0.0f
,
1.0f
,
},
/* size= */
4
);
}
else
if
(
copyAttribute
.
name
.
equals
(
"a_texcoord"
))
{
copyAttribute
.
setBuffer
(
new
float
[]
{
0.0f
,
0.0f
,
0.0f
,
1.0f
,
1.0f
,
0.0f
,
0.0f
,
1.0f
,
0.0f
,
1.0f
,
0.0f
,
1.0f
,
1.0f
,
1.0f
,
0.0f
,
1.0f
,
},
/* size= */
4
);
}
else
{
throw
new
IllegalStateException
(
"Unexpected attribute name."
);
}
copyAttribute
.
bind
();
}
GlUtil
.
Uniform
[]
copyUniforms
=
GlUtil
.
getUniforms
(
copyProgram
);
checkState
(
copyUniforms
.
length
==
2
,
"Expected program to have two uniforms."
);
for
(
GlUtil
.
Uniform
copyUniform
:
copyUniforms
)
{
if
(
copyUniform
.
name
.
equals
(
"tex_sampler"
))
{
copyUniform
.
setSamplerTexId
(
decoderTextureId
,
0
);
copyUniform
.
bind
();
}
else
if
(
copyUniform
.
name
.
equals
(
"tex_transform"
))
{
decoderTextureTransformUniform
=
copyUniform
;
}
else
{
throw
new
IllegalStateException
(
"Unexpected uniform name."
);
}
}
}
private
boolean
ensureDecoderConfigured
()
throws
ExoPlaybackException
{
private
boolean
ensureDecoderConfigured
()
throws
ExoPlaybackException
{
if
(
decoder
!=
null
)
{
if
(
decoder
!=
null
)
{
return
true
;
return
true
;
...
@@ -114,11 +256,13 @@ import java.nio.ByteBuffer;
...
@@ -114,11 +256,13 @@ import java.nio.ByteBuffer;
}
}
Format
inputFormat
=
checkNotNull
(
formatHolder
.
format
);
Format
inputFormat
=
checkNotNull
(
formatHolder
.
format
);
MediaCodecAdapterWrapper
encoder
=
checkNotNull
(
this
.
encoder
);
checkState
(
decoderTextureId
!=
GlUtil
.
TEXTURE_ID_UNSET
);
decoderSurfaceTexture
=
new
SurfaceTexture
(
decoderTextureId
);
decoderSurfaceTexture
.
setOnFrameAvailableListener
(
surfaceTexture
->
isDecoderSurfacePopulated
=
true
);
decoderSurface
=
new
Surface
(
decoderSurfaceTexture
);
try
{
try
{
decoder
=
decoder
=
MediaCodecAdapterWrapper
.
createForVideoDecoding
(
inputFormat
,
decoderSurface
);
MediaCodecAdapterWrapper
.
createForVideoDecoding
(
inputFormat
,
checkNotNull
(
encoder
.
getInputSurface
()));
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
throw
createRendererException
(
throw
createRendererException
(
e
,
formatHolder
.
format
,
PlaybackException
.
ERROR_CODE_DECODER_INIT_FAILED
);
e
,
formatHolder
.
format
,
PlaybackException
.
ERROR_CODE_DECODER_INIT_FAILED
);
...
@@ -126,23 +270,6 @@ import java.nio.ByteBuffer;
...
@@ -126,23 +270,6 @@ import java.nio.ByteBuffer;
return
true
;
return
true
;
}
}
private
boolean
ensureEncoderConfigured
()
throws
ExoPlaybackException
{
if
(
encoder
!=
null
)
{
return
true
;
}
try
{
encoder
=
MediaCodecAdapterWrapper
.
createForVideoEncoding
(
encoderConfigurationOutputFormat
);
}
catch
(
IOException
e
)
{
throw
createRendererException
(
// TODO(claincly): should be "ENCODER_INIT_FAILED"
e
,
checkNotNull
(
this
.
decoder
).
getOutputFormat
(),
PlaybackException
.
ERROR_CODE_DECODER_INIT_FAILED
);
}
return
true
;
}
private
boolean
feedDecoderFromInput
()
{
private
boolean
feedDecoderFromInput
()
{
MediaCodecAdapterWrapper
decoder
=
checkNotNull
(
this
.
decoder
);
MediaCodecAdapterWrapper
decoder
=
checkNotNull
(
this
.
decoder
);
if
(!
decoder
.
maybeDequeueInputBuffer
(
decoderInputBuffer
))
{
if
(!
decoder
.
maybeDequeueInputBuffer
(
decoderInputBuffer
))
{
...
@@ -174,14 +301,35 @@ import java.nio.ByteBuffer;
...
@@ -174,14 +301,35 @@ import java.nio.ByteBuffer;
return
false
;
return
false
;
}
}
// Rendering the decoder output queues input to the encoder because they share the same surface.
if
(!
isDecoderSurfacePopulated
)
{
boolean
hasProcessedOutputBuffer
=
decoder
.
maybeDequeueRenderAndReleaseOutputBuffer
();
if
(!
waitingForPopulatedDecoderSurface
)
{
if
(
decoder
.
getOutputBuffer
()
!=
null
)
{
nextEncoderTimeUs
=
checkNotNull
(
decoder
.
getOutputBufferInfo
()).
presentationTimeUs
;
decoder
.
releaseOutputBuffer
(
/* render= */
true
);
waitingForPopulatedDecoderSurface
=
true
;
}
if
(
decoder
.
isEnded
())
{
if
(
decoder
.
isEnded
())
{
checkNotNull
(
encoder
).
signalEndOfInputStream
();
checkNotNull
(
encoder
).
signalEndOfInputStream
();
// All decoded frames have been rendered to the encoder's input surface.
}
}
return
false
;
return
false
;
}
}
return
hasProcessedOutputBuffer
;
waitingForPopulatedDecoderSurface
=
false
;
SurfaceTexture
decoderSurfaceTexture
=
checkNotNull
(
this
.
decoderSurfaceTexture
);
decoderSurfaceTexture
.
updateTexImage
();
decoderSurfaceTexture
.
getTransformMatrix
(
decoderTextureTransformMatrix
);
GlUtil
.
Uniform
decoderTextureTransformUniform
=
checkNotNull
(
this
.
decoderTextureTransformUniform
);
decoderTextureTransformUniform
.
setFloats
(
decoderTextureTransformMatrix
);
decoderTextureTransformUniform
.
bind
();
GLES20
.
glDrawArrays
(
GLES20
.
GL_TRIANGLE_STRIP
,
0
,
4
);
EGLDisplay
eglDisplay
=
checkNotNull
(
this
.
eglDisplay
);
EGLSurface
eglSurface
=
checkNotNull
(
this
.
eglSurface
);
EGLExt
.
eglPresentationTimeANDROID
(
eglDisplay
,
eglSurface
,
nextEncoderTimeUs
*
1000L
);
EGL14
.
eglSwapBuffers
(
eglDisplay
,
eglSurface
);
isDecoderSurfacePopulated
=
false
;
return
true
;
}
}
private
boolean
feedMuxerFromEncoder
()
{
private
boolean
feedMuxerFromEncoder
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment