Media3 Transformer का इस्तेमाल करके वीडियो एडिटिंग के लिए एक बेसिक ऐप्लिकेशन बनाएं

Jetpack Media3 में मौजूद Transformer API, मीडिया एडिट करने की सुविधा को बेहतर और भरोसेमंद बनाने के लिए डिज़ाइन किए गए हैं. Transformer कई तरह के ऑपरेशन के साथ काम करता है. इनमें ये शामिल हैं:

  • वीडियो में काट-छांट करके, उसे स्केल करके, और घुमाकर बदलाव करना
  • ओवरले और फ़िल्टर जैसे इफ़ेक्ट जोड़ना
  • एचडीआर और स्लो-मोशन वीडियो जैसे खास फ़ॉर्मैट को प्रोसेस करना
  • बदलाव करने के बाद मीडिया आइटम को एक्सपोर्ट करना

इस पेज पर, Transformer के कुछ मुख्य इस्तेमाल के उदाहरणों के बारे में बताया गया है. ज़्यादा जानकारी के लिए, Media3 ट्रांसफ़ॉर्मर के बारे में पूरी जानकारी देने वाली हमारी गाइड देखें.

शुरू करें

शुरू करने के लिए, Jetpack Media3 के ट्रांसफ़ॉर्मर, इफ़ेक्ट, और सामान्य मॉड्यूल पर डिपेंडेंसी जोड़ें:

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

1.5.0 को लाइब्रेरी के अपने पसंदीदा वर्शन से बदलना न भूलें. नया वर्शन देखने के लिए, रिलीज़ नोट देखें.

अहम क्लास

कक्षा मकसद
Transformer ट्रांसफ़ॉर्मेशन शुरू और बंद करें. साथ ही, चल रहे ट्रांसफ़ॉर्मेशन की प्रोग्रेस के अपडेट देखें.
EditedMediaItem प्रोसेस किए जाने वाले मीडिया आइटम और उसमें किए जाने वाले बदलावों को दिखाता है.
Effects ऑडियो और वीडियो इफ़ेक्ट का कलेक्शन.

आउटपुट कॉन्फ़िगर करना

Transformer.Builder की मदद से, अब videoMimeType और audioMimetype डायरेक्ट्री को तय किया जा सकता है. इसके लिए, आपको TransformationRequest ऑब्जेक्ट बनाने की ज़रूरत नहीं है.

एक फ़ॉर्मैट से दूसरे फ़ॉर्मैट में ट्रांसकोड करना

यहां दिए गए कोड में, H.265/AVC वीडियो और AAC ऑडियो को आउटपुट करने के लिए, Transformer ऑब्जेक्ट को कॉन्फ़िगर करने का तरीका बताया गया है:

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

अगर इनपुट मीडिया फ़ॉर्मैट, ऑडियो या वीडियो के लिए ट्रांसफ़ॉर्मेशन के अनुरोध से पहले से मेल खाता है, तो ट्रांसफ़ॉर्मर अपने-आप ट्रांसम्यूक्सिंग पर स्विच हो जाता है. इसका मतलब है कि कॉम्प्रेस किए गए सैंपल को इनपुट कंटेनर से आउटपुट कंटेनर में बिना किसी बदलाव के कॉपी किया जाता है. इससे, एक ही फ़ॉर्मैट में डिकोड करने और फिर से एन्कोड करने के लिए, कंप्यूटेशनल लागत और क्वालिटी में होने वाली संभावित गिरावट से बचा जा सकता है.

एचडीआर मोड सेट करना

अगर इनपुट मीडिया फ़ाइल 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
ब्यौरा एचडीआर डेटा को सुरक्षित रखें. इसका मतलब है कि एचडीआर आउटपुट फ़ॉर्मैट, एचडीआर इनपुट फ़ॉर्मैट जैसा ही है. OpenGL टोन-मैपर का इस्तेमाल करके, एचडीआर इनपुट को एसडीआर में टोनमैप करें. इसका मतलब है कि आउटपुट फ़ॉर्मैट एसडीआर में होगा.
सहायता यह सुविधा, एपीआई लेवल 31 और उसके बाद के वर्शन वाले उन डिवाइसों पर काम करती है जिनमें FEATURE_HdrEditing क्षमता वाला एन्कोडर शामिल है. यह सुविधा, एपीआई लेवल 29 और उसके बाद के वर्शन पर काम करती है.
गड़बड़ियां अगर यह काम नहीं करता है, तो इसके बजाय HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL का इस्तेमाल करने की कोशिश की जाती है. अगर यह सुविधा काम नहीं करती है, तो ExportException दिखता है.

Transformer ऑब्जेक्ट की मदद से, एचडीआर वीडियो में बदलाव किया जा सकता है. ऐसा उन डिवाइसों पर किया जा सकता है जिनमें एन्कोड करने की ज़रूरी सुविधाएं मौजूद हों और Android 13 (एपीआई लेवल 33) या उसके बाद का वर्शन चल रहा हो. Composition ऑब्जेक्ट बनाते समय, HDR_MODE_KEEP_HDR डिफ़ॉल्ट मोड होता है, जैसा कि नीचे दिए गए कोड में दिखाया गया है:

KotlinJava
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 को इकट्ठा करता है और उस पर लागू होने वाले ट्रांसफ़ॉर्मेशन को इकट्ठा करता है.

वीडियो में काट-छांट करना

वीडियो के अनचाहे हिस्सों को हटाने के लिए, MediaItem में ClippingConfiguration जोड़कर, वीडियो के शुरू और खत्म होने की जगह को पसंद के मुताबिक सेट किया जा सकता है.

KotlinJava
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 के उदाहरणों का क्रम जोड़ा जा सकता है. इससे रॉ (पीसीएम) ऑडियो डेटा में बदलाव होगा. उदाहरण के लिए, ऑडियो चैनलों को मिक्स और स्केल करने के लिए, ChannelMixingAudioProcessor का इस्तेमाल किया जा सकता है.

इन इफ़ेक्ट का इस्तेमाल करने के लिए, इफ़ेक्ट या ऑडियो प्रोसेसर का एक इंस्टेंस बनाएं. इसके बाद, मीडिया आइटम पर लागू करने के लिए, ऑडियो और वीडियो इफ़ेक्ट के साथ Effects का एक इंस्टेंस बनाएं. इसके बाद, Effects ऑब्जेक्ट को EditedMediaItem में जोड़ें.

KotlinJava
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 का इस्तेमाल किया गया है. ऐसा, वीडियो चलाने के पहले सेकंड में किया गया है:

KotlinJava
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 ग्राफ़ के ज़रिए हर फ़्रेम को भेजने के लिए, MediaPipe FrameProcessor का इस्तेमाल किया जा सकता है. इसका उदाहरण, Transformer डेमो ऐप्लिकेशन में देखें.

इफ़ेक्ट की झलक देखना

ExoPlayer की मदद से, एक्सपोर्ट की प्रोसेस शुरू करने से पहले, मीडिया आइटम में जोड़े गए इफ़ेक्ट की झलक देखी जा सकती है. EditedMediaItem के लिए इस्तेमाल किए गए Effects ऑब्जेक्ट का इस्तेमाल करके, अपने ExoPlayer इंस्टेंस पर setVideoEffects() को कॉल करें.

KotlinJava
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 पास करें. यह RenderersFactory, प्लेयर के ऑडियो रेंडरर को कॉन्फ़िगर करता है, ताकि ऑडियो को AudioSink में आउटपुट किया जा सके. AudioSink, आपके AudioProcessor क्रम का इस्तेमाल करता है. नीचे दिए गए उदाहरण में, हम DefaultRenderersFactory के buildAudioSink() तरीके को बदलकर ऐसा करते हैं.

KotlinJava
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 बनाएं और नतीजे में मिलने वाले मीडिया आइटम को एक्सपोर्ट करना शुरू करें.

KotlinJava
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 को प्रोग्रेस के मौजूदा प्रतिशत के साथ अपडेट कर दिया जाएगा.

Transformer को लिसनर से भी जोड़ा जा सकता है, ताकि आपको पूरा होने या गड़बड़ी वाले इवेंट के बारे में सूचना मिल सके.