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 จะเปลี่ยนไปใช้ Transmuxing โดยอัตโนมัติ ซึ่งก็คือการคัดลอกตัวอย่างที่บีบอัดจากคอนเทนเนอร์อินพุตไปยังคอนเทนเนอร์เอาต์พุตโดยไม่มีการแก้ไข ซึ่งจะช่วยหลีกเลี่ยงต้นทุนการประมวลผลและการสูญเสียคุณภาพที่อาจเกิดขึ้นจากการถอดรหัสและการเข้ารหัสซ้ำในรูปแบบเดียวกัน
ตั้งค่าโหมด 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 | แมปโทนอินพุต 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();
สร้างเอฟเฟกต์ที่กำหนดเอง
คุณสร้างเอฟเฟกต์ที่กำหนดเองซึ่งเจาะจงสำหรับ Use Case ของคุณได้โดยการขยายเอฟเฟกต์ที่รวมอยู่ใน 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 คุณสามารถใช้ FrameProcessor ของ MediaPipe เพื่อส่งแต่ละเฟรมผ่านกราฟ 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 เพื่อรับการแจ้งเตือนเกี่ยวกับเหตุการณ์ที่เสร็จสมบูรณ์หรือข้อผิดพลาดได้ด้วย