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