এক্সোপ্লেয়ার লাইব্রেরির মূল অংশ হল Player ইন্টারফেস। Player ঐতিহ্যবাহী উচ্চ-স্তরের মিডিয়া প্লেয়ারের কার্যকারিতা যেমন মিডিয়া বাফার করার ক্ষমতা, প্লে, পজ এবং সিক করার ক্ষমতা প্রকাশ করে। ডিফল্ট বাস্তবায়ন ExoPlayer কোন ধরণের মিডিয়া প্লে করা হচ্ছে, কীভাবে এবং কোথায় সংরক্ষণ করা হয় এবং কীভাবে রেন্ডার করা হয় সে সম্পর্কে কিছু অনুমান করার জন্য (এবং তাই কিছু বিধিনিষেধ আরোপ করার জন্য) ডিজাইন করা হয়েছে। সরাসরি মিডিয়া লোডিং এবং রেন্ডারিং বাস্তবায়ন করার পরিবর্তে, ExoPlayer বাস্তবায়নগুলি এই কাজটি এমন উপাদানগুলিতে অর্পণ করে যা একটি প্লেয়ার তৈরি করার সময় বা প্লেয়ারে নতুন মিডিয়া উৎস প্রেরণ করার সময় ইনজেক্ট করা হয়। সমস্ত ExoPlayer বাস্তবায়নের জন্য সাধারণ উপাদানগুলি হল:
-
MediaSourceইনস্ট্যান্স যা মিডিয়া চালানোর জন্য সংজ্ঞায়িত করে, মিডিয়া লোড করে এবং যেখান থেকে লোড করা মিডিয়া পড়া যায়। প্লেয়ারের ভিতরে থাকা একটিMediaSource.Factoryদ্বারা একটিMediaItemথেকে একটিMediaSourceইনস্ট্যান্স তৈরি করা হয়। মিডিয়া সোর্স ভিত্তিক প্লেলিস্ট API ব্যবহার করে এগুলি সরাসরি প্লেয়ারে পাঠানো যেতে পারে। - A
MediaSource.Factoryinstances that converts aMediaItemto aMediaSource. TheMediaSource.Factoryis injected when the player is created. -
Rendererinstances that render individual components of the media. These are injected when the player is created. - A
TrackSelectorthat selects tracks provided by theMediaSourceto be consumed by each availableRenderer. ATrackSelectoris injected when the player is created. - A
LoadControlthat controls when theMediaSourcebuffers more media, and how much media is buffered. ALoadControlis injected when the player is created. - একটি
LivePlaybackSpeedControlযা লাইভ প্লেব্যাকের সময় প্লেব্যাকের গতি নিয়ন্ত্রণ করে যাতে প্লেয়ারটি একটি কনফিগার করা লাইভ অফসেটের কাছাকাছি থাকতে পারে। প্লেয়ার তৈরি করার সময় একটিLivePlaybackSpeedControlইনজেক্ট করা হয়।
প্লেয়ারের কার্যকারিতা বাস্তবায়নের জন্য উপাদানগুলিকে ইনজেকশন করার ধারণাটি সমগ্র লাইব্রেরি জুড়ে বিদ্যমান। কিছু উপাদানের ডিফল্ট বাস্তবায়নগুলি আরও ইনজেকশন করা উপাদানগুলিতে কাজ অর্পণ করে। এটি অনেক উপ-উপাদানকে স্বতন্ত্রভাবে এমন বাস্তবায়নের সাথে প্রতিস্থাপন করতে দেয় যা একটি কাস্টম উপায়ে কনফিগার করা হয়।
প্লেয়ার কাস্টমাইজেশন
Some common examples of customizing the player by injecting components are described below.
নেটওয়ার্ক স্ট্যাক কনফিগার করা হচ্ছে
We have a page about customizing the network stack used by ExoPlayer .
নেটওয়ার্ক থেকে লোড করা ডেটা ক্যাশে করা হচ্ছে
See the guides for temporary on-the-fly caching and downloading media .
সার্ভার ইন্টারঅ্যাকশন কাস্টমাইজ করা
কিছু অ্যাপ HTTP অনুরোধ এবং প্রতিক্রিয়াগুলিকে আটকাতে চাইতে পারে। আপনি কাস্টম অনুরোধ শিরোনামগুলি ইনজেক্ট করতে, সার্ভারের প্রতিক্রিয়া শিরোনামগুলি পড়তে, অনুরোধগুলির URI গুলি পরিবর্তন করতে ইত্যাদি চাইতে পারেন। উদাহরণস্বরূপ, মিডিয়া বিভাগগুলি অনুরোধ করার সময় আপনার অ্যাপটি হেডার হিসাবে একটি টোকেন ইনজেক্ট করে নিজেকে প্রমাণীকরণ করতে পারে।
The following example demonstrates how to implement these behaviors by injecting a custom DataSource.Factory into the 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();
In the code snippet above, the injected HttpDataSource includes the header "Header: Value" in every HTTP request. This behavior is fixed for every interaction with an HTTP source.
আরও সুক্ষ্ম পদ্ধতির জন্য, আপনি একটি ResolvingDataSource ব্যবহার করে just-in-time আচরণ ইনজেক্ট করতে পারেন। নিম্নলিখিত কোড স্নিপেটটি দেখায় যে কীভাবে 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)));
You may also use a ResolvingDataSource to perform just-in-time modifications of the URI, as shown in the following snippet:
কোটলিন
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();
The LoadErrorInfo argument contains more information about the failed load to customize the logic based on the error type or the failed request.
এক্সট্র্যাক্টর পতাকা কাস্টমাইজ করা
এক্সট্র্যাক্টর ফ্ল্যাগগুলি প্রগতিশীল মিডিয়া থেকে পৃথক ফর্ম্যাটগুলি কীভাবে এক্সট্র্যাক্ট করা হয় তা কাস্টমাইজ করতে ব্যবহার করা যেতে পারে। এগুলি 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);
The ExtractorsFactory can then be injected via DefaultMediaSourceFactory as described for customizing extractor flags above.
অ্যাসিঙ্ক্রোনাস বাফার কিউয়িং সক্ষম করা হচ্ছে
অ্যাসিঙ্ক্রোনাস বাফার কিউয়িং হল এক্সোপ্লেয়ারের রেন্ডারিং পাইপলাইনের একটি বর্ধিত রূপ, যা অ্যাসিঙ্ক্রোনাস মোডে MediaCodec ইনস্ট্যান্স পরিচালনা করে এবং ডেটা ডিকোডিং এবং রেন্ডারিং শিডিউল করার জন্য অতিরিক্ত থ্রেড ব্যবহার করে। এটি সক্ষম করলে ড্রপ হওয়া ফ্রেম এবং অডিও আন্ডাররান কমানো যায়।
অ্যান্ড্রয়েড ১২ (এপিআই লেভেল ৩১) এবং তার উপরে চলমান ডিভাইসগুলিতে অ্যাসিঙ্ক্রোনাস বাফার কিউইং ডিফল্টরূপে সক্ষম থাকে এবং অ্যান্ড্রয়েড ৬.০ (এপিআই লেভেল ২৩) দিয়ে শুরু করে ম্যানুয়ালি সক্ষম করা যেতে পারে। নির্দিষ্ট ডিভাইসগুলিতে আপনি যেগুলিতে ড্রপ করা ফ্রেম বা অডিও আন্ডাররানগুলি লক্ষ্য করেন, বিশেষ করে যখন ডিআরএম সুরক্ষিত বা উচ্চ-ফ্রেম-রেট সামগ্রী চালান, তাদের জন্য বৈশিষ্ট্যটি সক্ষম করার কথা বিবেচনা করুন।
In the simplest case, you need to inject a DefaultRenderersFactory to the player as follows:
কোটলিন
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 কনস্ট্রাক্টরগুলিতে পাস করুন।
Customizing operations with ForwardingSimpleBasePlayer
আপনি একটি Player ইনস্ট্যান্সের কিছু আচরণ ForwardingSimpleBasePlayer এর একটি সাবক্লাসে মোড়ানোর মাধ্যমে কাস্টমাইজ করতে পারেন। এই ক্লাসটি আপনাকে সরাসরি Player পদ্ধতি প্রয়োগ করার পরিবর্তে নির্দিষ্ট 'অপারেশন' আটকাতে দেয়। এটি play() , pause() এবং setPlayWhenReady(boolean) এর সামঞ্জস্যপূর্ণ আচরণ নিশ্চিত করে। এটি নিশ্চিত করে যে সমস্ত স্টেট পরিবর্তনগুলি নিবন্ধিত Player.Listener ইনস্ট্যান্সে সঠিকভাবে প্রচারিত হয়েছে। বেশিরভাগ কাস্টমাইজেশন ব্যবহারের ক্ষেত্রে, এই সামঞ্জস্যের গ্যারান্টির কারণে ForwardingSimpleBasePlayer ত্রুটি-প্রবণ ForwardingPlayer চেয়ে বেশি পছন্দ করা উচিত।
For example, to add some custom logic when playback is started or stopped:
কোটলিন
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); } }
Or to disallow the SEEK_TO_NEXT command (and ensure Player.seekToNext is a no-op):
কোটলিন
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. }
মিডিয়াসোর্স কাস্টমাইজেশন
উপরের উদাহরণগুলিতে প্লেয়ারে পাঠানো সমস্ত 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– You may want to implement a customRendererto handle a media type not supported by the default implementations provided by the library. -
TrackSelector- একটি কাস্টমTrackSelectorবাস্তবায়নের ফলে একজন অ্যাপ ডেভেলপারMediaSourceদ্বারা প্রকাশিত ট্র্যাকগুলি প্রতিটি উপলব্ধRendererব্যবহারের জন্য নির্বাচন করার পদ্ধতি পরিবর্তন করতে পারবেন। -
LoadControl– Implementing a customLoadControlallows an app developer to change the player's buffering policy. -
Extractor– If you need to support a container format not currently supported by the library, consider implementing a customExtractorclass. -
MediaSource– যদি আপনি কাস্টম পদ্ধতিতে রেন্ডারারদের কাছে ফিড করার জন্য মিডিয়া নমুনা পেতে চান, অথবা যদি আপনি কাস্টমMediaSourceকম্পোজিটিং আচরণ বাস্তবায়ন করতে চান, তাহলে একটি কাস্টমMediaSourceক্লাস বাস্তবায়ন করা উপযুক্ত হতে পারে। -
MediaSource.Factory– Implementing a customMediaSource.Factoryallows an application to customize the way in which aMediaSourceis created from aMediaItem. -
DataSource– এক্সোপ্লেয়ারের আপস্ট্রিম প্যাকেজে ইতিমধ্যেই বিভিন্ন ব্যবহারের ক্ষেত্রে বেশ কয়েকটিDataSourceবাস্তবায়ন রয়েছে। আপনি অন্য কোনও উপায়ে ডেটা লোড করার জন্য আপনার নিজস্বDataSourceক্লাস বাস্তবায়ন করতে চাইতে পারেন, যেমন একটি কাস্টম প্রোটোকলের মাধ্যমে, একটি কাস্টম HTTP স্ট্যাক ব্যবহার করে, অথবা একটি কাস্টম স্থায়ী ক্যাশে থেকে।
When building custom components, we recommend the following:
- যদি কোনও কাস্টম কম্পোনেন্টকে অ্যাপে ইভেন্ট রিপোর্ট করার প্রয়োজন হয়, তাহলে আমরা আপনাকে বিদ্যমান ExoPlayer কম্পোনেন্টের মতো একই মডেল ব্যবহার করে তা করার পরামর্শ দিচ্ছি, উদাহরণস্বরূপ
EventDispatcherক্লাস ব্যবহার করা অথবা কম্পোনেন্টের কনস্ট্রাক্টরের কাছে লিসেনারের সাথেHandlerপাস করা। - আমরা সুপারিশ করেছি যে কাস্টম কম্পোনেন্টগুলি প্লেব্যাকের সময় অ্যাপ দ্বারা পুনরায় কনফিগারেশনের অনুমতি দেওয়ার জন্য বিদ্যমান ExoPlayer কম্পোনেন্টগুলির মতো একই মডেল ব্যবহার করবে। এটি করার জন্য, কাস্টম কম্পোনেন্টগুলিকে
PlayerMessage.Targetবাস্তবায়ন করতে হবে এবংhandleMessageপদ্ধতিতে কনফিগারেশন পরিবর্তনগুলি গ্রহণ করতে হবে। অ্যাপ্লিকেশন কোডটি ExoPlayer এরcreateMessageপদ্ধতিতে কল করে, বার্তাটি কনফিগার করে এবংPlayerMessage.sendব্যবহার করে কম্পোনেন্টে প্রেরণ করে কনফিগারেশন পরিবর্তনগুলি পাস করতে হবে। প্লেব্যাক থ্রেডে ডেলিভারি করার জন্য বার্তা পাঠানো নিশ্চিত করে যে সেগুলি প্লেয়ারে সম্পাদিত অন্যান্য ক্রিয়াকলাপের সাথে ক্রমানুসারে কার্যকর করা হচ্ছে।