API ของ Transformer ใน Jetpack Media3 ได้รับการออกแบบมาเพื่อให้การตัดต่อสื่อมีประสิทธิภาพและเชื่อถือได้ Transformer รองรับการดำเนินการต่างๆ มากมาย ซึ่งรวมถึง
- การแก้ไขวิดีโอด้วยการตัด การปรับขนาด และการหมุน
- การเพิ่มเอฟเฟกต์ เช่น การซ้อนทับและฟิลเตอร์
- การประมวลผลรูปแบบพิเศษ เช่น วิดีโอ HDR และวิดีโอสโลว์โมชัน
- การส่งออกรายการสื่อหลังจากใช้การแก้ไข
หน้านี้จะแนะนำกรณีการใช้งานหลักบางส่วนที่ Transformer ครอบคลุม ดูรายละเอียดเพิ่มเติมได้ที่คำแนะนำฉบับเต็มเกี่ยวกับ Media3 Transformer
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน ให้เพิ่มทรัพยากร Dependency ในโมดูล Transformer, Effect และ Common ของ Jetpack Media3 ดังนี้
Kotlin
implementation("androidx.media3:media3-transformer:1.10.1")
implementation("androidx.media3:media3-effect:1.10.1")
implementation("androidx.media3:media3-common:1.10.1")
ดึงดูด
implementation "androidx.media3:media3-transformer:1.10.1"
implementation "androidx.media3:media3-effect:1.10.1"
implementation "androidx.media3:media3-common:1.10.1"
โปรดตรวจสอบว่าได้แทนที่ 1.10.1 ด้วยไลบรารีเวอร์ชันที่ต้องการแล้ว คุณสามารถดูเวอร์ชันล่าสุดได้ในบันทึกประจำรุ่น
คลาสสำคัญ
| ชั้นเรียน | วัตถุประสงค์ |
|---|---|
Transformer |
เริ่มและหยุดการแปลง รวมถึงตรวจสอบการอัปเดตความคืบหน้าในการแปลงที่กำลังทำงานอยู่ |
EditedMediaItem |
แสดงรายการสื่อที่จะประมวลผลและการแก้ไขที่จะใช้กับรายการนั้น |
Effects |
ชุดเอฟเฟกต์เสียงและวิดีโอ |
กำหนดค่าเอาต์พุต
ตอนนี้คุณสามารถระบุ videoMimeType และ audioMimetype ได้โดยตรงด้วย Transformer.Builder โดยการตั้งค่าฟังก์ชันโดยไม่ต้องสร้างออบเจ็กต์ 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 คุณสามารถเลือกระหว่างโหมดต่างๆ 2-3 โหมดสำหรับวิธีที่ 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 | Tonemap อินพุต HDR เป็น SDR โดยใช้ OpenGL tone-mapper ซึ่งหมายความว่ารูปแบบเอาต์พุตจะเป็น 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();
เตรียมรายการสื่อ
A MediaItem แสดงรายการเสียงหรือวิดีโอในแอป ส่วน An
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 ที่ระบุด้วยเปอร์เซ็นต์ความคืบหน้าปัจจุบัน
นอกจากนี้ คุณยังแนบ Listener กับ Transformer เพื่อรับการแจ้งเตือนเกี่ยวกับ
เหตุการณ์การเสร็จสมบูรณ์หรือข้อผิดพลาดได้ด้วย