تم تصميم واجهات برمجة التطبيقات Transformer API في Jetpack Media3 لجعل تعديل الوسائط فعّالاً وموثوقًا. تتيح واجهة Transformer تنفيذ عدد من العمليات، بما في ذلك:
- تعديل فيديو من خلال قطعه وتغيير حجمه وتدويره
- إضافة تأثيرات مثل العناصر المركّبة والفلاتر
- معالجة التنسيقات الخاصة، مثل فيديوهات النطاق العالي الديناميكية (HDR) والفيديوهات بالحركة البطيئة
- تصدير ملف وسائط بعد تطبيق التعديلات
توضّح لك هذه الصفحة بعض حالات الاستخدام الرئيسية التي يتيحها Transformer. لمزيد من التفاصيل، يمكنك الاطّلاع على الأدلة الكاملة حول Media3 Transformer.
البدء
للبدء، أضِف تبعية على وحدات Transformer وEffect وCommon في Jetpack Media3:
Kotlin
implementation("androidx.media3:media3-transformer:1.11.1")
implementation("androidx.media3:media3-effect:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")
أنيق
implementation "androidx.media3:media3-transformer:1.11.1"
implementation "androidx.media3:media3-effect:1.11.1"
implementation "androidx.media3:media3-common:1.11.1"
تأكَّد من استبدال 1.11.1 بالإصدار المفضّل لديك من
المكتبة. يمكنك الرجوع إلى ملاحظات الإصدار للاطّلاع على أحدث إصدار.
الصفوف المهمة
| الفئة | الغرض |
|---|---|
Transformer |
بدء عمليات التحويل وإيقافها والتحقّق من آخر المعلومات عن مستوى تقدّم عملية تحويل جارية |
EditedMediaItem |
تمثّل ملف وسائط مطلوب معالجته والتعديلات التي يجب تطبيقها عليه. |
Effects |
مجموعة من المؤثرات الصوتية والمرئية |
ضبط الناتج
باستخدام Transformer.Builder، يمكنك الآن تحديد videoMimeType وaudioMimetype مباشرةً من خلال ضبط الدالة بدون الحاجة إلى إنشاء عنصر TransformationRequest.
تحويل الترميز بين التنسيقات
يوضّح الرمز التالي كيفية ضبط عنصر Transformer لإخراج فيديو بتنسيق H.265/AVC وصوت بتنسيق الترميز المتقدّم للصوت:
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.
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، يمكنك معاينة المؤثرات المضافة إلى ملف وسائط قبل
بدء عملية التصدير. باستخدام عناصر Effect نفسها المستخدَمة في EditedMediaItem، يمكنك استدعاء setVideoEffects() في نسخة ExoPlayer.
Kotlin
val player = ExoPlayer.Builder(context)
.build()
.also { exoPlayer ->
exoPlayer.setMediaItem(inputMediaItem)
exoPlayer.setVideoEffects(listOf(zoomEffect))
exoPlayer.prepare()
}
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(ImmutableList.of(zoomEffect));
player.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 ليتم إعلامك بأحداث الإكمال أو الخطأ.