Jetpack Media3 中的 Transformer API 旨在提高媒体编辑的 性能和可靠性。Transformer 支持多种操作, 包括:
- 通过剪辑、缩放和旋转修改视频
- 添加叠加层和滤镜等特效
- 处理 HDR 和慢动作视频等特殊格式
- 应用修改后导出媒体项
本页将引导您了解 Transformer 涵盖的一些关键用例。如需了解详情,您可以参阅有关 Media3 Transformer的完整指南。
开始使用
如需开始使用,请添加对 Jetpack Media3 的 Transformer、Effect 和 Common 模块 的依赖项:
implementation "androidx.media3:media3-transformer:1.9.2" implementation "androidx.media3:media3-effect:1.9.2" implementation "androidx.media3:media3-common:1.9.2"
请务必将 1.9.2 替换为您偏好的
库版本。您可以参阅
版本说明
了解最新版本。
重要类
| 类 | 用途 |
|---|---|
Transformer |
启动和停止转换,并检查正在运行的转换的进度更新。 |
EditedMediaItem |
表示要处理的媒体项以及要对其应用的修改。 |
Effects |
音频和视频特效的集合。 |
配置输出
借助 Transformer.Builder,您现在可以通过设置函数来指定 videoMimeType 和
audioMimetype 目录,而无需创建
TransformationRequest 对象。
在格式之间进行转码
以下代码展示了如何配置 Transformer 对象以
输出 H.265/AVC 视频和 AAC 音频:
Kotlin
val transformer = Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build()
Java
Transformer transformer = new Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build();
如果输入媒体格式已与音频 或视频的转换请求匹配,Transformer 会自动切换到 混流 ,即从输入容器将压缩的样本复制到输出容器,而不进行 修改。这样可以避免以相同格式进行 解码和重新编码的计算成本和潜在的质量损失。
设置 HDR 模式
如果输入媒体文件采用 HDR 格式,您可以选择几种不同的模式来指定 Transformer 处理 HDR 信息的方式。您可能
需要使用 HDR_MODE_KEEP_HDR 或
HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL。
HDR_MODE_KEEP_HDR |
HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL |
|
|---|---|---|
| 说明 | 保留 HDR 数据,这意味着 HDR 输出格式与 HDR 输入格式相同。 | 使用 OpenGL 色调映射器将 HDR 输入色调映射到 SDR,这意味着输出格式将为 SDR。 |
| 支持 | 对于包含具有 FEATURE_HdrEditing 功能的编码器的设备,支持 API 级别 31 及更高级别。 |
支持 API 级别 29 及更高级别。 |
| 错误 | 如果不支持,则尝试改用 HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL。 |
如果不支持,则抛出 ExportException。 |
在支持所需编码功能且运行 Android 13
(API 级别 33)或更高版本的设备上,Transformer 对象可让您编辑 HDR 视频。
HDR_MODE_KEEP_HDR 是构建 Composition 对象时的默认模式,
如以下代码所示:
Kotlin
val composition = Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(HDR_MODE_KEEP_HDR) .build()
Java
Composition composition = new Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(Composition.HDR_MODE_KEEP_HDR) .build();
准备媒体项
MediaItem 表示应用中的音频
或视频项。EditedMediaItem 会收集 MediaItem 以及
要对其应用的转换。
剪辑视频
如需移除视频中不需要的部分,您可以向 MediaItem 添加 ClippingConfiguration 来设置自定义开始和结束
位置。
Kotlin
val clippingConfiguration = MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build() val mediaItem = MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build()
Java
ClippingConfiguration clippingConfiguration = new MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build(); MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build();
使用内置特效
Media3 包含许多用于常见转换的内置视频特效, 例如:
| 类 | 特效 |
|---|---|
Presentation |
按分辨率或宽高比缩放媒体项 |
ScaleAndRotateTransformation |
按乘数缩放媒体项和/或旋转媒体项 |
Crop |
将媒体项裁剪为较小或较大的帧 |
OverlayEffect |
在媒体项顶部添加文字或图片叠加层 |
对于音频特效,您可以添加一系列
AudioProcessor
实例,这些实例将转换原始 (PCM) 音频数据。例如,您可以使用
a ChannelMixingAudioProcessor
来混合和缩放音频声道。
如需使用这些特效,请创建特效或音频处理器的实例,使用要应用于媒体项的音频和视频特效构建
Effects实例,然后将Effects对象添加到EditedMediaItem。
Kotlin
val channelMixingProcessor = ChannelMixingAudioProcessor() val rotateEffect = ScaleAndRotateTransformation.Builder().setRotationDegrees(60f).build() val cropEffect = Crop(-0.5f, 0.5f, -0.5f, 0.5f) val effects = Effects(listOf(channelMixingProcessor), listOf(rotateEffect, cropEffect)) val editedMediaItem = EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build()
Java
ChannelMixingAudioProcessor channelMixingProcessor = new ChannelMixingAudioProcessor(); ScaleAndRotateTransformation rotateEffect = new ScaleAndRotateTransformation.Builder() .setRotationDegrees(60f) .build(); Crop cropEffect = new Crop(-0.5f, 0.5f, -0.5f, 0.5f); Effects effects = new Effects( ImmutableList.of(channelMixingProcessor), ImmutableList.of(rotateEffect, cropEffect) ); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build();
创建自定义特效
通过扩展 Media3 中包含的特效,您可以创建特定于您的用例的自定义特效
。在以下示例中,使用子类
MatrixTransformation 将视频放大以在播放的第一
秒内填充帧:
Kotlin
val zoomEffect = MatrixTransformation { presentationTimeUs -> val transformationMatrix = Matrix() // Set the scaling factor based on the playback position val scale = min(1f, presentationTimeUs / 1_000f) transformationMatrix.postScale(/* x */ scale, /* y */ scale) transformationMatrix } val editedMediaItem = EditedMediaItem.Builder(inputMediaItem) .setEffects(Effects(listOf(), listOf(zoomEffect)) .build()
Java
MatrixTransformation zoomEffect = presentationTimeUs -> { Matrix transformationMatrix = new Matrix(); // Set the scaling factor based on the playback position float scale = min(1f, presentationTimeUs / 1_000f); transformationMatrix.postScale(/* x */ scale, /* y */ scale); return transformationMatrix; }; EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(inputMediaItem) .setEffects(new Effects(ImmutableList.of(), ImmutableList.of(zoomEffect))) .build();
如需进一步自定义特效的行为,请实现
GlShaderProgram。
queueInputFrame() 方法用于处理输入帧。例如,如需
利用
MediaPipe的机器学习功能,您可以使用
MediaPipe FrameProcessor
通过 MediaPipe 图形发送每个帧。如需查看相关示例,请参阅
Transformer 演示应用。
预览特效
借助 ExoPlayer,您可以在开始导出过程之前预览添加到媒体项的特效
。使用与EditedMediaItem相同的
Effects对象,在
ExoPlayer 实例上调用setVideoEffects()。
Kotlin
val player = ExoPlayer.builder(context) .build() .also { exoPlayer -> exoPlayer.setMediaItem(inputMediaItem) exoPlayer.setVideoEffects(effects) exoPlayer.prepare() }
Java
ExoPlayer player = new ExoPlayer.builder(context).build(); player.setMediaItem(inputMediaItem); player.setVideoEffects(effects); exoPlayer.prepare();
您还可以使用 ExoPlayer 预览音频特效。构建您的
ExoPlayer 实例时,传入自定义 RenderersFactory,该实例会将
播放器的音频渲染器配置为将音频输出到使用您的
AudioProcessor 序列的 AudioSink。在下面的示例中,我们通过替换
buildAudioSink() 的 DefaultRenderersFactory 方法来实现此目的。
Kotlin
val player = ExoPlayer.Builder(context, object : DefaultRenderersFactory(context) { override fun buildAudioSink( context: Context, enableFloatOutput: Boolean, enableAudioTrackPlaybackParams: Boolean, enableOffload: Boolean ): AudioSink? { return DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode(if (enableOffload) { DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED } else { DefaultAudioSink.OFFLOAD_MODE_DISABLED }) .setAudioProcessors(arrayOf(channelMixingProcessor)) .build() } }).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context) { @Nullable @Override protected AudioSink buildAudioSink( Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload ) { return new DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode( enableOffload ? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED : DefaultAudioSink.OFFLOAD_MODE_DISABLED) .setAudioProcessors(new AudioProcessor[]{channelMixingProcessor}) .build(); } }).build();
开始转换
最后,创建一个 Transformer 以应用您的修改,并开始导出生成的媒体项。
Kotlin
val transformer = Transformer.Builder(context) .addListener(listener) .build() transformer.start(editedMediaItem, outputPath)
Java
Transformer transformer = new Transformer.Builder(context) .addListener(listener) .build(); transformer.start(editedMediaItem, outputPath);
同样,您可以根据需要使用
Transformer.cancel()取消导出过程。
检查进度更新
Transformer.start 会立即返回并异步运行。如需查询转换的
当前进度,请调用
Transformer.getProgress()。
此方法会使用 ProgressHolder,如果进度状态可用,
即该方法返回 PROGRESS_STATE_AVAILABLE,则提供的
ProgressHolder 将使用当前进度百分比进行更新。
您还可以将
监听器
附加到 Transformer,以便在收到完成或错误事件通知。