ממשקי ה-API של Transformer ב-Jetpack Media3 נועדו לשפר את הביצועים והאמינות של עריכת המדיה. Transformer תומך במספר פעולות, כולל:
- שינוי סרטון באמצעות חיתוך, שינוי קנה מידה וסיבוב
- הוספת אפקטים כמו שכבות-על ופילטרים
- עיבוד פורמטים מיוחדים כמו HDR וסרטונים בהילוך איטי
- ייצוא פריט מדיה אחרי החלת העריכות
בדף הזה נסביר על כמה מהתרחישים העיקריים לדוגמה שבהם Transformer יכול לעזור. לפרטים נוספים, אפשר לעיין במדריכים המלאים שלנו בנושא Media3 Transformer.
שנתחיל?
כדי להתחיל, מוסיפים יחסי תלות למודולים Transformer, Effect ו-Common של Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.4.1" implementation "androidx.media3:media3-effect:1.4.1" implementation "androidx.media3:media3-common:1.4.1"
חשוב להחליף את 1.4.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) ואילך, אובייקטים מסוג 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, אפשר להשתמש ב-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
מותאם אישית שמגדיר את ה-audio renderers של הנגן להפיק אודיו ל-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
כדי לקבל התראות על אירועי השלמה או שגיאה.