Создайте базовое приложение для редактирования видео с помощью Media3 Transformer

Transformer API в Jetpack Media3 предназначены для эффективного и надежного редактирования медиафайлов. Transformer поддерживает ряд операций, в том числе:

  • Изменение видео: обрезка, масштабирование и поворот.
  • Добавление эффектов, например оверлеев и фильтров.
  • Обработка видео в специальных форматах, например HDR и замедленной съемки.
  • Экспорт мультимедийного объекта после применения изменений

На этой странице описаны некоторые из основных вариантов использования Transformer. Подробную информацию вы найдете в руководствах по Media3 Transformer.

Начать

Для начала добавьте зависимость от модулей Transformer, Effect и Common в Jetpack Media3:

Kotlin

implementation("androidx.media3:media3-transformer:1.11.1")
implementation("androidx.media3:media3-effect:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")

Яркий

implementation "androidx.media3:media3-transformer:1.11.1"
implementation "androidx.media3:media3-effect:1.11.1"
implementation "androidx.media3:media3-common:1.11.1"

Замените 1.11.1 на нужную версию библиотеки. Последнюю версию можно найти в примечаниях к выпуску.

Важные занятия

Класс Назначение
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, вы можете выбрать один из нескольких различных режимов обработки информации HDR в Transformer. Скорее всего, вам подойдет 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 в SDR с помощью тонального компрессора OpenGL. Выходной формат будет SDR.
Поддержка Поддерживается на устройствах с уровнем API 31 и выше, у которых есть видеокодер с функцией FEATURE_HdrEditing. Поддерживается на уровнях 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 вместе с преобразованиями, которые нужно к нему применить.

Как обрезать видео

Чтобы удалить ненужные части видео, вы можете задать время начала и окончания воспроизведения, добавив ClippingConfiguration в MediaItem.

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). Например, вы можете использовать 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 вы можете посмотреть, как будут выглядеть эффекты, добавленные в мультимедийный объект, прежде чем начать экспорт. Используя те же объекты Effect, что и для EditedMediaItem, вызовите setVideoEffects() в экземпляре ExoPlayer.

Kotlin

val player = ExoPlayer.Builder(context)
    .build()
    .also { exoPlayer ->
        exoPlayer.setMediaItem(inputMediaItem)
        exoPlayer.setVideoEffects(listOf(zoomEffect))
        exoPlayer.prepare()
    }

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(ImmutableList.of(zoomEffect));
player.prepare();

Вы также можете прослушать аудиоэффекты с помощью ExoPlayer. При создании экземпляра ExoPlayer передайте пользовательский объект RenderersFactory, который настраивает аудиорендереры проигрывателя для вывода звука в AudioSink, использующий вашу последовательность AudioProcessor. В примере ниже мы делаем это, переопределяя метод 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, чтобы получать уведомления о завершении или ошибках.