ממשקי ה-API של Transformer ב-Jetpack Media3 נועדו להפוך את עריכת המדיה ליעילה ואמינה. הכלי לשינוי תומך במספר פעולות, כולל:
- שינוי סרטון באמצעות חיתוך, שינוי גודל וסיבוב
- הוספת אפקטים כמו שכבות-על ופילטרים
- עיבוד פורמטים מיוחדים כמו HDR וסרטונים בהילוך איטי
- ייצוא פריט מדיה אחרי עריכה
בדף הזה מוסבר על כמה מהתרחישים העיקריים לשימוש ב-Transformer. פרטים נוספים זמינים במדריכים המלאים שלנו בנושא Media3 Transformer.
שנתחיל?
כדי להתחיל, מוסיפים תלות במודולים Transformer, Effect ו-Common של Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.7.1" implementation "androidx.media3:media3-effect:1.7.1" implementation "androidx.media3:media3-common:1.7.1"
חשוב להחליף את 1.7.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, אפשר לבחור בין כמה מצבים שונים לעיבוד מידע ה-HDR על ידי Transformer. כדאי להשתמש ב-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) ומעלה, אפשר לערוך סרטוני HDR באמצעות אובייקטים של Transformer
.
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, אפשר להשתמש ב-MediaPipe FrameProcessor
כדי לשלוח כל פריים דרך גרף MediaPipe. דוגמה לכך אפשר לראות באפליקציית ההדגמה של Transformer.
תצוגה מקדימה של האפקטים
באמצעות ExoPlayer, אפשר לראות בתצוגה מקדימה את האפקטים שנוספו לפריט מדיה לפני שמתחילים בתהליך הייצוא. באמצעות אותו אובייקט Effects
כמו ב-EditedMediaItem
, קוראים ל-setVideoEffects()
במופע 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();
אפשר גם לראות תצוגה מקדימה של אפקטים קוליים באמצעות 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
כדי לקבל התראה על אירועי השלמה או שגיאה.