ממשקי ה-API של Transformer ב-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();
יצירת אפקטים בהתאמה אישית
אתם יכולים להרחיב את האפקטים שכלולים ב-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 שסופק יעודכן באחוז ההתקדמות הנוכחי.
אפשר גם לצרף מאזין ל-Transformer כדי לקבל התראות על אירועי השלמה או שגיאה.