เฟรมเวิร์กมัลติมีเดียของ Android รองรับการเล่นสื่อประเภทต่างๆ ที่พบได้ทั่วไป ดังนั้น
เพื่อให้คุณผสานรวมเสียง วิดีโอ และรูปภาพลงในแอปพลิเคชันได้อย่างง่ายดาย คุณสามารถเล่นเสียงหรือ
วิดีโอจากไฟล์สื่อที่เก็บไว้ในทรัพยากรของแอปพลิเคชัน (ทรัพยากรดิบ) จากไฟล์แบบสแตนด์อโลน
ในระบบไฟล์ หรือจากสตรีมข้อมูลที่ส่งมาผ่านการเชื่อมต่อเครือข่าย โดยทั้งหมดจะใช้ MediaPlayer
API
เอกสารนี้แสดงวิธีใช้
MediaPlayer
เพื่อเขียนการเล่นสื่อ
ที่โต้ตอบกับผู้ใช้และระบบ เพื่อให้ได้ประสิทธิภาพที่ดี
ที่น่าพอใจสำหรับผู้ใช้ หรือคุณอาจต้องการ
เพื่อใช้ ExoPlayer ซึ่งเป็นโอเพนซอร์สที่ปรับแต่งได้
ไลบรารีที่รองรับฟีเจอร์ประสิทธิภาพสูงที่ไม่มีใน MediaPlayer
หมายเหตุ: คุณจะเล่นข้อมูลเสียงเป็นเอาต์พุตมาตรฐานได้เท่านั้น อุปกรณ์ นั่นคือลำโพงของอุปกรณ์เคลื่อนที่หรือชุดหูฟังบลูทูธ คุณเล่นเสียงไม่ได้ ในเสียงการสนทนาระหว่างการโทร
ข้อมูลเบื้องต้น
ชั้นเรียนต่อไปนี้ใช้ในการเล่นเสียงและวิดีโอในเฟรมเวิร์กของ Android
MediaPlayer
- คลาสนี้เป็น API หลักสำหรับการเล่นเสียงและวิดีโอ
AudioManager
- ชั้นเรียนนี้จัดการแหล่งที่มาของเสียงและเอาต์พุตเสียงในอุปกรณ์
การประกาศไฟล์ Manifest
ก่อนเริ่มการพัฒนาแอปพลิเคชันโดยใช้ MediaPlayer โปรดตรวจสอบว่าไฟล์ Manifest การประกาศที่เหมาะสมเพื่ออนุญาตให้ใช้ฟีเจอร์ที่เกี่ยวข้อง
- สิทธิ์อินเทอร์เน็ต - หากคุณใช้ MediaPlayer เพื่อสตรีมผ่านเครือข่าย
แอปพลิเคชันของคุณต้องส่งคำขอเข้าถึงเครือข่าย
<uses-permission android:name="android.permission.INTERNET" />
- สิทธิ์ Wake Lock - หากแอปพลิเคชันโปรแกรมเล่นจำเป็นต้องเก็บหน้าจอไว้
ไม่ให้หรี่แสงหรือทำให้ตัวประมวลผลเข้าสู่โหมดสลีป หรือใช้
MediaPlayer.setScreenOnWhilePlaying()
หรือMediaPlayer.setWakeMode()
วิธี คุณต้องขอสิทธิ์นี้<uses-permission android:name="android.permission.WAKE_LOCK" />
การใช้ MediaPlayer
องค์ประกอบที่สำคัญที่สุดอย่างหนึ่งของเฟรมเวิร์กสื่อคือ
MediaPlayer
ออบเจ็กต์ของคลาสนี้สามารถดึงข้อมูล ถอดรหัส และเล่นทั้งเสียงและวิดีโอ
โดยใช้การตั้งค่าน้อยที่สุด รองรับแหล่งที่มาสื่อที่หลากหลาย เช่น
- แหล่งข้อมูลในท้องถิ่น
- URI ภายใน เช่น คุณอาจได้รับจากรีโซลเวอร์เนื้อหา
- URL ภายนอก (สตรีมมิง)
สำหรับรายการรูปแบบสื่อที่ Android รองรับ โปรดดูหัวข้อสื่อที่รองรับ รูปแบบ
ตัวอย่างมีดังนี้
วิธีเปิดเสียงที่พร้อมใช้งานเป็นแหล่งข้อมูลดิบในเครื่อง (บันทึกไว้ใน
ไดเรกทอรี res/raw/
):
Kotlin
var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1) mediaPlayer.start() // no need to call prepare(); create() does that for you
Java
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start(); // no need to call prepare(); create() does that for you
ในกรณีนี้ "ไฟล์ข้อมูล RAW" คือไฟล์ที่ระบบ ให้พยายามแยกวิเคราะห์ด้วยวิธีเฉพาะ แต่เนื้อหาของแหล่งข้อมูลนี้ไม่ควร เป็นเสียงดิบ ไฟล์ควรเป็นไฟล์สื่อที่เข้ารหัสและจัดรูปแบบอย่างถูกต้องใน รูปแบบที่รองรับ
และนี่คือวิธีเล่นจาก URI ที่มีในระบบภายในเครื่อง (ที่คุณได้มาผ่านตัวแปลค่าเนื้อหา):
Kotlin
val myUri: Uri = .... // initialize Uri here val mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext, myUri) prepare() start() }
Java
Uri myUri = ....; // initialize Uri here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();
การเล่นจาก URL ระยะไกลผ่านสตรีมมิง HTTP จะมีลักษณะดังนี้
Kotlin
val url = "http://........" // your URL here val mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(url) prepare() // might take long! (for buffering, etc) start() }
Java
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();
หมายเหตุ หากคุณส่ง URL เพื่อสตรีมไฟล์สื่อออนไลน์ ไฟล์จะต้องสามารถ การดาวน์โหลดแบบโพรเกรสซีฟ
ข้อควรระวัง: คุณต้องจับหรือข้าม
IllegalArgumentException
และIOException
เมื่อใช้
setDataSource()
เนื่องจาก
ไฟล์ที่คุณกำลังอ้างอิงอาจไม่มีอยู่
การเตรียมความพร้อมที่ไม่พร้อมกัน
การใช้ MediaPlayer
สามารถทำได้ง่ายใน
หลัก อย่างไรก็ตาม คุณควรคำนึงถึงสิ่งต่อไปนี้
ที่จำเป็นในการผสานรวมอย่างถูกต้องกับแอปพลิเคชัน Android ทั่วไป สำหรับ
ตัวอย่างเช่น การเรียกไปยัง prepare()
สามารถ
ใช้เวลานานในการดำเนินการ เนื่องจาก
อาจต้องดึงและถอดรหัสข้อมูลสื่อ เช่นเดียวกับกรณีของ
ซึ่งอาจใช้เวลานานในการดำเนินการ คุณไม่ควรเรียกใช้จาก
ชุดข้อความ UI ของแอปพลิเคชัน เนื่องจากจะทำให้ UI ค้างจนกว่าเมธอดจะกลับมา
ซึ่งเป็นประสบการณ์การใช้งานที่ไม่ดีต่อผู้ใช้และอาจทำให้เกิดข้อผิดพลาด ANR (แอปพลิเคชันไม่ตอบสนอง) แม้ว่า
คุณคาดหวังว่าทรัพยากรของคุณจะโหลดได้เร็ว อย่าลืมว่าทุกสิ่งที่ต้องใช้มากกว่า 10
ตอบสนองใน UI อย่างรวดเร็ว 1 วินาทีจะทำให้เกิดการหยุดชั่วคราว
ทำให้ผู้ใช้รู้สึกว่าแอปพลิเคชันของคุณช้า
หากต้องการหลีกเลี่ยงการแขวนชุดข้อความ UI โปรดสร้างชุดข้อความอื่นไปยัง
เตรียม MediaPlayer
และแจ้งชุดข้อความหลักเมื่อเสร็จสิ้น อย่างไรก็ตาม แม้ว่า
คุณสามารถเขียนตรรกะของชุดข้อความ
รูปแบบนี้พบได้ทั่วไปเมื่อใช้ MediaPlayer
ที่เฟรมเวิร์ก
ให้วิธีที่สะดวกในการทำงานนี้โดยใช้
prepareAsync()
วิธี วิธีนี้
เริ่มเตรียมสื่อไว้
ในเบื้องหลังและกลับมาทันที เมื่อสื่อ
เตรียมพร้อมเรียบร้อยแล้ว onPrepared()
เมธอดของ MediaPlayer.OnPreparedListener
ซึ่งกำหนดค่าผ่าน
มีการเรียกใช้ setOnPreparedListener()
การจัดการสถานะ
อีกแง่มุมหนึ่งของ MediaPlayer
ที่คุณควรคำนึงถึงคือ
ว่าเป็นแบบรัฐ นั่นคือ MediaPlayer
มีสถานะภายใน
ที่คุณต้องระวังเมื่อเขียนโค้ดอยู่เสมอ เพราะการดำเนินการบางอย่าง
จะใช้ได้เมื่อโปรแกรมเล่นอยู่ในสถานะที่เจาะจงเท่านั้น หากคุณดำเนินการในขณะที่
สถานะไม่ถูกต้อง ระบบอาจแสดงข้อผิดพลาดหรือก่อให้เกิดลักษณะการทำงานที่ไม่พึงประสงค์อื่นๆ
เอกสารประกอบใน
MediaPlayer
คลาสจะแสดงแผนภาพสถานะที่สมบูรณ์
ที่ชี้แจงว่าเมธอดใดย้าย MediaPlayer
จากสถานะหนึ่งไปยังอีกสถานะหนึ่ง
เช่น เมื่อคุณสร้าง MediaPlayer
ใหม่ ข้อมูลนั้นจะอยู่ในไม่ได้ใช้งาน
เมื่อถึงตอนนั้น คุณควรเริ่มต้นโดยการโทร
setDataSource()
จัดให้เลย
เป็นสถานะเริ่มต้นแล้ว หลังจากนั้น คุณต้องเตรียมเอกสารโดยใช้
prepare()
หรือ
prepareAsync()
วิธี วันและเวลา
MediaPlayer
เตรียมเสร็จแล้ว และเข้าสู่โหมดเตรียมพร้อมแล้ว
ซึ่งหมายความว่าคุณสามารถเรียกใช้ start()
เพื่อให้เล่นสื่อ ณ จุดนั้น ดังที่แสดงในแผนภาพ
คุณสามารถย้ายไปมาระหว่างสถานะ เริ่มแล้ว หยุดชั่วคราว และ การเล่นเสร็จสมบูรณ์ โดย
การเรียกใช้วิธีการดังกล่าว เช่น
start()
,
pause()
และ
seekTo()
,
อื่นๆ อีกมากมาย เมื่อคุณ
โทรหา stop()
แต่โปรดทราบว่าคุณ
ไม่สามารถโทรหา start()
ได้อีกจนกว่าคุณจะ
เตรียมระบบ MediaPlayer
อีกครั้ง
เก็บแผนภาพสถานะไว้เสมอ
ที่ต้องคำนึงถึงเมื่อเขียนโค้ดที่โต้ตอบกับ
MediaPlayer
เนื่องจากการเรียกเมธอดจากสถานะที่ไม่ถูกต้องเป็น
ที่ทำให้เกิดข้อบกพร่องได้
การเปิดตัว MediaPlayer
MediaPlayer
อาจมีประโยชน์
ทรัพยากรระบบ
ดังนั้น คุณควรระมัดระวังเป็นพิเศษเสมอเพื่อให้แน่ใจว่า
อยู่ในอินสแตนซ์ MediaPlayer
นานเกินความจำเป็น เมื่อคุณ
เสร็จแล้ว คุณควรจะโทรติดต่อ
release()
เพื่อให้แน่ใจว่า
ทรัพยากรระบบที่จัดสรร เพื่อให้ปล่อยวางอย่างเหมาะสม ตัวอย่างเช่น หากคุณคือ
ใช้ MediaPlayer
และกิจกรรมได้รับการโทรไปยัง onStop()
คุณต้องปล่อย MediaPlayer
เพราะว่า
แทบจะไม่รู้สึกถึงการยึดถือไว้ในขณะที่กิจกรรมไม่ได้มีปฏิสัมพันธ์กับ
ผู้ใช้ (เว้นแต่คุณจะเล่นสื่อในพื้นหลัง ซึ่งจะกล่าวถึงในส่วนถัดไป)
เมื่อกิจกรรมของคุณกลับมาทำงานอีกครั้งหรือเริ่มต้นใหม่ แน่นอนว่าคุณจะต้อง
สร้าง MediaPlayer
ใหม่และเตรียมความพร้อมอีกครั้งก่อนที่จะกลับมาเล่นต่อ
คุณควรเผยแพร่และทำให้ MediaPlayer
เป็นโมฆะ ดังนี้
Kotlin
mediaPlayer?.release() mediaPlayer = null
Java
mediaPlayer.release(); mediaPlayer = null;
ลองพิจารณาปัญหาที่อาจเกิดขึ้นหากคุณ
ลืมปล่อย MediaPlayer
เมื่อกิจกรรมหยุด แต่สร้าง
ขึ้นมาใหม่เมื่อกิจกรรมเริ่มต้นอีกครั้ง อย่างที่คุณอาจทราบอยู่แล้วเมื่อผู้ใช้เปลี่ยนแปลง
การวางแนวหน้าจอ (หรือเปลี่ยนการกำหนดค่าอุปกรณ์ด้วยวิธีอื่น)
ระบบจะจัดการเรื่องนี้ด้วยการรีสตาร์ทกิจกรรม (โดยค่าเริ่มต้น) ดังนั้นคุณจึงอาจ
ใช้ทรัพยากรระบบทั้งหมดในฐานะผู้ใช้
จะหมุนอุปกรณ์ไปมาระหว่างแนวตั้งและแนวนอน เนื่องจากแต่ละอุปกรณ์
การเปลี่ยนการวางแนวเป็นการสร้างMediaPlayer
ใหม่ที่คุณไม่เคยทำ
(ดูข้อมูลเพิ่มเติมเกี่ยวกับการรีสตาร์ทรันไทม์ได้ที่หัวข้อการจัดการการเปลี่ยนแปลงรันไทม์)
คุณอาจสงสัยว่าจะเกิดอะไรขึ้นหากต้องการเล่นต่อ
"สื่อพื้นหลัง" แม้ว่าผู้ใช้จะออกจากกิจกรรมของคุณ
ลักษณะการทำงานของแอปพลิเคชัน Music ในตัว ในกรณีนี้ สิ่งที่ต้องมีคือ
MediaPlayer
ควบคุมโดยบริการ
ที่กล่าวถึงในส่วนถัดไป
การใช้ MediaPlayer ในบริการ
หากต้องการให้สื่อของคุณเล่นขณะล็อกหน้าจอหรือขณะใช้แอปอื่น
ไม่อยู่บนหน้าจอ กล่าวคือ คุณต้องการเล่นวิดีโอต่อไปขณะที่ผู้ใช้
การโต้ตอบกับแอปพลิเคชันอื่น คุณจึงต้องเริ่ม
บริการและควบคุม
MediaPlayer
จากนั้น
คุณต้องฝัง
MediaPlayer ในบริการ MediaBrowserServiceCompat
และมี
โมเดลจะโต้ตอบกับ
MediaBrowserCompat
ในกิจกรรมอื่น
คุณควรใช้ความระมัดระวังเกี่ยวกับการตั้งค่าไคลเอ็นต์/เซิร์ฟเวอร์นี้ มีความคาดหวัง เกี่ยวกับวิธีที่ผู้เล่นที่ทำงานในบริการที่ทำงานอยู่เบื้องหลังโต้ตอบกับ ระบบ หากแอปพลิเคชันของคุณไม่ตรงตามความคาดหวังของผู้ใช้ ผู้ใช้อาจ ได้รับประสบการณ์ที่ไม่ดี อ่านแล้ว การสร้างแอปเสียง เพื่อดูรายละเอียดทั้งหมด
ส่วนนี้จะอธิบายวิธีการพิเศษสำหรับการจัดการ MediaPlayer เมื่อมีการติดตั้งภายในบริการ
การเรียกใช้แบบไม่พร้อมกัน
ก่อนอื่นเลย เช่น Activity
ทั้งหมดจะใช้งานได้ใน
Service
เสร็จแล้วในชุดข้อความเดียวโดย
ค่าเริ่มต้น อันที่จริงแล้ว ถ้าคุณเรียกใช้กิจกรรมและบริการจากแอปพลิเคชันเดียวกัน
ใช้ชุดข้อความเดียวกัน ("ชุดข้อความหลัก") โดยค่าเริ่มต้น ดังนั้น บริการจะต้อง
ประมวลผล Intent ขาเข้าอย่างรวดเร็ว
และไม่ควรคํานวณเป็นเวลานานเมื่อตอบสนอง หากมีบริเวณที่มีน้ำหนักมาก
ไม่พร้อมกัน หรือบล็อกการโทร คุณจะต้องทำงานเหล่านี้พร้อมกัน อย่างใดอย่างหนึ่งจาก
ชุดข้อความอื่นที่คุณติดตั้งใช้งานด้วยตนเอง หรือการใช้สิ่งอำนวยความสะดวกจำนวนมากของเฟรมเวิร์ก
เพื่อการประมวลผลแบบไม่พร้อมกัน
ตัวอย่างเช่น เมื่อใช้ MediaPlayer
จากชุดข้อความหลัก
คุณควรโทรหา prepareAsync()
แทน
prepare()
และใช้งาน
MediaPlayer.OnPreparedListener
เพื่อรับการแจ้งเตือนเมื่อเตรียมการเสร็จแล้วและคุณก็เริ่มเล่นเกมได้
เช่น
Kotlin
private const val ACTION_PLAY: String = "com.example.action.PLAY" class MyService: Service(), MediaPlayer.OnPreparedListener { private var mMediaPlayer: MediaPlayer? = null override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { ... val action: String = intent.action when(action) { ACTION_PLAY -> { mMediaPlayer = ... // initialize it here mMediaPlayer?.apply { setOnPreparedListener(this@MyService) prepareAsync() // prepare async to not block main thread } } } ... } /** Called when MediaPlayer is ready */ override fun onPrepared(mediaPlayer: MediaPlayer) { mediaPlayer.start() } }
Java
public class MyService extends Service implements MediaPlayer.OnPreparedListener { private static final String ACTION_PLAY = "com.example.action.PLAY"; MediaPlayer mediaPlayer = null; public int onStartCommand(Intent intent, int flags, int startId) { ... if (intent.getAction().equals(ACTION_PLAY)) { mediaPlayer = ... // initialize it here mediaPlayer.setOnPreparedListener(this); mediaPlayer.prepareAsync(); // prepare async to not block main thread } } /** Called when MediaPlayer is ready */ public void onPrepared(MediaPlayer player) { player.start(); } }
การจัดการข้อผิดพลาดที่ไม่พร้อมกัน
ตามปกติการดำเนินการแบบซิงโครนัส ข้อผิดพลาดมัก
จะได้รับสัญญาณโดยมีข้อยกเว้นหรือรหัสข้อผิดพลาด แต่เมื่อใดก็ตามที่คุณใช้อะซิงโครนัส
คุณควรตรวจสอบว่าแอปพลิเคชันได้รับการแจ้งเตือนแล้ว
อย่างเหมาะสม ในกรณีของ MediaPlayer
คุณสามารถทำได้โดยใช้
MediaPlayer.OnErrorListener
และ
ตั้งค่าในอินสแตนซ์ MediaPlayer
ของคุณ:
Kotlin
class MyService : Service(), MediaPlayer.OnErrorListener { private var mediaPlayer: MediaPlayer? = null fun initMediaPlayer() { // ...initialize the MediaPlayer here... mediaPlayer?.setOnErrorListener(this) } override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! } }
Java
public class MyService extends Service implements MediaPlayer.OnErrorListener { MediaPlayer mediaPlayer; public void initMediaPlayer() { // ...initialize the MediaPlayer here... mediaPlayer.setOnErrorListener(this); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! } }
สิ่งสำคัญที่ต้องจำไว้คือ เมื่อเกิดข้อผิดพลาด MediaPlayer
เปลี่ยนเป็นสถานะข้อผิดพลาด (ดูเอกสารประกอบสำหรับ
คลาส MediaPlayer
สำหรับแผนภาพสถานะทั้งหมด)
และคุณต้องรีเซ็ตก่อนจึงจะใช้ได้
การใช้ Wake Lock
เมื่อออกแบบแอปพลิเคชันที่เล่นสื่อ อุปกรณ์อาจเข้าสู่โหมดสลีป ขณะที่บริการทำงานอยู่ เนื่องจากระบบ Android พยายามอนุรักษ์ แบตเตอรี่ในขณะที่อุปกรณ์อยู่ในโหมดสลีป ระบบจะพยายามปิดเครื่อง ฟีเจอร์ของโทรศัพท์ที่ ก็ได้ แต่ไม่จำเป็น รวมทั้ง CPU และฮาร์ดแวร์ Wi-Fi แต่หากบริการกำลังเล่นหรือสตรีมเพลงอยู่ คุณต้องป้องกันไม่ให้ เพื่อไม่ให้ระบบรบกวนการเล่นของคุณ
เพื่อให้บริการของคุณยังคงทำงานภายใต้ คุณจึงต้องใช้ "Wake Lock" Wake Lock คือวิธีส่งสัญญาณไปยัง ระบบที่แอปพลิเคชันของคุณกำลังใช้งานคุณลักษณะบางอย่างที่ควร ยังคงใช้งานได้แม้ว่าโทรศัพท์จะไม่มีการใช้งาน
หมายเหตุ: คุณควรใช้ Wake Lock เมื่อจำเป็นเสมอและค้างไว้ นานเท่าที่จำเป็นจริงๆ เท่านั้น เนื่องจากจะลดอายุการใช้งานแบตเตอรี่ของอุปกรณ์ อุปกรณ์
เพื่อให้ CPU ทำงานต่อไปขณะที่ MediaPlayer
ทำงานอยู่
กำลังเล่น ให้เรียกเมธอด setWakeMode()
เมื่อเริ่มต้น MediaPlayer
เมื่อเรียบร้อยแล้ว
MediaPlayer
จะคงล็อกที่ระบุไว้ไว้ขณะเล่นและปล่อยล็อก
เมื่อหยุดชั่วคราวหรือหยุด:
Kotlin
mediaPlayer = MediaPlayer().apply { // ... other initialization here ... setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK) }
Java
mediaPlayer = new MediaPlayer(); // ... other initialization here ... mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
อย่างไรก็ตาม Wake Lock ที่ได้มาในตัวอย่างนี้รับประกันเพียงว่า CPU ยังคงทำงานอยู่ ถ้า
คุณกำลังสตรีมสื่อผ่าน
เครือข่ายและคุณใช้ Wi-Fi อยู่ คุณอาจต้องระงับ
WifiLock
เป็น
ซึ่งคุณจะต้องจัดหามาและเผยแพร่ด้วยตนเอง ดังนั้นเมื่อคุณเริ่มเตรียม
MediaPlayer
ที่มี URL ระยะไกล คุณควรสร้างและรับการล็อก Wi-Fi
เช่น
Kotlin
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiLock: WifiManager.WifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock") wifiLock.acquire()
Java
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); wifiLock.acquire();
เมื่อหยุดชั่วคราวหรือหยุดสื่อ หรือเมื่อไม่จำเป็นต้องใช้ เครือข่าย คุณควรยกเลิกการล็อก
Kotlin
wifiLock.release()
Java
wifiLock.release();
กำลังทำความสะอาด
ดังที่กล่าวไว้ก่อนหน้านี้ ออบเจ็กต์ MediaPlayer
อาจใช้
จำนวนทรัพยากรของระบบ ดังนั้นคุณควรเก็บไว้นานเท่าที่คุณต้องการและ
release()
เมื่อเสร็จแล้ว เป็นสิ่งสำคัญ
เพื่อเรียกใช้วิธีการล้างนี้อย่างชัดเจนแทนที่จะใช้การรวบรวมขยะของระบบเนื่องจาก
อาจใช้เวลาสักครู่ก่อนที่พนักงานเก็บขยะจะเรียกคืน MediaPlayer
เพราะมีความไวต่อความต้องการหน่วยความจำเท่านั้น ไม่ใช่การขาดแคลนทรัพยากรอื่นๆ ที่เกี่ยวข้องกับสื่อ
ดังนั้น ในกรณีที่คุณใช้บริการ คุณควรลบล้าง
onDestroy()
เมธอดเพื่อตรวจสอบว่าคุณกําลังเผยแพร่
MediaPlayer
:
Kotlin
class MyService : Service() { private var mediaPlayer: MediaPlayer? = null // ... override fun onDestroy() { super.onDestroy() mediaPlayer?.release() } }
Java
public class MyService extends Service { MediaPlayer mediaPlayer; // ... @Override public void onDestroy() { super.onDestroy(); if (mediaPlayer != null) mediaPlayer.release(); } }
คุณควรมองหาโอกาสอื่นๆ ในการเผยแพร่ MediaPlayer
เสมอ
นอกเหนือจากการปล่อยแอปเมื่อปิดเครื่อง เช่น หากคุณคาดว่าจะไม่
เพื่อให้สามารถเล่นสื่อได้เป็นเวลานาน (เช่น หลังจากสูญเสียโฟกัสของเสียง)
คุณควรเผยแพร่ MediaPlayer
ที่มีอยู่และสร้างอีกครั้ง
ในภายหลัง ใน
หรือถ้าคุณคาดว่าจะหยุดเล่นแค่ช่วงสั้นๆ
เก็บ MediaPlayer
ไว้เพื่อหลีกเลี่ยงไม่ให้เสียเวลาในการสร้างและจัดเตรียม
อีกครั้ง
การจัดการสิทธิ์ดิจิทัล (DRM)
ตั้งแต่ Android 8.0 (API ระดับ 26) เป็นต้นไป MediaPlayer
จะมี API ที่
รองรับการเล่นเนื้อหาที่ป้องกันด้วย DRM ซึ่งคล้ายกับ API ระดับต่ำที่ให้บริการโดย
MediaDrm
แต่ดำเนินธุรกิจในระดับที่สูงกว่าและไม่ได้
เปิดเผยออบเจ็กต์ DRM, และ Crypto เบื้องหลัง
แม้ว่า MediaPlayer DRM API จะไม่มีฟังก์ชันที่สมบูรณ์ของ
MediaDrm
ซึ่งจะรองรับกรณีการใช้งานที่พบบ่อยที่สุด
การติดตั้งปัจจุบันสามารถรองรับประเภทเนื้อหาต่อไปนี้
- ไฟล์สื่อในเครื่องที่ป้องกันด้วย Widevine
- ไฟล์สื่อสตรีมมิง/รีโมตที่ป้องกันด้วย Widevine
ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ DRM MediaPlayer ใหม่ ในการใช้งานแบบซิงโครนัสแบบง่าย
หากต้องการจัดการสื่อที่ควบคุมด้วย DRM คุณต้องรวมวิธีการใหม่เข้าไปด้วย การดำเนินการเรียก MediaPlayer ตามปกติ ดังที่แสดงด้านล่าง
Kotlin
mediaPlayer?.apply { setDataSource() setOnDrmConfigHelper() // optional, for custom configuration prepare() drmInfo?.also { prepareDrm() getKeyRequest() provideKeyResponse() } // MediaPlayer is now ready to use start() // ...play/pause/resume... stop() releaseDrm() }
Java
setDataSource(); setOnDrmConfigHelper(); // optional, for custom configuration prepare(); if (getDrmInfo() != null) { prepareDrm(); getKeyRequest(); provideKeyResponse(); } // MediaPlayer is now ready to use start(); // ...play/pause/resume... stop(); releaseDrm();
เริ่มต้นด้วยการเริ่มต้นออบเจ็กต์และการตั้งค่า MediaPlayer
แหล่งที่มาโดยใช้ setDataSource()
ตามปกติ จากนั้น หากต้องการใช้ DRM ให้ทำตามขั้นตอนต่อไปนี้
- หากต้องการให้แอปกำหนดค่าเอง ให้กำหนด
OnDrmConfigHelper
และแนบไปกับ โปรแกรมเล่นวิดีโอsetOnDrmConfigHelper()
- โทร
prepare()
- โทร
getDrmInfo()
หากแหล่งที่มามี DRM เนื้อหา เมธอดจะแสดงค่าที่ไม่เป็นค่าว่างMediaPlayer.DrmInfo
หากมี MediaPlayer.DrmInfo
อยู่ ให้ทำดังนี้
- ตรวจสอบแมปของ UUID ที่มีอยู่และเลือก 1 รายการ
- เตรียมการกำหนดค่า DRM สำหรับแหล่งที่มาปัจจุบันโดยเรียกใช้
prepareDrm()
- หากคุณสร้างและลงทะเบียน
OnDrmConfigHelper
Callback เรียกว่า ขณะที่prepareDrm()
กำลังทำงาน คุณสามารถกำหนดค่า DRM แบบกำหนดเองได้ ก่อนที่จะเปิดเซสชัน DRM Callback เรียกว่า พร้อมกันในชุดข้อความที่เรียกใช้prepareDrm()
ถึง เข้าถึงคุณสมบัติ DRM,getDrmPropertyString()
และsetDrmPropertyString()
หลีกเลี่ยงการดำเนินการที่ใช้เวลานาน - หากยังไม่ได้จัดสรรอุปกรณ์
prepareDrm()
และ เข้าถึงเซิร์ฟเวอร์การจัดสรรเพื่อจัดสรรอุปกรณ์ การดำเนินการนี้อาจใช้เวลา ระยะเวลาจะแตกต่างกันไป ขึ้นอยู่กับการเชื่อมต่อเครือข่าย - ในการรับอาร์เรย์ไบต์คำขอคีย์ที่ทึบแสงเพื่อส่งไปยังเซิร์ฟเวอร์ใบอนุญาต ให้เรียกใช้
getKeyRequest()
- ในการแจ้งเครื่องมือ DRM เกี่ยวกับการตอบสนองของคีย์ที่ได้รับจากเซิร์ฟเวอร์ใบอนุญาต ให้เรียก
provideKeyResponse()
ผลลัพธ์จะขึ้นอยู่กับประเภทของคำขอหลักดังนี้- หากเป็นการตอบสนองสำหรับคำขอคีย์ออฟไลน์ ผลลัพธ์จะเป็นตัวระบุชุดคีย์ คุณสามารถใช้
ตัวระบุชุดคีย์นี้ด้วย
restoreKeys()
เพื่อคืนค่าคีย์เป็น เซสชัน - หากเป็นการตอบสนองสำหรับคำขอสตรีมมิงหรือการเผยแพร่ ผลลัพธ์จะเป็นค่าว่าง
- หากเป็นการตอบสนองสำหรับคำขอคีย์ออฟไลน์ ผลลัพธ์จะเป็นตัวระบุชุดคีย์ คุณสามารถใช้
ตัวระบุชุดคีย์นี้ด้วย
การเรียกใช้ prepareDrm()
แบบไม่พร้อมกัน
โดยค่าเริ่มต้นคือ prepareDrm()
ทำงานแบบพร้อมกัน การบล็อกจนกว่าจะเสร็จสิ้น อย่างไรก็ตาม
การเตรียม DRM ครั้งแรกในอุปกรณ์ใหม่อาจต้องมีการจัดสรรด้วย ซึ่งเป็น
จัดการภายในโดย
prepareDrm()
และ
อาจใช้เวลาสักครู่จึงจะเสร็จเนื่องจากมีการดำเนินการของเครือข่ายที่เกี่ยวข้อง คุณสามารถ
หลีกเลี่ยงการบล็อก
prepareDrm()
โดย
กำหนดและตั้งค่า MediaPlayer.OnDrmPreparedListener
เมื่อคุณตั้งค่าOnDrmPreparedListener
วันที่ prepareDrm()
ดำเนินการจัดสรร (หากจำเป็น) และเตรียมความพร้อมในเบื้องหลัง วันและเวลา
การจัดเตรียมและการจัดเตรียมเสร็จสิ้นแล้ว จะมีการเรียกผู้ฟัง คุณควร
ไม่ได้คาดเดาใดๆ เกี่ยวกับลำดับการเรียกหรือเทรดที่มี
Listener ทำงาน (เว้นแต่ Listener จะลงทะเบียนด้วยเธรดของเครื่องจัดการ)
สามารถเรียกผู้ฟังก่อนหรือหลังได้
prepareDrm()
ที่เกินออกมา
การตั้งค่า DRM แบบไม่พร้อมกัน
คุณสามารถเริ่มต้น DRM แบบไม่พร้อมกันโดยสร้างและลงทะเบียน
MediaPlayer.OnDrmInfoListener
สำหรับการเตรียม DRM และ
MediaPlayer.OnDrmPreparedListener
เพื่อเริ่มโปรแกรมเล่น
โดยทำงานร่วมกับ
prepareAsync()
ตามที่แสดงด้านล่าง
Kotlin
setOnPreparedListener() setOnDrmInfoListener() setDataSource() prepareAsync() // ... // If the data source content is protected you receive a call to the onDrmInfo() callback. override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) { mediaPlayer.apply { prepareDrm() getKeyRequest() provideKeyResponse() } } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. override fun onPrepared(mediaPlayer: MediaPlayer) { mediaPlayer.start() }
Java
setOnPreparedListener(); setOnDrmInfoListener(); setDataSource(); prepareAsync(); // ... // If the data source content is protected you receive a call to the onDrmInfo() callback. onDrmInfo() { prepareDrm(); getKeyRequest(); provideKeyResponse(); } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. onPrepared() { start(); }
การจัดการสื่อที่เข้ารหัส
ตั้งแต่ Android 8.0 (API ระดับ 26) เป็นต้นไป MediaPlayer
จะถอดรหัสได้ด้วย
รูปแบบการเข้ารหัสทั่วไป (CENC) และ
สื่อที่เข้ารหัสระดับตัวอย่าง HLS (METHOD=SAMPLE-AES) สำหรับประเภทสตรีมพื้นฐาน
H.264 และ AAC ก่อนหน้านี้รองรับสื่อที่เข้ารหัสทั้งกลุ่ม (METHOD=AES-128)
การเรียกสื่อจาก Contentสิ่งที่ต้องทำ
อีกฟีเจอร์หนึ่งที่อาจเป็นประโยชน์ในแอปพลิเคชันมีเดียเพลเยอร์คือ
เรียกเพลงที่ผู้ใช้มีในอุปกรณ์ ซึ่งทำได้โดยค้นหา ContentResolver
สำหรับสื่อภายนอก ดังนี้
Kotlin
val resolver: ContentResolver = contentResolver val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI val cursor: Cursor? = resolver.query(uri, null, null, null, null) when { cursor == null -> { // query failed, handle error. } !cursor.moveToFirst() -> { // no media on the device } else -> { val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE) val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID) do { val thisId = cursor.getLong(idColumn) val thisTitle = cursor.getString(titleColumn) // ...process entry... } while (cursor.moveToNext()) } } cursor?.close()
Java
ContentResolver contentResolver = getContentResolver(); Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Cursor cursor = contentResolver.query(uri, null, null, null, null); if (cursor == null) { // query failed, handle error. } else if (!cursor.moveToFirst()) { // no media on the device } else { int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); do { long thisId = cursor.getLong(idColumn); String thisTitle = cursor.getString(titleColumn); // ...process entry... } while (cursor.moveToNext()); }
หากต้องการใช้กับ MediaPlayer
ให้ทำดังนี้
Kotlin
val id: Long = /* retrieve it from somewhere */ val contentUri: Uri = ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id ) mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext, contentUri) } // ...prepare and start...
Java
long id = /* retrieve it from somewhere */; Uri contentUri = ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(), contentUri); // ...prepare and start...
ดูข้อมูลเพิ่มเติม
หน้าเหล่านี้ครอบคลุมหัวข้อต่างๆ ที่เกี่ยวข้องกับการบันทึก การจัดเก็บ และการเล่นเสียงและวิดีโอ