تم تصميم واجهات برمجة تطبيقات 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، يمكنك الاختيار من بين عدة
أوضاع مختلفة لكيفية معالجة 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_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، يمكنك معاينة المؤثرات
المُضافة إلى عنصر وسائط قبل بدء عملية التصدير. باستخدام عنصر
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
لإخراج الصوت إلى 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
لتلقّي إشعارات بشأن أحداث اكتمال أو خطأ.