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();
ชิปแถบสถานะและการหยุดอัตโนมัติ
การลักลอบใช้การฉายหน้าจอจะเปิดเผยข้อมูลส่วนตัวของผู้ใช้ เช่น ข้อมูลทางการเงิน เนื่องจากผู้ใช้ไม่ทราบว่ามีการแชร์หน้าจออุปกรณ์
สำหรับแอปที่ทำงานในอุปกรณ์ที่ใช้ Android 15 QPR1 ขึ้นไป ชิปในแถบสถานะที่ใหญ่และเห็นได้ชัดจะแจ้งให้ผู้ใช้ทราบถึงการฉายหน้าจอที่กำลังดำเนินอยู่ ผู้ใช้สามารถแตะชิปเพื่อหยุดไม่ให้แชร์ แคสต์ หรือบันทึกหน้าจอ นอกจากนี้ การฉายหน้าจอจะหยุดโดยอัตโนมัติเมื่อหน้าจออุปกรณ์ล็อกอยู่
ทดสอบความพร้อมใช้งานของชิปแถบสถานะการฉายภาพสื่อโดยเริ่มการแชร์หน้าจอ การแคสต์ หรือการบันทึก ชิปควรปรากฏในแถบสถานะ
หากต้องการให้แอปปล่อยทรัพยากรและอัปเดต UI เมื่อผู้ใช้หยุดการฉายภาพหน้าจอโดยการโต้ตอบของผู้ใช้กับชิปแถบสถานะหรือโดยการเปิดใช้งานหน้าจอล็อก ให้ทำดังนี้
สร้างอินสแตนซ์ของ
MediaProjection.Callback.ใช้เมธอด
onStop()ของการเรียกกลับ ระบบจะเรียกเมธอดนี้เมื่อการฉายภาพหน้าจอหยุดลง ปล่อยทรัพยากรที่แอปถืออยู่และอัปเดต UI ของแอปตามที่จำเป็น
หากต้องการทดสอบการเรียกกลับ ให้แตะชิปแถบสถานะหรือล็อกหน้าจออุปกรณ์เพื่อหยุดการฉายภาพหน้าจอ ตรวจสอบว่าระบบเรียกเมธอด onStop() และแอปตอบสนองตามที่ต้องการ
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการฉายภาพสื่อได้ที่ บันทึกวิดีโอและเสียง การเล่น