ภาพรวม MediaPlayer

เฟรมเวิร์กมัลติมีเดียของ 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 ให้ทำตามขั้นตอนต่อไปนี้

  1. หากต้องการให้แอปกำหนดค่าเอง ให้กำหนด OnDrmConfigHelper และแนบไปกับ โปรแกรมเล่นวิดีโอ setOnDrmConfigHelper()
  2. โทร prepare()
  3. โทร getDrmInfo() หากแหล่งที่มามี DRM เนื้อหา เมธอดจะแสดงค่าที่ไม่เป็นค่าว่าง MediaPlayer.DrmInfo

หากมี MediaPlayer.DrmInfo อยู่ ให้ทำดังนี้

  1. ตรวจสอบแมปของ UUID ที่มีอยู่และเลือก 1 รายการ
  2. เตรียมการกำหนดค่า DRM สำหรับแหล่งที่มาปัจจุบันโดยเรียกใช้ prepareDrm()
    • หากคุณสร้างและลงทะเบียน OnDrmConfigHelper Callback เรียกว่า ขณะที่ prepareDrm() กำลังทำงาน คุณสามารถกำหนดค่า DRM แบบกำหนดเองได้ ก่อนที่จะเปิดเซสชัน DRM Callback เรียกว่า พร้อมกันในชุดข้อความที่เรียกใช้ prepareDrm() ถึง เข้าถึงคุณสมบัติ DRM, getDrmPropertyString() และ setDrmPropertyString() หลีกเลี่ยงการดำเนินการที่ใช้เวลานาน
    • หากยังไม่ได้จัดสรรอุปกรณ์ prepareDrm() และ เข้าถึงเซิร์ฟเวอร์การจัดสรรเพื่อจัดสรรอุปกรณ์ การดำเนินการนี้อาจใช้เวลา ระยะเวลาจะแตกต่างกันไป ขึ้นอยู่กับการเชื่อมต่อเครือข่าย
  3. ในการรับอาร์เรย์ไบต์คำขอคีย์ที่ทึบแสงเพื่อส่งไปยังเซิร์ฟเวอร์ใบอนุญาต ให้เรียกใช้ getKeyRequest()
  4. ในการแจ้งเครื่องมือ 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...

ดูข้อมูลเพิ่มเติม

หน้าเหล่านี้ครอบคลุมหัวข้อต่างๆ ที่เกี่ยวข้องกับการบันทึก การจัดเก็บ และการเล่นเสียงและวิดีโอ