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
cdd92450
authored
Feb 14, 2023
by
tofunmi
Committed by
christosts
Feb 14, 2023
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Fix InternalTextureManager so that all frames are sent downstream
PiperOrigin-RevId: 509478455
parent
08cf6db3
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
77 additions
and
54 deletions
library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java
library/effect/src/main/java/com/google/android/exoplayer2/effect/InternalTextureManager.java
library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java
View file @
cdd92450
...
@@ -145,7 +145,8 @@ public final class GlEffectsFrameProcessorPixelTest {
...
@@ -145,7 +145,8 @@ public final class GlEffectsFrameProcessorPixelTest {
assertThat
(
averagePixelAbsoluteDifference
).
isAtMost
(
MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE
);
assertThat
(
averagePixelAbsoluteDifference
).
isAtMost
(
MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE
);
}
}
// TODO(b/262693274): Once texture deletion is added to InternalTextureManager.java, add a test
// TODO(b/262693274): Once texture deletion is added to InternalTextureManager.java, add a test
// queuing multiple input bitmaps to ensure successfully completion without errors.
// queuing multiple input bitmaps to ensure successfully completion without errors, ensuring the
// correct number of frames haas been queued.
@Test
@Test
public
void
noEffects_withFrameCache_matchesGoldenFile
()
throws
Exception
{
public
void
noEffects_withFrameCache_matchesGoldenFile
()
throws
Exception
{
...
...
library/effect/src/main/java/com/google/android/exoplayer2/effect/InternalTextureManager.java
View file @
cdd92450
...
@@ -16,7 +16,7 @@
...
@@ -16,7 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
effect
;
package
com
.
google
.
android
.
exoplayer2
.
effect
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
java
.
lang
.
Math
.
round
;
import
static
java
.
lang
.
Math
.
floor
;
import
android.graphics.Bitmap
;
import
android.graphics.Bitmap
;
import
android.opengl.GLES20
;
import
android.opengl.GLES20
;
...
@@ -29,19 +29,31 @@ import com.google.android.exoplayer2.util.GlUtil;
...
@@ -29,19 +29,31 @@ import com.google.android.exoplayer2.util.GlUtil;
import
java.util.Queue
;
import
java.util.Queue
;
import
java.util.concurrent.LinkedBlockingQueue
;
import
java.util.concurrent.LinkedBlockingQueue
;
/** Forwards a frame produced from a {@link Bitmap} to a {@link GlShaderProgram} for consumption. */
/**
* Forwards a frame produced from a {@link Bitmap} to a {@link GlShaderProgram} for consumption
*
* <p>Methods in this class can be called from any thread.
*/
/* package */
class
InternalTextureManager
implements
GlShaderProgram
.
InputListener
{
/* package */
class
InternalTextureManager
implements
GlShaderProgram
.
InputListener
{
private
final
GlShaderProgram
shaderProgram
;
private
final
GlShaderProgram
shaderProgram
;
private
final
FrameProcessingTaskExecutor
frameProcessingTaskExecutor
;
private
final
FrameProcessingTaskExecutor
frameProcessingTaskExecutor
;
// The queue holds all bitmaps with one or more frames pending to be sent downstream.
private
final
Queue
<
BitmapFrameSequenceInfo
>
pendingBitmaps
;
private
final
Queue
<
BitmapFrameSequenceInfo
>
pendingBitmaps
;
private
int
downstreamShaderProgramCapacity
;
private
int
downstreamShaderProgramCapacity
;
private
int
availableFrameCount
;
private
int
framesToQueueForCurrentBitmap
;
private
long
currentPresentationTimeUs
;
private
long
currentPresentationTimeUs
;
private
long
totalDurationUs
;
private
boolean
inputEnded
;
private
boolean
inputEnded
;
private
boolean
outputEnded
;
/**
* Creates a new instance.
*
* @param shaderProgram The {@link GlShaderProgram} for which this {@code InternalTextureManager}
* will be set as the {@link GlShaderProgram.InputListener}.
* @param frameProcessingTaskExecutor The {@link FrameProcessingTaskExecutor} that the methods of
* this class run on.
*/
public
InternalTextureManager
(
public
InternalTextureManager
(
GlShaderProgram
shaderProgram
,
FrameProcessingTaskExecutor
frameProcessingTaskExecutor
)
{
GlShaderProgram
shaderProgram
,
FrameProcessingTaskExecutor
frameProcessingTaskExecutor
)
{
this
.
shaderProgram
=
shaderProgram
;
this
.
shaderProgram
=
shaderProgram
;
...
@@ -51,6 +63,10 @@ import java.util.concurrent.LinkedBlockingQueue;
...
@@ -51,6 +63,10 @@ import java.util.concurrent.LinkedBlockingQueue;
@Override
@Override
public
void
onReadyToAcceptInputFrame
()
{
public
void
onReadyToAcceptInputFrame
()
{
// TODO(b/262693274): Delete texture when last duplicate of the frame comes back from the shader
// program and change to only allocate one texId at a time. A change to the
// onInputFrameProcessed() method signature to include presentationTimeUs will probably be
// needed to do this.
frameProcessingTaskExecutor
.
submit
(
frameProcessingTaskExecutor
.
submit
(
()
->
{
()
->
{
downstreamShaderProgramCapacity
++;
downstreamShaderProgramCapacity
++;
...
@@ -58,84 +74,90 @@ import java.util.concurrent.LinkedBlockingQueue;
...
@@ -58,84 +74,90 @@ import java.util.concurrent.LinkedBlockingQueue;
});
});
}
}
@Override
/**
public
void
onInputFrameProcessed
(
TextureInfo
inputTexture
)
{
* Provides an input {@link Bitmap} to put into the video frames.
// TODO(b/262693274): Delete texture when last duplicate of the frame comes back from the shader
*
// program and change to only allocate one texId at a time. A change to method signature to
* @see FrameProcessor#queueInputBitmap
// include presentationTimeUs will probably be needed to do this.
*/
frameProcessingTaskExecutor
.
submit
(
()
->
{
if
(
availableFrameCount
==
0
)
{
signalEndOfInput
();
}
});
}
public
void
queueInputBitmap
(
public
void
queueInputBitmap
(
Bitmap
inputBitmap
,
long
durationUs
,
float
frameRate
,
boolean
useHdr
)
{
Bitmap
inputBitmap
,
long
durationUs
,
float
frameRate
,
boolean
useHdr
)
{
frameProcessingTaskExecutor
.
submit
(
frameProcessingTaskExecutor
.
submit
(
()
->
setupBitmap
(
inputBitmap
,
durationUs
,
frameRate
,
useHdr
));
()
->
setupBitmap
(
inputBitmap
,
durationUs
,
frameRate
,
useHdr
));
}
}
/**
* Signals the end of the input.
*
* @see FrameProcessor#signalEndOfInput()
*/
public
void
signalEndOfInput
()
{
frameProcessingTaskExecutor
.
submit
(
()
->
{
inputEnded
=
true
;
signalEndOfOutput
();
});
}
@WorkerThread
@WorkerThread
private
void
setupBitmap
(
Bitmap
bitmap
,
long
durationUs
,
float
frameRate
,
boolean
useHdr
)
private
void
setupBitmap
(
Bitmap
bitmap
,
long
durationUs
,
float
frameRate
,
boolean
useHdr
)
throws
FrameProcessingException
{
throws
FrameProcessingException
{
if
(
inputEnded
)
{
if
(
inputEnded
)
{
return
;
return
;
}
}
int
bitmapTexId
;
try
{
try
{
int
bitmapTexId
=
bitmapTexId
=
GlUtil
.
createTexture
(
GlUtil
.
createTexture
(
bitmap
.
getWidth
(),
bitmap
.
getHeight
(),
/* useHighPrecisionColorComponents= */
useHdr
);
bitmap
.
getWidth
(),
bitmap
.
getHeight
(),
/* useHighPrecisionColorComponents= */
useHdr
);
GLES20
.
glBindTexture
(
GLES20
.
GL_TEXTURE_2D
,
bitmapTexId
);
GLES20
.
glBindTexture
(
GLES20
.
GL_TEXTURE_2D
,
bitmapTexId
);
GLUtils
.
texImage2D
(
GLES20
.
GL_TEXTURE_2D
,
/* level= */
0
,
bitmap
,
/* border= */
0
);
GLUtils
.
texImage2D
(
GLES20
.
GL_TEXTURE_2D
,
/* level= */
0
,
bitmap
,
/* border= */
0
);
GlUtil
.
checkGlError
();
GlUtil
.
checkGlError
();
TextureInfo
textureInfo
=
new
TextureInfo
(
bitmapTexId
,
/* fboId= */
C
.
INDEX_UNSET
,
bitmap
.
getWidth
(),
bitmap
.
getHeight
());
int
timeIncrementUs
=
round
(
C
.
MICROS_PER_SECOND
/
frameRate
);
availableFrameCount
+=
round
((
frameRate
*
durationUs
)
/
C
.
MICROS_PER_SECOND
);
totalDurationUs
+=
durationUs
;
pendingBitmaps
.
add
(
new
BitmapFrameSequenceInfo
(
textureInfo
,
timeIncrementUs
,
totalDurationUs
));
}
catch
(
GlUtil
.
GlException
e
)
{
}
catch
(
GlUtil
.
GlException
e
)
{
throw
FrameProcessingException
.
from
(
e
);
throw
FrameProcessingException
.
from
(
e
);
}
}
TextureInfo
textureInfo
=
new
TextureInfo
(
bitmapTexId
,
/* fboId= */
C
.
INDEX_UNSET
,
bitmap
.
getWidth
(),
bitmap
.
getHeight
());
int
framesToAdd
=
(
int
)
floor
(
frameRate
*
(
durationUs
/
(
float
)
C
.
MICROS_PER_SECOND
));
long
frameDurationUs
=
(
long
)
floor
(
C
.
MICROS_PER_SECOND
/
frameRate
);
pendingBitmaps
.
add
(
new
BitmapFrameSequenceInfo
(
textureInfo
,
frameDurationUs
,
framesToAdd
));
maybeQueueToShaderProgram
();
maybeQueueToShaderProgram
();
}
}
@WorkerThread
@WorkerThread
private
void
maybeQueueToShaderProgram
()
{
private
void
maybeQueueToShaderProgram
()
{
if
(
inputEnded
||
availableFrameCount
==
0
||
downstreamShaderProgramCapacity
==
0
)
{
if
(
pendingBitmaps
.
isEmpty
()
||
downstreamShaderProgramCapacity
==
0
)
{
return
;
return
;
}
}
availableFrameCount
--;
BitmapFrameSequenceInfo
currentBitmap
=
checkNotNull
(
pendingBitmaps
.
peek
());
if
(
framesToQueueForCurrentBitmap
==
0
)
{
framesToQueueForCurrentBitmap
=
currentBitmap
.
numberOfFrames
;
}
framesToQueueForCurrentBitmap
--;
downstreamShaderProgramCapacity
--;
downstreamShaderProgramCapacity
--;
BitmapFrameSequenceInfo
currentFrame
=
checkNotNull
(
pendingBitmaps
.
peek
());
shaderProgram
.
queueInputFrame
(
currentBitmap
.
textureInfo
,
currentPresentationTimeUs
);
shaderProgram
.
queueInputFrame
(
currentFrame
.
textureInfo
,
currentPresentationTimeUs
);
currentPresentationTimeUs
+=
current
Frame
.
timeIncrement
Us
;
currentPresentationTimeUs
+=
current
Bitmap
.
frameDuration
Us
;
if
(
currentPresentationTimeUs
>=
currentFrame
.
endPresentationTimeUs
)
{
if
(
framesToQueueForCurrentBitmap
==
0
)
{
pendingBitmaps
.
remove
();
pendingBitmaps
.
remove
();
signalEndOfOutput
();
}
}
}
}
/**
@WorkerThread
* Signals the end of the input.
private
void
signalEndOfOutput
()
{
*
if
(
framesToQueueForCurrentBitmap
==
0
* @see FrameProcessor#signalEndOfInput()
&&
pendingBitmaps
.
isEmpty
()
*/
&&
inputEnded
public
void
signalEndOfInput
()
{
&&
!
outputEnded
)
{
frameProcessingTaskExecutor
.
submit
(
shaderProgram
.
signalEndOfCurrentInputStream
();
()
->
{
outputEnded
=
true
;
if
(
inputEnded
)
{
}
return
;
}
inputEnded
=
true
;
shaderProgram
.
signalEndOfCurrentInputStream
();
});
}
}
/**
/**
...
@@ -144,14 +166,14 @@ import java.util.concurrent.LinkedBlockingQueue;
...
@@ -144,14 +166,14 @@ import java.util.concurrent.LinkedBlockingQueue;
*/
*/
private
static
final
class
BitmapFrameSequenceInfo
{
private
static
final
class
BitmapFrameSequenceInfo
{
public
final
TextureInfo
textureInfo
;
public
final
TextureInfo
textureInfo
;
public
final
long
timeIncrement
Us
;
public
final
long
frameDuration
Us
;
public
final
long
endPresentationTimeU
s
;
public
final
int
numberOfFrame
s
;
public
BitmapFrameSequenceInfo
(
public
BitmapFrameSequenceInfo
(
TextureInfo
textureInfo
,
long
timeIncrementUs
,
long
endPresentationTimeU
s
)
{
TextureInfo
textureInfo
,
long
frameDurationUs
,
int
numberOfFrame
s
)
{
this
.
textureInfo
=
textureInfo
;
this
.
textureInfo
=
textureInfo
;
this
.
timeIncrementUs
=
timeIncrement
Us
;
this
.
frameDurationUs
=
frameDuration
Us
;
this
.
endPresentationTimeUs
=
endPresentationTimeU
s
;
this
.
numberOfFrames
=
numberOfFrame
s
;
}
}
}
}
}
}
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