تم تصميم واجهات برمجة التطبيقات Transformer في Jetpack Media3 لجعل عملية تعديل الوسائط تتسم بالأداء العالي والموثوقية. تتيح أداة Transformer عددًا من العمليات، بما في ذلك:
- تعديل فيديو من خلال القطع وتغيير الحجم والتدوير
- إضافة تأثيرات، مثل العناصر المركّبة والفلاتر
- معالجة التنسيقات الخاصة، مثل فيديوهات 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();
إذا كان تنسيق الوسائط المُدخلة يتطابق مع طلب التحويل الخاص بالصوت أو الفيديو، سيتم تلقائيًا التبديل إلى تحويل الترميز، أي نسخ عيّنات مضغوطة من حاوية الإدخال إلى حاوية الإخراج بدون تعديل. ويؤدي ذلك إلى تجنُّب التكلفة الحسابية وفقدان الجودة المحتمل الناتج عن فك الترميز وإعادة الترميز بالتنسيق نفسه.
ضبط وضع 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). على سبيل المثال، يمكنك استخدام
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
لتكبير الفيديو لملء الإطار خلال الثانية الأولى من التشغيل:
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، يمكنك معاينة التأثيرات
المضافة إلى عنصر وسائط قبل بدء عملية التصدير. استخدِم عنصر 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
لتلقّي إشعارات بشأن أحداث الاكتمال أو الخطأ.