با استفاده از Media3 Transformer یک برنامه اصلی ویرایش ویدیو ایجاد کنید

رابط‌های برنامه‌نویسی کاربردی (API) ترنسفورمر در Jetpack Media3 به گونه‌ای طراحی شده‌اند که ویرایش رسانه را کارآمد و قابل اعتماد کنند. ترنسفورمر از تعدادی عملیات پشتیبانی می‌کند، از جمله:

  • اصلاح ویدیو با برش، مقیاس‌بندی و چرخش
  • افزودن جلوه‌هایی مانند پوشش‌ها و فیلترها
  • پردازش فرمت‌های خاص مانند HDR و ویدیوی اسلوموشن
  • خروجی گرفتن از یک آیتم رسانه‌ای پس از اعمال ویرایش‌ها

این صفحه شما را با برخی از موارد استفاده کلیدی Transformer آشنا می‌کند. برای جزئیات بیشتر می‌توانید به راهنماهای کامل ما در مورد Media3 Transformer مراجعه کنید.

شروع کنید

برای شروع، یک وابستگی به ماژول‌های Transformer، Effect و Common از Jetpack Media3 اضافه کنید:

implementation "androidx.media3:media3-transformer:1.8.0"
implementation "androidx.media3:media3-effect:1.8.0"
implementation "androidx.media3:media3-common:1.8.0"

مطمئن شوید که 1.8.0 با نسخه دلخواه خود از کتابخانه جایگزین می‌کنید. برای مشاهده آخرین نسخه می‌توانید به یادداشت‌های انتشار مراجعه کنید.

کلاس‌های مهم

کلاس هدف
Transformer شروع و توقف تبدیل‌ها و بررسی به‌روزرسانی‌های پیشرفت در یک تبدیل در حال اجرا.
EditedMediaItem نشان‌دهنده یک آیتم رسانه‌ای برای پردازش و ویرایش‌هایی است که باید روی آن اعمال شود.
Effects مجموعه‌ای از جلوه‌های صوتی و تصویری.

پیکربندی خروجی

با Transformer.Builder ، اکنون می‌توانید دایرکتوری videoMimeType و audioMimetype را با تنظیم تابع و بدون نیاز به ایجاد شیء TransformationRequest مشخص کنید.

تبدیل کد بین فرمت‌ها

کد زیر نحوه پیکربندی یک شیء Transformer را برای خروجی ویدئوی H.265/AVC و صدای AAC نشان می‌دهد:

کاتلین

val transformer = Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build()

جاوا

Transformer transformer = new Transformer.Builder(context)
    .setVideoMimeType(MimeTypes.VIDEO_H265)
    .setAudioMimeType(MimeTypes.AUDIO_AAC)
    .build();

اگر فرمت رسانه ورودی از قبل با درخواست تبدیل برای صدا یا تصویر مطابقت داشته باشد، Transformer به طور خودکار به transmuxing تغییر می‌کند، یعنی نمونه‌های فشرده شده را از ظرف ورودی به ظرف خروجی بدون تغییر کپی می‌کند. این کار از هزینه محاسباتی و افت کیفیت احتمالی رمزگشایی و رمزگذاری مجدد در همان قالب جلوگیری می‌کند.

تنظیم حالت 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 با استفاده از نگاشت‌کننده‌ی تنِ OpenGL به SDR نگاشت می‌شود، به این معنی که فرمت خروجی SDR خواهد بود.
پشتیبانی پشتیبانی شده در سطوح API 31+ برای دستگاه‌هایی که شامل یک رمزگذار با قابلیت FEATURE_HdrEditing هستند. پشتیبانی شده در سطوح API 29+.
خطاها اگر پشتیبانی نشود، سعی می‌کند از HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL استفاده کند. اگر پشتیبانی نشود، یک ExportException صادر می‌کند.

در دستگاه‌هایی که از قابلیت‌های کدگذاری مورد نیاز پشتیبانی می‌کنند و اندروید ۱۳ (سطح API ۳۳) یا بالاتر را اجرا می‌کنند، اشیاء Transformer به شما امکان ویرایش ویدیوهای HDR را می‌دهند. HDR_MODE_KEEP_HDR حالت پیش‌فرض هنگام ساخت شیء Composition است، همانطور که در کد زیر نشان داده شده است:

کاتلین

val composition = Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(HDR_MODE_KEEP_HDR)
    .build()

جاوا

Composition composition = new Composition.Builder(
    ImmutableList.of(videoSequence))
    .setHdrMode(Composition.HDR_MODE_KEEP_HDR)
    .build();

تهیه یک مطلب رسانه‌ای

یک MediaItem نشان دهنده یک آیتم صوتی یا تصویری در برنامه شما است. یک EditedMediaItem یک MediaItem به همراه تبدیل‌هایی که باید روی آن اعمال شود، جمع‌آوری می‌کند.

کوتاه کردن یک ویدیو

برای حذف بخش‌های ناخواسته از یک ویدیو، می‌توانید با اضافه کردن ClippingConfiguration به MediaItem موقعیت‌های شروع و پایان دلخواه را تنظیم کنید.

کاتلین

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()

جاوا

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 اضافه کنید.

کاتلین

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()

جاوا

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 برای بزرگنمایی ویدیو به گونه‌ای استفاده کنید که قاب را در ثانیه اول پخش پر کند:

کاتلین

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()

جاوا

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 خود فراخوانی کنید.

کاتلین

val player = ExoPlayer.builder(context)
    .build()
    .also { exoPlayer ->
        exoPlayer.setMediaItem(inputMediaItem)
        exoPlayer.setVideoEffects(effects)
        exoPlayer.prepare()
    }

جاوا

ExoPlayer player = new ExoPlayer.builder(context).build();
player.setMediaItem(inputMediaItem);
player.setVideoEffects(effects);
exoPlayer.prepare();

همچنین می‌توانید جلوه‌های صوتی را با ExoPlayer پیش‌نمایش کنید. هنگام ساخت نمونه ExoPlayer خود، یک RenderersFactory سفارشی را که رندرکننده‌های صوتی پخش‌کننده را برای خروجی صدا به یک AudioSink که از توالی AudioProcessor شما استفاده می‌کند، پیکربندی می‌کند، به آن منتقل کنید. در مثال زیر، ما این کار را با override کردن متد buildAudioSink() از DefaultRenderersFactory انجام می‌دهیم.

کاتلین

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()

جاوا

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 ایجاد کنید تا ویرایش‌های خود را اعمال کنید و شروع به خروجی گرفتن از آیتم رسانه‌ای حاصل کنید.

کاتلین

val transformer = Transformer.Builder(context)
    .addListener(listener)
    .build()
transformer.start(editedMediaItem, outputPath)

جاوا

Transformer transformer = new Transformer.Builder(context)
    .addListener(listener)
    .build();
transformer.start(editedMediaItem, outputPath);

به طور مشابه می‌توانید در صورت نیاز فرآیند اکسپورت را با Transformer.cancel() لغو کنید.

بررسی به‌روزرسانی‌های پیشرفت

Transformer.start بلافاصله مقدار را برمی‌گرداند و به صورت ناهمگام اجرا می‌شود. برای پرس‌وجو از پیشرفت فعلی یک تبدیل، Transformer.getProgress() را فراخوانی کنید. این متد یک ProgressHolder می‌گیرد و اگر وضعیت پیشرفت در دسترس باشد، یعنی اگر متد PROGRESS_STATE_AVAILABLE را برگرداند، ProgressHolder ارائه شده با درصد پیشرفت فعلی به‌روزرسانی می‌شود.

همچنین می‌توانید یک شنونده (listener) به Transformer خود وصل کنید تا از رویدادهای تکمیل یا خطا مطلع شوید.