কাস্টমাইজেশন

এক্সোপ্লেয়ার লাইব্রেরির মূল অংশে রয়েছে Player ইন্টারফেস। একটি Player প্রথাগত উচ্চ-স্তরের মিডিয়া প্লেয়ার কার্যকারিতা যেমন মিডিয়া বাফার করার ক্ষমতা, খেলা, বিরতি এবং অনুসন্ধান করার ক্ষমতা প্রকাশ করে। ডিফল্ট বাস্তবায়ন ExoPlayer মিডিয়ার ধরন, কীভাবে এবং কোথায় এটি সংরক্ষণ করা হয় এবং এটি কীভাবে রেন্ডার করা হয় সে সম্পর্কে কিছু অনুমান করার জন্য (এবং তাই কিছু বিধিনিষেধ আরোপ করার জন্য) ডিজাইন করা হয়েছে। সরাসরি মিডিয়ার লোডিং এবং রেন্ডারিং বাস্তবায়নের পরিবর্তে, ExoPlayer বাস্তবায়নগুলি এই কাজটিকে এমন উপাদানগুলিতে অর্পণ করে যা প্লেয়ার তৈরি করার সময় বা প্লেয়ারের কাছে নতুন মিডিয়া উত্সগুলি পাস করার সময় ইনজেকশন দেওয়া হয়। সমস্ত ExoPlayer বাস্তবায়নের জন্য সাধারণ উপাদানগুলি হল:

  • MediaSource দৃষ্টান্তগুলি যে মিডিয়া সংজ্ঞায়িত করে প্লে করা হবে, মিডিয়া লোড হবে এবং যেখান থেকে লোড করা মিডিয়া পড়া যাবে। প্লেয়ারের ভিতরে একটি MediaSource.Factory দ্বারা একটি MediaItem থেকে একটি MediaSource উদাহরণ তৈরি করা হয়। এগুলি মিডিয়া সোর্স ভিত্তিক প্লেলিস্ট API ব্যবহার করে সরাসরি প্লেয়ারের কাছে প্রেরণ করা যেতে পারে।
  • একটি MediaSource.Factory উদাহরণ যা একটি MediaItem কে একটি MediaSource এ রূপান্তর করে। প্লেয়ার তৈরি করা হলে MediaSource.Factory ইনজেকশন দেওয়া হয়।
  • Renderer দৃষ্টান্ত যা মিডিয়ার পৃথক উপাদান রেন্ডার করে। প্লেয়ার তৈরি করা হলে এগুলো ইনজেকশন দেওয়া হয়।
  • একটি TrackSelector যেটি MediaSource দ্বারা প্রদত্ত ট্র্যাকগুলিকে প্রতিটি উপলব্ধ Renderer দ্বারা ব্যবহার করার জন্য নির্বাচন করে৷ প্লেয়ার তৈরি করার সময় একটি TrackSelector ইনজেকশন দেওয়া হয়।
  • একটি LoadControl যা নিয়ন্ত্রণ করে কখন MediaSource আরও মিডিয়া বাফার করে এবং কতটা মিডিয়া বাফার হয়। প্লেয়ার তৈরি করার সময় একটি LoadControl ইনজেকশন দেওয়া হয়।
  • একটি LivePlaybackSpeedControl যা লাইভ প্লেব্যাকের সময় প্লেব্যাকের গতি নিয়ন্ত্রণ করে যাতে প্লেয়ারকে কনফিগার করা লাইভ অফসেটের কাছাকাছি থাকতে দেয়। প্লেয়ার তৈরি করার সময় একটি LivePlaybackSpeedControl ইনজেকশন দেওয়া হয়।

প্লেয়ার কার্যকারিতার টুকরোগুলি বাস্তবায়ন করে এমন উপাদানগুলিকে ইনজেকশন দেওয়ার ধারণাটি লাইব্রেরি জুড়ে উপস্থিত রয়েছে। কিছু উপাদানের ডিফল্ট বাস্তবায়ন আরও ইনজেকশনের উপাদানগুলিতে কাজ করে। এটি একটি কাস্টম উপায়ে কনফিগার করা বাস্তবায়নের সাথে অনেক সাব-কম্পোনেন্টকে পৃথকভাবে প্রতিস্থাপন করার অনুমতি দেয়।

প্লেয়ার কাস্টমাইজেশন

কম্পোনেন্ট ইনজেকশন দিয়ে প্লেয়ার কাস্টমাইজ করার কিছু সাধারণ উদাহরণ নিচে বর্ণনা করা হয়েছে।

নেটওয়ার্ক স্ট্যাক কনফিগার করা হচ্ছে

ExoPlayer দ্বারা ব্যবহৃত নেটওয়ার্ক স্ট্যাক কাস্টমাইজ করার বিষয়ে আমাদের একটি পৃষ্ঠা রয়েছে।

নেটওয়ার্ক থেকে লোড করা ডেটা ক্যাশিং

অস্থায়ী অন-দ্য-ফ্লাই ক্যাশিং এবং মিডিয়া ডাউনলোড করার জন্য গাইড দেখুন।

সার্ভার মিথস্ক্রিয়া কাস্টমাইজ করা

কিছু অ্যাপ্লিকেশান HTTP অনুরোধ এবং প্রতিক্রিয়া বাধা দিতে চাইতে পারে৷ আপনি কাস্টম রিকোয়েস্ট হেডার ইনজেক্ট করতে চাইতে পারেন, সার্ভারের রেসপন্স হেডার পড়তে, অনুরোধের ইউআরআই পরিবর্তন করতে পারেন, ইত্যাদি

নিম্নোক্ত উদাহরণ প্রদর্শন করে যে কিভাবে একটি কাস্টম 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 প্রতিটি HTTP অনুরোধে হেডার "Header: Value" অন্তর্ভুক্ত করে। এই আচরণ একটি 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();

আপনি যদি রেন্ডারারদের সরাসরি ইনস্ট্যান্টিয়েটিং করেন, তাহলে MediaCodecVideoRenderer এবং MediaCodecAudioRenderer কনস্ট্রাক্টরদের কাছে একটি AsynchronousMediaCodecAdapter.Factory পাস করুন।

ForwardingSimpleBasePlayer এর সাথে কাস্টমাইজ করা অপারেশন

আপনি ForwardingSimpleBasePlayer এর একটি সাবক্লাসে মোড়ানো Player উদাহরণের কিছু আচরণ কাস্টমাইজ করতে পারেন। এই ক্লাসটি আপনাকে 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.
}

মিডিয়াসোর্স কাস্টমাইজেশন

উপরের উদাহরণগুলি প্লেয়ারে পাস করা সমস্ত MediaItem অবজেক্টের প্লেব্যাকের সময় ব্যবহারের জন্য কাস্টমাইজ করা উপাদানগুলিকে ইনজেক্ট করে৷ যেখানে সূক্ষ্ম কাস্টমাইজেশন প্রয়োজন, সেখানে পৃথক MediaSource দৃষ্টান্তগুলিতে কাস্টমাইজ করা উপাদানগুলি ইনজেকশন করাও সম্ভব, যা সরাসরি প্লেয়ারের কাছে প্রেরণ করা যেতে পারে। নীচের উদাহরণটি দেখায় যে কীভাবে একটি কাস্টম DataSource.Factory ফ্যাক্টরি, ExtractorsFactory এবং LoadErrorHandlingPolicy ব্যবহার করতে একটি ProgressiveMediaSource কাস্টমাইজ করা যায়:

কোটলিন

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 - ExoPlayer-এর আপস্ট্রিম প্যাকেজে ইতিমধ্যেই বিভিন্ন ব্যবহারের ক্ষেত্রে অনেকগুলি DataSource বাস্তবায়ন রয়েছে৷ আপনি অন্য উপায়ে ডেটা লোড করার জন্য আপনার নিজস্ব DataSource ক্লাস বাস্তবায়ন করতে চাইতে পারেন, যেমন একটি কাস্টম প্রোটোকলের মাধ্যমে, একটি কাস্টম HTTP স্ট্যাক ব্যবহার করে, বা একটি কাস্টম স্থায়ী ক্যাশে থেকে।

কাস্টম উপাদান নির্মাণ করার সময়, আমরা নিম্নলিখিত সুপারিশ:

  • যদি একটি কাস্টম কম্পোনেন্টের ইভেন্টগুলিকে অ্যাপে রিপোর্ট করার প্রয়োজন হয়, তাহলে আমরা সুপারিশ করি যে আপনি বিদ্যমান ExoPlayer উপাদানগুলির মতো একই মডেল ব্যবহার করুন, উদাহরণস্বরূপ, EventDispatcher ক্লাস ব্যবহার করা বা কম্পোনেন্টের কনস্ট্রাক্টরের সাথে একজন শ্রোতার সাথে একটি Handler পাস করা।
  • আমরা সুপারিশ করেছি যে কাস্টম উপাদানগুলি প্লেব্যাকের সময় অ্যাপ দ্বারা পুনরায় কনফিগারেশনের অনুমতি দেওয়ার জন্য বিদ্যমান ExoPlayer উপাদানগুলির মতো একই মডেল ব্যবহার করে৷ এটি করার জন্য, কাস্টম উপাদানগুলিকে PlayerMessage.Target প্রয়োগ করতে হবে এবং handleMessage পদ্ধতিতে কনফিগারেশন পরিবর্তনগুলি গ্রহণ করতে হবে৷ অ্যাপ্লিকেশন কোডটি ExoPlayer-এর createMessage পদ্ধতিতে কল করে, বার্তাটি কনফিগার করে এবং PlayerMessage.send ব্যবহার করে কম্পোনেন্টে পাঠানোর মাধ্যমে কনফিগারেশন পরিবর্তনগুলি পাস করতে হবে। প্লেব্যাক থ্রেডে পাঠানোর জন্য বার্তা পাঠানো নিশ্চিত করে যে প্লেয়ারে অন্য যেকোন ক্রিয়াকলাপ সম্পাদিত হওয়ার সাথে সাথে সেগুলি কার্যকর করা হয়েছে।