إضافة فيديو مكاني إلى تطبيقك

تتيح حزمة تطوير البرامج Jetpack XR تشغيل فيديوهات جنبًا إلى جنب مجسمة على أسطح مستوية. في الفيديو المجسّم، يتألف كل إطار من صورة للعين اليسرى وصورة للعين اليمنى لمنح المشاهدين إحساسًا بالعمق، ويُعرف ذلك أيضًا باسم الرؤية المجسّمة.

يمكنك عرض فيديوهات ثنائية الأبعاد غير مجسّمة في تطبيقات Android XR باستخدام واجهات برمجة التطبيقات العادية للوسائط المستخدَمة في تطوير تطبيقات Android على أشكال الأجهزة الأخرى.

تشغيل فيديو جنبًا إلى جنب باستخدام Jetpack SceneCore

في الفيديو جنبًا إلى جنب، يتم عرض كل إطار مجسّم على شكل صورتَين مرتّبتَين أفقيًا بجانب بعضهما البعض. يتم ترتيب لقطات الفيديو العلوية والسفلية بشكل عمودي بجانب بعضها البعض.

الفيديو جنبًا إلى جنب ليس برنامج ترميز، بل هو طريقة لتنظيم اللقطات المجسمة، ما يعني أنّه يمكن ترميزه بأي من برامج الترميز المتوافقة مع Android.

يمكنك تحميل فيديو جنبًا إلى جنب باستخدام Media3 Exoplayer ثم عرضه باستخدام SurfaceEntity الجديد. لإنشاء SurfaceEntity، استخدِم الدالة SurfaceEntity.create، كما هو موضّح في المثال التالي.

val stereoSurfaceEntity = SurfaceEntity.create(
    xrSession,
    SurfaceEntity.StereoMode.SIDE_BY_SIDE,
    Pose(Vector3(0.0f, 0.0f, -1.5f)),
    SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f)
)
val videoUri = Uri.Builder()
    .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
    .path("sbs_video.mp4")
    .build()
val mediaItem = MediaItem.fromUri(videoUri)

val exoPlayer = ExoPlayer.Builder(this).build()
exoPlayer.setVideoSurface(stereoSurfaceEntity.getSurface())
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()

تشغيل فيديو بتنسيق MV-HEVC باستخدام Jetpack SceneCore

تم تحسين معيار برنامج الترميز MV-HEVC وتصميمه للفيديوهات المجسّمة، ما يتيح لتطبيقك تشغيل فيديوهات غامرة بجودة عالية بكفاءة. تحتوي ملفات MV-HEVC على دفق أساسي، وهو عادةً العين اليسرى، ودفق استيريو مع العين الأخرى.

على غرار الفيديو جنبًا إلى جنب، يمكنك تحميل الفيديو باستخدام Media3 Exoplayer وعرضه باستخدام SurfaceEntity. عليك تحديد ما إذا كان ملف MV-HEVC يحتوي على صورة أساسية يمنى أو يسرى في المَعلمة stereoMode عند طلب SurfaceEntity.create.

// Create the SurfaceEntity with the StereoMode corresponding to the MV-HEVC content
val stereoSurfaceEntity = SurfaceEntity.create(
    xrSession,
    SurfaceEntity.StereoMode.MULTIVIEW_LEFT_PRIMARY,
    Pose(Vector3(0.0f, 0.0f, -1.5f)),
    SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f)
)
val videoUri = Uri.Builder()
    .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
    .path("mvhevc_video.mp4")
    .build()
val mediaItem = MediaItem.fromUri(videoUri)

val exoPlayer = ExoPlayer.Builder(this).build()
exoPlayer.setVideoSurface(stereoSurfaceEntity.getSurface())
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()

تشغيل فيديو مكاني محمي بموجب إدارة الحقوق الرقمية باستخدام Jetpack SceneCore

تتيح حزمة تطوير البرامج (SDK) الخاصة بـ Jetpack XR تشغيل فيديوهات مشفّرة باستخدام إطار عمل إدارة الحقوق الرقمية (DRM) المضمّن في Android. توفّر إدارة الحقوق الرقمية (DRM) الحماية لمحتواك من خلال إتاحة التوزيع الآمن ومنع النسخ أو التشغيل غير المصرَّح بهما.

تتضمّن العملية أن يتواصل تطبيق مشغّل الوسائط مع خادم ترخيص للحصول على مفاتيح فك التشفير. على أجهزة Android، تتم إدارة هذه العملية بشكل آمن، ويتم عرض إطارات الفيديو التي تم فك تشفيرها في مخزن مؤقت محمي للرسومات لا يمكن للنظام أو التطبيقات الأخرى الوصول إليه، ما يمنع تسجيل لقطات للشاشة.

لتشغيل فيديو محمي بموجب إدارة الحقوق الرقمية باستخدام Jetpack SceneCore، عليك إجراء ما يلي:

  1. اضبط SurfaceEntity لطلب سطح محمي.
  2. اضبط Media3 Exoplayer باستخدام معلومات إدارة الحقوق الرقمية اللازمة للتعامل مع عملية تبادل المفاتيح.
  3. اضبط إخراج الصوت من المشغّل على سطح SurfaceEntity.

يوضّح المثال التالي كيفية ضبط ExoPlayer لتشغيل بث محمي بنظام إدارة الحقوق الرقمية وعرضه على SurfaceEntity:

// Create a SurfaceEntity with DRM content

// Define the URI for your DRM-protected content and license server.
val videoUri = "https://your-content-provider.com/video.mpd"
val drmLicenseUrl = "https://your-license-server.com/license"

// Create the SurfaceEntity with the PROTECTED content security level.
val protectedSurfaceEntity = SurfaceEntity.create(
    session = xrSession,
    stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f)),
    canvasShape = SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f),
    contentSecurityLevel = SurfaceEntity.ContentSecurityLevel.PROTECTED
)

// Build a MediaItem with the necessary DRM configuration.
val mediaItem = MediaItem.Builder()
    .setUri(videoUri)
    .setDrmConfiguration(
        MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
            .setLicenseUri(drmLicenseUrl)
            .build()
    )
    .build()

// Initialize ExoPlayer and set the protected surface.
val exoPlayer = ExoPlayer.Builder(this).build()
exoPlayer.setVideoSurface(protectedSurfaceEntity.getSurface())

// Set the media item and start playback.
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()

للحصول على نظرة عامة أكثر تفصيلاً حول إطار عمل إدارة الحقوق الرقمية (DRM) للوسائط في Android، يُرجى الاطّلاع على مستندات إدارة الحقوق الرقمية (DRM) للوسائط على source.android.com.

تشغيل فيديوهات بنطاق 180 درجة و360 درجة باستخدام Jetpack SceneCore

يتيح SurfaceEntity تشغيل فيديوهات بنطاق 180 درجة على أسطح نصف كروية وفيديوهات بنطاق 360 درجة على أسطح كروية. تشير المَعلمة radius إلى الحجم الشعاعي للأسطح المعنية بالمتر تلقائيًا.

يوضّح الرمز التالي كيفية إعداد SurfaceEntity لتشغيل المحتوى على نصف كرة بزاوية 180 درجة وكرة بزاوية 360 درجة. عند استخدام أشكال لوحة العرض هذه، يجب تحديد موضع السطح من خلال الاستفادة من وضع رأس المستخدم لتوفير تجربة غامرة.

// Set up the surface for playing a 180° video on a hemisphere.
val hemisphereStereoSurfaceEntity =
    SurfaceEntity.create(
        xrSession,
        SurfaceEntity.StereoMode.SIDE_BY_SIDE,
        xrSession.scene.spatialUser.head?.transformPoseTo(
            Pose.Identity,
            xrSession.scene.activitySpace
        )!!,
        SurfaceEntity.CanvasShape.Vr180Hemisphere(1.0f),
    )
// ... and use the surface for playing the media.

// Set up the surface for playing a 360° video on a sphere.
val sphereStereoSurfaceEntity =
    SurfaceEntity.create(
        xrSession,
        SurfaceEntity.StereoMode.TOP_BOTTOM,
        xrSession.scene.spatialUser.head?.transformPoseTo(
            Pose.Identity,
            xrSession.scene.activitySpace
        )!!,
        SurfaceEntity.CanvasShape.Vr360Sphere(1.0f),
    )
// ... and use the surface for playing the media.

عناصر التحكّم المتقدّمة في SurfaceEntity

للحصول على تحكّم أكثر تقدّمًا في عرض الفيديو والصورة، مثل تطبيق تأثيرات مواد مخصّصة، يمكنك العمل مباشرةً مع SurfaceEntity من مكتبة SceneCore.

توضّح الأقسام التالية بعض الميزات المتقدّمة المتوفّرة على SurfaceEntity.

تطبيق التمويه على الحواف

يمكنك تنعيم حواف السطح لمساعدته على الاندماج مع البيئة المحيطة من خلال ضبط السمة edgeFeather.

// Create a SurfaceEntity.
val surfaceEntity = SurfaceEntity.create(
    session = xrSession,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f))
)

// Feather the edges of the surface.
surfaceEntity.edgeFeather =
    SurfaceEntity.EdgeFeatheringParams.SmoothFeather(0.1f, 0.1f)

تطبيق قناع ألفا

تطبيق قناع ألفا لإنشاء أسطح غير مستطيلة أو إضافة تأثيرات الشفافية أولاً، حمِّل Texture من مادة عرض، ثمّ عيّنه للسمة primaryAlphaMaskTexture:

// Create a SurfaceEntity.
val surfaceEntity = SurfaceEntity.create(
    session = xrSession,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f))
)

// Load the texture in a coroutine scope.
activity.lifecycleScope.launch {
    val alphaMaskTexture =
        Texture.create(
            xrSession,
            Paths.get("textures", "alpha_mask.png"),
            TextureSampler.create()
        )

    // Apply the alpha mask.
    surfaceEntity.primaryAlphaMaskTexture = alphaMaskTexture

    // To remove the mask, set the property to null.
    surfaceEntity.primaryAlphaMaskTexture = null
}

تشغيل فيديو مكاني باستخدام Jetpack Compose للواقع الممتد

إذا كنت مهتمًا بمعرفة كيفية تشغيل الفيديو باستخدام Jetpack Compose للواقع الممتد، تعرَّف على كيفية إضافة مساحة لعرض محتوى الصور أو الفيديو.