API android.media.projection ที่เปิดตัวใน Android 5 (ระดับ API 21)
ช่วยให้คุณบันทึกเนื้อหาของจอแสดงผลอุปกรณ์เป็นสตรีมสื่อที่
สามารถเล่น บันทึก หรือแคสต์ไปยังอุปกรณ์อื่นๆ เช่น ทีวีได้
Android 14 (ระดับ API 34) เปิดตัวการแชร์หน้าจอเฉพาะแอป ซึ่งช่วยให้ผู้ใช้แชร์หน้าต่างแอปเดียวแทนหน้าจออุปกรณ์ทั้งหมดได้ ไม่ว่าจะเป็นโหมดการแสดงหน้าต่างแบบใดก็ตาม การแชร์หน้าจอเฉพาะแอปจะไม่รวมแถบสถานะ แถบนำทาง การแจ้งเตือน และองค์ประกอบ UI ของระบบอื่นๆ จากการแสดงผลที่แชร์ แม้ว่าจะใช้การแชร์หน้าจอเฉพาะแอปเพื่อบันทึกแอปแบบเต็มหน้าจอก็ตาม ระบบจะแชร์เฉพาะเนื้อหาของแอปที่เลือก
การแชร์หน้าจอเฉพาะแอปช่วยให้มั่นใจได้ถึงความเป็นส่วนตัวของผู้ใช้ เพิ่มประสิทธิภาพการทำงานของผู้ใช้ และปรับปรุงการทำงานหลายอย่างพร้อมกันด้วยการอนุญาตให้ผู้ใช้เรียกใช้แอปหลายแอป แต่จำกัดการแชร์เนื้อหาไว้ที่แอปเดียว
การแสดงผล 3 แบบ
การฉายภาพสื่อจะบันทึกเนื้อหาของจอแสดงผลอุปกรณ์หรือหน้าต่างแอป แล้วฉายภาพที่บันทึกไว้ไปยังจอแสดงผลเสมือนที่แสดงภาพบน Surface
Surface ที่แอปพลิเคชันจัดเตรียมไว้
แอปพลิเคชันจะจัดเตรียม Surface ผ่าน MediaRecorder,
SurfaceTexture หรือ ImageReader ซึ่งใช้เนื้อหาของ
จอแสดงผลที่บันทึกไว้และช่วยให้คุณจัดการรูปภาพที่แสดงบน Surface ได้แบบ
เรียลไทม์ คุณสามารถบันทึกรูปภาพเป็นวิดีโอหรือแคสต์ไปยังทีวีหรืออุปกรณ์อื่นๆ ได้
จอแสดงผลจริง
เริ่มเซสชันการฉายภาพสื่อโดยรับโทเค็นที่ให้สิทธิ์แอปในการบันทึกเนื้อหาของจอแสดงผลอุปกรณ์หรือหน้าต่างแอป โทเค็น
จะแสดงด้วยอินสแตนซ์ของคลาส MediaProjection
ใช้เมธอด getMediaProjection() ของบริการของระบบ MediaProjectionManager เพื่อสร้างอินสแตนซ์ MediaProjection เมื่อคุณเริ่มกิจกรรมใหม่ เริ่มกิจกรรมด้วย Intent จากเมธอด
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];
ActivityResultLauncherstartMediaProjection = 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) (ดูรายละเอียดได้ในส่วนขนาดการฉายภาพสื่อ)
Surface
ปรับขนาด Surface การฉายภาพสื่อเพื่อให้ได้เอาต์พุตที่มีความละเอียดเหมาะสม ทำให้ Surface มีขนาดใหญ่ (ความละเอียดต่ำ) สำหรับการแคสต์หน้าจอไปยังทีวีหรือจอคอมพิวเตอร์ และมีขนาดเล็ก (ความละเอียดสูง) สำหรับการบันทึกจอแสดงผลอุปกรณ์
ตั้งแต่ Android 12L (ระดับ API 32) เป็นต้นไป เมื่อแสดงเนื้อหาที่บันทึกไว้บน Surface ระบบจะปรับขนาดเนื้อหาอย่างสม่ำเสมอโดยรักษาอัตราส่วนกว้างยาวไว้ เพื่อให้ขนาดทั้ง 2 ด้านของเนื้อหา (ความกว้างและความสูง) มีขนาดเท่ากับหรือเล็กกว่าขนาดที่เกี่ยวข้องของ Surface จากนั้นระบบจะจัดเนื้อหาที่บันทึกไว้ให้อยู่ตรงกลาง Surface
แนวทางการปรับขนาดของ Android 12L ช่วยปรับปรุงการแคสต์หน้าจอไปยังโทรทัศน์และจอแสดงผลขนาดใหญ่อื่นๆ โดยการเพิ่มขนาดของรูปภาพ Surface ให้ได้มากที่สุดในขณะที่ยังคงอัตราส่วนกว้างยาวที่เหมาะสมไว้
สิทธิ์สำหรับบริการที่ทำงานอยู่เบื้องหน้า
หากแอปกำหนดเป้าหมายเป็น 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() จากไลบรารี WindowManager
ของ Jetpack
เรียกเมธอด WindowMetrics getBounds() เพื่อรับความกว้างและความสูงของ
จอแสดงผลอุปกรณ์
การเปลี่ยนแปลงขนาด
ขนาดของการฉายภาพสื่ออาจเปลี่ยนแปลงได้เมื่อหมุนอุปกรณ์หรือผู้ใช้เลือกหน้าต่างแอปเป็นภูมิภาคที่จะบันทึกในการแชร์หน้าจอเฉพาะแอป การฉายภาพสื่ออาจมีแถบสีดำด้านบนและด้านล่างหากเนื้อหาที่บันทึกไว้มีขนาดต่างจากเมตริกหน้าต่างสูงสุดที่ได้รับเมื่อตั้งค่าการฉายภาพสื่อ
หากต้องการให้การฉายภาพสื่อสอดคล้องกับขนาดของเนื้อหาที่บันทึกไว้อย่างแม่นยำสำหรับภูมิภาคที่บันทึกไว้ทั้งหมดและเมื่อหมุนอุปกรณ์ ให้ใช้การเรียกกลับ onCapturedContentResize() เพื่อปรับขนาดการบันทึก (ดูข้อมูลเพิ่มเติมได้ในส่วนการปรับแต่ง
ด้านล่าง)
การปรับแต่ง
แอปของคุณสามารถปรับแต่งประสบการณ์ของผู้ใช้ในการฉายภาพสื่อได้ด้วย API ต่อไปนี้
MediaProjection.Callback
onCapturedContentVisibilityChanged(): ช่วยให้แอปโฮสต์ (แอปที่เริ่มการฉายภาพสื่อ) แสดงหรือซ่อนเนื้อหาที่แชร์ใช้การเรียกกลับนี้เพื่อปรับแต่ง UI ของแอปตามภูมิภาคที่บันทึกไว้ที่ผู้ใช้มองเห็นหรือไม่ ตัวอย่างเช่น หากแอปของคุณแสดงต่อผู้ใช้และแสดงเนื้อหาที่บันทึกไว้ภายใน UI ของแอป และแอปที่บันทึกไว้ก็แสดงต่อผู้ใช้ด้วย (ตามที่ระบุผ่านการเรียกกลับนี้) ผู้ใช้จะเห็นเนื้อหาเดียวกัน 2 ครั้ง ใช้การเรียกกลับเพื่ออัปเดต UI ของแอปให้ซ่อนเนื้อหาที่บันทึกไว้และเพิ่มพื้นที่เลย์เอาต์ในแอปสำหรับเนื้อหาอื่นๆ
onCapturedContentResize(): ช่วยให้แอปโฮสต์เปลี่ยนขนาดของการฉายภาพสื่อบนจอแสดงผลเสมือนและSurfaceการฉายภาพสื่อตามขนาดของภูมิภาคจอแสดงผลที่บันทึกไว้ทริกเกอร์ทุกครั้งที่เนื้อหาที่บันทึกไว้ (หน้าต่างแอปเดียวหรือจอแสดงผลอุปกรณ์ทั้งหมด) เปลี่ยนขนาด (เนื่องจากการหมุนอุปกรณ์หรือแอปที่บันทึกไว้เข้าสู่โหมดการแสดงหน้าต่างอื่น) ใช้ API นี้เพื่อปรับขนาดทั้งจอแสดงผลเสมือนและ Surface เพื่อให้แน่ใจว่าอัตราส่วนกว้างยาวตรงกับเนื้อหาที่บันทึกไว้และไม่มีแถบสีดำด้านบนและด้านล่าง
การกู้คืนทรัพยากร
แอปของคุณควรลงทะเบียนการเรียกกลับ MediaProjection onStop() เพื่อรับข้อมูลเมื่อเซสชันการฉายภาพสื่อหยุดลงและไม่ถูกต้อง เมื่อเซสชันหยุดลง แอปของคุณควรปล่อยทรัพยากรที่ถืออยู่ เช่น จอแสดงผลเสมือนและ Surface การฉายภาพ เซสชันการฉายภาพสื่อที่หยุดลงจะไม่สามารถสร้างจอแสดงผลเสมือนใหม่ได้อีกต่อไป แม้ว่าแอปของคุณจะไม่ได้สร้างจอแสดงผลเสมือนสำหรับการฉายภาพสื่อนั้นไว้ก่อนหน้านี้ก็ตาม
ระบบจะเรียกใช้การเรียกกลับเมื่อการฉายภาพสื่อสิ้นสุดลง การสิ้นสุดนี้อาจเกิดขึ้นได้จากหลายสาเหตุ เช่น
- ผู้ใช้หยุดเซสชันโดยใช้ UI ของแอปหรือชิปแถบสถานะการฉายภาพสื่อของระบบ
- กำลังล็อกหน้าจอ
- เซสชันการฉายภาพสื่ออื่นเริ่มต้นขึ้น
- ระบบหยุดกระบวนการของแอป
หากแอปของคุณไม่ได้ลงทะเบียนการเรียกกลับ การเรียก 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();
ชิปแถบสถานะและการหยุดอัตโนมัติ
Screen projection exploits expose private user data such as financial information because users don't realize their device screen is being shared.
For apps running on devices with Android 15 QPR1 or higher, a status bar chip that is large and prominent alerts users to any in‑progress screen projection. Users can tap the chip to stop their screen from being shared, cast, or recorded. Also, screen projection automatically stops when the device screen is locked.
ทดสอบความพร้อมใช้งานของชิปแถบสถานะการฉายภาพสื่อโดยเริ่มการแชร์หน้าจอ การแคสต์ หรือการบันทึก ชิปควรปรากฏในแถบสถานะ
หากต้องการให้แอปปล่อยทรัพยากรและอัปเดต UI เมื่อผู้ใช้หยุดการฉายภาพหน้าจอโดยการโต้ตอบของผู้ใช้กับชิปแถบสถานะหรือโดยการเปิดใช้งานหน้าจอล็อก ให้ทำดังนี้
สร้างอินสแตนซ์ของ
MediaProjection.Callback.ใช้เมธอด
onStop()ของการเรียกกลับ ระบบจะเรียกเมธอดนี้เมื่อการฉายภาพหน้าจอหยุดลง ปล่อยทรัพยากรที่แอปถืออยู่และอัปเดต UI ของแอปตามที่จำเป็น
หากต้องการทดสอบการเรียกกลับ ให้แตะชิปแถบสถานะหรือล็อกหน้าจออุปกรณ์เพื่อหยุดการฉายภาพหน้าจอ ตรวจสอบว่าระบบเรียกเมธอด onStop() และแอปตอบสนองตามที่ต้องการ
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการฉายภาพสื่อได้ที่ บันทึกวิดีโอและเสียง การเล่น