Tạo ứng dụng chỉnh sửa video cơ bản bằng Media3 Transformer

API Transformer trong Jetpack Media3 được thiết kế để giúp việc chỉnh sửa nội dung nghe nhìn hoạt động hiệu quả và đáng tin cậy. Transformer hỗ trợ một số thao tác, bao gồm:

  • Sửa đổi video bằng cách cắt, điều chỉnh tỷ lệ và xoay
  • Thêm các hiệu ứng như lớp phủ và bộ lọc
  • Xử lý các định dạng đặc biệt như video HDR và video chuyển động chậm
  • Xuất một mục nội dung nghe nhìn sau khi áp dụng các bản chỉnh sửa

Trang này sẽ hướng dẫn bạn một số trường hợp sử dụng chính mà Transformer hỗ trợ. Để biết thêm thông tin chi tiết, bạn có thể xem hướng dẫn đầy đủ của chúng tôi về Transformer Media3.

Bắt đầu

Để bắt đầu, hãy thêm phần phụ thuộc vào các mô-đun Transformer, Effect và Common của Jetpack Media3:

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

Nhớ thay thế 1.9.3 bằng phiên bản thư viện mà bạn muốn. Bạn có thể tham khảo ghi chú phát hành để xem phiên bản mới nhất.

Các lớp quan trọng

Lớp Mục đích
Transformer Bắt đầu và dừng các quá trình biến đổi, đồng thời kiểm tra các bản cập nhật tiến trình về quá trình biến đổi đang chạy.
EditedMediaItem Đại diện cho một mục nội dung nghe nhìn cần xử lý và các bản chỉnh sửa cần áp dụng cho mục đó.
Effects Một tập hợp các hiệu ứng âm thanh và video.

Định cấu hình đầu ra

Với Transformer.Builder, giờ đây, bạn có thể chỉ định thư mục videoMimeTypeaudioMimetype bằng cách đặt hàm mà không cần tạo đối tượng TransformationRequest.

Chuyển mã giữa các định dạng

Đoạn mã sau đây cho biết cách định cấu hình đối tượng Transformer để xuất video H.265/AVC và âm thanh 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();

Nếu định dạng nội dung nghe nhìn đầu vào đã khớp với yêu cầu biến đổi cho âm thanh hoặc video, thì Transformer sẽ tự động chuyển sang transmuxing (tức là sao chép các mẫu đã nén từ vùng chứa đầu vào sang vùng chứa đầu ra mà không cần sửa đổi). Điều này giúp tránh chi phí tính toán và khả năng giảm chất lượng của việc giải mã và mã hoá lại ở cùng một định dạng.

Đặt chế độ HDR

Nếu tệp nội dung nghe nhìn đầu vào ở định dạng HDR, bạn có thể chọn một trong một số chế độ để Transformer xử lý thông tin HDR. Bạn có thể muốn sử dụng HDR_MODE_KEEP_HDR hoặc HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL.

HDR_MODE_KEEP_HDR HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
Mô tả Giữ nguyên dữ liệu HDR, nghĩa là định dạng đầu ra HDR giống với định dạng đầu vào HDR. Ánh xạ tông màu đầu vào HDR sang SDR bằng trình ánh xạ tông màu OpenGL, nghĩa là định dạng đầu ra sẽ ở định dạng SDR.
Hỗ trợ Được hỗ trợ trên API cấp 31 trở lên cho các thiết bị có bộ mã hoá với khả năng FEATURE_HdrEditing. Được hỗ trợ trên API cấp 29 trở lên.
Lỗi Nếu không được hỗ trợ, hãy thử sử dụng HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL. Nếu không được hỗ trợ, hãy gửi một ExportException.

Trên các thiết bị hỗ trợ các khả năng mã hoá bắt buộc và chạy Android 13 (cấp độ API 33) trở lên, các đối tượng Transformer cho phép bạn chỉnh sửa video HDR. HDR_MODE_KEEP_HDR là chế độ mặc định khi tạo đối tượng Composition, như trong mã sau:

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();

Chuẩn bị một mục nội dung nghe nhìn

A MediaItem đại diện cho một mục âm thanh hoặc video trong ứng dụng của bạn. Một EditedMediaItem thu thập một MediaItem cùng với các quá trình biến đổi để áp dụng cho mục đó.

Cắt video

Để xoá các phần không mong muốn của video, bạn có thể đặt vị trí bắt đầu và kết thúc tuỳ chỉnh bằng cách thêm ClippingConfiguration vào 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();

Sử dụng các hiệu ứng tích hợp sẵn

Media3 bao gồm một số hiệu ứng video tích hợp sẵn cho các quá trình biến đổi thông thường, ví dụ:

Lớp Hiệu ứng
Presentation Điều chỉnh tỷ lệ mục nội dung nghe nhìn theo độ phân giải hoặc tỷ lệ khung hình
ScaleAndRotateTransformation Điều chỉnh tỷ lệ mục nội dung nghe nhìn theo hệ số nhân và/hoặc xoay mục nội dung nghe nhìn
Crop Cắt mục nội dung nghe nhìn thành một khung hình nhỏ hơn hoặc lớn hơn
OverlayEffect Thêm lớp phủ văn bản hoặc hình ảnh lên trên mục nội dung nghe nhìn

Đối với hiệu ứng âm thanh, bạn có thể thêm một chuỗi các AudioProcessor thực thể sẽ biến đổi dữ liệu âm thanh thô (PCM). Ví dụ: bạn có thể sử dụng a ChannelMixingAudioProcessor để trộn và điều chỉnh tỷ lệ các kênh âm thanh.

Để sử dụng các hiệu ứng này, hãy tạo một thực thể của hiệu ứng hoặc bộ xử lý âm thanh, tạo một thực thể của Effects với các hiệu ứng âm thanh và video mà bạn muốn áp dụng cho mục nội dung nghe nhìn, sau đó thêm đối tượng Effects vào 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();

Tạo hiệu ứng tuỳ chỉnh

Bằng cách mở rộng các hiệu ứng có trong Media3, bạn có thể tạo các hiệu ứng tuỳ chỉnh dành riêng cho trường hợp sử dụng của mình. Trong ví dụ sau, hãy sử dụng lớp con MatrixTransformation để phóng to video nhằm lấp đầy khung hình trong giây đầu tiên phát:

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();

Để tuỳ chỉnh thêm hành vi của hiệu ứng, hãy triển khai a GlShaderProgram. Phương thức queueInputFrame() được dùng để xử lý các khung hình đầu vào. Ví dụ: để tận dụng các khả năng học máy của MediaPipe, bạn có thể sử dụng MediaPipe FrameProcessor để gửi từng khung hình thông qua biểu đồ MediaPipe. Hãy xem ví dụ về việc này trong ứng dụng minh hoạ Transformer.

Xem trước hiệu ứng

Với ExoPlayer, bạn có thể xem trước các hiệu ứng được thêm vào một mục nội dung nghe nhìn trước khi bắt đầu quá trình xuất. Sử dụng cùng một Effects đối tượng như đối với EditedMediaItem, hãy gọi setVideoEffects() trên thực thể ExoPlayer.

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();

Bạn cũng có thể xem trước hiệu ứng âm thanh bằng ExoPlayer. Khi tạo thực thể ExoPlayer, hãy truyền vào một RenderersFactory tuỳ chỉnh để định cấu hình trình kết xuất âm thanh của trình phát nhằm xuất âm thanh sang AudioSink sử dụng chuỗi AudioProcessor của bạn. Trong ví dụ bên dưới, chúng tôi thực hiện việc này bằng cách ghi đè phương thức buildAudioSink() của 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();

Bắt đầu quá trình biến đổi

Cuối cùng, hãy tạo một Transformer để áp dụng các bản chỉnh sửa và bắt đầu xuất mục nội dung nghe nhìn kết quả.

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);

Bạn cũng có thể huỷ quá trình xuất nếu cần bằng Transformer.cancel().

Kiểm tra các bản cập nhật tiến trình

Transformer.start trả về ngay lập tức và chạy không đồng bộ. Để truy vấn tiến trình hiện tại của quá trình biến đổi, hãy gọi Transformer.getProgress(). Phương thức này lấy một ProgressHolder và nếu trạng thái tiến trình có sẵn (tức là nếu phương thức trả về PROGRESS_STATE_AVAILABLE), thì ProgressHolder đã cung cấp sẽ được cập nhật theo tỷ lệ phần trăm tiến trình hiện tại.

Bạn cũng có thể đính kèm một trình nghe vào Transformer để được thông báo về các sự kiện hoàn tất hoặc lỗi.