چارچوب چند رسانه ای اندروید شامل پشتیبانی از پخش انواع رسانه های رایج است، به طوری که می توانید به راحتی صدا، تصویر و تصاویر را در برنامه های خود ادغام کنید. میتوانید صدا یا ویدیو را از فایلهای رسانهای ذخیرهشده در منابع برنامهتان (منابع خام)، از فایلهای مستقل در سیستم فایل، یا از جریان دادهای که از طریق اتصال شبکه میرسد، پخش کنید، همه با استفاده از MediaPlayer
API.
این سند به شما نشان می دهد که چگونه از MediaPlayer
برای نوشتن یک برنامه پخش رسانه ای استفاده کنید که با کاربر و سیستم در تعامل است تا عملکرد خوب و تجربه کاربری دلپذیری را به دست آورید. از طرف دیگر، ممکن است بخواهید از ExoPlayer استفاده کنید، که یک کتابخانه منبع باز قابل تنظیم است که از ویژگی های با کارایی بالا پشتیبانی می کند که در MediaPlayer
موجود نیستند.
توجه: می توانید داده های صوتی را فقط در دستگاه خروجی استاندارد پخش کنید. در حال حاضر، این بلندگوی دستگاه تلفن همراه یا هدست بلوتوث است. در حین تماس نمی توانید فایل های صوتی را در صدای مکالمه پخش کنید.
اصول اولیه
کلاس های زیر برای پخش صدا و ویدئو در فریم ورک اندروید استفاده می شود:
-
MediaPlayer
- این کلاس API اصلی برای پخش صدا و ویدئو است.
-
AudioManager
- این کلاس منابع صوتی و خروجی صدا را روی یک دستگاه مدیریت می کند.
اعلامیه های آشکار
قبل از شروع توسعه برنامه خود با استفاده از MediaPlayer، مطمئن شوید که مانیفست شما دارای اعلان های مناسب برای اجازه استفاده از ویژگی های مرتبط است.
- مجوز اینترنت - اگر از 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 های داخلی، مانند نمونه ای که ممکن است از یک Content Resolver دریافت کنید
- URL های خارجی (جریان سازی)
برای فهرستی از فرمتهای رسانهای که اندروید پشتیبانی میکند، به صفحه فرمتهای رسانه پشتیبانی شده مراجعه کنید.
در اینجا مثالی از نحوه پخش صدایی که به عنوان یک منبع خام محلی در دسترس است (ذخیره شده در دایرکتوری res/raw/
برنامه شما) آورده شده است:
کاتلین
var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1) mediaPlayer.start() // no need to call prepare(); create() does that for you
جاوا
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start(); // no need to call prepare(); create() does that for you
در این مورد، یک منبع "خام" فایلی است که سیستم سعی نمی کند به روش خاصی آن را تجزیه کند. با این حال، محتوای این منبع نباید صوتی خام باشد. باید یک فایل رسانه ای با کدگذاری و فرمت مناسب در یکی از فرمت های پشتیبانی شده باشد.
و در اینجا نحوه بازی از یک URI موجود در سیستم به صورت محلی (که به عنوان مثال از طریق یک Content Resolver به دست آورده اید):
کاتلین
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() }
جاوا
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 به شکل زیر است:
کاتلین
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() }
جاوا
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 را برای پخش جریانی یک فایل رسانه آنلاین ارسال می کنید، فایل باید قابلیت دانلود تدریجی را داشته باشد.
احتیاط: هنگام استفاده از setDataSource()
باید IllegalArgumentException
و IOException
بگیرید یا پاس کنید، زیرا ممکن است فایلی که به آن ارجاع می دهید وجود نداشته باشد.
آماده سازی ناهمزمان
استفاده از MediaPlayer
در اصل می تواند ساده باشد. با این حال، مهم است که در نظر داشته باشید که چند چیز دیگر برای ادغام صحیح آن با یک برنامه معمولی اندروید ضروری است. برای مثال، اجرای فراخوانی prepare()
میتواند زمان زیادی طول بکشد، زیرا ممکن است شامل واکشی و رمزگشایی دادههای رسانه باشد. بنابراین، مانند هر روشی که اجرای آن ممکن است طولانی باشد، هرگز نباید آن را از رشته رابط کاربری برنامه خود فراخوانی کنید . انجام این کار باعث می شود UI تا زمانی که متد برگردد هنگ کند، که تجربه کاربری بسیار بدی است و می تواند باعث خطای ANR (Application Not Responding) شود. حتی اگر انتظار دارید منبع شما به سرعت بارگذاری شود، به یاد داشته باشید که هر چیزی که بیش از یک دهم ثانیه طول می کشد تا در UI پاسخ دهد باعث توقف قابل توجهی می شود و به کاربر این تصور را می دهد که برنامه شما کند است.
برای جلوگیری از آویزان شدن رشته رابط کاربری خود، یک رشته دیگر ایجاد کنید تا MediaPlayer
آماده شود و پس از اتمام به موضوع اصلی اطلاع دهید. با این حال، در حالی که میتوانید منطق threading را خودتان بنویسید، این الگو در هنگام استفاده از MediaPlayer
آنقدر رایج است که فریمورک با استفاده از متد prepareAsync()
راه مناسبی برای انجام این کار ارائه میکند. این روش شروع به آماده سازی رسانه در پس زمینه می کند و بلافاصله برمی گردد. هنگامی که آماده سازی رسانه تمام شد، متد onPrepared()
MediaPlayer.OnPreparedListener
که از طریق setOnPreparedListener()
پیکربندی شده است فراخوانی می شود.
دولت مدیریت
یکی دیگر از جنبه های MediaPlayer
که باید در نظر داشته باشید این است که مبتنی بر حالت است. یعنی MediaPlayer
دارای یک وضعیت داخلی است که شما باید همیشه هنگام نوشتن کد خود از آن آگاه باشید، زیرا برخی از عملیات ها تنها زمانی معتبر هستند که پخش کننده در حالت های خاص باشد. اگر عملیاتی را در حالت اشتباه انجام دهید، سیستم ممکن است یک استثنا ایجاد کند یا رفتارهای نامطلوب دیگری ایجاد کند.
اسناد موجود در کلاس MediaPlayer
یک نمودار وضعیت کامل را نشان می دهد که مشخص می کند کدام روش ها MediaPlayer
از یک حالت به حالت دیگر منتقل می کنند. به عنوان مثال، هنگامی که یک MediaPlayer
جدید ایجاد می کنید، در حالت Idle است. در آن مرحله، باید آن را با فراخوانی setDataSource()
مقداردهی کنید و آن را به حالت Initialized برسانید. پس از آن، باید آن را با استفاده از متد prepare()
یا prepareAsync()
آماده کنید. وقتی MediaPlayer
آمادهسازی شد، وارد حالت Prepared میشود، یعنی میتوانید start()
را فراخوانی کنید تا رسانه را پخش کند. در آن نقطه، همانطور که نمودار نشان میدهد، میتوانید با فراخوانی متدهایی مانند start()
، pause()
و seekTo()
و سایر حالتهای Started ، Paused و PlaybackCompleted حرکت کنید. با این حال، هنگامی که stop()
را فرا می خوانید، متوجه شوید که تا زمانی که MediaPlayer
دوباره آماده نکنید، نمی توانید دوباره start()
فراخوانی کنید.
همیشه هنگام نوشتن کدی که با یک شی MediaPlayer
تعامل دارد ، نمودار حالت را در نظر داشته باشید، زیرا فراخوانی متدهای آن از حالت اشتباه یکی از دلایل رایج اشکال است.
انتشار MediaPlayer
یک MediaPlayer
می تواند منابع ارزشمند سیستم را مصرف کند. بنابراین، همیشه باید اقدامات احتیاطی بیشتری انجام دهید تا مطمئن شوید که بیش از حد لازم به یک نمونه MediaPlayer
معطل نمی شوید. وقتی کار با آن تمام شد، همیشه باید release()
را فراخوانی کنید تا مطمئن شوید منابع سیستمی که به آن اختصاص داده شده است به درستی آزاد شده اند. به عنوان مثال، اگر از MediaPlayer
استفاده میکنید و فعالیت شما با onStop()
تماس دریافت میکند، باید MediaPlayer
را آزاد کنید، زیرا نگه داشتن آن در حالی که فعالیت شما با کاربر ارتباط برقرار نمیکند منطقی نیست (مگر اینکه در حال بازی کردن باشید). رسانه در پس زمینه، که در بخش بعدی مورد بحث قرار می گیرد). وقتی فعالیت شما از سر گرفته می شود یا دوباره شروع می شود، البته باید یک MediaPlayer
جدید ایجاد کنید و قبل از از سرگیری پخش دوباره آن را آماده کنید.
در اینجا نحوه انتشار و سپس لغو MediaPlayer
آمده است:
کاتلین
mediaPlayer?.release() mediaPlayer = null
جاوا
mediaPlayer.release(); mediaPlayer = null;
به عنوان مثال، مشکلاتی را در نظر بگیرید که اگر فراموش کردید MediaPlayer
هنگام توقف فعالیت خود آزاد کنید، اما پس از شروع مجدد فعالیت، یکی جدید ایجاد کنید. همانطور که میدانید، وقتی کاربر جهت صفحه نمایش را تغییر میدهد (یا پیکربندی دستگاه را به روش دیگری تغییر میدهد)، سیستم با راهاندازی مجدد فعالیت (به طور پیشفرض) آن را مدیریت میکند، بنابراین ممکن است با چرخش کاربر به سرعت تمام منابع سیستم را مصرف کنید. دستگاه را بین عمودی و افقی به جلو و عقب برگردانید، زیرا در هر تغییر جهت، یک MediaPlayer
جدید ایجاد می کنید که هرگز آن را آزاد نمی کنید. (برای اطلاعات بیشتر در مورد راه اندازی مجدد زمان اجرا، به مدیریت تغییرات زمان اجرا مراجعه کنید.)
ممکن است از خود بپرسید که اگر بخواهید حتی زمانی که کاربر فعالیت شما را ترک میکند، به پخش "رسانه پسزمینه" ادامه دهید، چه اتفاقی میافتد، دقیقاً به همان روشی که برنامه موسیقی داخلی رفتار میکند. در این مورد، چیزی که شما نیاز دارید یک MediaPlayer
است که توسط یک سرویس کنترل می شود، همانطور که در بخش بعدی بحث شد
استفاده از MediaPlayer در یک سرویس
اگر میخواهید رسانهتان حتی زمانی که برنامهتان روی صفحه نیست، در پسزمینه پخش شود - یعنی میخواهید در حالی که کاربر با برنامههای دیگر در حال تعامل است به پخش ادامه دهد - باید یک Service را راهاندازی کنید و نمونه MediaPlayer
را از آنجا کنترل کنید. شما باید MediaPlayer را در یک سرویس MediaBrowserServiceCompat
جاسازی کنید و آن را در یک فعالیت دیگر با MediaBrowserCompat
تعامل کنید.
شما باید مراقب این تنظیم مشتری/سرور باشید. انتظاراتی در مورد نحوه تعامل بازیکنی که در یک سرویس پس زمینه اجرا می شود با بقیه سیستم وجود دارد. اگر برنامه شما این انتظارات را برآورده نکند، ممکن است کاربر تجربه بدی داشته باشد. برای جزئیات کامل ، ساختن اپلیکیشن صوتی را بخوانید.
این بخش دستورالعمل های ویژه ای را برای مدیریت MediaPlayer هنگامی که در داخل یک سرویس پیاده سازی می شود، توضیح می دهد.
در حال اجرا به صورت ناهمزمان
اول از همه، مانند یک Activity
، تمام کارها در یک Service
به طور پیشفرض در یک رشته انجام میشود—در واقع، اگر یک اکتیویتی و یک سرویس را از یک برنامه اجرا میکنید، آنها از همان رشته استفاده میکنند («رشته اصلی» ") به طور پیش فرض. بنابراین، سرویس ها باید مقاصد ورودی را به سرعت پردازش کنند و هرگز محاسبات طولانی را هنگام پاسخ دادن به آنها انجام ندهند. اگر کار سنگین یا مسدود کردن تماسها انتظار میرود، باید آن وظایف را بهصورت ناهمزمان انجام دهید: یا از رشته دیگری که خودتان پیادهسازی میکنید، یا با استفاده از امکانات فراوان چارچوب برای پردازش ناهمزمان.
به عنوان مثال، هنگام استفاده از MediaPlayer
از رشته اصلی خود، باید بهجای آمادهسازی prepareAsync()
prepare()
را فراخوانی کنید و یک MediaPlayer.OnPreparedListener
را پیادهسازی کنید تا زمانی که آمادهسازی کامل شد مطلع شوید و بتوانید بازی را شروع کنید. به عنوان مثال:
کاتلین
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() } }
جاوا
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
خود انجام دهید:
کاتلین
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! } }
جاوا
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
به حالت Error منتقل می شود (به مستندات کلاس MediaPlayer
برای نمودار وضعیت کامل مراجعه کنید) و قبل از اینکه بتوانید دوباره از آن استفاده کنید باید آن را بازنشانی کنید.
استفاده از ویک لاک
هنگام طراحی برنامههایی که رسانهها را در پسزمینه پخش میکنند، ممکن است وقتی سرویس شما در حال اجرا است، دستگاه به حالت خواب برود. از آنجایی که سیستم اندروید سعی میکند در زمانی که دستگاه در حالت خواب است، باتری را ذخیره کند، سیستم سعی میکند هر یک از ویژگیهای تلفن را که ضروری نیستند، از جمله CPU و سختافزار WiFi را خاموش کند. با این حال، اگر سرویس شما در حال پخش یا پخش موسیقی است، میخواهید از تداخل سیستم در پخش شما جلوگیری کنید.
برای اطمینان از اینکه سرویس شما تحت آن شرایط به کار خود ادامه می دهد، باید از "wake locks" استفاده کنید. Wake lock راهی برای سیگنال دادن به سیستم است که برنامه شما از برخی ویژگیها استفاده میکند که حتی اگر تلفن بیحرکت است در دسترس بماند.
توجه: همیشه باید از wake lock ها کم استفاده کنید و آنها را فقط تا زمانی که واقعاً لازم است نگه دارید، زیرا عمر باتری دستگاه را به میزان قابل توجهی کاهش می دهند.
برای اطمینان از ادامه کار CPU در حین پخش MediaPlayer
، روش setWakeMode()
را هنگام راه اندازی MediaPlayer
فراخوانی کنید. پس از انجام این کار، MediaPlayer
قفل مشخص شده را در حین بازی نگه می دارد و در صورت توقف یا توقف، قفل را آزاد می کند:
کاتلین
mediaPlayer = MediaPlayer().apply { // ... other initialization here ... setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK) }
جاوا
mediaPlayer = new MediaPlayer(); // ... other initialization here ... mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
با این حال، wake lock به دست آمده در این مثال تنها بیدار ماندن CPU را تضمین می کند. اگر در حال پخش رسانه از طریق شبکه هستید و از Wi-Fi استفاده می کنید، احتمالاً می خواهید WifiLock
نیز نگه دارید، که باید آن را به صورت دستی خریداری و آزاد کنید. بنابراین، هنگامی که شروع به آماده سازی MediaPlayer
با URL راه دور می کنید، باید قفل Wi-Fi را ایجاد و خریداری کنید. به عنوان مثال:
کاتلین
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiLock: WifiManager.WifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock") wifiLock.acquire()
جاوا
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); wifiLock.acquire();
هنگامی که رسانه خود را مکث یا متوقف می کنید، یا زمانی که دیگر به شبکه نیاز ندارید، باید قفل را آزاد کنید:
کاتلین
wifiLock.release()
جاوا
wifiLock.release();
انجام پاکسازی
همانطور که قبلاً ذکر شد، یک شی MediaPlayer
می تواند مقدار قابل توجهی از منابع سیستم را مصرف کند، بنابراین شما باید آن را فقط تا زمانی که نیاز دارید نگه دارید و پس از اتمام کار با آن release()
را فراخوانی کنید. مهم است که این روش پاکسازی را صریحاً به جای تکیه بر جمعآوری زبالههای سیستم فراخوانی کنید، زیرا ممکن است مدتی طول بکشد تا جمعآورنده زباله MediaPlayer
را بازیابی کند، زیرا فقط به نیازهای حافظه حساس است و به کمبود منابع مرتبط با رسانه دیگر حساس نیست. بنابراین، زمانی که از یک سرویس استفاده می کنید، همیشه باید متد onDestroy()
را لغو کنید تا مطمئن شوید که MediaPlayer
آزاد می کنید:
کاتلین
class MyService : Service() { private var mediaPlayer: MediaPlayer? = null // ... override fun onDestroy() { super.onDestroy() mediaPlayer?.release() } }
جاوا
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 و اشیاء رمزنگاری زیرین را در معرض دید قرار نمی دهند.
اگرچه MediaPlayer DRM API عملکرد کامل MediaDrm
را ارائه نمی دهد، اما از رایج ترین موارد استفاده پشتیبانی می کند. پیاده سازی فعلی می تواند انواع محتوای زیر را مدیریت کند:
- فایلهای رسانه محلی محافظت شده با Widevine
- فایلهای رسانهای از راه دور/جریانگذاری محافظتشده با Widevine
قطعه کد زیر نحوه استفاده از روش های جدید DRM MediaPlayer را در یک پیاده سازی همزمان ساده نشان می دهد.
برای مدیریت رسانه های کنترل شده با DRM، باید روش های جدید را در کنار جریان معمول تماس های MediaPlayer قرار دهید، همانطور که در زیر نشان داده شده است:
کاتلین
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() }
جاوا
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 های موجود را بررسی کنید و یکی را انتخاب کنید.
- با فراخوانی
prepareDrm()
پیکربندی DRM را برای منبع فعلی آماده کنید. - اگر یک
OnDrmConfigHelper
را ایجاد و ثبت کرده باشید، در حالی کهprepareDrm()
در حال اجرا است فراخوانی می شود. این به شما امکان می دهد تا قبل از باز کردن جلسه DRM، تنظیمات سفارشی خصوصیات DRM را انجام دهید. فراخوانی به طور همزمان در رشته ای کهprepareDrm()
نامیده می شود فراخوانی می شود. برای دسترسی به خصوصیات DRM،getDrmPropertyString()
وsetDrmPropertyString()
را فراخوانی کنید. از انجام عملیات طولانی مدت خودداری کنید. - اگر دستگاه هنوز تدارک دیده نشده است،
prepareDrm()
نیز به سرور تأمین کننده دسترسی پیدا می کند تا دستگاه را تأمین کند. بسته به اتصال شبکه، ممکن است زمان متغیری طول بکشد. - برای دریافت آرایه بایت درخواست کلید غیرشفاف برای ارسال به سرور مجوز،
getKeyRequest()
را فراخوانی کنید. - برای اطلاع دادن به موتور DRM در مورد پاسخ کلید دریافتی از سرور مجوز، با
provideKeyResponse()
تماس بگیرید. نتیجه به نوع درخواست کلید بستگی دارد:- اگر پاسخ برای درخواست کلید آفلاین باشد، نتیجه یک شناسه مجموعه کلید است. می توانید از این شناسه مجموعه کلید با
restoreKeys()
برای بازگرداندن کلیدها به یک جلسه جدید استفاده کنید. - اگر پاسخ برای درخواست پخش یا انتشار باشد، نتیجه صفر است.
- اگر پاسخ برای درخواست کلید آفلاین باشد، نتیجه یک شناسه مجموعه کلید است. می توانید از این شناسه مجموعه کلید با
اجرای prepareDrm()
به صورت ناهمزمان
به طور پیش فرض، prepareDrm()
به صورت همزمان اجرا می شود و تا زمانی که آماده سازی تمام شود مسدود می شود. با این حال، اولین آماده سازی DRM در یک دستگاه جدید نیز ممکن است نیاز به تهیه داشته باشد، که به صورت داخلی توسط prepareDrm()
مدیریت می شود، و ممکن است به دلیل عملیات شبکه درگیر مدتی طول بکشد تا پایان یابد. شما می توانید با تعریف و تنظیم MediaPlayer.OnDrmPreparedListener
از مسدود کردن در prepareDrm()
جلوگیری کنید.
هنگامی که یک OnDrmPreparedListener
را تنظیم می کنید، prepareDrm()
تهیه و آماده سازی (در صورت نیاز) را در پس زمینه انجام می دهد. هنگامی که تهیه و آماده سازی به پایان رسید، شنونده فراخوانی می شود. شما نباید هیچ گونه فرضی در مورد دنباله فراخوان یا رشته ای که شنونده در آن اجرا می شود داشته باشید (مگر اینکه شنونده با یک رشته کنترل کننده ثبت شده باشد). شنونده را می توان قبل یا بعد از prepareDrm()
فراخوانی کرد.
راه اندازی DRM به صورت ناهمزمان
می توانید با ایجاد و ثبت MediaPlayer.OnDrmInfoListener
برای آماده سازی DRM و MediaPlayer.OnDrmPreparedListener
برای شروع پخش کننده، DRM را به صورت ناهمزمان مقداردهی کنید. همانطور که در زیر نشان داده شده است، آنها با prepareAsync()
کار می کنند:
کاتلین
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() }
جاوا
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
همچنین میتواند Common Encryption Scheme (CENC) و رسانه رمزگذاریشده در سطح نمونه HLS (METHOD=SAMPLE-AES) را برای انواع جریان ابتدایی H.264 و AAC رمزگشایی کند. رسانه رمزگذاری شده تمام بخش (METHOD=AES-128) قبلاً پشتیبانی می شد.
بازیابی رسانه از ContentResolver
یکی دیگر از ویژگی هایی که ممکن است در برنامه پخش کننده رسانه مفید باشد، امکان بازیابی موسیقی است که کاربر روی دستگاه دارد. می توانید این کار را با پرس و جو از ContentResolver
برای رسانه های خارجی انجام دهید:
کاتلین
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()
جاوا
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
، می توانید این کار را انجام دهید:
کاتلین
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...
جاوا
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...
بیشتر بدانید
این صفحات موضوعات مربوط به ضبط، ذخیره و پخش صدا و تصویر را پوشش می دهند.