هسته اصلی کتابخانه ExoPlayer رابط Player
است. یک Player
عملکردهای سنتی پخش کننده رسانه سطح بالا مانند توانایی بافر رسانه، پخش، مکث و جستجو را در معرض دید قرار می دهد. اجرای پیشفرض ExoPlayer
به گونهای طراحی شده است که فرضیات کمی در مورد نوع رسانه در حال پخش، نحوه و مکان ذخیره و نحوه ارائه آن ایجاد کند (و از این رو محدودیتهای کمی بر روی آن اعمال میکند). به جای پیاده سازی بارگذاری و رندر رسانه به طور مستقیم، پیاده سازی های ExoPlayer
این کار را به اجزایی واگذار می کنند که هنگام ایجاد یک پخش کننده یا زمانی که منابع رسانه جدید به پخش کننده منتقل می شوند، تزریق می شوند. مؤلفه های مشترک در همه پیاده سازی های ExoPlayer
عبارتند از:
- موارد
MediaSource
که رسانه ای را برای پخش تعریف می کنند، رسانه را بارگیری می کنند و می توان رسانه بارگذاری شده را از آن خواند. یک نمونهMediaSource
از یکMediaItem
توسطMediaSource.Factory
در داخل پخش کننده ایجاد می شود. آنها همچنین می توانند مستقیماً با استفاده از API لیست پخش مبتنی بر منبع رسانه به پخش کننده منتقل شوند. - یک نمونه
MediaSource.Factory
که یکMediaItem
را بهMediaSource
تبدیل می کند.MediaSource.Factory
هنگام ایجاد پخش کننده تزریق می شود. - نمونههای
Renderer
که اجزای جداگانه رسانه را رندر میکنند. اینها هنگام ایجاد پخش کننده تزریق می شوند. -
TrackSelector
که آهنگ های ارائه شده توسطMediaSource
را برای مصرف هرRenderer
در دسترس انتخاب می کند. هنگام ایجاد پخش کنندهTrackSelector
تزریق می شود. -
LoadControl
که کنترل می کندMediaSource
چه زمانی رسانه های بیشتری را بافر می کند و چه مقدار رسانه بافر می شود. هنگامی که پخش کننده ایجاد می شود،LoadControl
تزریق می شود. -
LivePlaybackSpeedControl
که سرعت پخش را در حین پخش زنده کنترل می کند تا به پخش کننده اجازه دهد نزدیک به یک تنظیم زنده تنظیم شده باقی بماند. هنگامی که پخش کننده ایجاد می شود،LivePlaybackSpeedControl
تزریق می شود.
مفهوم تزریق اجزایی که قطعاتی از عملکرد پخش کننده را پیاده سازی می کنند در سراسر کتابخانه وجود دارد. اجرای پیشفرض برخی مؤلفهها کار را به مؤلفههای تزریقشده بیشتر واگذار میکند. این اجازه می دهد تا بسیاری از اجزای فرعی به صورت جداگانه با پیاده سازی هایی که به روشی سفارشی پیکربندی شده اند جایگزین شوند.
سفارشی سازی پلیر
برخی از نمونههای متداول شخصیسازی پخشکننده با تزریق قطعات در زیر توضیح داده شدهاند.
پیکربندی پشته شبکه
ما یک صفحه در مورد سفارشی کردن پشته شبکه استفاده شده توسط ExoPlayer داریم.
ذخیره داده های بارگیری شده از شبکه
به راهنماهای ذخیره موقت موقت و بارگیری رسانه مراجعه کنید.
سفارشی کردن تعاملات سرور
برخی از برنامه ها ممکن است بخواهند درخواست ها و پاسخ های HTTP را رهگیری کنند. ممکن است بخواهید هدر درخواست سفارشی را تزریق کنید، سرصفحههای پاسخ سرور را بخوانید، URI درخواستها را تغییر دهید، و غیره. به عنوان مثال، برنامه شما ممکن است با تزریق یک نشانه به عنوان هدر هنگام درخواست بخشهای رسانه، خود را احراز هویت کند.
مثال زیر نحوه پیاده سازی این رفتارها را با تزریق یک DataSource.Factory
سفارشی به DefaultMediaSourceFactory
نشان می دهد:
کاتلین
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
جاوا
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
در قطعه کد بالا، HttpDataSource
تزریق شده شامل سرصفحه "Header: Value"
در هر درخواست HTTP است. این رفتار برای هر تعامل با منبع HTTP ثابت می شود.
برای یک رویکرد دقیق تر، می توانید رفتار به موقع را با استفاده از ResolvingDataSource
تزریق کنید. قطعه کد زیر نحوه تزریق هدر درخواست را درست قبل از تعامل با منبع HTTP نشان می دهد:
کاتلین
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
جاوا
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
همچنین می توانید از ResolvingDataSource
برای انجام تغییرات به موقع URI استفاده کنید، همانطور که در قطعه زیر نشان داده شده است:
کاتلین
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
جاوا
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
سفارشی کردن رسیدگی به خطا
پیادهسازی یک LoadErrorHandlingPolicy
سفارشی به برنامهها اجازه میدهد تا نحوه واکنش ExoPlayer به خطاهای بارگذاری را سفارشی کنند. برای مثال، ممکن است برنامهای بخواهد بهجای چندین بار تلاش مجدد، سریع از کار بیفتد، یا ممکن است بخواهد منطق عقبنشینی را سفارشی کند که مدت زمان انتظار پخشکننده بین هر تلاش مجدد را کنترل میکند. قطعه زیر نشان می دهد که چگونه منطق بازپس گیری سفارشی را پیاده سازی کنید:
کاتلین
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
جاوا
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
آرگومان LoadErrorInfo
حاوی اطلاعات بیشتری درباره بار ناموفق برای سفارشی کردن منطق بر اساس نوع خطا یا درخواست ناموفق است.
سفارشی کردن پرچم استخراج
از پرچم های استخراج کننده می توان برای سفارشی کردن نحوه استخراج فرمت های فردی از رسانه های پیشرو استفاده کرد. آنها را می توان در DefaultExtractorsFactory
که به DefaultMediaSourceFactory
ارائه شده است، تنظیم کرد. مثال زیر پرچمی را ارسال می کند که جستجوی مبتنی بر فهرست را برای جریان های MP3 فعال می کند.
کاتلین
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
جاوا
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
فعال کردن جستجوی نرخ بیت ثابت
برای جریانهای MP3، ADTS و AMR، میتوانید جستجوی تقریبی را با استفاده از یک فرض نرخ بیت ثابت با پرچمهای FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
فعال کنید. این پرچم ها را می توان برای استخراج کننده های فردی با استفاده از روش های DefaultExtractorsFactory.setXyzExtractorFlags
فردی که در بالا توضیح داده شد، تنظیم کرد. برای فعال کردن جستجوی نرخ بیت ثابت برای همه استخراجکنندههایی که از آن پشتیبانی میکنند، از DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
استفاده کنید.
کاتلین
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
جاوا
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
سپس ExtractorsFactory
می توان از طریق DefaultMediaSourceFactory
همانطور که برای سفارشی کردن پرچم های استخراج کننده در بالا توضیح داده شد، تزریق کرد.
فعال کردن صف بافر ناهمزمان
صف بافر ناهمزمان یک پیشرفت در خط لوله رندر ExoPlayer است که نمونه های MediaCodec
را در حالت ناهمزمان اجرا می کند و از رشته های اضافی برای برنامه ریزی رمزگشایی و رندر داده ها استفاده می کند. فعال کردن آن میتواند فریمهای افت شده و کاهش صدا را کاهش دهد.
صف بافر ناهمزمان به طور پیشفرض در دستگاههای دارای Android 12 (سطح API 31) و بالاتر فعال است و میتوان آن را با شروع Android 6.0 (سطح API 23) به صورت دستی فعال کرد. فعال کردن این ویژگی را برای دستگاههای خاصی در نظر بگیرید که در آنها فریمهای افت شده یا کاهش صدا را مشاهده میکنید، بهویژه هنگام پخش محتوای محافظتشده با DRM یا محتوای با نرخ فریم بالا.
در ساده ترین حالت، باید یک DefaultRenderersFactory
را به صورت زیر به پخش کننده تزریق کنید:
کاتلین
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
جاوا
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
اگر مستقیماً رندرکنندهها را نمونهسازی میکنید، یک AsynchronousMediaCodecAdapter.Factory
را به سازنده MediaCodecVideoRenderer
و MediaCodecAudioRenderer
ارسال کنید.
سفارشی کردن عملیات با ForwardingSimpleBasePlayer
میتوانید برخی از رفتارهای یک نمونه Player
را با قرار دادن آن در زیر کلاس ForwardingSimpleBasePlayer
سفارشی کنید. این کلاس به شما امکان می دهد به جای اینکه مستقیماً متدهای Player
پیاده سازی کنید، «عملیات» خاصی را رهگیری کنید. این امر رفتار ثابتی را تضمین می کند، به عنوان مثال، play()
, pause()
و setPlayWhenReady(boolean)
. همچنین تضمین می کند که همه تغییرات حالت به درستی به نمونه های ثبت شده Player.Listener
منتشر می شوند. برای بیشتر موارد سفارشیسازی، به دلیل این تضمینهای سازگاری ForwardingSimpleBasePlayer
باید به ForwardingPlayer
مستعد خطا ترجیح داده شود.
به عنوان مثال، برای افزودن برخی منطق سفارشی هنگام شروع یا توقف پخش:
کاتلین
class PlayerWithCustomPlay(player: Player) : ForwardingSimpleBasePlayer(player) { override fun handleSetPlayWhenReady(playWhenReady: Boolean): ListenableFuture<*> { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady) } }
جاوا
class PlayerWithCustomPlay extends ForwardingSimpleBasePlayer { public PlayerWithCustomPlay(Player player) { super(player); } @Override protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) { // Add custom logic return super.handleSetPlayWhenReady(playWhenReady); } }
یا اینکه دستور SEEK_TO_NEXT
را مجاز نکنید (و اطمینان حاصل کنید که Player.seekToNext
غیر فعال است):
کاتلین
class PlayerWithoutSeekToNext(player: Player) : ForwardingSimpleBasePlayer(player) { override fun getState(): State { val state = super.getState() return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build() ) .build() } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
جاوا
class PlayerWithoutSeekToNext extends ForwardingSimpleBasePlayer { public PlayerWithoutSeekToNext(Player player) { super(player); } @Override protected State getState() { State state = super.getState(); return state .buildUpon() .setAvailableCommands( state.availableCommands.buildUpon().remove(COMMAND_SEEK_TO_NEXT).build()) .build(); } // We don't need to override handleSeek, because it is guaranteed not to be called for // COMMAND_SEEK_TO_NEXT since we've marked that command unavailable. }
سفارشی سازی MediaSource
مثالهای بالا اجزای سفارشیسازیشده را برای استفاده در حین پخش تمام اشیاء MediaItem
که به پخشکننده ارسال میشوند، تزریق میکنند. در مواردی که نیاز به سفارشیسازی دقیق باشد، میتوان اجزای سفارشیشده را به نمونههای MediaSource
جداگانه تزریق کرد، که میتواند مستقیماً به پخشکننده ارسال شود. مثال زیر نشان می دهد که چگونه یک ProgressiveMediaSource
برای استفاده از DataSource.Factory
، ExtractorsFactory
و LoadErrorHandlingPolicy
سفارشی کنید:
کاتلین
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
جاوا
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
ایجاد اجزای سفارشی
این کتابخانه پیادهسازیهای پیشفرض مؤلفههای فهرستشده در بالای این صفحه را برای موارد استفاده رایج فراهم میکند. یک ExoPlayer
می تواند از این مؤلفه ها استفاده کند، اما ممکن است برای استفاده از پیاده سازی های سفارشی در صورت نیاز به رفتارهای غیر استاندارد نیز ساخته شود. برخی از موارد استفاده برای پیاده سازی های سفارشی عبارتند از:
-
Renderer
- ممکن است بخواهید یکRenderer
سفارشی را برای مدیریت نوع رسانه ای که توسط پیاده سازی های پیش فرض ارائه شده توسط کتابخانه پشتیبانی نمی شود، پیاده سازی کنید. -
TrackSelector
– پیادهسازیTrackSelector
سفارشی به توسعهدهنده برنامه اجازه میدهد تا نحوه انتخاب آهنگهای در معرض دیدMediaSource
را برای مصرف هر یک ازRenderer
های موجود تغییر دهد. -
LoadControl
– اجرای یکLoadControl
سفارشی به توسعهدهنده برنامه اجازه میدهد تا سیاست بافر پخشکننده را تغییر دهد. -
Extractor
- اگر نیاز به پشتیبانی از فرمت کانتینری دارید که در حال حاضر توسط کتابخانه پشتیبانی نمی شود، یک کلاسExtractor
سفارشی را پیاده سازی کنید. -
MediaSource
– اگر میخواهید نمونههای رسانهای را برای ارائه به رندرها به روش سفارشی دریافت کنید، یا اگر میخواهید رفتار ترکیبیMediaSource
سفارشی را پیادهسازی کنید، ممکن است پیادهسازی یک کلاسMediaSource
سفارشی مناسب باشد. -
MediaSource.Factory
– پیاده سازی یکMediaSource.Factory
سفارشی به یک برنامه اجازه می دهد تا روش ایجادMediaSource
را از یکMediaItem
سفارشی کند. -
DataSource
– بسته بالادستی ExoPlayer در حال حاضر شامل تعدادی پیاده سازیDataSource
برای موارد استفاده مختلف است. ممکن است بخواهید کلاسDataSource
خود را پیاده سازی کنید تا داده ها را به روش دیگری بارگیری کنید، مانند یک پروتکل سفارشی، با استفاده از یک پشته HTTP سفارشی، یا از یک کش دائمی سفارشی.
هنگام ساخت اجزای سفارشی، موارد زیر را توصیه می کنیم:
- اگر یک مؤلفه سفارشی نیاز دارد رویدادها را به برنامه گزارش دهد، توصیه میکنیم این کار را با استفاده از همان مدل اجزای ExoPlayer موجود انجام دهید، برای مثال با استفاده از کلاسهای
EventDispatcher
یا ارسال یکHandler
همراه با یک شنونده به سازنده مؤلفه. - توصیه میکنیم که اجزای سفارشی از همان مدل اجزای ExoPlayer موجود استفاده کنند تا امکان پیکربندی مجدد توسط برنامه در حین پخش فراهم شود. برای انجام این کار، اجزای سفارشی باید
PlayerMessage.Target
پیاده سازی کنند و تغییرات پیکربندی را در متدhandleMessage
دریافت کنند. کد برنامه باید تغییرات پیکربندی را با فراخوانی روشcreateMessage
ExoPlayer، پیکربندی پیام و ارسال آن به مؤلفه با استفاده ازPlayerMessage.send
ارسال کند. ارسال پیامهایی که باید در رشته پخش ارسال شوند، تضمین میکند که آنها به ترتیب با هر عملیات دیگری که روی پخشکننده انجام میشود، اجرا میشوند.