Transformer API ใน Jetpack Media3 ออกแบบมาเพื่อให้การตัดต่อสื่อมีประสิทธิภาพและเชื่อถือได้ Transformer รองรับการดำเนินการหลายอย่าง ได้แก่
- การแก้ไขวิดีโอด้วยการตัด ปรับขนาด และหมุน
- การเพิ่มเอฟเฟกต์ เช่น การวางซ้อนและฟิลเตอร์
- การประมวลผลรูปแบบพิเศษ เช่น วิดีโอ HDR และวิดีโอสโลว์โมชัน
- การส่งออกรายการสื่อหลังจากใช้การแก้ไข
หน้านี้จะอธิบายกรณีการใช้งานที่สำคัญบางส่วนซึ่งครอบคลุมโดย Transformer ดูรายละเอียดเพิ่มเติมได้ที่คำแนะนำฉบับเต็มเกี่ยวกับ Media3 Transformer
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน ให้เพิ่มทรัพยากร Dependency ในโมดูล 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 คุณสามารถเลือกโหมดต่างๆ สำหรับวิธีที่ 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();
สร้างเอฟเฟกต์ที่กำหนดเอง
การขยายเอฟเฟกต์ที่รวมอยู่ใน Media3 จะช่วยให้คุณสร้างเอฟเฟกต์ที่กำหนดเอง
ซึ่งเหมาะกับกรณีการใช้งานของคุณได้ ในตัวอย่างต่อไปนี้ ให้ใช้ subclass
MatrixTransformation เพื่อซูมวิดีโอให้เต็มเฟรมในช่วง 1
วินาทีแรกของการเล่น
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 เพื่อรับการแจ้งเตือนเกี่ยวกับ
เหตุการณ์ที่เสร็จสมบูรณ์หรือข้อผิดพลาดได้ด้วย