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
ef7d8c66
authored
Feb 08, 2022
by
claincly
Committed by
Ian Baker
Feb 08, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Make Codec an interface and introduce DefaultCodec.
PiperOrigin-RevId: 427191610
parent
17159f66
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
483 additions
and
453 deletions
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AudioTranscodingSamplePipeline.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CodecFactoryUtil.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodec.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultDecoderFactory.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformationException.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/AudioTranscodingSamplePipeline.java
View file @
ef7d8c66
...
@@ -150,7 +150,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -150,7 +150,7 @@ import org.checkerframework.dataflow.qual.Pure;
@Override
@Override
public
void
releaseOutputBuffer
()
throws
TransformationException
{
public
void
releaseOutputBuffer
()
throws
TransformationException
{
encoder
.
releaseOutputBuffer
();
encoder
.
releaseOutputBuffer
(
/* render= */
false
);
}
}
@Override
@Override
...
@@ -188,7 +188,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -188,7 +188,7 @@ import org.checkerframework.dataflow.qual.Pure;
feedEncoder
(
decoderOutputBuffer
);
feedEncoder
(
decoderOutputBuffer
);
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
decoder
.
releaseOutputBuffer
();
decoder
.
releaseOutputBuffer
(
/* render= */
false
);
}
}
return
true
;
return
true
;
}
}
...
@@ -243,7 +243,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -243,7 +243,7 @@ import org.checkerframework.dataflow.qual.Pure;
speedChangingAudioProcessor
.
queueInput
(
decoderOutputBuffer
);
speedChangingAudioProcessor
.
queueInput
(
decoderOutputBuffer
);
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
if
(!
decoderOutputBuffer
.
hasRemaining
())
{
decoder
.
releaseOutputBuffer
();
decoder
.
releaseOutputBuffer
(
/* render= */
false
);
}
}
return
true
;
return
true
;
}
}
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java
View file @
ef7d8c66
...
@@ -16,35 +16,26 @@
...
@@ -16,35 +16,26 @@
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec.BufferInfo
;
import
android.media.MediaCodec.BufferInfo
;
import
android.media.MediaFormat
;
import
android.view.Surface
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.common.collect.ImmutableList
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.util.List
;
import
java.util.List
;
import
org.checkerframework.checker.nullness.qual.EnsuresNonNullIf
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
/**
*
A wrapper around {@link MediaCodec}
.
*
Provides a layer of abstraction for interacting with decoders and encoders
.
*
*
* <p>Provides a layer of abstraction for callers that need to interact with {@link MediaCodec}.
* <p>{@link DecoderInputBuffer DecoderInputBuffers} are used as both decoders' and encoders' input
* This is done by simplifying the calls needed to queue and dequeue buffers, removing the need to
* buffers.
* track buffer indices and codec events.
*/
*/
public
final
class
Codec
{
public
interface
Codec
{
/** A factory for {@link Codec decoder} instances. */
/** A factory for {@link Codec decoder} instances. */
public
interface
DecoderFactory
{
interface
DecoderFactory
{
/** A default {@code DecoderFactory} implementation. */
/** A default {@code DecoderFactory} implementation. */
DecoderFactory
DEFAULT
=
new
DefaultDecoderFactory
();
DecoderFactory
DEFAULT
=
new
DefaultDecoderFactory
();
...
@@ -52,28 +43,28 @@ public final class Codec {
...
@@ -52,28 +43,28 @@ public final class Codec {
/**
/**
* Returns a {@link Codec} for audio decoding.
* Returns a {@link Codec} for audio decoding.
*
*
* @param format The {@link Format} (of the input data) used to determine the underlying
{@link
* @param format The {@link Format} (of the input data) used to determine the underlying
decoder
*
MediaCodec}
and its configuration values.
* and its configuration values.
* @return A
configured and started decoder wrapper
.
* @return A
{@link Codec} for audio decoding
.
* @throws TransformationException If no suitable
codec
can be created.
* @throws TransformationException If no suitable
{@link Codec}
can be created.
*/
*/
Codec
createForAudioDecoding
(
Format
format
)
throws
TransformationException
;
Codec
createForAudioDecoding
(
Format
format
)
throws
TransformationException
;
/**
/**
* Returns a {@link Codec} for video decoding.
* Returns a {@link Codec} for video decoding.
*
*
* @param format The {@link Format} (of the input data) used to determine the underlying
{@link
* @param format The {@link Format} (of the input data) used to determine the underlying
decoder
*
MediaCodec}
and its configuration values.
* and its configuration values.
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
* @return A
configured and started decoder wrapper
.
* @return A
{@link Codec} for video decoding
.
* @throws TransformationException If no suitable
codec
can be created.
* @throws TransformationException If no suitable
{@link Codec}
can be created.
*/
*/
Codec
createForVideoDecoding
(
Format
format
,
Surface
outputSurface
)
Codec
createForVideoDecoding
(
Format
format
,
Surface
outputSurface
)
throws
TransformationException
;
throws
TransformationException
;
}
}
/** A factory for {@link Codec encoder} instances. */
/** A factory for {@link Codec encoder} instances. */
public
interface
EncoderFactory
{
interface
EncoderFactory
{
/** A default {@code EncoderFactory} implementation. */
/** A default {@code EncoderFactory} implementation. */
EncoderFactory
DEFAULT
=
new
DefaultEncoderFactory
();
EncoderFactory
DEFAULT
=
new
DefaultEncoderFactory
();
...
@@ -85,12 +76,12 @@ public final class Codec {
...
@@ -85,12 +76,12 @@ public final class Codec {
* {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code
* {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code
* format} is not necessarily allowed.
* format} is not necessarily allowed.
*
*
* @param format The {@link Format} (of the output data) used to determine the underlying
{@link
* @param format The {@link Format} (of the output data) used to determine the underlying
*
MediaCodec}
and its configuration values.
*
encoder
and its configuration values.
* @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME
* @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME
* types}.
* types}.
* @return A
configured and started encoder wrapper
.
* @return A
{@link Codec} for audio encoding
.
* @throws TransformationException If no suitable
codec
can be created.
* @throws TransformationException If no suitable
{@link Codec}
can be created.
*/
*/
Codec
createForAudioEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
Codec
createForAudioEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
throws
TransformationException
;
throws
TransformationException
;
...
@@ -102,334 +93,123 @@ public final class Codec {
...
@@ -102,334 +93,123 @@ public final class Codec {
* {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code
* {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code
* format} is not necessarily allowed.
* format} is not necessarily allowed.
*
*
* @param format The {@link Format} (of the output data) used to determine the underlying {@link
* @param format The {@link Format} (of the output data) used to determine the underlying
* MediaCodec} and its configuration values. {@link Format#sampleMimeType}, {@link
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width}
* Format#width} and {@link Format#height} must be set to those of the desired output video
* and {@link Format#height} must be set to those of the desired output video format. {@link
* format. {@link Format#rotationDegrees} should be 0. The video should always be in
* Format#rotationDegrees} should be 0. The video should always be in landscape orientation.
* landscape orientation.
* @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME
* @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME
* types}.
* types}.
* @return A
configured and started encoder wrapper
.
* @return A
{@link Codec} for video encoding
.
* @throws TransformationException If no suitable
codec
can be created.
* @throws TransformationException If no suitable
{@link Codec}
can be created.
*/
*/
Codec
createForVideoEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
Codec
createForVideoEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
throws
TransformationException
;
throws
TransformationException
;
}
}
// MediaCodec decoders always output 16 bit PCM, unless configured to output PCM float.
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers.
private
static
final
int
MEDIA_CODEC_PCM_ENCODING
=
C
.
ENCODING_PCM_16BIT
;
private
final
BufferInfo
outputBufferInfo
;
private
final
MediaCodec
mediaCodec
;
private
final
Format
configurationFormat
;
@Nullable
private
final
Surface
inputSurface
;
private
@MonotonicNonNull
Format
outputFormat
;
@Nullable
private
ByteBuffer
outputBuffer
;
private
int
inputBufferIndex
;
private
int
outputBufferIndex
;
private
boolean
inputStreamEnded
;
private
boolean
outputStreamEnded
;
/**
/**
*
Creates a {@code Codec} from a configured and started {@link Media
Codec}.
*
Returns the {@link Format} used for configuring the {@code
Codec}.
*
*
* @param mediaCodec The configured and started {@link MediaCodec}.
* <p>The configuration {@link Format} is the input {@link Format} used by the {@link
* @param configurationFormat See {@link #getConfigurationFormat()}.
* DecoderFactory} or output {@link Format} used by the {@link EncoderFactory} for selecting and
* @param inputSurface The input {@link Surface} if the {@link MediaCodec} receives input from a
* configuring the underlying decoder or encoder.
* surface.
*/
*/
public
Codec
(
MediaCodec
mediaCodec
,
Format
configurationFormat
,
@Nullable
Surface
inputSurface
)
{
Format
getConfigurationFormat
();
this
.
mediaCodec
=
mediaCodec
;
this
.
configurationFormat
=
configurationFormat
;
this
.
inputSurface
=
inputSurface
;
outputBufferInfo
=
new
BufferInfo
();
inputBufferIndex
=
C
.
INDEX_UNSET
;
outputBufferIndex
=
C
.
INDEX_UNSET
;
}
/**
/**
* Returns the
{@link Format} used for configuring the codec
.
* Returns the
input {@link Surface} of an underlying video encoder
.
*
*
* <p>The configuration {@link Format} is the input {@link Format} used by the {@link
* <p>This method must only be called on video encoders because audio/video decoders and audio
* DecoderFactory} or output {@link Format} used by the {@link EncoderFactory} for selecting and
* encoders don't use a {@link Surface} as input.
* configuring the underlying {@link MediaCodec}.
*/
*/
public
Format
getConfigurationFormat
()
{
Surface
getInputSurface
();
return
configurationFormat
;
}
/** Returns the input {@link Surface}, or null if the input is not a surface. */
@Nullable
public
Surface
getInputSurface
()
{
return
inputSurface
;
}
/**
/**
* Dequeues a writable input buffer, if available.
* Dequeues a writable input buffer, if available.
*
*
* @param inputBuffer The buffer where the dequeued buffer data is stored.
* <p>This method must not be called from video encoders because they must use {@link Surface
* surfaces} as inputs.
*
* @param inputBuffer The buffer where the dequeued buffer data is stored, at {@link
* DecoderInputBuffer#data inputBuffer.data}.
* @return Whether an input buffer is ready to be used.
* @return Whether an input buffer is ready to be used.
* @throws TransformationException If the underlying
{@link MediaCodec}
encounters a problem.
* @throws TransformationException If the underlying
decoder or encoder
encounters a problem.
*/
*/
@EnsuresNonNullIf
(
expression
=
"#1.data"
,
result
=
true
)
boolean
maybeDequeueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
;
public
boolean
maybeDequeueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
{
if
(
inputStreamEnded
)
{
return
false
;
}
if
(
inputBufferIndex
<
0
)
{
try
{
inputBufferIndex
=
mediaCodec
.
dequeueInputBuffer
(
/* timeoutUs= */
0
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
if
(
inputBufferIndex
<
0
)
{
return
false
;
}
try
{
inputBuffer
.
data
=
mediaCodec
.
getInputBuffer
(
inputBufferIndex
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
inputBuffer
.
clear
();
}
checkNotNull
(
inputBuffer
.
data
);
return
true
;
}
/**
/**
* Queues an input buffer to the
decoder. No buffers may be queued after an
{@link
* Queues an input buffer to the
{@code Codec}. No buffers may be queued after
{@link
* DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued.
* DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued.
*
*
* <p>This method must not be called from video encoders because they must use {@link Surface
* surfaces} as inputs.
*
* @param inputBuffer The {@link DecoderInputBuffer input buffer}.
* @param inputBuffer The {@link DecoderInputBuffer input buffer}.
* @throws IllegalStateException If called again after an {@link
* @throws TransformationException If the underlying decoder or encoder encounters a problem.
* DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued.
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
*/
*/
public
void
queueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
{
void
queueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
;
checkState
(
!
inputStreamEnded
,
"Input buffer can not be queued after the input stream has ended."
);
int
offset
=
0
;
int
size
=
0
;
if
(
inputBuffer
.
data
!=
null
&&
inputBuffer
.
data
.
hasRemaining
())
{
offset
=
inputBuffer
.
data
.
position
();
size
=
inputBuffer
.
data
.
remaining
();
}
int
flags
=
0
;
if
(
inputBuffer
.
isEndOfStream
())
{
inputStreamEnded
=
true
;
flags
=
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
;
}
try
{
mediaCodec
.
queueInputBuffer
(
inputBufferIndex
,
offset
,
size
,
inputBuffer
.
timeUs
,
flags
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
inputBufferIndex
=
C
.
INDEX_UNSET
;
inputBuffer
.
data
=
null
;
}
/**
/**
* Signals end-of-stream on input to a video encoder.
* Signals end-of-stream on input to a video encoder.
*
*
* <p>This method
does not need to be called for audio/video decoders or audio encoders. For these
* <p>This method
must only be called on video encoders because they must use a {@link Surface} as
*
the {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag should be set on the last input buffer
*
input. For audio/video decoders or audio encoders, the {@link C#BUFFER_FLAG_END_OF_STREAM} flag
* {@link #queueInputBuffer(DecoderInputBuffer) queued}.
*
should be set on the last input buffer
{@link #queueInputBuffer(DecoderInputBuffer) queued}.
*
*
* @throws IllegalStateException If the codec is not an encoder receiving input from a {@link
* @throws TransformationException If the underlying video encoder encounters a problem.
* Surface}.
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
*/
*/
public
void
signalEndOfInputStream
()
throws
TransformationException
{
void
signalEndOfInputStream
()
throws
TransformationException
;
checkState
(
mediaCodec
.
getCodecInfo
().
isEncoder
()
&&
inputSurface
!=
null
);
try
{
mediaCodec
.
signalEndOfInputStream
();
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
}
/**
/**
* Returns the current output format,
if
available.
* Returns the current output format,
or {@code null} if un
available.
*
*
* @throws TransformationException If the underlying
{@link MediaCodec}
encounters a problem.
* @throws TransformationException If the underlying
decoder or encoder
encounters a problem.
*/
*/
@Nullable
@Nullable
public
Format
getOutputFormat
()
throws
TransformationException
{
Format
getOutputFormat
()
throws
TransformationException
;
// The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now.
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
false
);
return
outputFormat
;
}
/**
/**
* Returns the current output {@link ByteBuffer}, if available.
* Returns the current output {@link ByteBuffer}, or {@code null} if unavailable.
*
* <p>This method must not be called on video decoders because they must output to a {@link
* Surface}.
*
*
* @throws TransformationException If the underlying
{@link MediaCodec}
encounters a problem.
* @throws TransformationException If the underlying
decoder or encoder
encounters a problem.
*/
*/
@Nullable
@Nullable
public
ByteBuffer
getOutputBuffer
()
throws
TransformationException
{
ByteBuffer
getOutputBuffer
()
throws
TransformationException
;
return
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
true
)
?
outputBuffer
:
null
;
}
/**
/**
* Returns the {@link BufferInfo} associated with the current output buffer, if available.
* Returns the {@link BufferInfo} associated with the current output buffer, or {@code null} if
* there is no output buffer available.
*
*
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
* <p>This method returns {@code null} if and only if {@link #getOutputBuffer()} returns null.
*
* @throws TransformationException If the underlying decoder or encoder encounters a problem.
*/
*/
@Nullable
@Nullable
public
BufferInfo
getOutputBufferInfo
()
throws
TransformationException
{
BufferInfo
getOutputBufferInfo
()
throws
TransformationException
;
return
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
false
)
?
outputBufferInfo
:
null
;
}
/**
/**
* Releases the current output buffer.
* Releases the current output buffer.
*
*
* <p>This should be called after the buffer has been processed. The next output buffer will not
* <p>Only set {@code render} to {@code true} when the {@code Codec} is a video decoder. Setting
* be available until the previous has been released.
* {@code render} to {@code true} will first render the buffer to the output surface. In this
*
* case, the surface will release the buffer back to the {@code Codec} once it is no longer
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
*/
public
void
releaseOutputBuffer
()
throws
TransformationException
{
releaseOutputBuffer
(
/* render= */
false
);
}
/**
* Releases the current output buffer. If the {@link MediaCodec} was configured with an output
* surface, setting {@code render} to {@code true} will first send the buffer to the output
* surface. The surface will release the buffer back to the codec once it is no longer
* used/displayed.
* used/displayed.
*
*
* <p>This should be called after the buffer has been processed. The next output buffer will not
* <p>This should be called after the buffer has been processed. The next output buffer will not
* be available until the
previous
has been released.
* be available until the
current output buffer
has been released.
*
*
* @param render Whether the buffer needs to be
sent
to the output {@link Surface}.
* @param render Whether the buffer needs to be
rendered
to the output {@link Surface}.
* @throws TransformationException If the underlying
{@link MediaCodec}
encounters a problem.
* @throws TransformationException If the underlying
decoder or encoder
encounters a problem.
*/
*/
public
void
releaseOutputBuffer
(
boolean
render
)
throws
TransformationException
{
void
releaseOutputBuffer
(
boolean
render
)
throws
TransformationException
;
outputBuffer
=
null
;
try
{
mediaCodec
.
releaseOutputBuffer
(
outputBufferIndex
,
render
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
outputBufferIndex
=
C
.
INDEX_UNSET
;
}
/** Returns whether the codec output stream has ended, and no more data can be dequeued. */
public
boolean
isEnded
()
{
return
outputStreamEnded
&&
outputBufferIndex
==
C
.
INDEX_UNSET
;
}
/** Releases the underlying codec. */
public
void
release
()
{
outputBuffer
=
null
;
if
(
inputSurface
!=
null
)
{
inputSurface
.
release
();
}
mediaCodec
.
release
();
}
/**
/**
* Attempts to dequeue an output buffer if there is no output buffer pending. Does nothing
* Returns whether the {@code Codec}'s output stream has ended, and no more data can be dequeued.
* otherwise.
*
* @param setOutputBuffer Whether to read the bytes of the dequeued output buffer and copy them
* into {@link #outputBuffer}.
* @return Whether there is an output buffer available.
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
*/
*/
private
boolean
maybeDequeueOutputBuffer
(
boolean
setOutputBuffer
)
throws
TransformationException
{
boolean
isEnded
();
if
(
outputBufferIndex
>=
0
)
{
return
true
;
}
if
(
outputStreamEnded
)
{
return
false
;
}
try
{
/** Releases the {@code Codec}. */
outputBufferIndex
=
mediaCodec
.
dequeueOutputBuffer
(
outputBufferInfo
,
/* timeoutUs= */
0
);
void
release
();
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
if
(
outputBufferIndex
<
0
)
{
if
(
outputBufferIndex
==
MediaCodec
.
INFO_OUTPUT_FORMAT_CHANGED
)
{
outputFormat
=
getFormat
(
mediaCodec
.
getOutputFormat
());
}
return
false
;
}
if
((
outputBufferInfo
.
flags
&
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
)
!=
0
)
{
outputStreamEnded
=
true
;
if
(
outputBufferInfo
.
size
==
0
)
{
releaseOutputBuffer
();
return
false
;
}
}
if
((
outputBufferInfo
.
flags
&
MediaCodec
.
BUFFER_FLAG_CODEC_CONFIG
)
!=
0
)
{
// Encountered a CSD buffer, skip it.
releaseOutputBuffer
();
return
false
;
}
if
(
setOutputBuffer
)
{
try
{
outputBuffer
=
checkNotNull
(
mediaCodec
.
getOutputBuffer
(
outputBufferIndex
));
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
outputBuffer
.
position
(
outputBufferInfo
.
offset
);
outputBuffer
.
limit
(
outputBufferInfo
.
offset
+
outputBufferInfo
.
size
);
}
return
true
;
}
private
TransformationException
createTransformationException
(
Exception
cause
)
{
boolean
isEncoder
=
mediaCodec
.
getCodecInfo
().
isEncoder
();
boolean
isVideo
=
MimeTypes
.
isVideo
(
configurationFormat
.
sampleMimeType
);
String
componentName
=
(
isVideo
?
"Video"
:
"Audio"
)
+
(
isEncoder
?
"Encoder"
:
"Decoder"
);
return
TransformationException
.
createForCodec
(
cause
,
componentName
,
configurationFormat
,
mediaCodec
.
getName
(),
isEncoder
?
TransformationException
.
ERROR_CODE_ENCODING_FAILED
:
TransformationException
.
ERROR_CODE_DECODING_FAILED
);
}
private
static
Format
getFormat
(
MediaFormat
mediaFormat
)
{
ImmutableList
.
Builder
<
byte
[]>
csdBuffers
=
new
ImmutableList
.
Builder
<>();
int
csdIndex
=
0
;
while
(
true
)
{
@Nullable
ByteBuffer
csdByteBuffer
=
mediaFormat
.
getByteBuffer
(
"csd-"
+
csdIndex
);
if
(
csdByteBuffer
==
null
)
{
break
;
}
byte
[]
csdBufferData
=
new
byte
[
csdByteBuffer
.
remaining
()];
csdByteBuffer
.
get
(
csdBufferData
);
csdBuffers
.
add
(
csdBufferData
);
csdIndex
++;
}
String
mimeType
=
mediaFormat
.
getString
(
MediaFormat
.
KEY_MIME
);
Format
.
Builder
formatBuilder
=
new
Format
.
Builder
()
.
setSampleMimeType
(
mediaFormat
.
getString
(
MediaFormat
.
KEY_MIME
))
.
setInitializationData
(
csdBuffers
.
build
());
if
(
MimeTypes
.
isVideo
(
mimeType
))
{
formatBuilder
.
setWidth
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_WIDTH
))
.
setHeight
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_HEIGHT
));
}
else
if
(
MimeTypes
.
isAudio
(
mimeType
))
{
// TODO(internal b/178685617): Only set the PCM encoding for audio/raw, once we have a way to
// simulate more realistic codec input/output formats in tests.
formatBuilder
.
setChannelCount
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
))
.
setSampleRate
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
))
.
setPcmEncoding
(
MEDIA_CODEC_PCM_ENCODING
);
}
return
formatBuilder
.
build
();
}
}
}
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/CodecFactoryUtil.java
deleted
100644 → 0
View file @
17159f66
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
android.media.MediaCodec
;
import
android.media.MediaFormat
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.util.TraceUtil
;
import
java.io.IOException
;
import
org.checkerframework.checker.nullness.qual.RequiresNonNull
;
/** Utility methods for {@link Codec}'s factory methods. */
/* package */
final
class
CodecFactoryUtil
{
/** Creates a {@link Codec}. */
@RequiresNonNull
(
"#1.sampleMimeType"
)
public
static
Codec
createCodec
(
Format
format
,
MediaFormat
mediaFormat
,
@Nullable
String
mediaCodecName
,
boolean
isVideo
,
boolean
isDecoder
,
@Nullable
Surface
outputSurface
)
throws
TransformationException
{
@Nullable
MediaCodec
mediaCodec
=
null
;
@Nullable
Surface
inputSurface
=
null
;
try
{
mediaCodec
=
mediaCodecName
!=
null
?
MediaCodec
.
createByCodecName
(
mediaCodecName
)
:
isDecoder
?
MediaCodec
.
createDecoderByType
(
format
.
sampleMimeType
)
:
MediaCodec
.
createEncoderByType
(
format
.
sampleMimeType
);
configureCodec
(
mediaCodec
,
mediaFormat
,
isDecoder
,
outputSurface
);
if
(
isVideo
&&
!
isDecoder
)
{
inputSurface
=
mediaCodec
.
createInputSurface
();
}
startCodec
(
mediaCodec
);
}
catch
(
Exception
e
)
{
if
(
inputSurface
!=
null
)
{
inputSurface
.
release
();
}
if
(
mediaCodec
!=
null
)
{
mediaCodecName
=
mediaCodec
.
getName
();
mediaCodec
.
release
();
}
throw
createTransformationException
(
e
,
format
,
isVideo
,
isDecoder
,
mediaCodecName
);
}
return
new
Codec
(
mediaCodec
,
format
,
inputSurface
);
}
/** Creates a {@link TransformationException}. */
public
static
TransformationException
createTransformationException
(
Exception
cause
,
Format
format
,
boolean
isVideo
,
boolean
isDecoder
,
@Nullable
String
mediaCodecName
)
{
String
componentName
=
(
isVideo
?
"Video"
:
"Audio"
)
+
(
isDecoder
?
"Decoder"
:
"Encoder"
);
if
(
cause
instanceof
IOException
||
cause
instanceof
MediaCodec
.
CodecException
)
{
return
TransformationException
.
createForCodec
(
cause
,
componentName
,
format
,
mediaCodecName
,
isDecoder
?
TransformationException
.
ERROR_CODE_DECODER_INIT_FAILED
:
TransformationException
.
ERROR_CODE_ENCODER_INIT_FAILED
);
}
if
(
cause
instanceof
IllegalArgumentException
)
{
return
TransformationException
.
createForCodec
(
cause
,
componentName
,
format
,
mediaCodecName
,
isDecoder
?
TransformationException
.
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED
:
TransformationException
.
ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED
);
}
return
TransformationException
.
createForUnexpected
(
cause
);
}
private
static
void
configureCodec
(
MediaCodec
codec
,
MediaFormat
mediaFormat
,
boolean
isDecoder
,
@Nullable
Surface
outputSurface
)
{
TraceUtil
.
beginSection
(
"configureCodec"
);
codec
.
configure
(
mediaFormat
,
outputSurface
,
/* crypto= */
null
,
isDecoder
?
0
:
MediaCodec
.
CONFIGURE_FLAG_ENCODE
);
TraceUtil
.
endSection
();
}
private
static
void
startCodec
(
MediaCodec
codec
)
{
TraceUtil
.
beginSection
(
"startCodec"
);
codec
.
start
();
TraceUtil
.
endSection
();
}
private
CodecFactoryUtil
()
{}
}
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodec.java
0 → 100644
View file @
ef7d8c66
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkStateNotNull
;
import
android.media.MediaCodec
;
import
android.media.MediaCodec.BufferInfo
;
import
android.media.MediaFormat
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.TraceUtil
;
import
com.google.common.collect.ImmutableList
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
org.checkerframework.checker.nullness.qual.EnsuresNonNullIf
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/** A default {@link Codec} implementation that uses {@link MediaCodec}. */
public
final
class
DefaultCodec
implements
Codec
{
// MediaCodec decoders always output 16 bit PCM, unless configured to output PCM float.
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers.
private
static
final
int
MEDIA_CODEC_PCM_ENCODING
=
C
.
ENCODING_PCM_16BIT
;
private
final
BufferInfo
outputBufferInfo
;
private
final
Format
configurationFormat
;
private
final
MediaCodec
mediaCodec
;
@Nullable
private
final
Surface
inputSurface
;
private
@MonotonicNonNull
Format
outputFormat
;
@Nullable
private
ByteBuffer
outputBuffer
;
private
int
inputBufferIndex
;
private
int
outputBufferIndex
;
private
boolean
inputStreamEnded
;
private
boolean
outputStreamEnded
;
/**
* Creates a {@code DefaultCodec}.
*
* @param configurationFormat The {@link Format} to configure the {@code DefaultCodec}. See {@link
* #getConfigurationFormat()}. The {@link Format#sampleMimeType sampleMimeType} must not be
* {@code null}.
* @param mediaFormat The {@link MediaFormat} to configure the underlying {@link MediaCodec}.
* @param mediaCodecName The name of a specific {@link MediaCodec} to instantiate. If {@code
* null}, {@code DefaultCodec} uses {@link Format#sampleMimeType
* configurationFormat.sampleMimeType} to create the underlying {@link MediaCodec codec}.
* @param isDecoder Whether the {@code DefaultCodec} is intended as a decoder.
* @param outputSurface The output {@link Surface} if the {@link MediaCodec} outputs to a surface.
*/
public
DefaultCodec
(
Format
configurationFormat
,
MediaFormat
mediaFormat
,
@Nullable
String
mediaCodecName
,
boolean
isDecoder
,
@Nullable
Surface
outputSurface
)
throws
TransformationException
{
this
.
configurationFormat
=
configurationFormat
;
outputBufferInfo
=
new
BufferInfo
();
inputBufferIndex
=
C
.
INDEX_UNSET
;
outputBufferIndex
=
C
.
INDEX_UNSET
;
String
sampleMimeType
=
checkNotNull
(
configurationFormat
.
sampleMimeType
);
boolean
isVideo
=
MimeTypes
.
isVideo
(
sampleMimeType
);
@Nullable
MediaCodec
mediaCodec
=
null
;
@Nullable
Surface
inputSurface
=
null
;
try
{
mediaCodec
=
mediaCodecName
!=
null
?
MediaCodec
.
createByCodecName
(
mediaCodecName
)
:
isDecoder
?
MediaCodec
.
createDecoderByType
(
sampleMimeType
)
:
MediaCodec
.
createEncoderByType
(
sampleMimeType
);
configureCodec
(
mediaCodec
,
mediaFormat
,
isDecoder
,
outputSurface
);
if
(
isVideo
&&
!
isDecoder
)
{
inputSurface
=
mediaCodec
.
createInputSurface
();
}
startCodec
(
mediaCodec
);
}
catch
(
Exception
e
)
{
if
(
inputSurface
!=
null
)
{
inputSurface
.
release
();
}
if
(
mediaCodec
!=
null
)
{
mediaCodecName
=
mediaCodec
.
getName
();
mediaCodec
.
release
();
}
throw
createInitializationTransformationException
(
e
,
configurationFormat
,
isVideo
,
isDecoder
,
mediaCodecName
);
}
this
.
mediaCodec
=
mediaCodec
;
this
.
inputSurface
=
inputSurface
;
}
@Override
public
Format
getConfigurationFormat
()
{
return
configurationFormat
;
}
@Override
public
Surface
getInputSurface
()
{
return
checkStateNotNull
(
inputSurface
);
}
@Override
@EnsuresNonNullIf
(
expression
=
"#1.data"
,
result
=
true
)
public
boolean
maybeDequeueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
{
if
(
inputStreamEnded
)
{
return
false
;
}
if
(
inputBufferIndex
<
0
)
{
try
{
inputBufferIndex
=
mediaCodec
.
dequeueInputBuffer
(
/* timeoutUs= */
0
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
if
(
inputBufferIndex
<
0
)
{
return
false
;
}
try
{
inputBuffer
.
data
=
mediaCodec
.
getInputBuffer
(
inputBufferIndex
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
inputBuffer
.
clear
();
}
checkNotNull
(
inputBuffer
.
data
);
return
true
;
}
@Override
public
void
queueInputBuffer
(
DecoderInputBuffer
inputBuffer
)
throws
TransformationException
{
checkState
(
!
inputStreamEnded
,
"Input buffer can not be queued after the input stream has ended."
);
int
offset
=
0
;
int
size
=
0
;
if
(
inputBuffer
.
data
!=
null
&&
inputBuffer
.
data
.
hasRemaining
())
{
offset
=
inputBuffer
.
data
.
position
();
size
=
inputBuffer
.
data
.
remaining
();
}
int
flags
=
0
;
if
(
inputBuffer
.
isEndOfStream
())
{
inputStreamEnded
=
true
;
flags
=
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
;
}
try
{
mediaCodec
.
queueInputBuffer
(
inputBufferIndex
,
offset
,
size
,
inputBuffer
.
timeUs
,
flags
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
inputBufferIndex
=
C
.
INDEX_UNSET
;
inputBuffer
.
data
=
null
;
}
@Override
public
void
signalEndOfInputStream
()
throws
TransformationException
{
try
{
mediaCodec
.
signalEndOfInputStream
();
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
}
@Override
@Nullable
public
Format
getOutputFormat
()
throws
TransformationException
{
// The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now.
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
false
);
return
outputFormat
;
}
@Override
@Nullable
public
ByteBuffer
getOutputBuffer
()
throws
TransformationException
{
return
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
true
)
?
outputBuffer
:
null
;
}
@Override
@Nullable
public
BufferInfo
getOutputBufferInfo
()
throws
TransformationException
{
return
maybeDequeueOutputBuffer
(
/* setOutputBuffer= */
false
)
?
outputBufferInfo
:
null
;
}
@Override
public
void
releaseOutputBuffer
(
boolean
render
)
throws
TransformationException
{
outputBuffer
=
null
;
try
{
mediaCodec
.
releaseOutputBuffer
(
outputBufferIndex
,
render
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
outputBufferIndex
=
C
.
INDEX_UNSET
;
}
@Override
public
boolean
isEnded
()
{
return
outputStreamEnded
&&
outputBufferIndex
==
C
.
INDEX_UNSET
;
}
@Override
public
void
release
()
{
outputBuffer
=
null
;
if
(
inputSurface
!=
null
)
{
inputSurface
.
release
();
}
mediaCodec
.
release
();
}
/**
* Attempts to dequeue an output buffer if there is no output buffer pending. Does nothing
* otherwise.
*
* @param setOutputBuffer Whether to read the bytes of the dequeued output buffer and copy them
* into {@link #outputBuffer}.
* @return Whether there is an output buffer available.
* @throws TransformationException If the underlying {@link MediaCodec} encounters a problem.
*/
private
boolean
maybeDequeueOutputBuffer
(
boolean
setOutputBuffer
)
throws
TransformationException
{
if
(
outputBufferIndex
>=
0
)
{
return
true
;
}
if
(
outputStreamEnded
)
{
return
false
;
}
try
{
outputBufferIndex
=
mediaCodec
.
dequeueOutputBuffer
(
outputBufferInfo
,
/* timeoutUs= */
0
);
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
if
(
outputBufferIndex
<
0
)
{
if
(
outputBufferIndex
==
MediaCodec
.
INFO_OUTPUT_FORMAT_CHANGED
)
{
outputFormat
=
getFormat
(
mediaCodec
.
getOutputFormat
());
}
return
false
;
}
if
((
outputBufferInfo
.
flags
&
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
)
!=
0
)
{
outputStreamEnded
=
true
;
if
(
outputBufferInfo
.
size
==
0
)
{
releaseOutputBuffer
(
/* render= */
false
);
return
false
;
}
}
if
((
outputBufferInfo
.
flags
&
MediaCodec
.
BUFFER_FLAG_CODEC_CONFIG
)
!=
0
)
{
// Encountered a CSD buffer, skip it.
releaseOutputBuffer
(
/* render= */
false
);
return
false
;
}
if
(
setOutputBuffer
)
{
try
{
outputBuffer
=
checkNotNull
(
mediaCodec
.
getOutputBuffer
(
outputBufferIndex
));
}
catch
(
RuntimeException
e
)
{
throw
createTransformationException
(
e
);
}
outputBuffer
.
position
(
outputBufferInfo
.
offset
);
outputBuffer
.
limit
(
outputBufferInfo
.
offset
+
outputBufferInfo
.
size
);
}
return
true
;
}
private
TransformationException
createTransformationException
(
Exception
cause
)
{
boolean
isDecoder
=
!
mediaCodec
.
getCodecInfo
().
isEncoder
();
boolean
isVideo
=
MimeTypes
.
isVideo
(
configurationFormat
.
sampleMimeType
);
return
TransformationException
.
createForCodec
(
cause
,
configurationFormat
,
isVideo
,
isDecoder
,
mediaCodec
.
getName
(),
isDecoder
?
TransformationException
.
ERROR_CODE_DECODING_FAILED
:
TransformationException
.
ERROR_CODE_ENCODING_FAILED
);
}
private
static
TransformationException
createInitializationTransformationException
(
Exception
cause
,
Format
format
,
boolean
isVideo
,
boolean
isDecoder
,
@Nullable
String
mediaCodecName
)
{
if
(
cause
instanceof
IOException
||
cause
instanceof
MediaCodec
.
CodecException
)
{
return
TransformationException
.
createForCodec
(
cause
,
format
,
isVideo
,
isDecoder
,
mediaCodecName
,
isDecoder
?
TransformationException
.
ERROR_CODE_DECODER_INIT_FAILED
:
TransformationException
.
ERROR_CODE_ENCODER_INIT_FAILED
);
}
if
(
cause
instanceof
IllegalArgumentException
)
{
return
TransformationException
.
createForCodec
(
cause
,
format
,
isVideo
,
isDecoder
,
mediaCodecName
,
isDecoder
?
TransformationException
.
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED
:
TransformationException
.
ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED
);
}
return
TransformationException
.
createForUnexpected
(
cause
);
}
private
static
Format
getFormat
(
MediaFormat
mediaFormat
)
{
ImmutableList
.
Builder
<
byte
[]>
csdBuffers
=
new
ImmutableList
.
Builder
<>();
int
csdIndex
=
0
;
while
(
true
)
{
@Nullable
ByteBuffer
csdByteBuffer
=
mediaFormat
.
getByteBuffer
(
"csd-"
+
csdIndex
);
if
(
csdByteBuffer
==
null
)
{
break
;
}
byte
[]
csdBufferData
=
new
byte
[
csdByteBuffer
.
remaining
()];
csdByteBuffer
.
get
(
csdBufferData
);
csdBuffers
.
add
(
csdBufferData
);
csdIndex
++;
}
String
mimeType
=
mediaFormat
.
getString
(
MediaFormat
.
KEY_MIME
);
Format
.
Builder
formatBuilder
=
new
Format
.
Builder
()
.
setSampleMimeType
(
mediaFormat
.
getString
(
MediaFormat
.
KEY_MIME
))
.
setInitializationData
(
csdBuffers
.
build
());
if
(
MimeTypes
.
isVideo
(
mimeType
))
{
formatBuilder
.
setWidth
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_WIDTH
))
.
setHeight
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_HEIGHT
));
}
else
if
(
MimeTypes
.
isAudio
(
mimeType
))
{
// TODO(internal b/178685617): Only set the PCM encoding for audio/raw, once we have a way to
// simulate more realistic codec input/output formats in tests.
formatBuilder
.
setChannelCount
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
))
.
setSampleRate
(
mediaFormat
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
))
.
setPcmEncoding
(
MEDIA_CODEC_PCM_ENCODING
);
}
return
formatBuilder
.
build
();
}
private
static
void
configureCodec
(
MediaCodec
codec
,
MediaFormat
mediaFormat
,
boolean
isDecoder
,
@Nullable
Surface
outputSurface
)
{
TraceUtil
.
beginSection
(
"configureCodec"
);
codec
.
configure
(
mediaFormat
,
outputSurface
,
/* crypto= */
null
,
isDecoder
?
0
:
MediaCodec
.
CONFIGURE_FLAG_ENCODE
);
TraceUtil
.
endSection
();
}
private
static
void
startCodec
(
MediaCodec
codec
)
{
TraceUtil
.
beginSection
(
"startCodec"
);
codec
.
start
();
TraceUtil
.
endSection
();
}
}
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultDecoderFactory.java
View file @
ef7d8c66
...
@@ -16,7 +16,6 @@
...
@@ -16,7 +16,6 @@
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
com
.
google
.
android
.
exoplayer2
.
transformer
.
CodecFactoryUtil
.
createCodec
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Util
.
SDK_INT
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Util
.
SDK_INT
;
...
@@ -36,11 +35,10 @@ import com.google.android.exoplayer2.util.MediaFormatUtil;
...
@@ -36,11 +35,10 @@ import com.google.android.exoplayer2.util.MediaFormatUtil;
mediaFormat
,
MediaFormat
.
KEY_MAX_INPUT_SIZE
,
format
.
maxInputSize
);
mediaFormat
,
MediaFormat
.
KEY_MAX_INPUT_SIZE
,
format
.
maxInputSize
);
MediaFormatUtil
.
setCsdBuffers
(
mediaFormat
,
format
.
initializationData
);
MediaFormatUtil
.
setCsdBuffers
(
mediaFormat
,
format
.
initializationData
);
return
create
Codec
(
return
new
Default
Codec
(
format
,
format
,
mediaFormat
,
mediaFormat
,
/* mediaCodecName= */
null
,
/* mediaCodecName= */
null
,
/* isVideo= */
false
,
/* isDecoder= */
true
,
/* isDecoder= */
true
,
/* outputSurface= */
null
);
/* outputSurface= */
null
);
}
}
...
@@ -61,12 +59,7 @@ import com.google.android.exoplayer2.util.MediaFormatUtil;
...
@@ -61,12 +59,7 @@ import com.google.android.exoplayer2.util.MediaFormatUtil;
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_ALLOW_FRAME_DROP
,
0
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_ALLOW_FRAME_DROP
,
0
);
}
}
return
createCodec
(
return
new
DefaultCodec
(
format
,
format
,
mediaFormat
,
/* mediaCodecName= */
null
,
/* isDecoder= */
true
,
outputSurface
);
mediaFormat
,
/* mediaCodecName= */
null
,
/* isVideo= */
true
,
/* isDecoder= */
true
,
outputSurface
);
}
}
}
}
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java
View file @
ef7d8c66
...
@@ -16,8 +16,6 @@
...
@@ -16,8 +16,6 @@
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
package
com
.
google
.
android
.
exoplayer2
.
transformer
;
import
static
com
.
google
.
android
.
exoplayer2
.
transformer
.
CodecFactoryUtil
.
createCodec
;
import
static
com
.
google
.
android
.
exoplayer2
.
transformer
.
CodecFactoryUtil
.
createTransformationException
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
...
@@ -79,12 +77,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -79,12 +77,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
// capabilities limitations.
// capabilities limitations.
format
=
format
.
buildUpon
().
setSampleMimeType
(
allowedMimeTypes
.
get
(
0
)).
build
();
format
=
format
.
buildUpon
().
setSampleMimeType
(
allowedMimeTypes
.
get
(
0
)).
build
();
}
else
{
}
else
{
throw
createTransformationException
(
throw
TransformationException
.
createForCodec
(
new
IllegalArgumentException
(
"The requested output format is not supported."
),
new
IllegalArgumentException
(
"The requested output format is not supported."
),
format
,
format
,
/* isVideo= */
false
,
/* isVideo= */
false
,
/* isDecoder= */
false
,
/* isDecoder= */
false
,
/* mediaCodecName= */
null
);
/* mediaCodecName= */
null
,
TransformationException
.
ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED
);
}
}
}
}
MediaFormat
mediaFormat
=
MediaFormat
mediaFormat
=
...
@@ -92,11 +91,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -92,11 +91,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
checkNotNull
(
format
.
sampleMimeType
),
format
.
sampleRate
,
format
.
channelCount
);
checkNotNull
(
format
.
sampleMimeType
),
format
.
sampleRate
,
format
.
channelCount
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_BIT_RATE
,
format
.
bitrate
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_BIT_RATE
,
format
.
bitrate
);
return
create
Codec
(
return
new
Default
Codec
(
format
,
format
,
mediaFormat
,
mediaFormat
,
/* mediaCodecName= */
null
,
/* mediaCodecName= */
null
,
/* isVideo= */
false
,
/* isDecoder= */
false
,
/* isDecoder= */
false
,
/* outputSurface= */
null
);
/* outputSurface= */
null
);
}
}
...
@@ -119,12 +117,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -119,12 +117,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
findEncoderWithClosestFormatSupport
(
findEncoderWithClosestFormatSupport
(
format
,
videoEncoderSelector
,
allowedMimeTypes
,
disableFallback
);
format
,
videoEncoderSelector
,
allowedMimeTypes
,
disableFallback
);
if
(
encoderAndClosestFormatSupport
==
null
)
{
if
(
encoderAndClosestFormatSupport
==
null
)
{
throw
createTransformationException
(
throw
TransformationException
.
createForCodec
(
new
IllegalArgumentException
(
"The requested output format is not supported."
),
new
IllegalArgumentException
(
"The requested output format is not supported."
),
format
,
format
,
/* isVideo= */
true
,
/* isVideo= */
true
,
/* isDecoder= */
false
,
/* isDecoder= */
false
,
/* mediaCodecName= */
null
);
/* mediaCodecName= */
null
,
TransformationException
.
ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED
);
}
}
MediaCodecInfo
encoderInfo
=
encoderAndClosestFormatSupport
.
first
;
MediaCodecInfo
encoderInfo
=
encoderAndClosestFormatSupport
.
first
;
...
@@ -196,11 +195,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -196,11 +195,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_COLOR_FORMAT
,
DEFAULT_COLOR_FORMAT
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_COLOR_FORMAT
,
DEFAULT_COLOR_FORMAT
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_I_FRAME_INTERVAL
,
DEFAULT_I_FRAME_INTERVAL_SECS
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_I_FRAME_INTERVAL
,
DEFAULT_I_FRAME_INTERVAL_SECS
);
return
create
Codec
(
return
new
Default
Codec
(
format
,
format
,
mediaFormat
,
mediaFormat
,
encoderInfo
.
getName
(),
encoderInfo
.
getName
(),
/* isVideo= */
true
,
/* isDecoder= */
false
,
/* isDecoder= */
false
,
/* outputSurface= */
null
);
/* outputSurface= */
null
);
}
}
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformationException.java
View file @
ef7d8c66
...
@@ -205,24 +205,23 @@ public final class TransformationException extends Exception {
...
@@ -205,24 +205,23 @@ public final class TransformationException extends Exception {
* Creates an instance for a decoder or encoder related exception.
* Creates an instance for a decoder or encoder related exception.
*
*
* @param cause The cause of the failure.
* @param cause The cause of the failure.
* @param componentName The name of the component used, e.g. 'VideoEncoder'.
* @param format The {@link Format} used for configuring the decoder/encoder.
* @param configurationFormat The {@link Format} used for configuring the decoder/encoder.
* @param isVideo Whether the decoder or encoder is configured for video.
* @param isDecoder Whether the exception is created for a decoder.
* @param mediaCodecName The name of the {@link MediaCodec} used, if known.
* @param mediaCodecName The name of the {@link MediaCodec} used, if known.
* @param errorCode See {@link #errorCode}.
* @param errorCode See {@link #errorCode}.
* @return The created instance.
* @return The created instance.
*/
*/
public
static
TransformationException
createForCodec
(
public
static
TransformationException
createForCodec
(
Throwable
cause
,
Throwable
cause
,
String
componentName
,
Format
format
,
Format
configurationFormat
,
boolean
isVideo
,
boolean
isDecoder
,
@Nullable
String
mediaCodecName
,
@Nullable
String
mediaCodecName
,
int
errorCode
)
{
int
errorCode
)
{
String
componentName
=
(
isVideo
?
"Video"
:
"Audio"
)
+
(
isDecoder
?
"Decoder"
:
"Encoder"
);
return
new
TransformationException
(
return
new
TransformationException
(
componentName
componentName
+
" error, format = "
+
format
+
", mediaCodecName="
+
mediaCodecName
,
+
" error, format = "
+
configurationFormat
+
", mediaCodecName="
+
mediaCodecName
,
cause
,
cause
,
errorCode
);
errorCode
);
}
}
...
...
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java
View file @
ef7d8c66
...
@@ -135,7 +135,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -135,7 +135,7 @@ import org.checkerframework.dataflow.qual.Pure;
actualOutputFormat
.
height
,
actualOutputFormat
.
height
,
inputFormat
.
pixelWidthHeightRatio
,
inputFormat
.
pixelWidthHeightRatio
,
transformationMatrix
,
transformationMatrix
,
/* outputSurface= */
checkNotNull
(
encoder
.
getInputSurface
()
),
/* outputSurface= */
encoder
.
getInputSurface
(
),
transformationRequest
.
enableHdrEditing
,
transformationRequest
.
enableHdrEditing
,
debugViewProvider
);
debugViewProvider
);
}
else
{
}
else
{
...
@@ -145,9 +145,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -145,9 +145,7 @@ import org.checkerframework.dataflow.qual.Pure;
decoder
=
decoder
=
decoderFactory
.
createForVideoDecoding
(
decoderFactory
.
createForVideoDecoding
(
inputFormat
,
inputFormat
,
frameEditor
==
null
frameEditor
==
null
?
encoder
.
getInputSurface
()
:
frameEditor
.
getInputSurface
());
?
checkNotNull
(
encoder
.
getInputSurface
())
:
frameEditor
.
getInputSurface
());
}
}
@Override
@Override
...
@@ -262,7 +260,7 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -262,7 +260,7 @@ import org.checkerframework.dataflow.qual.Pure;
@Override
@Override
public
void
releaseOutputBuffer
()
throws
TransformationException
{
public
void
releaseOutputBuffer
()
throws
TransformationException
{
encoder
.
releaseOutputBuffer
();
encoder
.
releaseOutputBuffer
(
/* render= */
false
);
}
}
@Override
@Override
...
...
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