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

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

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

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

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

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

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

این برنامه Surface با استفاده از MediaRecorder ، SurfaceTexture ، یا ImageReader ارائه می‌کند، که محتویات نمایشگر گرفته شده را مصرف می‌کند و شما را قادر می‌سازد تا تصاویر رندر شده روی 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<Intent> 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 معرفی شده در اندروید 11 (سطح API 30) استفاده کنید. (برای جزئیات، به بخش اندازه طرح ریزی رسانه مراجعه کنید.)

سطح

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

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

رویکرد مقیاس‌بندی Android 12L با به حداکثر رساندن اندازه تصویر سطح و حصول اطمینان از نسبت تصویر مناسب، پخش صفحه نمایش را به تلویزیون و سایر نمایشگرهای بزرگ بهبود می‌بخشد.

مجوز خدمات پیش زمینه

اگر برنامه شما Android 14 یا بالاتر را هدف قرار می‌دهد، مانیفست برنامه باید شامل یک اعلامیه مجوز برای نوع سرویس پیش‌زمینه 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 باید فقط یک بار برای برقراری تماس استفاده شود.

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

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

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

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

اندازه اولیه

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

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

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

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

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

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

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

سفارشی سازی

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

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

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

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

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

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

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

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

اگر برنامه شما پاسخ تماس را ثبت نمی کند، هر فراخوانی برای createVirtualDisplay() IllegalStateException را ارسال می کند.

انصراف دهید

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

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

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

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

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

اگر برنامه شما قابل تغییر اندازه نیست، باید محدوده نمایش را از یک زمینه پنجره پرس و جو کند و از 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();

منابع اضافی

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