تم تصميم واجهات برمجة التطبيقات Transformer في Jetpack Media3 لجعل تعديل الوسائط موثوقًا وعالي الأداء. تتيح أداة Transformer عددًا من العمليات، بما في ذلك:
- تعديل فيديو من خلال القطع وتغيير الحجم والتدوير
- إضافة تأثيرات، مثل التراكبات والفلاتر
- معالجة التنسيقات الخاصة، مثل فيديو HDR والفيديو بالحركة البطيئة
- تصدير ملف وسائط بعد تطبيق التعديلات
توضّح هذه الصفحة بعض حالات الاستخدام الرئيسية التي تغطيها أداة Transformer. لمزيد من التفاصيل، يمكنكم الانتقال إلى الأدلة الكاملة حول أداة Media3 Transformer.
البدء
للبدء، أضيفوا اعتمادية على وحدات Transformer وEffect وCommon في Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.10.0" implementation "androidx.media3:media3-effect:1.10.0" implementation "androidx.media3:media3-common:1.10.0"
تأكّدوا من استبدال 1.10.0 بالإصدار المفضّل من المكتبة. يمكنكم الرجوع إلى
ملاحظات الإصدار
للاطّلاع على أحدث إصدار.
الفئات المهمة
| الفئة | الغرض |
|---|---|
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 |
| الدعم | متوافق مع مستويات واجهة برمجة التطبيقات 31 والإصدارات الأحدث للأجهزة التي تتضمّن برنامج ترميز بإمكانية FEATURE_HdrEditing |
متوافق مع مستويات واجهة برمجة التطبيقات 29 والإصدارات الأحدث |
| الأخطاء | إذا لم يكن هذا الوضع غير متاح، تتم محاولة استخدام HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL بدلاً منه. |
إذا لم يكن هذا الوضع غير متاح، يتم عرض ExportException. |
على الأجهزة التي تتيح إمكانات الترميز المطلوبة وتعمل بالإصدار 13 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 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). على سبيل المثال، يمكنكم استخدام
a 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 لتلقّي إشعارات بشأن أحداث الإكمال أو الخطأ.