در هسته کتابخانه ExoPlayer، میانای Player قرار دارد. Player
عملکرد سنتی پخشکننده رسانه سطح بالا را نشان میدهد، مانند توانایی
بافر کردن رسانه، پخش، توقف موقت، و جستجو. پیادهسازی پیشفرض ExoPlayer
طوری طراحی شده است که چند فرض درباره نوع رسانه درحال پخش، نحوه و مکان ذخیره آن، و نحوه ارائه آن داشته باشد (و درنتیجه چند محدودیت برای آنها اعمال کند). بهجای پیادهسازی بار کردن و رندر کردن مستقیم رسانه،
ExoPlayer پیادهسازیها این کار را به مؤلفههایی واگذار میکنند که هنگام ایجاد پخشکننده یا هنگام ارسال منابع رسانه جدید به پخشکننده تزریق میشوند.
مؤلفههای مشترک در همه پیادهسازیهای ExoPlayer عبارتاند از:
MediaSourceنمونهای که رسانه را برای پخش تعریف میکند، رسانه را بار میکند، و از آن رسانه بارشده میتواند خوانده شود. نمونهMediaSourceازMediaItemتوسطMediaSource.Factoryدر داخل پخشکننده ایجاد میشود. همچنین میتوانند بااستفاده از میانای برنامهسازی کاربردی فهرست پخش مبتنی بر منبع رسانه مستقیماً به پخشکننده منتقل شوند.- نمونه
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: LoadErrorHandlingPolicy.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(LoadErrorHandlingPolicy.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 نسخه ۱۲ (سطح میانای برنامهسازی کاربردی ۳۱) یا بالاتر فعال است و میتوان آن را بهطور دستی از Android نسخه ۶.۰ (سطح میانای برنامهسازی کاربردی ۲۳) فعال کرد. این ویژگی را برای دستگاههای خاصی که در آنها افت فریم یا کمبود داده صوتی مشاهده میکنید فعال کنید، بهویژه هنگام پخش محتوای محافظتشده با 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();
اگر پردازنده را مستقیماً نمونهسازی میکنید، new
DefaultMediaCodecAdapter.Factory(context).forceEnableAsynchronous() را به سازندههای
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) } }
جاوا
public static final 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. }
جاوا
public static final 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– Implementing a customTrackSelectorallows an app developer to change the way in which tracks exposed by aMediaSourceare selected for consumption by each of the availableRenderers.LoadControl– پیادهسازیLoadControlسفارشی به توسعهدهنده برنامه امکان میدهد خطمشی میانگیری پخشکننده را تغییر دهد.Extractor– اگر لازم است از قالب محفظهای پشتیبانی کنید که درحالحاضر توسط کتابخانه پشتیبانی نمیشود، پیادهسازی کلاسExtractorسفارشی را درنظر بگیرید.MediaSource– پیادهسازی کلاسMediaSourceسفارشی ممکن است مناسب باشد اگر بخواهید نمونههای رسانه را برای تغذیه پردازنده گرافیکی به روشی سفارشی بهدست آورید، یا اگر بخواهید رفتار ترکیببندیMediaSourceسفارشی را پیادهسازی کنید.MediaSource.Factory– Implementing a customMediaSource.Factoryallows an application to customize the way in which aMediaSourceis created from aMediaItem.DataSource– بسته بالادستی ExoPlayer درحالحاضر شامل تعدادیDataSourceپیادهسازی برای موارد استفاده مختلف است. ممکن است بخواهید کلاسDataSourceخودتان را برای بار کردن دادهها به روشی دیگر پیادهسازی کنید، مثلاً ازطریق پروتکل سفارشی، بااستفاده از پشته HTTP سفارشی، یا از حافظه نهان پایدار سفارشی.
هنگام ساختن مؤلفههای سفارشی، موارد زیر را توصیه میکنیم:
- اگر مؤلفه سفارشی باید رویدادها را به برنامه گزارش دهد، توصیه میکنیم
این کار را بااستفاده از همان مدل مؤلفههای ExoPlayer موجود انجام دهید، برای
مثال بااستفاده از کلاسهای
EventDispatcherیا ارسالHandlerبههمراه شنونده به سازنده مؤلفه. - توصیه میکنیم مؤلفههای سفارشی از همان مدل مؤلفههای ExoPlayer موجود استفاده کنند تا برنامه بتواند درطول بازپخش پیکربندی را تغییر دهد. برای انجام این کار،
مؤلفههای سفارشی باید
PlayerMessage.Targetرا پیادهسازی کنند و تغییرات پیکربندی را در روشhandleMessageدریافت کنند. کد برنامه باید تغییرات پیکربندی را با فراخوانی روشcreateMessageExoPlayer، پیکربندی پیام، و ارسال آن به مؤلفه بااستفاده ازPlayerMessage.sendمنتقل کند. ارسال پیامها برای تحویل در رشته بازپخش تضمین میکند که آنها بهترتیب با هر عملیات دیگری که روی پخشکننده انجام میشود اجرا میشوند.