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
fb42f818
authored
Jan 07, 2020
by
christosts
Committed by
Oliver Woodman
Jan 08, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add start() method in MediaCodecAdapter
PiperOrigin-RevId: 288476415
parent
c5535e82
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
135 additions
and
357 deletions
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapter.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapter.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java
View file @
fb42f818
...
...
@@ -38,7 +38,7 @@ import com.google.android.exoplayer2.util.Assertions;
private
final
MediaCodec
codec
;
@Nullable
private
IllegalStateException
internalException
;
private
boolean
flushing
;
private
Runnable
onCodecStart
;
private
Runnable
codecStartRunnable
;
/**
* Create a new {@code AsynchronousMediaCodecAdapter}.
...
...
@@ -55,7 +55,12 @@ import com.google.android.exoplayer2.util.Assertions;
handler
=
new
Handler
(
looper
);
this
.
codec
=
codec
;
this
.
codec
.
setCallback
(
mediaCodecAsyncCallback
);
onCodecStart
=
()
->
codec
.
start
();
codecStartRunnable
=
codec:
:
start
;
}
@Override
public
void
start
()
{
codecStartRunnable
.
run
();
}
@Override
...
...
@@ -105,7 +110,7 @@ import com.google.android.exoplayer2.util.Assertions;
flushing
=
false
;
mediaCodecAsyncCallback
.
flush
();
try
{
onCodecStart
.
run
();
codecStartRunnable
.
run
();
}
catch
(
IllegalStateException
e
)
{
// Catch IllegalStateException directly so that we don't have to wrap it.
internalException
=
e
;
...
...
@@ -115,8 +120,8 @@ import com.google.android.exoplayer2.util.Assertions;
}
@VisibleForTesting
/* package */
void
set
OnCodecStart
(
Runnable
onCodecStart
)
{
this
.
onCodecStart
=
onCodecStart
;
/* package */
void
set
CodecStartRunnable
(
Runnable
codecStartRunnable
)
{
this
.
codecStartRunnable
=
codecStartRunnable
;
}
private
void
maybeThrowException
()
throws
IllegalStateException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapter.java
View file @
fb42f818
...
...
@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
import
androidx.annotation.RequiresApi
;
import
androidx.annotation.VisibleForTesting
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
...
...
@@ -54,7 +53,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@MonotonicNonNull
private
Handler
handler
;
private
long
pendingFlushCount
;
private
@State
int
state
;
private
Runnable
onCodecStart
;
private
Runnable
codecStartRunnable
;
@Nullable
private
IllegalStateException
internalException
;
/**
...
...
@@ -77,31 +76,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this
.
codec
=
codec
;
this
.
handlerThread
=
handlerThread
;
state
=
STATE_CREATED
;
onCodecStart
=
codec:
:
start
;
codecStartRunnable
=
codec:
:
start
;
}
/**
* Starts the operation of the instance.
*
* <p>After a call to this method, make sure to call {@link #shutdown()} to terminate the internal
* Thread. You can only call this method once during the lifetime of this instance; calling this
* method again will throw an {@link IllegalStateException}.
*
* @throws IllegalStateException If this method has been called already.
*/
@Override
public
synchronized
void
start
()
{
Assertions
.
checkState
(
state
==
STATE_CREATED
);
handlerThread
.
start
();
handler
=
new
Handler
(
handlerThread
.
getLooper
());
codec
.
setCallback
(
this
,
handler
);
codecStartRunnable
.
run
();
state
=
STATE_STARTED
;
}
@Override
public
synchronized
int
dequeueInputBufferIndex
()
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
if
(
isFlushing
())
{
return
MediaCodec
.
INFO_TRY_AGAIN_LATER
;
}
else
{
...
...
@@ -112,8 +100,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
synchronized
int
dequeueOutputBufferIndex
(
MediaCodec
.
BufferInfo
bufferInfo
)
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
if
(
isFlushing
())
{
return
MediaCodec
.
INFO_TRY_AGAIN_LATER
;
}
else
{
...
...
@@ -124,15 +110,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
synchronized
MediaFormat
getOutputFormat
()
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
return
mediaCodecAsyncCallback
.
getOutputFormat
();
}
@Override
public
synchronized
void
flush
()
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
codec
.
flush
();
++
pendingFlushCount
;
Util
.
castNonNull
(
handler
).
post
(
this
::
onFlushCompleted
);
...
...
@@ -177,8 +159,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
@VisibleForTesting
/* package */
void
set
OnCodecStart
(
Runnable
onCodecStart
)
{
this
.
onCodecStart
=
onCodecStart
;
/* package */
void
set
CodecStartRunnable
(
Runnable
codecStartRunnable
)
{
this
.
codecStartRunnable
=
codecStartRunnable
;
}
private
synchronized
void
onFlushCompleted
()
{
...
...
@@ -199,7 +181,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
mediaCodecAsyncCallback
.
flush
();
try
{
onCodecStart
.
run
();
codecStartRunnable
.
run
();
}
catch
(
IllegalStateException
e
)
{
internalException
=
e
;
}
catch
(
Exception
e
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java
View file @
fb42f818
...
...
@@ -32,6 +32,13 @@ import android.media.MediaFormat;
/* package */
interface
MediaCodecAdapter
{
/**
* Starts this instance.
*
* @see MediaCodec#start().
*/
void
start
();
/**
* Returns the next available input buffer index from the underlying {@link MediaCodec} or {@link
* MediaCodec#INFO_TRY_AGAIN_LATER} if no such buffer exists.
*
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
fb42f818
...
...
@@ -995,11 +995,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
else
if
(
mediaCodecOperationMode
==
OPERATION_MODE_ASYNCHRONOUS_DEDICATED_THREAD
&&
Util
.
SDK_INT
>=
23
)
{
codecAdapter
=
new
DedicatedThreadAsyncMediaCodecAdapter
(
codec
,
getTrackType
());
((
DedicatedThreadAsyncMediaCodecAdapter
)
codecAdapter
).
start
();
}
else
if
(
mediaCodecOperationMode
==
OPERATION_MODE_ASYNCHRONOUS_DEDICATED_THREAD_MULTI_LOCK
&&
Util
.
SDK_INT
>=
23
)
{
codecAdapter
=
new
MultiLockAsyncMediaCodecAdapter
(
codec
,
getTrackType
());
((
MultiLockAsyncMediaCodecAdapter
)
codecAdapter
).
start
();
}
else
{
codecAdapter
=
new
SynchronousMediaCodecAdapter
(
codec
);
}
...
...
@@ -1009,7 +1007,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
configureCodec
(
codecInfo
,
codec
,
inputFormat
,
crypto
,
codecOperatingRate
);
TraceUtil
.
endSection
();
TraceUtil
.
beginSection
(
"startCodec"
);
codec
.
start
();
codec
Adapter
.
start
();
TraceUtil
.
endSection
();
codecInitializedTimestamp
=
SystemClock
.
elapsedRealtime
();
getCodecBuffers
(
codec
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapter.java
View file @
fb42f818
...
...
@@ -27,7 +27,6 @@ import androidx.annotation.Nullable;
import
androidx.annotation.RequiresApi
;
import
androidx.annotation.VisibleForTesting
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.IntArrayQueue
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayDeque
;
...
...
@@ -94,7 +93,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private
final
HandlerThread
handlerThread
;
@MonotonicNonNull
private
Handler
handler
;
private
Runnable
onCodecStart
;
private
Runnable
codecStartRunnable
;
/** Creates a new instance that wraps the specified {@link MediaCodec}. */
/* package */
MultiLockAsyncMediaCodecAdapter
(
MediaCodec
codec
,
int
trackType
)
{
...
...
@@ -114,25 +113,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
codecException
=
null
;
state
=
STATE_CREATED
;
this
.
handlerThread
=
handlerThread
;
onCodecStart
=
codec:
:
start
;
codecStartRunnable
=
codec:
:
start
;
}
/**
* Starts the operation of this instance.
*
* <p>After a call to this method, make sure to call {@link #shutdown()} to terminate the internal
* Thread. You can only call this method once during the lifetime of an instance; calling this
* method again will throw an {@link IllegalStateException}.
*
* @throws IllegalStateException If this method has been called already.
*/
@Override
public
void
start
()
{
synchronized
(
objectStateLock
)
{
Assertions
.
checkState
(
state
==
STATE_CREATED
);
handlerThread
.
start
();
handler
=
new
Handler
(
handlerThread
.
getLooper
());
codec
.
setCallback
(
this
,
handler
);
codecStartRunnable
.
run
();
state
=
STATE_STARTED
;
}
}
...
...
@@ -140,8 +130,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
int
dequeueInputBufferIndex
()
{
synchronized
(
objectStateLock
)
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
if
(
isFlushing
())
{
return
MediaCodec
.
INFO_TRY_AGAIN_LATER
;
}
else
{
...
...
@@ -154,8 +142,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
int
dequeueOutputBufferIndex
(
MediaCodec
.
BufferInfo
bufferInfo
)
{
synchronized
(
objectStateLock
)
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
if
(
isFlushing
())
{
return
MediaCodec
.
INFO_TRY_AGAIN_LATER
;
}
else
{
...
...
@@ -168,8 +154,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
MediaFormat
getOutputFormat
()
{
synchronized
(
objectStateLock
)
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
if
(
currentFormat
==
null
)
{
throw
new
IllegalStateException
();
}
...
...
@@ -181,8 +165,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public
void
flush
()
{
synchronized
(
objectStateLock
)
{
Assertions
.
checkState
(
state
==
STATE_STARTED
);
codec
.
flush
();
pendingFlush
++;
Util
.
castNonNull
(
handler
).
post
(
this
::
onFlushComplete
);
...
...
@@ -200,8 +182,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
@VisibleForTesting
/* package */
void
set
OnCodecStart
(
Runnable
onCodecStart
)
{
this
.
onCodecStart
=
onCodecStart
;
/* package */
void
set
CodecStartRunnable
(
Runnable
codecStartRunnable
)
{
this
.
codecStartRunnable
=
codecStartRunnable
;
}
private
int
dequeueAvailableInputBufferIndex
()
{
...
...
@@ -307,7 +289,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
clearAvailableOutput
();
codecException
=
null
;
try
{
onCodecStart
.
run
();
codecStartRunnable
.
run
();
}
catch
(
IllegalStateException
e
)
{
codecException
=
e
;
}
catch
(
Exception
e
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java
View file @
fb42f818
...
...
@@ -23,6 +23,7 @@ import android.media.MediaFormat;
* A {@link MediaCodecAdapter} that operates the underlying {@link MediaCodec} in synchronous mode.
*/
/* package */
final
class
SynchronousMediaCodecAdapter
implements
MediaCodecAdapter
{
private
final
MediaCodec
codec
;
public
SynchronousMediaCodecAdapter
(
MediaCodec
mediaCodec
)
{
...
...
@@ -30,6 +31,11 @@ import android.media.MediaFormat;
}
@Override
public
void
start
()
{
codec
.
start
();
}
@Override
public
int
dequeueInputBufferIndex
()
{
return
codec
.
dequeueInputBuffer
(
0
);
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java
View file @
fb42f818
...
...
@@ -19,7 +19,7 @@ package com.google.android.exoplayer2.mediacodec;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
areEqual
;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
waitUntilAllEventsAreExecuted
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
android.media.MediaCodec
;
import
android.media.MediaFormat
;
...
...
@@ -29,7 +29,7 @@ import android.os.Looper;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
java.io.IOException
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.Atomic
Boolean
;
import
java.util.concurrent.atomic.Atomic
Integer
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.Test
;
...
...
@@ -45,27 +45,32 @@ public class AsynchronousMediaCodecAdapterTest {
private
MediaCodec
.
BufferInfo
bufferInfo
;
@Before
public
void
set
u
p
()
throws
IOException
{
public
void
set
U
p
()
throws
IOException
{
handlerThread
=
new
HandlerThread
(
"TestHandlerThread"
);
handlerThread
.
start
();
looper
=
handlerThread
.
getLooper
();
codec
=
MediaCodec
.
createByCodecName
(
"h264"
);
adapter
=
new
AsynchronousMediaCodecAdapter
(
codec
,
looper
);
adapter
.
setCodecStartRunnable
(()
->
{});
bufferInfo
=
new
MediaCodec
.
BufferInfo
();
}
@After
public
void
tearDown
()
{
adapter
.
shutdown
();
handlerThread
.
quit
();
}
@Test
public
void
dequeueInputBufferIndex_withoutInputBuffer_returnsTryAgainLater
()
{
adapter
.
start
();
assertThat
(
adapter
.
dequeueInputBufferIndex
()).
isEqualTo
(
MediaCodec
.
INFO_TRY_AGAIN_LATER
);
}
@Test
public
void
dequeueInputBufferIndex_withInputBuffer_returnsInputBuffer
()
{
adapter
.
start
();
adapter
.
getMediaCodecCallback
().
onInputBufferAvailable
(
codec
,
/* index=*/
0
);
assertThat
(
adapter
.
dequeueInputBufferIndex
()).
isEqualTo
(
0
);
...
...
@@ -73,6 +78,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueInputBufferIndex_whileFlushing_returnsTryAgainLater
()
{
adapter
.
start
();
adapter
.
getMediaCodecCallback
().
onInputBufferAvailable
(
codec
,
/* index=*/
0
);
adapter
.
flush
();
adapter
.
getMediaCodecCallback
().
onInputBufferAvailable
(
codec
,
/* index=*/
1
);
...
...
@@ -83,9 +89,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueInputBufferIndex_afterFlushCompletes_returnsNextInputBuffer
()
throws
InterruptedException
{
// Disable calling codec.start() after flush() completes to avoid receiving buffers from the
// shadow codec impl
adapter
.
setOnCodecStart
(()
->
{});
adapter
.
start
();
Handler
handler
=
new
Handler
(
looper
);
handler
.
post
(
()
->
adapter
.
getMediaCodecCallback
().
onInputBufferAvailable
(
codec
,
/* index=*/
0
));
...
...
@@ -100,28 +104,35 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueInputBufferIndex_afterFlushCompletesWithError_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
calls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
IllegalStateException
(
"codec#start() exception"
);
if
(
calls
.
incrementAndGet
()
==
2
)
{
throw
new
IllegalStateException
();
}
});
adapter
.
start
();
adapter
.
flush
();
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
/* time= */
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
{
adapter
.
dequeueInputBufferIndex
();
});
}
@Test
public
void
dequeueOutputBufferIndex_withoutOutputBuffer_returnsTryAgainLater
()
{
adapter
.
start
();
assertThat
(
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
))
.
isEqualTo
(
MediaCodec
.
INFO_TRY_AGAIN_LATER
);
}
@Test
public
void
dequeueOutputBufferIndex_withOutputBuffer_returnsOutputBuffer
()
{
adapter
.
start
();
MediaCodec
.
BufferInfo
outBufferInfo
=
new
MediaCodec
.
BufferInfo
();
outBufferInfo
.
presentationTimeUs
=
10
;
adapter
.
getMediaCodecCallback
().
onOutputBufferAvailable
(
codec
,
/* index=*/
0
,
outBufferInfo
);
...
...
@@ -132,6 +143,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueOutputBufferIndex_whileFlushing_returnsTryAgainLater
()
{
adapter
.
start
();
adapter
.
getMediaCodecCallback
().
onOutputBufferAvailable
(
codec
,
/* index=*/
0
,
bufferInfo
);
adapter
.
flush
();
adapter
.
getMediaCodecCallback
().
onOutputBufferAvailable
(
codec
,
/* index=*/
1
,
bufferInfo
);
...
...
@@ -143,9 +155,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueOutputBufferIndex_afterFlushCompletes_returnsNextOutputBuffer
()
throws
InterruptedException
{
// Disable calling codec.start() after flush() completes to avoid receiving buffers from the
// shadow codec impl
adapter
.
setOnCodecStart
(()
->
{});
adapter
.
start
();
Handler
handler
=
new
Handler
(
looper
);
MediaCodec
.
BufferInfo
info0
=
new
MediaCodec
.
BufferInfo
();
handler
.
post
(
...
...
@@ -164,31 +174,23 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
dequeueOutputBufferIndex_afterFlushCompletesWithError_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
calls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
RuntimeException
(
"codec#start() exception"
);
if
(
calls
.
incrementAndGet
()
==
2
)
{
throw
new
RuntimeException
(
"codec#start() exception"
);
}
});
adapter
.
start
();
adapter
.
flush
();
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
/* time= */
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
getOutputFormat_withoutFormat_throwsException
()
{
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
));
}
@Test
public
void
getOutputFormat_withMultipleFormats_returnsFormatsInCorrectOrder
()
{
adapter
.
start
();
MediaFormat
[]
formats
=
new
MediaFormat
[
10
];
MediaCodec
.
Callback
mediaCodecCallback
=
adapter
.
getMediaCodecCallback
();
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
...
...
@@ -212,6 +214,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
getOutputFormat_afterFlush_returnsPreviousFormat
()
throws
InterruptedException
{
adapter
.
start
();
MediaFormat
format
=
new
MediaFormat
();
adapter
.
getMediaCodecCallback
().
onOutputFormatChanged
(
codec
,
format
);
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
...
...
@@ -223,13 +226,13 @@ public class AsynchronousMediaCodecAdapterTest {
@Test
public
void
shutdown_withPendingFlush_cancelsFlush
()
throws
InterruptedException
{
Atomic
Boolean
onCodecStartCalled
=
new
AtomicBoolean
(
false
);
Runnable
onCodecStart
=
()
->
onCodecStartCalled
.
set
(
true
);
adapter
.
s
etOnCodecStart
(
onCodecStart
);
Atomic
Integer
onCodecStartCalled
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(()
->
onCodecStartCalled
.
incrementAndGet
()
);
adapter
.
s
tart
(
);
adapter
.
flush
();
adapter
.
shutdown
();
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
/* time= */
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
assertThat
(
onCodecStartCalled
.
get
()).
is
False
(
);
assertThat
(
onCodecStartCalled
.
get
()).
is
EqualTo
(
1
);
}
}
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/DedicatedThreadAsyncMediaCodecAdapterTest.java
View file @
fb42f818
...
...
@@ -19,7 +19,7 @@ package com.google.android.exoplayer2.mediacodec;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
areEqual
;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
waitUntilAllEventsAreExecuted
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
static
org
.
robolectric
.
Shadows
.
shadowOf
;
import
android.media.MediaCodec
;
...
...
@@ -47,16 +47,18 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
private
MediaCodec
.
BufferInfo
bufferInfo
=
null
;
@Before
public
void
set
u
p
()
throws
IOException
{
public
void
set
U
p
()
throws
IOException
{
codec
=
MediaCodec
.
createByCodecName
(
"h264"
);
handlerThread
=
new
TestHandlerThread
(
"TestHandlerThread"
);
adapter
=
new
DedicatedThreadAsyncMediaCodecAdapter
(
codec
,
handlerThread
);
adapter
.
setCodecStartRunnable
(()
->
{});
bufferInfo
=
new
MediaCodec
.
BufferInfo
();
}
@After
public
void
tearDown
()
{
adapter
.
shutdown
();
assertThat
(
TestHandlerThread
.
INSTANCES_STARTED
.
get
()).
isEqualTo
(
0
);
}
...
...
@@ -67,41 +69,14 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
}
@Test
public
void
start_calledTwice_throwsException
()
{
adapter
.
start
();
try
{
adapter
.
start
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_withoutStart_throwsException
()
{
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_withAfterFlushFailed_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
IllegalStateException
(
"codec#start() exception"
);
if
(
codecStartCalls
.
incrementAndGet
()
==
2
)
{
throw
new
IllegalStateException
(
"codec#start() exception"
);
}
});
adapter
.
start
();
adapter
.
flush
();
...
...
@@ -110,11 +85,8 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
waitUntilAllEventsAreExecuted
(
handlerThread
.
getLooper
(),
/* time= */
5
,
TimeUnit
.
SECONDS
))
.
isTrue
();
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueInputBufferIndex
());
}
@Test
...
...
@@ -144,9 +116,6 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
@Test
public
void
dequeueInputBufferIndex_withFlushCompletedAndInputBuffer_returnsInputBuffer
()
throws
InterruptedException
{
// Disable calling codec.start() after flush to avoid receiving buffers from the
// shadow codec impl
adapter
.
setOnCodecStart
(()
->
{});
adapter
.
start
();
Looper
looper
=
handlerThread
.
getLooper
();
Handler
handler
=
new
Handler
(
looper
);
...
...
@@ -169,39 +138,18 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
adapter
.
start
();
adapter
.
onMediaCodecError
(
new
IllegalStateException
(
"error from codec"
));
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueOutputBufferIndex_withoutStart_throwsException
()
{
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueOutputBufferIndex_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueInputBufferIndex
());
}
@Test
public
void
dequeueOutputBufferIndex_withInternalException_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
RuntimeException
(
"codec#start() exception"
);
if
(
codecStartCalls
.
incrementAndGet
()
==
2
)
{
throw
new
RuntimeException
(
"codec#start() exception"
);
}
});
adapter
.
start
();
adapter
.
flush
();
...
...
@@ -210,11 +158,7 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
waitUntilAllEventsAreExecuted
(
handlerThread
.
getLooper
(),
/* time= */
5
,
TimeUnit
.
SECONDS
))
.
isTrue
();
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
));
}
@Test
...
...
@@ -275,42 +219,14 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
adapter
.
start
();
adapter
.
onMediaCodecError
(
new
IllegalStateException
(
"error from codec"
));
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
getOutputFormat_withoutStart_throwsException
()
{
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
getOutputFormat_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
));
}
@Test
public
void
getOutputFormat_withoutFormatReceived_throwsException
()
{
adapter
.
start
();
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
getOutputFormat
());
}
@Test
...
...
@@ -352,27 +268,9 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
}
@Test
public
void
flush_withoutStarted_throwsException
()
{
try
{
adapter
.
flush
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
flush_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
flush
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
flush_multipleTimes_onlyLastFlushExecutes
()
throws
InterruptedException
{
AtomicInteger
onCodecStartCount
=
new
AtomicInteger
(
0
);
adapter
.
set
OnCodecStart
(()
->
onCodecStartCount
.
incrementAndGet
());
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
set
CodecStartRunnable
(()
->
codecStartCalls
.
incrementAndGet
());
adapter
.
start
();
Looper
looper
=
handlerThread
.
getLooper
();
Handler
handler
=
new
Handler
(
looper
);
...
...
@@ -384,23 +282,23 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
adapter
.
flush
();
// Enqueues a second flush event
handler
.
post
(()
->
adapter
.
onInputBufferAvailable
(
codec
,
3
));
// Progress the looper until the milestoneCount is increased
- first flush event
//
should have been a no-op
// Progress the looper until the milestoneCount is increased
.
//
adapter.start() will call codec.start(). First flush event should not call codec.start().
ShadowLooper
shadowLooper
=
shadowOf
(
looper
);
while
(
milestoneCount
.
get
()
<
1
)
{
shadowLooper
.
runOneTask
();
}
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
0
);
assertThat
(
codecStartCalls
.
get
()).
isEqualTo
(
1
);
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
/* time= */
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
assertThat
(
adapter
.
dequeueInputBufferIndex
()).
isEqualTo
(
3
);
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
1
);
assertThat
(
codecStartCalls
.
get
()).
isEqualTo
(
2
);
}
@Test
public
void
flush_andImmediatelyShutdown_flushIsNoOp
()
throws
InterruptedException
{
AtomicInteger
onCodecStartCount
=
new
AtomicInteger
(
0
);
adapter
.
set
OnCodecStart
(()
->
onCodecStartCount
.
incrementAndGet
());
adapter
.
set
CodecStartRunnable
(()
->
onCodecStartCount
.
incrementAndGet
());
adapter
.
start
();
// Obtain looper when adapter is started
Looper
looper
=
handlerThread
.
getLooper
();
...
...
@@ -408,8 +306,8 @@ public class DedicatedThreadAsyncMediaCodecAdapterTest {
adapter
.
shutdown
();
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
//
only shutdown flushes the MediaCodecAsync handler
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
0
);
//
Only adapter.start() calls onCodecStart.
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
1
);
}
private
static
class
TestHandlerThread
extends
HandlerThread
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/mediacodec/MultiLockAsyncMediaCodecAdapterTest.java
View file @
fb42f818
...
...
@@ -19,7 +19,7 @@ package com.google.android.exoplayer2.mediacodec;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
areEqual
;
import
static
com
.
google
.
android
.
exoplayer2
.
mediacodec
.
MediaCodecTestUtils
.
waitUntilAllEventsAreExecuted
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
static
org
.
robolectric
.
Shadows
.
shadowOf
;
import
android.media.MediaCodec
;
...
...
@@ -44,20 +44,21 @@ public class MultiLockAsyncMediaCodecAdapterTest {
private
MultiLockAsyncMediaCodecAdapter
adapter
;
private
MediaCodec
codec
;
private
MediaCodec
.
BufferInfo
bufferInfo
=
null
;
private
MediaCodecAsyncCallback
mediaCodecAsyncCallbackSpy
;
private
TestHandlerThread
handlerThread
;
@Before
public
void
set
u
p
()
throws
IOException
{
public
void
set
U
p
()
throws
IOException
{
codec
=
MediaCodec
.
createByCodecName
(
"h264"
);
handlerThread
=
new
TestHandlerThread
(
"TestHandlerThread"
);
adapter
=
new
MultiLockAsyncMediaCodecAdapter
(
codec
,
handlerThread
);
adapter
.
setCodecStartRunnable
(()
->
{});
bufferInfo
=
new
MediaCodec
.
BufferInfo
();
}
@After
public
void
tearDown
()
{
adapter
.
shutdown
();
assertThat
(
TestHandlerThread
.
INSTANCES_STARTED
.
get
()).
isEqualTo
(
0
);
}
...
...
@@ -68,41 +69,14 @@ public class MultiLockAsyncMediaCodecAdapterTest {
}
@Test
public
void
start_calledTwice_throwsException
()
{
adapter
.
start
();
try
{
adapter
.
start
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_withoutStart_throwsException
()
{
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueInputBufferIndex_withAfterFlushFailed_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
IllegalStateException
(
"codec#start() exception"
);
if
(
codecStartCalls
.
incrementAndGet
()
==
2
)
{
throw
new
IllegalStateException
(
"codec#start() exception"
);
}
});
adapter
.
start
();
adapter
.
flush
();
...
...
@@ -111,11 +85,7 @@ public class MultiLockAsyncMediaCodecAdapterTest {
waitUntilAllEventsAreExecuted
(
handlerThread
.
getLooper
(),
/* time= */
5
,
TimeUnit
.
SECONDS
))
.
isTrue
();
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueInputBufferIndex
());
}
@Test
...
...
@@ -145,9 +115,6 @@ public class MultiLockAsyncMediaCodecAdapterTest {
@Test
public
void
dequeueInputBufferIndex_withFlushCompletedAndInputBuffer_returnsInputBuffer
()
throws
InterruptedException
{
// Disable calling codec.start() after flush to avoid receiving buffers from the
// shadow codec impl
adapter
.
setOnCodecStart
(()
->
{});
adapter
.
start
();
Looper
looper
=
handlerThread
.
getLooper
();
Handler
handler
=
new
Handler
(
looper
);
...
...
@@ -170,39 +137,19 @@ public class MultiLockAsyncMediaCodecAdapterTest {
adapter
.
start
();
adapter
.
onMediaCodecError
(
new
IllegalStateException
(
"error from codec"
));
try
{
adapter
.
dequeueInputBufferIndex
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueOutputBufferIndex_withoutStart_throwsException
()
{
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueInputBufferIndex
());
}
@Test
public
void
dequeueOutputBufferIndex_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
dequeueOutputBufferIndex_withInternalException_throwsException
()
throws
InterruptedException
{
adapter
.
setOnCodecStart
(
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
setCodecStartRunnable
(
()
->
{
throw
new
RuntimeException
(
"codec#start() exception"
);
if
(
codecStartCalls
.
incrementAndGet
()
==
2
)
{
throw
new
RuntimeException
(
"codec#start() exception"
);
}
});
adapter
.
start
();
adapter
.
flush
();
...
...
@@ -211,11 +158,7 @@ public class MultiLockAsyncMediaCodecAdapterTest {
waitUntilAllEventsAreExecuted
(
handlerThread
.
getLooper
(),
/* time= */
5
,
TimeUnit
.
SECONDS
))
.
isTrue
();
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
));
}
@Test
...
...
@@ -276,42 +219,14 @@ public class MultiLockAsyncMediaCodecAdapterTest {
adapter
.
start
();
adapter
.
onMediaCodecError
(
new
IllegalStateException
(
"error from codec"
));
try
{
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
);
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
getOutputFormat_withoutStart_throwsException
()
{
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
getOutputFormat_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
dequeueOutputBufferIndex
(
bufferInfo
));
}
@Test
public
void
getOutputFormat_withoutFormatReceived_throwsException
()
{
adapter
.
start
();
try
{
adapter
.
getOutputFormat
();
fail
();
}
catch
(
IllegalStateException
expected
)
{
}
assertThrows
(
IllegalStateException
.
class
,
()
->
adapter
.
getOutputFormat
());
}
@Test
...
...
@@ -353,27 +268,9 @@ public class MultiLockAsyncMediaCodecAdapterTest {
}
@Test
public
void
flush_withoutStarted_throwsException
()
{
try
{
adapter
.
flush
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
flush_afterShutdown_throwsException
()
{
adapter
.
start
();
adapter
.
shutdown
();
try
{
adapter
.
flush
();
}
catch
(
IllegalStateException
expected
)
{
}
}
@Test
public
void
flush_multipleTimes_onlyLastFlushExecutes
()
throws
InterruptedException
{
AtomicInteger
onCodecStartCount
=
new
AtomicInteger
(
0
);
adapter
.
set
OnCodecStart
(()
->
onCodecStartCount
.
incrementAndGet
());
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
set
CodecStartRunnable
(()
->
codecStartCalls
.
incrementAndGet
());
adapter
.
start
();
Looper
looper
=
handlerThread
.
getLooper
();
Handler
handler
=
new
Handler
(
looper
);
...
...
@@ -385,23 +282,23 @@ public class MultiLockAsyncMediaCodecAdapterTest {
adapter
.
flush
();
// Enqueues a second flush event
handler
.
post
(()
->
adapter
.
onInputBufferAvailable
(
codec
,
3
));
// Progress the looper until the milestoneCount is increased
- first flush event
// should have been a no-op
// Progress the looper until the milestoneCount is increased
:
//
adapter.start() called codec.start() but first flush event
should have been a no-op
ShadowLooper
shadowLooper
=
shadowOf
(
looper
);
while
(
milestoneCount
.
get
()
<
1
)
{
shadowLooper
.
runOneTask
();
}
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
0
);
assertThat
(
codecStartCalls
.
get
()).
isEqualTo
(
1
);
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
/* time= */
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
assertThat
(
adapter
.
dequeueInputBufferIndex
()).
isEqualTo
(
3
);
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
1
);
assertThat
(
codecStartCalls
.
get
()).
isEqualTo
(
2
);
}
@Test
public
void
flush_andImmediatelyShutdown_flushIsNoOp
()
throws
InterruptedException
{
AtomicInteger
onCodecStartCount
=
new
AtomicInteger
(
0
);
adapter
.
set
OnCodecStart
(()
->
onCodecStartCount
.
incrementAndGet
());
AtomicInteger
codecStartCalls
=
new
AtomicInteger
(
0
);
adapter
.
set
CodecStartRunnable
(()
->
codecStartCalls
.
incrementAndGet
());
adapter
.
start
();
// Obtain looper when adapter is started.
Looper
looper
=
handlerThread
.
getLooper
();
...
...
@@ -409,8 +306,8 @@ public class MultiLockAsyncMediaCodecAdapterTest {
adapter
.
shutdown
();
assertThat
(
waitUntilAllEventsAreExecuted
(
looper
,
5
,
TimeUnit
.
SECONDS
)).
isTrue
();
// Only
shutdown flushes the MediaCodecAsync handler.
assertThat
(
onCodecStartCount
.
get
()).
isEqualTo
(
0
);
// Only
adapter.start() called codec#start()
assertThat
(
codecStartCalls
.
get
()).
isEqualTo
(
1
);
}
private
static
class
TestHandlerThread
extends
HandlerThread
{
...
...
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