Warning: The IMA SDK for Cast SDK v2 has been officially deprecated, as of November 17th, 2021. All existing users are urged to migrate to CAF native Ad Breaks.

تطبيق مرسل Android

Stay organized with collections Save and categorize content based on your preferences.

يوضح لك هذا الدليل كيفية إنشاء تطبيق مرسل يعمل بنظام التشغيل Android ويعمل بتكنولوجيا Google Cast من خلال توجيهك من خلال نموذج تطبيق.

لمتابعة هذا الدليل، يمكنك تنزيل AdvancedExample.zip، وهو تطبيق كامل من Android ينفّذ وظيفة الإرسال لمقاطع الفيديو التي تحتوي على إعلانات باستخدام حزمة IMA Android SDK.

المتطلبات الأساسية

ويستند هذا الدليل إلى المعرفة الحالية لأداة Google Cast SDK وIMA SDK لنظام التشغيل Android. ويجب على المستخدمين التعرف على ما يلي:

يناقش هذا الدليل الإعداد المحدد المستخدم في المثال المتقدم للتواصل مع المتلقي. ولا تغطي شفرة إعداد Cast القياسية أو تشغيل إعلانات الوسائط التفاعلية العادية. لمزيد من التفاصيل حول تنفيذ جهاز الاستقبال، يُرجى مراجعة دليل استقبال HTML5.

مشغّل الوسائط

ويتم تنفيذ وظيفة تحميل الوسائط على جهاز الاستقبال في CastApplication.java، إلى جانب معظم الرموز المتعلقة بالإرسال. الوسائط التي يحاول تحميلها هي عنوان URL للمحتوى، ثم تُرسل رسالة مخصّصة إلى المُستلِم تطلب منه طلب الإعلانات باستخدام علامة الإعلان نفسها والبحث عن الطابع الزمني الحالي للمُرسِل.

CastApplication.java

private void createMediaPlayer() {
    Log.d(TAG, "making media player");
    // Create a Remote Media Player
    mRemoteMediaPlayer = new RemoteMediaPlayer();

    mRemoteMediaPlayer.setOnStatusUpdatedListener(
            new RemoteMediaPlayer.OnStatusUpdatedListener() {
                @Override
                public void onStatusUpdated() {
                    MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
                    Log.d(TAG, "Media status: " + mediaStatus);
                }
            });

    mRemoteMediaPlayer.setOnMetadataUpdatedListener(
            new RemoteMediaPlayer.OnMetadataUpdatedListener() {
                @Override
                public void onMetadataUpdated() {
                    MediaInfo mediaInfo = mRemoteMediaPlayer.getMediaInfo();
                    Log.d(TAG, "Media info: " + mediaInfo);
                }
            });

    try {
        Cast.CastApi.setMessageReceivedCallbacks(sApiClient,
                mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
    } catch (IOException e) {
        Log.e(TAG, "Exception while creating media channel", e);
    }

    mRemoteMediaPlayer
            .requestStatus(sApiClient)
            .setResultCallback(
                    new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
                        @Override
                        public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
                            if (!result.getStatus().isSuccess()) {
                                Log.e(TAG, "Failed to request status.");
                            } else {
                                MediaMetadata mediaMetadata =
                                        new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
                                mediaMetadata.putString(MediaMetadata.KEY_TITLE, "My video");
                                MediaInfo mediaInfo = new MediaInfo.Builder(mContentUrl)
                                        .setContentType("video/mp4")
                                        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                                        .setMetadata(mediaMetadata)
                                        .build();
                                loadMedia(mediaInfo, false);
                            }
                        }
                    });
}


private void loadMedia(MediaInfo mediaInfo, Boolean autoplay) {
    try {
        Log.d(TAG, "loading media");
        mRemoteMediaPlayer.load(sApiClient, mediaInfo, autoplay)
                .setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
                    @Override
                    public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
                        if (result.getStatus().isSuccess()) {
                            boolean adStarted = mVideoPlayerController.hasAdStarted();
                            if (mVideoFragment.isVmap() || !adStarted) {
                                sendMessage("requestAd," + mAdTagUrl + ","
                                        + mVideoPlayerController.getCurrentContentTime());
                            } else {
                                sendMessage("seek,"
                                    + mVideoPlayerController.getCurrentContentTime());
                            }
                        } else {
                            Log.e(TAG, "Error loading Media : "
                                    + result.getStatus().getStatusCode());
                        }
                    }
                });
    } catch (Exception e) {
        Log.e(TAG, "Problem opening media during loading", e);
    }
}

private void sendMessage(String message) {
    if (sApiClient != null && incomingMsgHandler != null) {
        try {
            Log.d(TAG, "Sending message: " + message);
            Cast.CastApi.sendMessage(sApiClient, NAMESPACE, message)
                    .setResultCallback(new ResultCallback<Status>() {
                        @Override
                        public void onResult(Status result) {
                            if (!result.isSuccess()) {
                                Log.e(TAG, "Sending message failed");
                            }
                        }
                    });
        } catch (Exception e) {
            Log.e(TAG, "Exception while sending message", e);
        }
    }
}

تم تحديد رسالتين في استدعاء onResult: seek لاستئناف التشغيل على جهاز الاستقبال دون إعلانات والرسالة requestAd التي تعيد طلب علامة الإعلان نفسها على جهاز الاستقبال. يتم استخدام الرسالة requestAd لقوائم التشغيل الإعلانية وقائمة تشغيل إعلانات الفيديو (VMAP) في هذا المثال، ولكن يمكنك تحديد سلوكك الخاص بما يجب أن يحدث عند إرسال المحتوى. يتم الفصل بين هذه الرسائل بفواصل، حيث تكون المعلمة الأولى هي اسم الرسالة، وتكون المعلمات المتبقية هي علامة الإعلان ووقت البحث.

معاودة الاتصال بـ MediaRouter

يتعامل MediaRouter.Callback مع بدء البث وإيقافه بالإضافة إلى التحكم في تبديل تشغيل الفيديو بين Android وأجهزة البث. يتضمن هذا إيقاف الفيديو مؤقتًا والبحث عنه.

CastApplication.java

private final MediaRouter.Callback mediaRouterCallback = new MediaRouter.Callback() {
    @Override
    public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
        // User starts casting. Pause video on device and set Google Cast device.
        mVideoPlayerController = mVideoFragment.getVideoPlayerController();
        mVideoPlayerController.pause();
        mAdTagUrl = mVideoPlayerController.getAdTagUrl();
        mContentUrl = mVideoPlayerController.getContentVideoUrl();
        CastDevice device = CastDevice.getFromBundle(info.getExtras());
        setSelectedDevice(device);
    }

    @Override
    public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
        // User stops casting. Resume video on device and seek to current time of Google Cast.
        mVideoPlayerController.resume();
        if (mCastAdPlaying) {
            mVideoPlayerController.seek(mCastContentTime);
        } else {
            double videoPosition = mRemoteMediaPlayer.getApproximateStreamPosition();
            mVideoPlayerController.seek(videoPosition / 1000.0);
        }

        stopCastApplication();
        setSelectedDevice(null);
    }
};

الرسائل

يحدّد التطبيق MessageReceivedCallback للسماح للمُستلِم بإرسال إشعارات حول الوقت المحدّد من فيديو يتم فيه إرسال البث وعندما يبدأ الإعلان الذي تم إرساله ثم يبدأ المحتوى. ويتم إرسال هذه الرسائل من قِبل المستلم ويتم التعامل معها من خلال رسالتين، onContentPauseRequested وonContentResumeRequested. ويتم الفصل بين هذه الرسائل بفواصل وبتنسيق مشابه للرسائل seek وrequestAd الموضحة أعلاه.

CastApplication.java

public final Cast.MessageReceivedCallback incomingMsgHandler =
        new Cast.MessageReceivedCallback() {
            @Override
            public void onMessageReceived(CastDevice castDevice, String namespace,
                                          String message) {
                Log.d(TAG, "Receiving message: " + message);
                String[] splitMessage = message.split(",");
                String event = splitMessage[0];
                switch (event) {
                    case "onContentPauseRequested":
                        mCastAdPlaying = true;
                        mCastContentTime = Double.parseDouble(splitMessage[1]);
                        return;
                    case "onContentResumeRequested":
                        mCastAdPlaying = false;
                        return;
                }
            }
        };

الخطوات التالية

في هذه المرحلة، تكون على دراية جيدة بكيفية تفاعل تطبيق مرسل Android مع جهاز الاستقبال. لتجربتها، أنشئ تطبيق مستلِم وسجِّل المُستلِم لتلقي مُعرِّف التطبيق الذي يتم استخدامه مع طلبات البيانات من واجهة برمجة التطبيقات من تطبيق المُرسِل.