การฉายภาพสื่อ

android.media.projection API ที่เปิดตัวใน Android 5 (API ระดับ 21) จะช่วยให้คุณบันทึกเนื้อหาได้ ของอุปกรณ์จะแสดงเป็นสตรีมสื่อที่คุณสามารถเล่น บันทึก หรือแคสต์ได้ อุปกรณ์อื่นๆ เช่น ทีวี

Android 14 (API ระดับ 34) เปิดตัวการแชร์หน้าจอแอปซึ่งช่วยให้ผู้ใช้ แชร์หน้าต่างแอปเดียวแทนการแชร์ทั้งหน้าจอของอุปกรณ์ โหมดการจัดกรอบ การแชร์หน้าจอแอปไม่รวมแถบสถานะ แถบนำทาง และองค์ประกอบ UI อื่นๆ ของระบบจากจอแสดงผลที่ใช้ร่วมกัน เมื่อใช้การแชร์หน้าจอแอปเพื่อจับภาพแอปแบบเต็มหน้าจอ ระบบจะแชร์เฉพาะเนื้อหาของแอปที่เลือกเท่านั้น

การแชร์หน้าจอแอปช่วยรับประกันความเป็นส่วนตัวของผู้ใช้ เพิ่มประสิทธิภาพการทำงานของผู้ใช้ และ เพิ่มประสิทธิภาพการทำงานหลายอย่างพร้อมกันโดยให้ผู้ใช้เรียกใช้แอปหลายรายการ แต่มีข้อจำกัด การแชร์เนื้อหาไปยังแอปเดียว

จอแสดงผล 3 แบบ

การฉายภาพสื่อบันทึกเนื้อหาของการแสดงผลของอุปกรณ์หรือหน้าต่างแอป และ จากนั้นฉายภาพที่จับภาพไปยังจอแสดงผลเสมือนที่แสดงรูปภาพบน Surface

วันที่ จอแสดงผลของอุปกรณ์จริงฉายอยู่บนจอแสดงผลเสมือน เนื้อหาของ
              การแสดงผลเสมือนที่เขียนลงใน "Surface" ที่แอปพลิเคชันมีให้
รูปที่ 1 หน้าจออุปกรณ์หรือหน้าต่างแอปจริงฉายมาที่ การแสดงผลเสมือนจริง การแสดงผลเสมือนที่เขียนโดยแอปพลิเคชัน Surface

แอปพลิเคชันจะให้ Surface โดยใช้ MediaRecorder, SurfaceTexture หรือ ImageReader ซึ่งใช้เวลา เนื้อหาของจอแสดงผลที่บันทึกไว้ ซึ่งจะช่วยให้คุณจัดการรูปภาพที่แสดงผลได้ ใน Surface แบบเรียลไทม์ คุณบันทึกรูปภาพเป็นไฟล์บันทึกเสียงหรือแคสต์ได้ ไปยังทีวีหรืออุปกรณ์อื่นๆ

การแสดงผลจริง

เริ่มเซสชันการฉายภาพสื่อโดยรับโทเค็นที่ให้สิทธิ์แอปพลิเคชันของคุณ สามารถจับภาพเนื้อหาในหน้าจอของอุปกรณ์หรือหน้าต่างแอปได้ โทเค็น จะแสดงโดยอินสแตนซ์ของ 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) (โปรดดูรายละเอียดที่ ขนาดการฉายภาพสื่อ)

แพลตฟอร์ม

ปรับขนาดพื้นผิวการฉายภาพสื่อเพื่อสร้างเอาต์พุตใน ความละเอียดสูงสุดของคุณ ทำให้พื้นผิวมีขนาดใหญ่ (ความละเอียดต่ำ) สำหรับการแคสต์หน้าจอไปยังทีวี หรือ จอคอมพิวเตอร์และขนาดเล็ก (ความละเอียดสูง) สำหรับการบันทึกการแสดงผลของอุปกรณ์

สำหรับ Android 12L (API ระดับ 32) เมื่อแสดงผลเนื้อหาที่จับภาพไว้ใน ระบบจะปรับขนาดเนื้อหา ให้เป็นแบบเดียวกัน โดยรักษาสัดส่วนการแสดงผลไว้ เพื่อให้ขนาดของเนื้อหาทั้ง 2 ด้าน (ความกว้างและความสูง) เท่ากันหรือน้อยกว่า ขนาดที่สอดคล้องกันของพื้นผิว จากนั้นเนื้อหาที่บันทึก ให้อยู่ตรงกลางพื้นผิว

แนวทางการปรับขนาดของ Android 12L ปรับปรุงการแคสต์หน้าจอไปยังทีวีและ จอแสดงผลขนาดใหญ่อื่นๆ ด้วยการเพิ่มขนาดของรูปภาพพื้นผิวให้มากที่สุด ในขณะเดียวกันก็ตรวจสอบให้แน่ใจว่า สัดส่วนภาพที่เหมาะสม

สิทธิ์สำหรับบริการที่ทำงานอยู่เบื้องหน้า

หากแอปกำหนดเป้าหมายเป็น Android 14 ขึ้นไป ไฟล์ Manifest ของแอปต้องมี การประกาศสิทธิ์สำหรับ ประเภทบริการที่ทำงานอยู่เบื้องหน้า 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()

หากไม่ระบุประเภทบริการที่ทำงานอยู่เบื้องหน้าในการโทร ประเภทจะเป็นค่าเริ่มต้น เป็นจำนวนเต็มบิตของประเภทบริการที่ทำงานอยู่เบื้องหน้าที่กำหนดไว้ในไฟล์ Manifest ถ้า ไฟล์ Manifest ไม่ได้ระบุประเภทบริการใดๆ แต่ระบบแสดงข้อผิดพลาด MissingForegroundServiceTypeException

แอปของคุณต้องขอความยินยอมจากผู้ใช้ก่อนเซสชันการฉายภาพสื่อแต่ละเซสชัน ต เซสชันคือการเรียก createVirtualDisplay() ครั้งเดียว โทเค็น MediaProjection จะต้องใช้เพียงครั้งเดียวในการโทร

ใน Android 14 ขึ้นไป เมธอด createVirtualDisplay() จะแสดง SecurityException หาก ดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้

  • ส่งต่ออินสแตนซ์ Intent ที่แสดงผลจาก createScreenCaptureIntent() ไปยัง getMediaProjection() มากกว่า 1 ครั้ง
  • โทร createVirtualDisplay() มากกว่า 1 ครั้งในวันMediaProjectionเดียวกัน อินสแตนซ์

ขนาดการฉายภาพสื่อ

การฉายภาพสื่อสามารถจับภาพจอแสดงผลของอุปกรณ์ทั้งเครื่องหรือหน้าต่างแอปได้ โดยไม่คำนึงถึงโหมดกรอบเวลา

ขนาดเริ่มต้น

เมื่อใช้การฉายสื่อแบบเต็มหน้าจอ แอปต้องระบุขนาดของ หน้าจออุปกรณ์ ในการแชร์หน้าจอแอป แอปของคุณจะไม่สามารถระบุ ของภาพที่ถ่ายไว้จนกว่าผู้ใช้จะเลือกพื้นที่ในการจับภาพ ดังนั้น ขนาดเริ่มต้นของการฉายภาพสื่อใดๆ ก็คือขนาดของหน้าจออุปกรณ์

ใช้แพลตฟอร์ม WindowManager getMaximumWindowMetrics() เพื่อแสดง WindowMetrics ของออบเจ็กต์ หน้าจออุปกรณ์แม้ว่าแอปโฮสต์การฉายภาพสื่อจะอยู่ในหลายหน้าต่าง ให้ทำงานเฉพาะในจอแสดงผล

สำหรับความเข้ากันได้จนถึงระดับ API 14 ให้ใช้ WindowMetricsCalculator computeMaximumWindowMetrics() จากไลบรารี Jetpack WindowManager

เรียกใช้เมธอด WindowMetrics getBounds() เพื่อดูความกว้างและความสูงของหน้าจออุปกรณ์

การเปลี่ยนขนาด

ขนาดของการฉายภาพสื่ออาจเปลี่ยนแปลงได้เมื่อหมุนอุปกรณ์ หรือผู้ใช้เลือกหน้าต่างแอปเป็นพื้นที่จับภาพในการแชร์หน้าจอแอป การฉายภาพสื่ออาจมีแถบดำด้านบน-ล่างของภาพหากเนื้อหาที่บันทึกเป็น ขนาดต่างจากเมตริกกรอบเวลาสูงสุดที่ได้รับเมื่อสื่อ ตั้งค่าการฉายภาพแล้ว

เพื่อให้แน่ใจว่าการฉายภาพสื่ออยู่ในแนวเดียวกับขนาดของภาพที่บันทึก เนื้อหาสำหรับพื้นที่ที่บันทึกและในการหมุนรอบอุปกรณ์ ให้ใช้ onCapturedContentResize() Callback เพื่อปรับขนาดการบันทึก (สำหรับข้อมูลเพิ่มเติม โปรดดูส่วนการปรับแต่ง ซึ่งจะต่อท้าย)

การปรับแต่ง

แอปของคุณสามารถปรับแต่งประสบการณ์ของผู้ใช้การฉายภาพสื่อด้วยสิ่งต่อไปนี้ MediaProjection.Callback API:

  • onCapturedContentVisibilityChanged(): เปิดใช้แอปโฮสต์ (แอปที่เริ่มการฉายภาพสื่อ) เพื่อแสดงหรือ ซ่อนเนื้อหาที่แชร์

    ใช้ Callback นี้เพื่อปรับแต่ง UI ของแอปโดยพิจารณาว่า ผู้ใช้มองเห็นภูมิภาค เช่น หากแอปของคุณปรากฏแก่ และแสดงเนื้อหาที่บันทึกไว้ภายใน UI ของแอป และ แอปที่บันทึกไว้ยังแสดงต่อผู้ใช้ (ตามที่ระบุไว้ผ่าน Callback) ผู้ใช้จะเห็นเนื้อหาเดียวกัน 2 ครั้ง ใช้ Callback เพื่ออัปเดต UI ของแอปเพื่อซ่อนเนื้อหาที่บันทึก และเพิ่มพื้นที่ว่างในเลย์เอาต์ใน แอปสำหรับเนื้อหาอื่น

  • onCapturedContentResize(): อนุญาตให้แอปโฮสต์เปลี่ยนขนาดของการฉายภาพสื่อบนอุปกรณ์เสมือน จอแสดงผลและฉายภาพสื่อ Surface ตามขนาดของภาพ ภูมิภาคที่แสดง

    ทริกเกอร์ทุกครั้งที่เนื้อหาที่จับภาพไว้ เช่น หน้าต่างแอปเดียวหรือเต็ม การแสดงผลของอุปกรณ์ - เปลี่ยนขนาด (เนื่องจากการหมุนของอุปกรณ์หรือ เข้าสู่โหมดหน้าต่างอื่น) ใช้ API นี้เพื่อปรับขนาด การแสดงผลเสมือนและพื้นผิวเพื่อให้แน่ใจว่าสัดส่วนภาพตรงกับภาพที่บันทึกไว้ และเนื้อหาที่บันทึกไม่ได้เป็นแถบดำด้านบน-ล่างของภาพ

การกู้คืนทรัพยากร

แอปของคุณควรลงทะเบียน MediaProjection onStop() Callback เพื่อรับการแจ้งเตือนเมื่อเซสชันการฉายภาพสื่อหยุดลงและ ไม่ถูกต้อง เมื่อเซสชันหยุดลงแล้ว แอปของคุณควรปล่อยทรัพยากรที่ เช่น จอแสดงผลและพื้นผิวการฉายภาพเสมือน 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();

แหล่งข้อมูลเพิ่มเติม

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการฉายภาพสื่อ โปรดดู บันทึกการเล่นวิดีโอและเสียง