এক্সোপ্লেয়ার লাইব্রেরির মূল অংশ হল Player ইন্টারফেস। Player ঐতিহ্যবাহী উচ্চ-স্তরের মিডিয়া প্লেয়ারের কার্যকারিতা যেমন মিডিয়া বাফার করার ক্ষমতা, প্লে, পজ এবং সিক করার ক্ষমতা প্রকাশ করে। ডিফল্ট বাস্তবায়ন ExoPlayer কোন ধরণের মিডিয়া প্লে করা হচ্ছে, কীভাবে এবং কোথায় সংরক্ষণ করা হয় এবং কীভাবে রেন্ডার করা হয় সে সম্পর্কে কিছু অনুমান করার জন্য (এবং তাই কিছু বিধিনিষেধ আরোপ করার জন্য) ডিজাইন করা হয়েছে। সরাসরি মিডিয়া লোডিং এবং রেন্ডারিং বাস্তবায়ন করার পরিবর্তে, ExoPlayer বাস্তবায়নগুলি এই কাজটি এমন উপাদানগুলিতে অর্পণ করে যা একটি প্লেয়ার তৈরি করার সময় বা প্লেয়ারে নতুন মিডিয়া উৎস প্রেরণ করার সময় ইনজেক্ট করা হয়। সমস্ত ExoPlayer বাস্তবায়নের জন্য সাধারণ উপাদানগুলি হল:
-
MediaSourceইনস্ট্যান্স যা মিডিয়া চালানোর জন্য সংজ্ঞায়িত করে, মিডিয়া লোড করে এবং যেখান থেকে লোড করা মিডিয়া পড়া যায়। প্লেয়ারের ভিতরে থাকা একটিMediaSource.Factoryদ্বারা একটিMediaItemথেকে একটিMediaSourceইনস্ট্যান্স তৈরি করা হয়। মিডিয়া সোর্স ভিত্তিক প্লেলিস্ট API ব্যবহার করে এগুলি সরাসরি প্লেয়ারে পাঠানো যেতে পারে। - একটি
MediaSource.Factoryইনস্ট্যান্স যা একটিMediaItemকেMediaSourceতে রূপান্তর করে। প্লেয়ার তৈরি করার সময়MediaSource.Factoryইনজেক্ট করা হয়। -
Rendererইনস্ট্যান্স যা মিডিয়ার পৃথক উপাদানগুলিকে রেন্ডার করে। প্লেয়ার তৈরি করার সময় এগুলি ইনজেক্ট করা হয়। - একটি
TrackSelectorযা প্রতিটি উপলব্ধRendererব্যবহারের জন্যMediaSourceদ্বারা সরবরাহিত ট্র্যাকগুলি নির্বাচন করে। প্লেয়ার তৈরি হওয়ার সময় একটিTrackSelectorইনজেক্ট করা হয়। - একটি
LoadControlযা নিয়ন্ত্রণ করে কখনMediaSourceআরও মিডিয়া বাফার করে এবং কতটা মিডিয়া বাফার করা হয়। প্লেয়ার তৈরি করার সময় একটিLoadControlইনজেক্ট করা হয়। - একটি
LivePlaybackSpeedControlযা লাইভ প্লেব্যাকের সময় প্লেব্যাকের গতি নিয়ন্ত্রণ করে যাতে প্লেয়ারটি একটি কনফিগার করা লাইভ অফসেটের কাছাকাছি থাকতে পারে। প্লেয়ার তৈরি করার সময় একটিLivePlaybackSpeedControlইনজেক্ট করা হয়।
প্লেয়ারের কার্যকারিতা বাস্তবায়নের জন্য উপাদানগুলিকে ইনজেকশন করার ধারণাটি সমগ্র লাইব্রেরি জুড়ে বিদ্যমান। কিছু উপাদানের ডিফল্ট বাস্তবায়নগুলি আরও ইনজেকশন করা উপাদানগুলিতে কাজ অর্পণ করে। এটি অনেক উপ-উপাদানকে স্বতন্ত্রভাবে এমন বাস্তবায়নের সাথে প্রতিস্থাপন করতে দেয় যা একটি কাস্টম উপায়ে কনফিগার করা হয়।
প্লেয়ার কাস্টমাইজেশন
কম্পোনেন্ট ইনজেক্ট করে প্লেয়ার কাস্টমাইজ করার কিছু সাধারণ উদাহরণ নিচে বর্ণনা করা হল।
নেটওয়ার্ক স্ট্যাক কনফিগার করা হচ্ছে
ExoPlayer দ্বারা ব্যবহৃত নেটওয়ার্ক স্ট্যাক কাস্টমাইজ করার বিষয়ে আমাদের একটি পৃষ্ঠা আছে।
নেটওয়ার্ক থেকে লোড করা ডেটা ক্যাশে করা হচ্ছে
অস্থায়ী অন-দ্য-ফ্লাই ক্যাশিং এবং ডাউনলোডিং মিডিয়ার জন্য নির্দেশিকাগুলি দেখুন।
সার্ভার ইন্টারঅ্যাকশন কাস্টমাইজ করা
কিছু অ্যাপ HTTP অনুরোধ এবং প্রতিক্রিয়াগুলিকে আটকাতে চাইতে পারে। আপনি কাস্টম অনুরোধ শিরোনামগুলি ইনজেক্ট করতে, সার্ভারের প্রতিক্রিয়া শিরোনামগুলি পড়তে, অনুরোধগুলির URI গুলি পরিবর্তন করতে ইত্যাদি চাইতে পারেন। উদাহরণস্বরূপ, মিডিয়া বিভাগগুলি অনুরোধ করার সময় আপনার অ্যাপটি হেডার হিসাবে একটি টোকেন ইনজেক্ট করে নিজেকে প্রমাণীকরণ করতে পারে।
নিম্নলিখিত উদাহরণটি DefaultMediaSourceFactory তে একটি কাস্টম DataSource.Factory ইনজেক্ট করে এই আচরণগুলি কীভাবে বাস্তবায়ন করা যায় তা দেখায়:
কোটলিন
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 প্রতিটি HTTP অনুরোধে "Header: Value" শিরোনামটি অন্তর্ভুক্ত করে। এই আচরণটি HTTP উৎসের সাথে প্রতিটি ইন্টারঅ্যাকশনের জন্য স্থির করা হয়।
আরও সুক্ষ্ম পদ্ধতির জন্য, আপনি একটি 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)));
আপনি নিম্নলিখিত স্নিপেটে দেখানো হিসাবে, URI-এর সময়োপযোগী পরিবর্তনগুলি সম্পাদন করতে একটি ResolvingDataSource ব্যবহার করতে পারেন:
কোটলিন
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);
উপরে এক্সট্র্যাক্টর ফ্ল্যাগ কাস্টমাইজ করার জন্য বর্ণিত DefaultMediaSourceFactory এর মাধ্যমে ExtractorsFactory ইনজেক্ট করা যেতে পারে।
অ্যাসিঙ্ক্রোনাস বাফার কিউয়িং সক্ষম করা হচ্ছে
অ্যাসিঙ্ক্রোনাস বাফার কিউয়িং হল এক্সোপ্লেয়ারের রেন্ডারিং পাইপলাইনের একটি বর্ধিত রূপ, যা অ্যাসিঙ্ক্রোনাস মোডে MediaCodec ইনস্ট্যান্স পরিচালনা করে এবং ডেটা ডিকোডিং এবং রেন্ডারিং শিডিউল করার জন্য অতিরিক্ত থ্রেড ব্যবহার করে। এটি সক্ষম করলে ড্রপ হওয়া ফ্রেম এবং অডিও আন্ডাররান কমানো যায়।
অ্যান্ড্রয়েড ১২ (এপিআই লেভেল ৩১) এবং তার উপরে চলমান ডিভাইসগুলিতে অ্যাসিঙ্ক্রোনাস বাফার কিউইং ডিফল্টরূপে সক্ষম থাকে এবং অ্যান্ড্রয়েড ৬.০ (এপিআই লেভেল ২৩) দিয়ে শুরু করে ম্যানুয়ালি সক্ষম করা যেতে পারে। নির্দিষ্ট ডিভাইসগুলিতে আপনি যেগুলিতে ড্রপ করা ফ্রেম বা অডিও আন্ডাররানগুলি লক্ষ্য করেন, বিশেষ করে যখন ডিআরএম সুরক্ষিত বা উচ্চ-ফ্রেম-রেট সামগ্রী চালান, তাদের জন্য বৈশিষ্ট্যটি সক্ষম করার কথা বিবেচনা করুন।
সবচেয়ে সহজ ক্ষেত্রে, আপনাকে প্লেয়ারে নিম্নলিখিতভাবে একটি 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. }
মিডিয়াসোর্স কাস্টমাইজেশন
উপরের উদাহরণগুলিতে প্লেয়ারে পাঠানো সমস্ত 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বাস্তবায়নের মাধ্যমে একটি অ্যাপ্লিকেশনMediaItemথেকেMediaSourceতৈরির পদ্ধতি কাস্টমাইজ করতে পারে। -
DataSource– এক্সোপ্লেয়ারের আপস্ট্রিম প্যাকেজে ইতিমধ্যেই বিভিন্ন ব্যবহারের ক্ষেত্রে বেশ কয়েকটিDataSourceবাস্তবায়ন রয়েছে। আপনি অন্য কোনও উপায়ে ডেটা লোড করার জন্য আপনার নিজস্বDataSourceক্লাস বাস্তবায়ন করতে চাইতে পারেন, যেমন একটি কাস্টম প্রোটোকলের মাধ্যমে, একটি কাস্টম HTTP স্ট্যাক ব্যবহার করে, অথবা একটি কাস্টম স্থায়ী ক্যাশে থেকে।
কাস্টম উপাদান তৈরি করার সময়, আমরা নিম্নলিখিতগুলি সুপারিশ করি:
- যদি কোনও কাস্টম কম্পোনেন্টকে অ্যাপে ইভেন্ট রিপোর্ট করার প্রয়োজন হয়, তাহলে আমরা আপনাকে বিদ্যমান ExoPlayer কম্পোনেন্টের মতো একই মডেল ব্যবহার করে তা করার পরামর্শ দিচ্ছি, উদাহরণস্বরূপ
EventDispatcherক্লাস ব্যবহার করা অথবা কম্পোনেন্টের কনস্ট্রাক্টরের কাছে লিসেনারের সাথেHandlerপাস করা। - আমরা সুপারিশ করেছি যে কাস্টম কম্পোনেন্টগুলি প্লেব্যাকের সময় অ্যাপ দ্বারা পুনরায় কনফিগারেশনের অনুমতি দেওয়ার জন্য বিদ্যমান ExoPlayer কম্পোনেন্টগুলির মতো একই মডেল ব্যবহার করবে। এটি করার জন্য, কাস্টম কম্পোনেন্টগুলিকে
PlayerMessage.Targetবাস্তবায়ন করতে হবে এবংhandleMessageপদ্ধতিতে কনফিগারেশন পরিবর্তনগুলি গ্রহণ করতে হবে। অ্যাপ্লিকেশন কোডটি ExoPlayer এরcreateMessageপদ্ধতিতে কল করে, বার্তাটি কনফিগার করে এবংPlayerMessage.sendব্যবহার করে কম্পোনেন্টে প্রেরণ করে কনফিগারেশন পরিবর্তনগুলি পাস করতে হবে। প্লেব্যাক থ্রেডে ডেলিভারি করার জন্য বার্তা পাঠানো নিশ্চিত করে যে সেগুলি প্লেয়ারে সম্পাদিত অন্যান্য ক্রিয়াকলাপের সাথে ক্রমানুসারে কার্যকর করা হচ্ছে।