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
e476337d
authored
Mar 17, 2022
by
claincly
Committed by
Ian Baker
Mar 17, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add encoding options.
PiperOrigin-RevId: 435398814
parent
481b9bd6
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
384 additions
and
102 deletions
libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java
libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java
libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java
libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java
libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java
View file @
e476337d
...
@@ -97,8 +97,10 @@ public interface Codec {
...
@@ -97,8 +97,10 @@ public interface Codec {
*
*
* @param format The {@link Format} (of the output data) used to determine the underlying
* @param format The {@link Format} (of the output data) used to determine the underlying
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width}
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width}
* and {@link Format#height} must be set to those of the desired output video format. {@link
* and {@link Format#height} are set to those of the desired output video format. {@link
* Format#rotationDegrees} should be 0. The video should always be in landscape orientation.
* Format#rotationDegrees} is 0 and {@link Format#width} {@code >=} {@link Format#height},
* therefore the video is always in landscape orientation. {@link Format#frameRate} is set
* to the output video's frame rate, if available.
* @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 {@link Codec} for video encoding.
* @return A {@link Codec} for video encoding.
...
...
libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java
View file @
e476337d
...
@@ -32,40 +32,57 @@ import androidx.media3.common.Format;
...
@@ -32,40 +32,57 @@ import androidx.media3.common.Format;
import
androidx.media3.common.MimeTypes
;
import
androidx.media3.common.MimeTypes
;
import
androidx.media3.common.util.UnstableApi
;
import
androidx.media3.common.util.UnstableApi
;
import
androidx.media3.common.util.Util
;
import
androidx.media3.common.util.Util
;
import
androidx.media3.exoplayer.mediacodec.MediaCodecUtil
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.ImmutableList
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
org.checkerframework.checker.nullness.qual.RequiresNonNull
;
import
org.checkerframework.checker.nullness.qual.RequiresNonNull
;
/** A default implementation of {@link Codec.EncoderFactory}. */
/** A default implementation of {@link Codec.EncoderFactory}. */
// TODO(b/224949986) Split audio and video encoder factory.
@UnstableApi
@UnstableApi
public
final
class
DefaultEncoderFactory
implements
Codec
.
EncoderFactory
{
public
final
class
DefaultEncoderFactory
implements
Codec
.
EncoderFactory
{
private
static
final
int
DEFAULT_COLOR_FORMAT
=
private
static
final
int
DEFAULT_FRAME_RATE
=
30
;
MediaCodecInfo
.
CodecCapabilities
.
COLOR_FormatSurface
;
private
static
final
int
DEFAULT_FRAME_RATE
=
60
;
private
static
final
int
DEFAULT_I_FRAME_INTERVAL_SECS
=
1
;
@Nullable
private
final
EncoderSelector
videoEncoderSelector
;
private
final
EncoderSelector
videoEncoderSelector
;
private
final
VideoEncoderSettings
requestedVideoEncoderSettings
;
private
final
boolean
enableFallback
;
private
final
boolean
enableFallback
;
/**
/**
* Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}, and
* Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}, a
* format fallback enabled.
* default {@link VideoEncoderSettings}, and with format fallback enabled.
*/
public
DefaultEncoderFactory
()
{
this
(
EncoderSelector
.
DEFAULT
,
/* enableFallback= */
true
);
}
/** Creates a new instance using a default {@link VideoEncoderSettings}. */
public
DefaultEncoderFactory
(
EncoderSelector
videoEncoderSelector
,
boolean
enableFallback
)
{
this
(
videoEncoderSelector
,
new
VideoEncoderSettings
.
Builder
().
build
(),
enableFallback
);
}
/**
* Creates a new instance.
*
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing
* {@link Codec.EncoderFactory} if such adjustments are unwanted.
*
*
* <p>With format fallback enabled, and when the requested {@link Format} is not supported, {@code
* <p>With format fallback enabled, and when the requested {@link Format} is not supported, {@code
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc.
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc.
*
* @param videoEncoderSelector The {@link EncoderSelector}.
* @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}.
* @param enableFallback Whether to enable fallback.
*/
*/
public
DefaultEncoderFactory
()
{
this
(
EncoderSelector
.
DEFAULT
,
/* enableFallback= */
true
);
}
/** Creates a new instance. */
public
DefaultEncoderFactory
(
public
DefaultEncoderFactory
(
@Nullable
EncoderSelector
videoEncoderSelector
,
boolean
enableFallback
)
{
EncoderSelector
videoEncoderSelector
,
VideoEncoderSettings
requestedVideoEncoderSettings
,
boolean
enableFallback
)
{
this
.
videoEncoderSelector
=
videoEncoderSelector
;
this
.
videoEncoderSelector
=
videoEncoderSelector
;
this
.
requestedVideoEncoderSettings
=
requestedVideoEncoderSettings
;
this
.
enableFallback
=
enableFallback
;
this
.
enableFallback
=
enableFallback
;
}
}
...
@@ -101,6 +118,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -101,6 +118,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
@Override
@Override
public
Codec
createForVideoEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
public
Codec
createForVideoEncoding
(
Format
format
,
List
<
String
>
allowedMimeTypes
)
throws
TransformationException
{
throws
TransformationException
{
if
(
format
.
frameRate
==
Format
.
NO_VALUE
)
{
format
=
format
.
buildUpon
().
setFrameRate
(
DEFAULT_FRAME_RATE
).
build
();
}
checkArgument
(
format
.
width
!=
Format
.
NO_VALUE
);
checkArgument
(
format
.
width
!=
Format
.
NO_VALUE
);
checkArgument
(
format
.
height
!=
Format
.
NO_VALUE
);
checkArgument
(
format
.
height
!=
Format
.
NO_VALUE
);
// According to interface Javadoc, format.rotationDegrees should be 0. The video should always
// According to interface Javadoc, format.rotationDegrees should be 0. The video should always
...
@@ -112,78 +132,51 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -112,78 +132,51 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
checkStateNotNull
(
videoEncoderSelector
);
checkStateNotNull
(
videoEncoderSelector
);
@Nullable
@Nullable
Pair
<
MediaCodecInfo
,
Format
>
encoderAndClosestFormatSupport
=
VideoEncoderQueryResult
encoderAndClosestFormatSupport
=
findEncoderWithClosestFormatSupport
(
findEncoderWithClosestFormatSupport
(
format
,
videoEncoderSelector
,
allowedMimeTypes
,
enableFallback
);
format
,
requestedVideoEncoderSettings
,
videoEncoderSelector
,
allowedMimeTypes
,
enableFallback
);
if
(
encoderAndClosestFormatSupport
==
null
)
{
if
(
encoderAndClosestFormatSupport
==
null
)
{
throw
createTransformationException
(
format
);
throw
createTransformationException
(
format
);
}
}
MediaCodecInfo
encoderInfo
=
encoderAndClosestFormatSupport
.
first
;
MediaCodecInfo
encoderInfo
=
encoderAndClosestFormatSupport
.
encoder
;
format
=
encoderAndClosestFormatSupport
.
second
;
format
=
encoderAndClosestFormatSupport
.
supportedFormat
;
VideoEncoderSettings
supportedVideoEncoderSettings
=
encoderAndClosestFormatSupport
.
supportedEncoderSettings
;
String
mimeType
=
checkNotNull
(
format
.
sampleMimeType
);
String
mimeType
=
checkNotNull
(
format
.
sampleMimeType
);
MediaFormat
mediaFormat
=
MediaFormat
.
createVideoFormat
(
mimeType
,
format
.
width
,
format
.
height
);
MediaFormat
mediaFormat
=
MediaFormat
.
createVideoFormat
(
mimeType
,
format
.
width
,
format
.
height
);
mediaFormat
.
setFloat
(
MediaFormat
.
KEY_FRAME_RATE
,
format
.
frameRate
);
mediaFormat
.
setFloat
(
MediaFormat
.
KEY_FRAME_RATE
,
format
.
frameRate
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_BIT_RATE
,
format
.
averageBitrate
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_BIT_RATE
,
supportedVideoEncoderSettings
.
bitrate
!=
VideoEncoderSettings
.
NO_VALUE
?
supportedVideoEncoderSettings
.
bitrate
:
getSuggestedBitrate
(
format
.
width
,
format
.
height
,
format
.
frameRate
));
@Nullable
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_BITRATE_MODE
,
supportedVideoEncoderSettings
.
bitrateMode
);
Pair
<
Integer
,
Integer
>
codecProfileAndLevel
=
MediaCodecUtil
.
getCodecProfileAndLevel
(
format
);
if
(
codecProfileAndLevel
!=
null
)
{
if
(
supportedVideoEncoderSettings
.
profile
!=
VideoEncoderSettings
.
NO_VALUE
// The codecProfileAndLevel is supported by the encoder.
&&
supportedVideoEncoderSettings
.
level
!=
VideoEncoderSettings
.
NO_VALUE
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
codecProfileAndLevel
.
first
);
&&
SDK_INT
>=
23
)
{
if
(
SDK_INT
>=
23
)
{
// Set profile and level at the same time to maximize compatibility, or the encoder will pick
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
codecProfileAndLevel
.
second
);
// the values.
}
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
supportedVideoEncoderSettings
.
profile
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedVideoEncoderSettings
.
level
);
}
}
// TODO(b/210593256): Remove overriding profile/level (before API 29) after switching to in-app
// muxing.
if
(
mimeType
.
equals
(
MimeTypes
.
VIDEO_H264
))
{
if
(
mimeType
.
equals
(
MimeTypes
.
VIDEO_H264
))
{
// Applying suggested profile/level settings from
adjustMediaFormatForH264EncoderSettings
(
mediaFormat
,
encoderInfo
);
// https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles
if
(
Util
.
SDK_INT
>=
29
)
{
int
supportedEncodingLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
);
if
(
supportedEncodingLevel
!=
EncoderUtil
.
LEVEL_UNSET
)
{
// Use the highest supported profile and use B-frames.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedEncodingLevel
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_MAX_B_FRAMES
,
1
);
}
}
else
if
(
Util
.
SDK_INT
>=
26
)
{
int
supportedEncodingLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
);
if
(
supportedEncodingLevel
!=
EncoderUtil
.
LEVEL_UNSET
)
{
// Use the highest-supported profile, but disable the generation of B-frames using
// MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these
// system versions.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedEncodingLevel
);
// TODO(b/210593256): Set KEY_LATENCY to 2 to enable B-frame production after switching to
// in-app muxing.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LATENCY
,
1
);
}
}
else
if
(
Util
.
SDK_INT
>=
24
)
{
int
supportedLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileBaseline
);
checkState
(
supportedLevel
!=
EncoderUtil
.
LEVEL_UNSET
);
// Use the baseline profile for safest results, as encoding in baseline is required per
// https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileBaseline
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedLevel
);
}
// For API levels below 24, setting profile and level can lead to failures in MediaCodec
// configuration. The encoder selects the profile/level when we don't set them.
}
}
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_COLOR_FORMAT
,
DEFAULT_COLOR_FORMAT
);
mediaFormat
.
setInteger
(
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_I_FRAME_INTERVAL
,
DEFAULT_I_FRAME_INTERVAL_SECS
);
MediaFormat
.
KEY_COLOR_FORMAT
,
supportedVideoEncoderSettings
.
colorProfile
);
mediaFormat
.
setFloat
(
MediaFormat
.
KEY_I_FRAME_INTERVAL
,
supportedVideoEncoderSettings
.
iFrameIntervalSeconds
);
return
new
DefaultCodec
(
return
new
DefaultCodec
(
format
,
format
,
...
@@ -201,8 +194,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -201,8 +194,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
*/
*/
@RequiresNonNull
(
"#1.sampleMimeType"
)
@RequiresNonNull
(
"#1.sampleMimeType"
)
@Nullable
@Nullable
private
static
Pair
<
MediaCodecInfo
,
Format
>
findEncoderWithClosestFormatSupport
(
private
static
VideoEncoderQueryResult
findEncoderWithClosestFormatSupport
(
Format
requestedFormat
,
Format
requestedFormat
,
VideoEncoderSettings
videoEncoderSettings
,
EncoderSelector
encoderSelector
,
EncoderSelector
encoderSelector
,
List
<
String
>
allowedMimeTypes
,
List
<
String
>
allowedMimeTypes
,
boolean
enableFallback
)
{
boolean
enableFallback
)
{
...
@@ -218,7 +212,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -218,7 +212,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
return
null
;
return
null
;
}
}
if
(!
enableFallback
)
{
if
(!
enableFallback
)
{
return
Pair
.
create
(
encodersForMimeType
.
get
(
0
),
requestedFormat
);
return
new
VideoEncoderQueryResult
(
encodersForMimeType
.
get
(
0
),
requestedFormat
,
videoEncoderSettings
);
}
}
ImmutableList
<
MediaCodecInfo
>
filteredEncoders
=
ImmutableList
<
MediaCodecInfo
>
filteredEncoders
=
filterEncoders
(
filterEncoders
(
...
@@ -240,6 +235,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -240,6 +235,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
if
(
filteredEncoders
.
isEmpty
())
{
if
(
filteredEncoders
.
isEmpty
())
{
return
null
;
return
null
;
}
}
// The supported resolution is the same for all remaining encoders.
// The supported resolution is the same for all remaining encoders.
Size
finalResolution
=
Size
finalResolution
=
checkNotNull
(
checkNotNull
(
...
@@ -247,14 +243,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -247,14 +243,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
filteredEncoders
.
get
(
0
),
mimeType
,
requestedFormat
.
width
,
requestedFormat
.
height
));
filteredEncoders
.
get
(
0
),
mimeType
,
requestedFormat
.
width
,
requestedFormat
.
height
));
int
requestedBitrate
=
int
requestedBitrate
=
requestedFormat
.
averageBitrate
==
Format
.
NO_VALUE
videoEncoderSettings
.
bitrate
!=
VideoEncoderSettings
.
NO_VALUE
?
getSuggestedBitrate
(
?
videoEncoderSettings
.
bitrate
finalResolution
.
getWidth
(),
:
getSuggestedBitrate
(
finalResolution
.
getHeight
(),
finalResolution
.
getWidth
(),
finalResolution
.
getHeight
(),
requestedFormat
.
frameRate
);
requestedFormat
.
frameRate
==
Format
.
NO_VALUE
?
DEFAULT_FRAME_RATE
:
requestedFormat
.
frameRate
)
:
requestedFormat
.
averageBitrate
;
filteredEncoders
=
filteredEncoders
=
filterEncoders
(
filterEncoders
(
filteredEncoders
,
filteredEncoders
,
...
@@ -268,32 +260,99 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -268,32 +260,99 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
}
}
MediaCodecInfo
pickedEncoder
=
filteredEncoders
.
get
(
0
);
MediaCodecInfo
pickedEncoder
=
filteredEncoders
.
get
(
0
);
@Nullable
int
closestSupportedBitrate
=
Pair
<
Integer
,
Integer
>
profileLevel
=
MediaCodecUtil
.
getCodecProfileAndLevel
(
requestedFormat
);
EncoderUtil
.
getClosestSupportedBitrate
(
pickedEncoder
,
mimeType
,
requestedBitrate
);
@Nullable
String
codecs
=
null
;
VideoEncoderSettings
.
Builder
supportedEncodingSettingBuilder
=
if
(
profileLevel
!=
null
videoEncoderSettings
.
buildUpon
().
setBitrate
(
closestSupportedBitrate
);
&&
requestedFormat
.
sampleMimeType
.
equals
(
mimeType
)
if
(
videoEncoderSettings
.
profile
==
VideoEncoderSettings
.
NO_VALUE
&&
profileLevel
.
second
||
videoEncoderSettings
.
level
==
VideoEncoderSettings
.
NO_VALUE
<=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
||
videoEncoderSettings
.
level
pickedEncoder
,
mimeType
,
/* profile= */
profileLevel
.
first
))
{
>
EncoderUtil
.
findHighestSupportedEncodingLevel
(
codecs
=
requestedFormat
.
codecs
;
pickedEncoder
,
mimeType
,
videoEncoderSettings
.
profile
))
{
supportedEncodingSettingBuilder
.
setEncodingProfileLevel
(
VideoEncoderSettings
.
NO_VALUE
,
VideoEncoderSettings
.
NO_VALUE
);
}
}
Format
encoderSupported
Format
=
Format
supportedEncoder
Format
=
requestedFormat
requestedFormat
.
buildUpon
()
.
buildUpon
()
.
setSampleMimeType
(
mimeType
)
.
setSampleMimeType
(
mimeType
)
.
setCodecs
(
codecs
)
.
setWidth
(
finalResolution
.
getWidth
())
.
setWidth
(
finalResolution
.
getWidth
())
.
setHeight
(
finalResolution
.
getHeight
())
.
setHeight
(
finalResolution
.
getHeight
())
.
setFrameRate
(
.
setAverageBitrate
(
closestSupportedBitrate
)
requestedFormat
.
frameRate
!=
Format
.
NO_VALUE
?
requestedFormat
.
frameRate
:
DEFAULT_FRAME_RATE
)
.
setAverageBitrate
(
EncoderUtil
.
getClosestSupportedBitrate
(
pickedEncoder
,
mimeType
,
requestedBitrate
))
.
build
();
.
build
();
return
Pair
.
create
(
pickedEncoder
,
encoderSupportedFormat
);
return
new
VideoEncoderQueryResult
(
pickedEncoder
,
supportedEncoderFormat
,
supportedEncodingSettingBuilder
.
build
());
}
private
static
final
class
VideoEncoderQueryResult
{
public
final
MediaCodecInfo
encoder
;
public
final
Format
supportedFormat
;
public
final
VideoEncoderSettings
supportedEncoderSettings
;
public
VideoEncoderQueryResult
(
MediaCodecInfo
encoder
,
Format
supportedFormat
,
VideoEncoderSettings
supportedEncoderSettings
)
{
this
.
encoder
=
encoder
;
this
.
supportedFormat
=
supportedFormat
;
this
.
supportedEncoderSettings
=
supportedEncoderSettings
;
}
}
/**
* Applying suggested profile/level settings from
* https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles
*
* <p>The adjustment is applied in-place to {@code mediaFormat}.
*/
private
static
void
adjustMediaFormatForH264EncoderSettings
(
MediaFormat
mediaFormat
,
MediaCodecInfo
encoderInfo
)
{
// TODO(b/210593256): Remove overriding profile/level (before API 29) after switching to in-app
// muxing.
String
mimeType
=
MimeTypes
.
VIDEO_H264
;
if
(
Util
.
SDK_INT
>=
29
)
{
int
expectedEncodingProfile
=
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
;
int
supportedEncodingLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
expectedEncodingProfile
);
if
(
supportedEncodingLevel
!=
EncoderUtil
.
LEVEL_UNSET
)
{
// Use the highest supported profile and use B-frames.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
expectedEncodingProfile
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedEncodingLevel
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_MAX_B_FRAMES
,
1
);
}
}
else
if
(
Util
.
SDK_INT
>=
26
)
{
int
expectedEncodingProfile
=
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
;
int
supportedEncodingLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
expectedEncodingProfile
);
if
(
supportedEncodingLevel
!=
EncoderUtil
.
LEVEL_UNSET
)
{
// Use the highest-supported profile, but disable the generation of B-frames using
// MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these
// system versions.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
expectedEncodingProfile
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedEncodingLevel
);
// TODO(b/210593256): Set KEY_LATENCY to 2 to enable B-frame production after switching to
// in-app muxing.
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LATENCY
,
1
);
}
}
else
if
(
Util
.
SDK_INT
>=
24
)
{
int
expectedEncodingProfile
=
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileBaseline
;
int
supportedLevel
=
EncoderUtil
.
findHighestSupportedEncodingLevel
(
encoderInfo
,
mimeType
,
expectedEncodingProfile
);
checkState
(
supportedLevel
!=
EncoderUtil
.
LEVEL_UNSET
);
// Use the baseline profile for safest results, as encoding in baseline is required per
// https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_PROFILE
,
expectedEncodingProfile
);
mediaFormat
.
setInteger
(
MediaFormat
.
KEY_LEVEL
,
supportedLevel
);
}
else
{
// For API levels below 24, setting profile and level can lead to failures in MediaCodec
// configuration. The encoder selects the profile/level when we don't set them.
mediaFormat
.
setString
(
MediaFormat
.
KEY_PROFILE
,
null
);
mediaFormat
.
setString
(
MediaFormat
.
KEY_LEVEL
,
null
);
}
}
}
private
interface
EncoderFallbackCost
{
private
interface
EncoderFallbackCost
{
...
@@ -307,6 +366,15 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
...
@@ -307,6 +366,15 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
int
getParameterSupportGap
(
MediaCodecInfo
encoderInfo
);
int
getParameterSupportGap
(
MediaCodecInfo
encoderInfo
);
}
}
/**
* Filters a list of {@link MediaCodecInfo encoders} by a {@link EncoderFallbackCost cost
* function}.
*
* @param encoders A list of {@link MediaCodecInfo encoders}.
* @param cost A {@link EncoderFallbackCost cost function}.
* @return A list of {@link MediaCodecInfo encoders} with the lowest costs, empty if the costs of
* all encoders are {@link Integer#MAX_VALUE}.
*/
private
static
ImmutableList
<
MediaCodecInfo
>
filterEncoders
(
private
static
ImmutableList
<
MediaCodecInfo
>
filterEncoders
(
List
<
MediaCodecInfo
>
encoders
,
EncoderFallbackCost
cost
)
{
List
<
MediaCodecInfo
>
encoders
,
EncoderFallbackCost
cost
)
{
List
<
MediaCodecInfo
>
filteredEncoders
=
new
ArrayList
<>(
encoders
.
size
());
List
<
MediaCodecInfo
>
filteredEncoders
=
new
ArrayList
<>(
encoders
.
size
());
...
...
libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java
0 → 100644
View file @
e476337d
/*
* 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
androidx
.
media3
.
transformer
;
import
static
androidx
.
media3
.
common
.
util
.
Assertions
.
checkArgument
;
import
static
java
.
lang
.
annotation
.
ElementType
.
TYPE_USE
;
import
android.media.MediaCodecInfo
;
import
androidx.annotation.IntDef
;
import
androidx.media3.common.Format
;
import
androidx.media3.common.util.UnstableApi
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/** Represents the video encoder settings. */
@UnstableApi
public
final
class
VideoEncoderSettings
{
/** A value for various fields to indicate that the field's value is unknown or not applicable. */
public
static
final
int
NO_VALUE
=
Format
.
NO_VALUE
;
/** The default encoding color profile. */
public
static
final
int
DEFAULT_COLOR_PROFILE
=
MediaCodecInfo
.
CodecCapabilities
.
COLOR_FormatSurface
;
/** The default I-frame interval in seconds. */
public
static
final
float
DEFAULT_I_FRAME_INTERVAL_SECONDS
=
1.0f
;
/**
* The allowed values for {@code bitrateMode}, one of
*
* <ul>
* <li>Constant quality: {@link MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_CQ}.
* <li>Variable bitrate: {@link MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_VBR}.
* <li>Constant bitrate: {@link MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_CBR}.
* <li>Constant bitrate with frame drops: {@link
* MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_CBR_FD}, available from API31.
* </ul>
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@Target
(
TYPE_USE
)
@IntDef
({
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_CQ
,
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_VBR
,
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_CBR
,
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_CBR_FD
})
public
@interface
BitrateMode
{}
/** Builds {@link VideoEncoderSettings} instances. */
public
static
final
class
Builder
{
private
int
bitrate
;
private
@BitrateMode
int
bitrateMode
;
private
int
profile
;
private
int
level
;
private
int
colorProfile
;
private
float
iFrameIntervalSeconds
;
/** Creates a new instance. */
public
Builder
()
{
this
.
bitrate
=
NO_VALUE
;
this
.
bitrateMode
=
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_VBR
;
this
.
profile
=
NO_VALUE
;
this
.
level
=
NO_VALUE
;
this
.
colorProfile
=
DEFAULT_COLOR_PROFILE
;
this
.
iFrameIntervalSeconds
=
DEFAULT_I_FRAME_INTERVAL_SECONDS
;
}
private
Builder
(
VideoEncoderSettings
videoEncoderSettings
)
{
this
.
bitrate
=
videoEncoderSettings
.
bitrate
;
this
.
bitrateMode
=
videoEncoderSettings
.
bitrateMode
;
this
.
profile
=
videoEncoderSettings
.
profile
;
this
.
level
=
videoEncoderSettings
.
level
;
this
.
colorProfile
=
videoEncoderSettings
.
colorProfile
;
this
.
iFrameIntervalSeconds
=
videoEncoderSettings
.
iFrameIntervalSeconds
;
}
/**
* Sets {@link VideoEncoderSettings#bitrate}. The default value is {@link #NO_VALUE}.
*
* @param bitrate The {@link VideoEncoderSettings#bitrate}.
* @return This builder.
*/
public
Builder
setBitrate
(
int
bitrate
)
{
this
.
bitrate
=
bitrate
;
return
this
;
}
/**
* Sets {@link VideoEncoderSettings#bitrateMode}. The default value is {@code
* MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR}.
*
* <p>Only {@link MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_VBR} and {@link
* MediaCodecInfo.EncoderCapabilities#BITRATE_MODE_CBR} are allowed.
*
* @param bitrateMode The {@link VideoEncoderSettings#bitrateMode}.
* @return This builder.
*/
public
Builder
setBitrateMode
(
@BitrateMode
int
bitrateMode
)
{
checkArgument
(
bitrateMode
==
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_VBR
||
bitrateMode
==
MediaCodecInfo
.
EncoderCapabilities
.
BITRATE_MODE_CBR
);
this
.
bitrateMode
=
bitrateMode
;
return
this
;
}
/**
* Sets {@link VideoEncoderSettings#profile} and {@link VideoEncoderSettings#level}. The default
* values are both {@link #NO_VALUE}.
*
* <p>The value must be one of the values defined in {@link MediaCodecInfo.CodecProfileLevel},
* or {@link #NO_VALUE}.
*
* <p>Profile and level settings will be ignored when using {@link DefaultEncoderFactory} and
* encoding to H264.
*
* @param encodingProfile The {@link VideoEncoderSettings#profile}.
* @param encodingLevel The {@link VideoEncoderSettings#level}.
* @return This builder.
*/
public
Builder
setEncodingProfileLevel
(
int
encodingProfile
,
int
encodingLevel
)
{
this
.
profile
=
encodingProfile
;
this
.
level
=
encodingLevel
;
return
this
;
}
/**
* Sets {@link VideoEncoderSettings#colorProfile}. The default value is {@link
* #DEFAULT_COLOR_PROFILE}.
*
* <p>The value must be one of the {@code COLOR_*} constants defined in {@link
* MediaCodecInfo.CodecCapabilities}.
*
* @param colorProfile The {@link VideoEncoderSettings#colorProfile}.
* @return This builder.
*/
public
Builder
setColorProfile
(
int
colorProfile
)
{
this
.
colorProfile
=
colorProfile
;
return
this
;
}
/**
* Sets {@link VideoEncoderSettings#iFrameIntervalSeconds}. The default value is {@link
* #DEFAULT_I_FRAME_INTERVAL_SECONDS}.
*
* @param iFrameIntervalSeconds The {@link VideoEncoderSettings#iFrameIntervalSeconds}.
* @return This builder.
*/
public
Builder
setiFrameIntervalSeconds
(
float
iFrameIntervalSeconds
)
{
this
.
iFrameIntervalSeconds
=
iFrameIntervalSeconds
;
return
this
;
}
/** Builds the instance. */
public
VideoEncoderSettings
build
()
{
return
new
VideoEncoderSettings
(
bitrate
,
bitrateMode
,
profile
,
level
,
colorProfile
,
iFrameIntervalSeconds
);
}
}
/** The encoding bitrate. */
public
final
int
bitrate
;
/** One of {@link BitrateMode the allowed modes}. */
public
final
@BitrateMode
int
bitrateMode
;
/** The encoding profile. */
public
final
int
profile
;
/** The encoding level. */
public
final
int
level
;
/** The encoding color profile. */
public
final
int
colorProfile
;
/** The encoding I-Frame interval in seconds. */
public
final
float
iFrameIntervalSeconds
;
private
VideoEncoderSettings
(
int
bitrate
,
int
bitrateMode
,
int
profile
,
int
level
,
int
colorProfile
,
float
iFrameIntervalSeconds
)
{
this
.
bitrate
=
bitrate
;
this
.
bitrateMode
=
bitrateMode
;
this
.
profile
=
profile
;
this
.
level
=
level
;
this
.
colorProfile
=
colorProfile
;
this
.
iFrameIntervalSeconds
=
iFrameIntervalSeconds
;
}
/**
* Returns a {@link VideoEncoderSettings.Builder} initialized with the values of this instance.
*/
public
Builder
buildUpon
()
{
return
new
Builder
(
this
);
}
}
libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java
View file @
e476337d
...
@@ -82,11 +82,13 @@ import org.checkerframework.dataflow.qual.Pure;
...
@@ -82,11 +82,13 @@ import org.checkerframework.dataflow.qual.Pure;
.
setWidth
(
requestedEncoderDimensions
.
getWidth
())
.
setWidth
(
requestedEncoderDimensions
.
getWidth
())
.
setHeight
(
requestedEncoderDimensions
.
getHeight
())
.
setHeight
(
requestedEncoderDimensions
.
getHeight
())
.
setRotationDegrees
(
0
)
.
setRotationDegrees
(
0
)
.
setFrameRate
(
inputFormat
.
frameRate
)
.
setSampleMimeType
(
.
setSampleMimeType
(
transformationRequest
.
videoMimeType
!=
null
transformationRequest
.
videoMimeType
!=
null
?
transformationRequest
.
videoMimeType
?
transformationRequest
.
videoMimeType
:
inputFormat
.
sampleMimeType
)
:
inputFormat
.
sampleMimeType
)
.
build
();
.
build
();
encoder
=
encoderFactory
.
createForVideoEncoding
(
requestedEncoderFormat
,
allowedOutputMimeTypes
);
encoder
=
encoderFactory
.
createForVideoEncoding
(
requestedEncoderFormat
,
allowedOutputMimeTypes
);
Format
encoderSupportedFormat
=
encoder
.
getConfigurationFormat
();
Format
encoderSupportedFormat
=
encoder
.
getConfigurationFormat
();
fallbackListener
.
onTransformationRequestFinalized
(
fallbackListener
.
onTransformationRequestFinalized
(
...
...
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