فرافکنی رسانه ای

رابط‌های برنامه‌نویسی کاربردی (API) android.media.projection که در اندروید ۵ (سطح API 21) معرفی شدند، شما را قادر می‌سازند تا محتوای صفحه نمایش دستگاه را به عنوان یک جریان رسانه‌ای ضبط کنید و آن را پخش، ضبط یا در دستگاه‌های دیگر مانند تلویزیون پخش کنید.

اندروید ۱۴ (سطح API ۳۴) اشتراک‌گذاری صفحه برنامه را معرفی می‌کند که به کاربران امکان می‌دهد صرف نظر از حالت پنجره‌بندی، به جای کل صفحه دستگاه، یک پنجره برنامه را به اشتراک بگذارند. اشتراک‌گذاری صفحه برنامه، نوار وضعیت، نوار ناوبری، اعلان‌ها و سایر عناصر رابط کاربری سیستم را از صفحه نمایش مشترک مستثنی می‌کند - حتی زمانی که از اشتراک‌گذاری صفحه برنامه برای ضبط یک برنامه در حالت تمام صفحه استفاده می‌شود. فقط محتوای برنامه انتخاب شده به اشتراک گذاشته می‌شود.

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

سه نمایش نمایشی

یک مدیا پروژکتور، محتوای صفحه نمایش دستگاه یا پنجره برنامه را ضبط می‌کند و سپس تصویر ضبط شده را به یک نمایشگر مجازی می‌تاباند که تصویر را روی یک Surface رندر می‌کند.

نمایش دستگاه واقعی روی نمایشگر مجازی منعکس شده است. محتویات نمایشگر مجازی روی «سرفیس» ارائه شده توسط برنامه نوشته شده است.
شکل ۱. صفحه نمایش دستگاه واقعی یا پنجره برنامه که روی صفحه نمایش مجازی نمایش داده می‌شود. صفحه نمایش مجازی که روی Surface ارائه شده توسط برنامه نوشته شده است.

این برنامه از طریق MediaRecorder ، SurfaceTexture یا ImageReader ، که محتوای صفحه نمایش ضبط شده را مصرف می‌کند و شما را قادر می‌سازد تصاویر رندر شده روی Surface را به صورت بلادرنگ مدیریت کنید، Surface در اختیار شما قرار می‌دهد. می‌توانید تصاویر را به صورت ضبط شده ذخیره کنید یا آنها را به تلویزیون یا دستگاه دیگری منتقل کنید.

نمایش واقعی

با دریافت توکنی که به برنامه شما امکان ضبط محتوای صفحه نمایش دستگاه یا پنجره برنامه را می‌دهد، یک جلسه نمایش رسانه را آغاز کنید. این توکن توسط نمونه‌ای از کلاس MediaProjection نمایش داده می‌شود.

هنگام شروع یک اکتیویتی جدید، از متد getMediaProjection() از سرویس سیستمی MediaProjectionManager برای ایجاد یک نمونه MediaProjection استفاده کنید. اکتیویتی را با یک intent از متد createScreenCaptureIntent() برای تعیین عملیات ضبط صفحه نمایش، شروع کنید:

کاتلین

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection
val startMediaProjection = registerForActivityResult( StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { mediaProjection = mediaProjectionManager .getMediaProjection(result.resultCode, result.data!!) } }
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

جاوا

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];
ActivityResultLauncher startMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } );
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

نمایش مجازی

بخش مرکزی یک نمایش رسانه‌ای، نمایش مجازی است که شما با فراخوانی createVirtualDisplay() روی یک نمونه MediaProjection ایجاد می‌کنید:

کاتلین

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

جاوا

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

پارامترهای width و height ، ابعاد نمایشگر مجازی را مشخص می‌کنند. برای به دست آوردن مقادیر عرض و ارتفاع، از APIهای WindowMetrics معرفی شده در اندروید ۱۱ (سطح API ۳۰) استفاده کنید. (برای جزئیات بیشتر، به بخش اندازه تصویر رسانه مراجعه کنید.)

سطح

سطح پخش رسانه را برای تولید خروجی با وضوح مناسب، اندازه گیری کنید. برای پخش روی صفحه نمایش تلویزیون یا مانیتور کامپیوتر، سطح را بزرگ (با وضوح پایین) و برای ضبط صفحه نمایش دستگاه، سطح را کوچک (با وضوح بالا) در نظر بگیرید.

از اندروید ۱۲L (سطح API 32)، هنگام رندر کردن محتوای ضبط‌شده روی سطح، سیستم محتوا را به طور یکنواخت مقیاس‌بندی می‌کند و نسبت ابعاد را حفظ می‌کند، به طوری که هر دو بعد محتوا (عرض و ارتفاع) برابر یا کمتر از ابعاد مربوطه در سطح باشند. سپس محتوای ضبط‌شده در مرکز سطح قرار می‌گیرد.

رویکرد مقیاس‌بندی اندروید ۱۲L با به حداکثر رساندن اندازه تصویر سطح و در عین حال تضمین نسبت ابعاد مناسب، پخش تصویر روی تلویزیون‌ها و سایر نمایشگرهای بزرگ را بهبود می‌بخشد.

مجوز سرویس پیش‌زمینه

اگر برنامه شما اندروید ۱۴ یا بالاتر را هدف قرار می‌دهد، مانیفست برنامه باید شامل یک اعلان مجوز برای نوع سرویس پیش‌زمینه mediaProjection باشد:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

سرویس نمایش رسانه را با فراخوانی startForeground() آغاز کنید.

اگر نوع سرویس پیش‌زمینه را در فراخوانی مشخص نکنید، نوع آن به صورت پیش‌فرض یک عدد صحیح بیتی از انواع سرویس‌های پیش‌زمینه تعریف شده در مانیفست در نظر گرفته می‌شود. اگر مانیفست هیچ نوع سرویسی را مشخص نکند، سیستم MissingForegroundServiceTypeException را صادر می‌کند.

برنامه شما باید قبل از هر جلسه نمایش رسانه، رضایت کاربر را درخواست کند. یک جلسه، یک فراخوانی واحد برای createVirtualDisplay() است. توکن MediaProjection فقط باید یک بار برای برقراری فراخوانی استفاده شود.

در اندروید ۱۴ یا بالاتر، اگر برنامه شما هر یک از موارد زیر را انجام دهد، متد createVirtualDisplay() یک SecurityException ایجاد می‌کند:

  • یک نمونه Intent برگردانده شده از createScreenCaptureIntent() را بیش از یک بار به getMediaProjection() ارسال می‌کند.
  • فراخوانی‌های createVirtualDisplay() بیش از یک بار در همان نمونه MediaProjection

اندازه طرح رسانه ای

یک مدیا پروجکشن می‌تواند کل صفحه نمایش دستگاه یا یک پنجره برنامه را صرف نظر از حالت پنجره‌بندی، ضبط کند.

اندازه اولیه

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

از متد getMaximumWindowMetrics() WindowManager پلتفرم برای برگرداندن یک شیء WindowMetrics برای صفحه نمایش دستگاه استفاده کنید، حتی اگر برنامه میزبان نمایش رسانه در حالت چند پنجره‌ای باشد و تنها بخشی از صفحه نمایش را اشغال کند.

برای سازگاری تا سطح API 14، از متد computeMaximumWindowMetrics() WindowMetricsCalculator کتابخانه‌ی Jetpack WindowManager استفاده کنید.

برای دریافت عرض و ارتفاع نمایشگر دستگاه، متد getBounds() از WindowMetrics را فراخوانی کنید.

تغییرات اندازه

اندازه تصویر رسانه‌ای می‌تواند با چرخش دستگاه یا انتخاب یک پنجره برنامه به عنوان ناحیه ضبط در اشتراک‌گذاری صفحه برنامه توسط کاربر تغییر کند. اگر اندازه محتوای ضبط شده با حداکثر معیارهای پنجره به دست آمده هنگام تنظیم تصویر رسانه‌ای متفاوت باشد، تصویر رسانه‌ای ممکن است به صورت Letterbox نمایش داده شود.

برای اطمینان از اینکه تصویر رسانه‌ای دقیقاً با اندازه محتوای ضبط‌شده برای هر ناحیه ضبط‌شده و در چرخش‌های دستگاه هم‌تراز است، از تابع onCapturedContentResize() برای تغییر اندازه ضبط استفاده کنید. (برای اطلاعات بیشتر، به بخش سفارشی‌سازی که در ادامه آمده است مراجعه کنید).

سفارشی‌سازی

برنامه شما می‌تواند تجربه کاربری نمایش رسانه را با استفاده از APIهای MediaProjection.Callback زیر سفارشی کند:

  • onCapturedContentVisibilityChanged() : برنامه میزبان (برنامه‌ای که پخش رسانه را آغاز کرده است) را قادر می‌سازد تا محتوای اشتراک‌گذاری شده را نمایش یا پنهان کند.

    از این فراخوانی برای سفارشی‌سازی رابط کاربری برنامه خود بر اساس اینکه آیا منطقه ضبط شده برای کاربر قابل مشاهده است یا خیر، استفاده کنید. به عنوان مثال، اگر برنامه شما برای کاربر قابل مشاهده است و محتوای ضبط شده را در رابط کاربری برنامه نمایش می‌دهد، و برنامه ضبط شده نیز برای کاربر قابل مشاهده است (همانطور که از طریق این فراخوانی نشان داده شده است)، کاربر همان محتوا را دو بار می‌بیند. از این فراخوانی برای به‌روزرسانی رابط کاربری برنامه خود استفاده کنید تا محتوای ضبط شده را پنهان کنید و فضای طرح‌بندی را در برنامه خود برای محتوای دیگر آزاد کنید.

  • onCapturedContentResize() : برنامه میزبان را قادر می‌سازد تا اندازه تصویر رسانه‌ای روی نمایشگر مجازی و Surface تصویر رسانه‌ای را بر اساس اندازه ناحیه نمایش ضبط شده تغییر دهد.

    هر زمان که محتوای ضبط‌شده - یک پنجره برنامه یا کل صفحه نمایش دستگاه - تغییر اندازه دهد (به دلیل چرخش دستگاه یا ورود برنامه ضبط‌شده به حالت پنجره‌بندی متفاوت)، فعال می‌شود. از این API برای تغییر اندازه صفحه نمایش مجازی و سطح استفاده کنید تا مطمئن شوید نسبت ابعاد با محتوای ضبط‌شده مطابقت دارد و ضبط به صورت Letterbox انجام نمی‌شود.

بازیابی منابع

برنامه شما باید تابع فراخوانی onStop() MediaProjection onStop را ثبت کند تا از توقف و نامعتبر شدن جلسه نمایش رسانه مطلع شود. هنگامی که جلسه متوقف می‌شود، برنامه شما باید منابعی را که در اختیار دارد، مانند صفحه نمایش مجازی و سطح نمایش، آزاد کند. یک جلسه نمایش رسانه متوقف شده دیگر نمی‌تواند یک صفحه نمایش مجازی جدید ایجاد کند، حتی اگر برنامه شما قبلاً یک صفحه نمایش مجازی برای آن نمایش رسانه ایجاد نکرده باشد.

سیستم زمانی که پخش رسانه‌ای خاتمه می‌یابد، تابع فراخوانی را فراخوانی می‌کند. این خاتمه می‌تواند به دلایل مختلفی رخ دهد، مانند:

  • کاربر با استفاده از رابط کاربری برنامه یا تراشه نوار وضعیت نمایش رسانه سیستم، جلسه را متوقف می‌کند.
  • صفحه نمایش قفل می‌شود
  • جلسه دیگری برای پیش‌بینی رسانه‌ای آغاز می‌شود
  • فرآیند برنامه متوقف می‌شود

اگر برنامه شما callback را ثبت نکند، هرگونه فراخوانی createVirtualDisplay() IllegalStateException را صادر می‌کند.

انصراف

اندروید ۱۴ یا بالاتر به طور پیش‌فرض اشتراک‌گذاری صفحه برنامه را فعال می‌کند. هر جلسه پخش رسانه به کاربران این امکان را می‌دهد که یک پنجره برنامه یا کل صفحه نمایش را به اشتراک بگذارند.

برنامه شما می‌تواند با فراخوانی متد createScreenCaptureIntent(MediaProjectionConfig) به همراه آرگومان MediaProjectionConfig که از فراخوانی createConfigForDefaultDisplay() برگردانده شده است، از اشتراک‌گذاری صفحه نمایش برنامه انصراف دهد.

فراخوانی تابع createScreenCaptureIntent(MediaProjectionConfig) با آرگومان MediaProjectionConfig که از فراخوانی تابع createConfigForUserChoice() برگردانده می‌شود، مشابه رفتار پیش‌فرض، یعنی فراخوانی تابع createScreenCaptureIntent() است.

برنامه‌های قابل تغییر اندازه

همیشه برنامه‌های پخش رسانه‌ای خود را قابل تغییر اندازه کنید ( resizeableActivity="true" ). برنامه‌های قابل تغییر اندازه از تغییرات پیکربندی دستگاه و حالت چند پنجره‌ای پشتیبانی می‌کنند ( به پشتیبانی چند پنجره‌ای مراجعه کنید).

اگر برنامه شما قابلیت تغییر اندازه ندارد، باید محدوده‌های نمایش را از یک زمینه پنجره (window context) پرس‌وجو کند و از getMaximumWindowMetrics() برای بازیابی WindowMetrics مربوط به حداکثر ناحیه نمایش موجود برای برنامه استفاده کند:

کاتلین

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

جاوا

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

تراشه نوار وضعیت و توقف خودکار

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

برای برنامه‌هایی که روی دستگاه‌های دارای Android 15 QPR1 یا بالاتر اجرا می‌شوند، یک تراشه نوار وضعیت که بزرگ و برجسته است، به کاربران هشدار می‌دهد که هر گونه نمایش صفحه نمایش در حال پیشرفت را نشان دهند. کاربران می‌توانند روی تراشه ضربه بزنند تا از اشتراک‌گذاری، پخش یا ضبط صفحه نمایش خود جلوگیری کنند. همچنین، با قفل شدن صفحه نمایش دستگاه، نمایش صفحه به طور خودکار متوقف می شود.

شکل ۲. تراشه نوار وضعیت برای اشتراک‌گذاری، پخش و ضبط صفحه نمایش

با شروع اشتراک‌گذاری، پخش یا ضبط صفحه، در دسترس بودن تراشه نوار وضعیت پخش رسانه را آزمایش کنید. تراشه باید در نوار وضعیت ظاهر شود.

برای اطمینان از اینکه برنامه شما منابع را آزاد می‌کند و رابط کاربری خود را به‌روزرسانی می‌کند، هنگامی که نمایش صفحه نمایش توسط تعامل کاربر با تراشه نوار وضعیت یا با فعال شدن قفل صفحه متوقف می‌شود، موارد زیر را انجام دهید:

  • یک نمونه از MediaProjection.Callback ایجاد کنید.

  • متد فراخوانی onStop() را پیاده‌سازی کنید. این متد زمانی فراخوانی می‌شود که نمایش صفحه متوقف شود. منابعی را که برنامه شما در اختیار دارد آزاد کنید و رابط کاربری برنامه را در صورت نیاز به‌روزرسانی کنید.

برای آزمایش فراخوانی، روی تراشه نوار وضعیت ضربه بزنید یا صفحه دستگاه را قفل کنید تا نمایش صفحه متوقف شود. تأیید کنید که متد onStop() فراخوانی شده است و برنامه شما طبق انتظار پاسخ می‌دهد.

منابع اضافی

برای اطلاعات بیشتر در مورد پخش رسانه، به ضبط ویدیو و پخش صدا مراجعه کنید.