הקרנת מדיה

android.media.projection ממשקי API שהושקו ב-Android 5 (רמת API 21) מאפשרים לכם לתעד את התוכן של מסך המכשיר כסטרימינג של מדיה שאפשר להפעיל, להקליט או להפעיל בו Cast במכשירים אחרים, כמו טלוויזיות.

Android 14 (רמת API 34) כולל שיתוף מסך של אפליקציות, שמאפשר למשתמשים משתפים חלון אפליקציה אחד במקום את כל מסך המכשיר, ללא קשר וחלון הראווה. שיתוף מסך של אפליקציה לא כולל את שורת הסטטוס, סרגל הניווט, וגם רכיבים אחרים של ממשק המשתמש מהמסך המשותף – אפילו כשנעשה שימוש בשיתוף מסך של אפליקציה כדי לצלם אפליקציה במסך מלא. רק התוכן של האפליקציה שנבחרה ישותף.

שיתוף מסך של אפליקציות מבטיח את פרטיות המשתמשים, מגביר את הפרודוקטיביות של המשתמשים משפר את ריבוי המשימות בכך שהוא מאפשר למשתמשים להריץ כמה אפליקציות אבל מגביל את שיתוף התוכן לאפליקציה אחת בלבד.

שלושה ייצוגי תצוגה

הקרנת מדיה מתעדת את התוכן של מסך המכשיר או חלון האפליקציה, לאחר מכן המערכת מקרית את התמונה שצולמה במסך וירטואלי שמציגה את התמונה Surface.

מסך של מכשיר אמיתי מוקרן על מסך וירטואלי. תוכן של
              תצוגה וירטואלית שנכתבת אל 'Surface' שסופק על ידי האפליקציה.
איור 1. מסך של מכשיר אמיתי או חלון של אפליקציה מוקרן על מסך וירטואלי. תצוגה וירטואלית שנכתבה על ידי האפליקציה שסופקה Surface

האפליקציה מספקת את Surface באמצעות MediaRecorder, SurfaceTexture, או ImageReader, שצורכת את התוכן של מסך הצילום ואפשר לנהל את התמונות שמעובדות ב-Surface בזמן אמת. אפשר לשמור את התמונות כהקלטה או כהפעלת Cast לטלוויזיה או למכשיר אחר.

מסך אמיתי

מתחילים פעילות של הקרנת מדיה באמצעות קבלת אסימון שמעניק לאפליקציה את יכולת לתעד את התוכן של מסך המכשיר או של חלון האפליקציה. האסימון מיוצג על ידי מופע MediaProjection בכיתה.

משתמשים בשיטה getMediaProjection() MediaProjectionManager שירות מערכת ליצירת מופע MediaProjection כשמתחילים פעילות חדשה. להתחיל את הפעילות עם כוונה createScreenCaptureIntent() לציון מסך פעולת צילום:

Kotlin

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

Java

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:

Kotlin

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

Java

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

הפרמטרים width ו-height מציינים את מידות האובייקט הווירטואלי מסך. כדי לקבל ערכי רוחב וגובה, השתמש בפונקציה השקה של WindowMetrics ממשקי API ב-Android 11 (רמת API: 30). (פרטים נוספים זמינים במאמר הקטע גודל הקרנת המדיה).

סוג המסך

להתאים את גודל משטח הקרנת המדיה כדי להפיק פלט ורזולוציה. הגדלת המשטח (רזולוציה נמוכה) לצורך הפעלת Cast של התוכן לטלוויזיות או צגי מחשב וקטן (רזולוציה גבוהה) להקלטת מסך במכשיר.

החל מ-Android 12L (רמת API 32), כשמעבדים תוכן שתועד פני השטח, המערכת משנה את קנה המידה של התוכן באופן אחיד, תוך שמירה על יחס גובה-רוחב, כך ששני מידות התוכן (רוחב וגובה) שווים ל- או פחות מהמידות המתאימות של פני השטח. לאחר מכן התוכן שצולם במרכז,

גישת ההתאמה לעומס (scaling) של 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 צריך להשתמש בה רק פעם אחת כדי לבצע את השיחה.

ב-Android מגרסה 14 ואילך, השיטה createVirtualDisplay() מקפיצה SecurityException אם האפליקציה מבצעת אחת מהפעולות הבאות:

  • מעבירה מופע Intent שהוחזר מ-createScreenCaptureIntent() ל-getMediaProjection() יותר מפעם אחת
  • שיחות אל createVirtualDisplay() יותר מפעם אחת באותו MediaProjection מופע

גודל הקרנת מדיה

הקרנת מדיה יכולה לצלם את כל מסך המכשיר או חלון של אפליקציה ללא קשר למצב עיבוד החלק הנצפה בלבד.

גודל ראשוני

באמצעות הקרנת מדיה במסך מלא, האפליקציה שלך חייבת לקבוע את גודל מסך המכשיר. בשיתוף מסך של אפליקציה, האפליקציה שלך לא תוכל לזהות גודל התצוגה שתועדה עד שהמשתמש בחר את אזור הצילום. לכן, הגודל הראשוני של כל הקרנת מדיה הוא הגודל של מסך המכשיר.

שימוש בפלטפורמה WindowManager השיטה getMaximumWindowMetrics() כדי להחזיר אובייקט WindowMetrics של מסך המכשיר, גם אם האפליקציה המארחת של הקרנת המדיה פועלת בחלונות מרובים כך שתופס רק חלק מהמסך.

לקבלת תאימות עד לרמת API 14, אפשר להשתמש ברכיב WindowMetricsCalculator computeMaximumWindowMetrics() מספריית Jetpack WindowManager.

צריך להפעיל את השיטה WindowMetrics getBounds() כדי לקבל מידע על הרוחב והגובה של מסך המכשיר.

שינויים בגודל

גודל הקרנת המדיה יכול להשתנות כשמסובבים את המכשיר או שהמשתמש בוחר חלון אפליקציה כאזור הצילום בשיתוף המסך של האפליקציה. הקרנת המדיה עשויה להופיע בפורמט letterbox אם התוכן שצולם שונה ממדדי החלון המקסימלי שמתקבלים כשמדיה הוגדר הקרנה.

כדי לוודא שהקרנת המדיה תואמת במדויק לגודל של התמונה שצולמה עבור כל אזור שנקלט ובסיבובי מכשירים שונים, קריאה חוזרת (callback) של onCapturedContentResize() כדי לשנות את גודל הצילום. (לקבלת מידע נוסף אפשר לקרוא מידע נוסף בקטע התאמה אישית שבהמשך.

התאמה אישית

האפליקציה שלך יכולה להתאים אישית את חוויית המשתמש להקרנת המדיה עם הרכיבים הבאים MediaProjection.Callback ממשקי API:

  • onCapturedContentVisibilityChanged(): המדיניות מאפשרת לאפליקציה המארחת (האפליקציה שהפעילה את הקרנת המדיה) להציג או להסתיר את התוכן המשותף.

    אפשר להשתמש בקריאה חוזרת (callback) כדי להתאים אישית את ממשק המשתמש של האפליקציה בהתאם האזור גלוי למשתמש. לדוגמה, אם האפליקציה גלויה ל משתמש בתוכן המוצג בממשק המשתמש של האפליקציה, האפליקציה שתועדה גלויה גם למשתמש (כפי שמצוין קריאה חוזרת), המשתמש רואה את אותו תוכן פעמיים. שימוש בקריאה חוזרת (callback) כדי לעדכן בממשק המשתמש של האפליקציה כדי להסתיר את התוכן שתועד ולפנות מקום בפריסה עבור תוכן אחר.

  • onCapturedContentResize(): המדיניות הזו מאפשרת לאפליקציה המארחת לשנות את הגודל של הקרנת המדיה במרחב הווירטואלי הקרנת המדיה והתצוגה Surface על סמך גודל הסרטון אזור תצוגה.

    מופעל בכל פעם שהתוכן שתועד – חלון אפליקציה אחד או חלון מלא מסך המכשיר - משנה את הגודל (עקב סיבוב המכשיר או מעבר למצב חלון אחר). השתמשו ב-API הזה כדי לשנות את הגודל של שני האובייקטים: מסך וירטואלי ומשטח וירטואלי כדי להבטיח שיחס הגובה-רוחב תואם התוכן והצילום לא בפורמט letterbox.

שחזור משאבים

האפליקציה שלך צריכה לרשום את MediaProjection onStop() להתקשרות חזרה כדי לקבל הודעה כשסשן הקרנת המדיה נעצר והופך לא תקין. כשהסשן יופסק, האפליקציה אמורה לשחרר את המשאבים. שהוא מחזיק, למשל המסך הווירטואלי ושטח ההקרנה. A הופסק סשן הקרנת מדיה לא יכול עוד ליצור תצוגה וירטואלית חדשה, גם האפליקציה שלך לא יצרה בעבר תצוגה וירטואלית עבור הקרנת המדיה הזו.

הקריאה החוזרת מופעלת כאשר הקרנת המדיה מסתיימת, כי המשתמש מפסיק את הסשן באופן ידני, או מכיוון שהמערכת מפסיקה את הסשן עבור מסיבה כלשהי.

אם האפליקציה לא מתעדת את הקריאה החוזרת (callback), כל קריאה למספר createVirtualDisplay() זורקות IllegalStateException.

ביטול ההסכמה

מערכת Android מגרסה 14 ואילך מאפשרת שיתוף מסך של אפליקציות כברירת מחדל. כל מדיה סשן הקרנה של האפליקציה מאפשר למשתמשים לשתף חלון של אפליקציה, כל המסך.

כדי להפסיק לשתף את המסך של האפליקציה, צריך להפעיל את אמצעי תשלום createScreenCaptureIntent(MediaProjectionConfig) עם ארגומנט MediaProjectionConfig שהוחזר מקריאה אל createConfigForDefaultDisplay().

שיחה אל createScreenCaptureIntent(MediaProjectionConfig) עם ארגומנט MediaProjectionConfig הוחזר מקריאה אל createConfigForUserChoice() זהה כהתנהגות ברירת המחדל, כלומר קריאה createScreenCaptureIntent().

אפליקציות שניתן לשנות את הגודל שלהן

תמיד צריך להגדיל את גודל האפליקציות להקרנת המדיה (resizeableActivity="true"). ניתן לשנות את הגודל אפליקציות תומכות בשינויי תצורת המכשיר ובמצב ריבוי חלונות (ראו תמיכה בריבוי חלונות).

אם לא ניתן לשנות את גודל האפליקציה, עליה לבצע שאילתה על גבולות התצוגה מתוך חלון ולהשתמש ב-getMaximumWindowMetrics() כדי לאחזר את WindowMetrics של אזור התצוגה המקסימלי הזמין לאפליקציה :

Kotlin

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

Java

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

מקורות מידע נוספים

לקבלת מידע נוסף על הקרנת מדיה, ראה צילום וידאו ואודיו.