একটি MediaSession ব্যবহার করে প্লেব্যাক নিয়ন্ত্রণ এবং বিজ্ঞাপন দিন

মিডিয়া সেশনগুলি একটি অডিও বা ভিডিও প্লেয়ারের সাথে ইন্টারঅ্যাক্ট করার একটি সর্বজনীন উপায় প্রদান করে। Media3 এ, ডিফল্ট প্লেয়ার হল ExoPlayer ক্লাস, যা Player ইন্টারফেস প্রয়োগ করে। প্লেয়ারের সাথে মিডিয়া সেশন সংযুক্ত করা একটি অ্যাপকে বাহ্যিকভাবে মিডিয়া প্লেব্যাকের বিজ্ঞাপন দিতে এবং বাহ্যিক উত্স থেকে প্লেব্যাক কমান্ড পেতে অনুমতি দেয়৷

কমান্ডগুলি হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতামের মতো শারীরিক বোতামগুলি থেকে উদ্ভূত হতে পারে। এগুলি ক্লায়েন্ট অ্যাপ থেকেও আসতে পারে যেগুলির একটি মিডিয়া কন্ট্রোলার রয়েছে, যেমন Google সহকারীকে "পজ" নির্দেশ দেওয়া। মিডিয়া সেশন এই কমান্ডগুলি মিডিয়া অ্যাপের প্লেয়ারে অর্পণ করে।

কখন মিডিয়া সেশন বেছে নেবেন

আপনি যখন MediaSession প্রয়োগ করেন, আপনি ব্যবহারকারীদের প্লেব্যাক নিয়ন্ত্রণ করার অনুমতি দেন:

  • তাদের হেডফোনের মাধ্যমে। মিডিয়া প্লে বা পজ করতে বা পরবর্তী বা পূর্ববর্তী ট্র্যাকে যাওয়ার জন্য ব্যবহারকারীরা তাদের হেডফোনে প্রায়শই বোতাম বা টাচ ইন্টারঅ্যাকশন করতে পারেন।
  • গুগল সহকারীর সাথে কথা বলে। একটি সাধারণ প্যাটার্ন হল "ওকে গুগল, পজ" বলার জন্য যে কোনো মিডিয়া বর্তমানে ডিভাইসে চলছে।
  • তাদের Wear OS ঘড়ির মাধ্যমে। এটি তাদের ফোনে খেলার সময় সবচেয়ে সাধারণ প্লেব্যাক নিয়ন্ত্রণগুলিতে সহজে অ্যাক্সেসের অনুমতি দেয়।
  • মিডিয়া নিয়ন্ত্রণের মাধ্যমে। এই ক্যারাউজেল প্রতিটি চলমান মিডিয়া সেশনের জন্য নিয়ন্ত্রণ দেখায়।
  • টিভিতে । ফিজিক্যাল প্লেব্যাক বোতাম, প্ল্যাটফর্ম প্লেব্যাক কন্ট্রোল এবং পাওয়ার ম্যানেজমেন্ট সহ অ্যাকশনের অনুমতি দেয় (উদাহরণস্বরূপ যদি টিভি, সাউন্ডবার বা A/V রিসিভার বন্ধ হয়ে যায় বা ইনপুট সুইচ করা হয়, প্লেব্যাক অ্যাপে বন্ধ করা উচিত)।
  • এবং অন্য কোনো বাহ্যিক প্রক্রিয়া যা প্লেব্যাককে প্রভাবিত করতে হবে।

এটি অনেক ব্যবহারের ক্ষেত্রে দুর্দান্ত। বিশেষ করে, আপনার MediaSession ব্যবহার করার বিষয়ে দৃঢ়ভাবে বিবেচনা করা উচিত যখন:

  • আপনি সিনেমা বা লাইভ টিভির মতো দীর্ঘ-ফর্মের ভিডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি পডকাস্ট বা সঙ্গীত প্লেলিস্টের মতো দীর্ঘ-ফর্মের অডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি একটি টিভি অ্যাপ তৈরি করছেন।

যাইহোক, সমস্ত ব্যবহারের ক্ষেত্রে MediaSession সাথে ভালভাবে মানানসই নয়। আপনি নিম্নলিখিত ক্ষেত্রে শুধুমাত্র Player ব্যবহার করতে চাইতে পারেন:

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

যদি আপনার ব্যবহারের ক্ষেত্রে উপরে তালিকাভুক্ত কোনটির সাথে মানানসই না হয়, তাহলে ব্যবহারকারী যখন সক্রিয়ভাবে বিষয়বস্তুর সাথে জড়িত না হয় তখন আপনার অ্যাপ চালিয়ে যাওয়া প্লেব্যাক আপনি ঠিক আছেন কিনা তা বিবেচনা করুন। উত্তর যদি হ্যাঁ হয়, আপনি সম্ভবত MediaSession বেছে নিতে চান। উত্তর না হলে, আপনি সম্ভবত Player ব্যবহার করতে চান।

একটি মিডিয়া সেশন তৈরি করুন

একটি মিডিয়া সেশন প্লেয়ারের সাথে থাকে যা এটি পরিচালনা করে। আপনি একটি Context এবং একটি Player অবজেক্ট সহ একটি মিডিয়া সেশন তৈরি করতে পারেন। আপনার প্রয়োজন হলে একটি মিডিয়া সেশন তৈরি এবং আরম্ভ করা উচিত, যেমন onStart() বা onResume() লাইফসাইকেল পদ্ধতি Activity বা Fragment , অথবা Service onCreate() পদ্ধতি যা মিডিয়া সেশন এবং এর সাথে সম্পর্কিত প্লেয়ারের মালিক।

একটি মিডিয়া সেশন তৈরি করতে, একটি Player শুরু করুন এবং এটিকে MediaSession.Builder এ এইভাবে সরবরাহ করুন:

কোটলিন

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

জাভা

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

স্বয়ংক্রিয় রাষ্ট্র পরিচালনা

Media3 লাইব্রেরি স্বয়ংক্রিয়ভাবে প্লেয়ারের অবস্থা ব্যবহার করে মিডিয়া সেশন আপডেট করে। যেমন, আপনাকে প্লেয়ার থেকে সেশনে ম্যাপিং ম্যানুয়ালি পরিচালনা করতে হবে না।

এটি প্ল্যাটফর্ম মিডিয়া সেশন থেকে ভিন্ন যেখানে আপনাকে PlaybackState তৈরি এবং বজায় রাখার প্রয়োজন ছিল প্লেয়ার থেকে স্বাধীনভাবে, উদাহরণস্বরূপ কোনো ত্রুটি নির্দেশ করার জন্য।

অনন্য সেশন আইডি

ডিফল্টরূপে, MediaSession.Builder সেশন আইডি হিসাবে একটি খালি স্ট্রিং সহ একটি সেশন তৈরি করে। এটি যথেষ্ট যদি একটি অ্যাপ শুধুমাত্র একটি একক সেশনের উদাহরণ তৈরি করতে চায়, যা সবচেয়ে সাধারণ ক্ষেত্রে।

যদি একটি অ্যাপ একই সময়ে একাধিক সেশন ইনস্ট্যান্স পরিচালনা করতে চায়, তবে অ্যাপটিকে নিশ্চিত করতে হবে যে প্রতিটি সেশনের সেশন আইডি অনন্য। MediaSession.Builder.setId(String id) দিয়ে সেশন তৈরি করার সময় সেশন আইডি সেট করা যেতে পারে।

আপনি যদি দেখেন একটি IllegalStateException ত্রুটি বার্তা সহ আপনার অ্যাপ ক্র্যাশ করছে IllegalStateException: Session ID must be unique. ID= তাহলে সম্ভবত একই আইডি সহ পূর্বে তৈরি করা ইন্সট্যান্স রিলিজ হওয়ার আগে একটি সেশন অপ্রত্যাশিতভাবে তৈরি হয়েছে। একটি প্রোগ্রামিং ত্রুটি দ্বারা ফাঁস করা সেশন এড়াতে, এই ধরনের কেস সনাক্ত করা হয় এবং একটি ব্যতিক্রম নিক্ষেপ করে বিজ্ঞপ্তি দেওয়া হয়।

অন্যান্য ক্লায়েন্টদের নিয়ন্ত্রণ মঞ্জুর করুন

মিডিয়া সেশন প্লেব্যাক নিয়ন্ত্রণের চাবিকাঠি। এটি আপনাকে বাহ্যিক উত্স থেকে প্লেয়ারের কাছে কমান্ড রুট করতে সক্ষম করে যা আপনার মিডিয়া চালানোর কাজ করে। এই উত্সগুলি হতে পারে ফিজিক্যাল বোতাম যেমন হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতাম বা পরোক্ষ কমান্ড যেমন Google অ্যাসিস্ট্যান্টকে "পজ" নির্দেশ দেওয়া। একইভাবে, আপনি বিজ্ঞপ্তি এবং লক স্ক্রিন নিয়ন্ত্রণের সুবিধার্থে অ্যান্ড্রয়েড সিস্টেমে বা Wear OS ঘড়িতে অ্যাক্সেস দিতে চাইতে পারেন যাতে আপনি ওয়াচফেস থেকে প্লেব্যাক নিয়ন্ত্রণ করতে পারেন। বাহ্যিক ক্লায়েন্টরা আপনার মিডিয়া অ্যাপে প্লেব্যাক কমান্ড ইস্যু করতে একটি মিডিয়া কন্ট্রোলার ব্যবহার করতে পারে। এগুলি আপনার মিডিয়া সেশন দ্বারা গৃহীত হয়, যা শেষ পর্যন্ত মিডিয়া প্লেয়ারকে কমান্ড অর্পণ করে।

একটি ডায়াগ্রাম যা একটি MediaSession এবং MediaController এর মধ্যে মিথস্ক্রিয়া প্রদর্শন করে।
চিত্র 1 : মিডিয়া কন্ট্রোলার বাহ্যিক উত্স থেকে মিডিয়া সেশনে কমান্ড পাস করার সুবিধা দেয়৷

যখন একটি নিয়ামক আপনার মিডিয়া সেশনের সাথে সংযোগ করতে চলেছে, onConnect() পদ্ধতিটি বলা হয়। অনুরোধ গ্রহণ বা প্রত্যাখ্যান করার সিদ্ধান্ত নিতে আপনি প্রদত্ত ControllerInfo ব্যবহার করতে পারেন। কাস্টম কমান্ড ঘোষণা বিভাগে সংযোগের অনুরোধ গ্রহণ করার একটি উদাহরণ দেখুন।

সংযোগ করার পরে, একটি নিয়ামক সেশনে প্লেব্যাক কমান্ড পাঠাতে পারে। সেশন তারপর সেই কমান্ডগুলি প্লেয়ারের কাছে অর্পণ করে। Player ইন্টারফেসে সংজ্ঞায়িত প্লেব্যাক এবং প্লেলিস্ট কমান্ডগুলি সেশন দ্বারা স্বয়ংক্রিয়ভাবে পরিচালনা করা হয়।

অন্যান্য কলব্যাক পদ্ধতি আপনাকে পরিচালনা করতে দেয়, উদাহরণস্বরূপ, কাস্টম কমান্ডের অনুরোধ এবং প্লেলিস্ট পরিবর্তন করা । এই কলব্যাকগুলিতে একইভাবে একটি ControllerInfo অবজেক্ট অন্তর্ভুক্ত থাকে যাতে আপনি প্রতি-নিয়ন্ত্রকের ভিত্তিতে প্রতিটি অনুরোধে কীভাবে প্রতিক্রিয়া জানান তা আপনি সংশোধন করতে পারেন।

প্লেলিস্ট পরিবর্তন করুন

প্লেলিস্টের জন্য ExoPlayer গাইডে ব্যাখ্যা করা একটি মিডিয়া সেশন সরাসরি তার প্লেয়ারের প্লেলিস্ট পরিবর্তন করতে পারে। যদি COMMAND_SET_MEDIA_ITEM বা COMMAND_CHANGE_MEDIA_ITEMS কন্ট্রোলারের কাছে উপলব্ধ থাকে তাহলে কন্ট্রোলাররাও প্লেলিস্ট সংশোধন করতে সক্ষম৷

প্লেলিস্টে নতুন আইটেম যোগ করার সময়, প্লেয়ারের সাধারণত একটি সংজ্ঞায়িত URI সহ MediaItem দৃষ্টান্ত প্রয়োজন হয় যাতে সেগুলিকে প্লে করা যায়৷ ডিফল্টরূপে, নতুন যোগ করা আইটেমগুলি প্লেয়ার পদ্ধতিতে স্বয়ংক্রিয়ভাবে ফরোয়ার্ড হয় যেমন player.addMediaItem যদি তাদের একটি URI সংজ্ঞায়িত থাকে।

আপনি যদি প্লেয়ারে যোগ করা MediaItem দৃষ্টান্তগুলি কাস্টমাইজ করতে চান, তাহলে আপনি onAddMediaItems() ওভাররাইড করতে পারেন। এই পদক্ষেপটি প্রয়োজন হয় যখন আপনি একটি সংজ্ঞায়িত URI ছাড়া মিডিয়ার অনুরোধকারী নিয়ামকদের সমর্থন করতে চান। পরিবর্তে, MediaItem সাধারণত অনুরোধ করা মিডিয়া বর্ণনা করতে নিম্নলিখিত ক্ষেত্রগুলির মধ্যে এক বা একাধিক সেট থাকে:

  • MediaItem.id : একটি জেনেরিক আইডি যা মিডিয়াকে চিহ্নিত করে।
  • MediaItem.RequestMetadata.mediaUri : একটি অনুরোধ URI যা একটি কাস্টম স্কিমা ব্যবহার করতে পারে এবং প্লেয়ার দ্বারা সরাসরি খেলার যোগ্য নয়৷
  • MediaItem.RequestMetadata.searchQuery : একটি পাঠ্য অনুসন্ধান ক্যোয়ারী, উদাহরণস্বরূপ Google সহকারী থেকে।
  • MediaItem.MediaMetadata : কাঠামোবদ্ধ মেটাডেটা যেমন 'শিরোনাম' বা 'শিল্পী'।

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

মিডিয়া বোতাম পছন্দগুলি পরিচালনা করুন

প্রতিটি কন্ট্রোলার, উদাহরণস্বরূপ সিস্টেম UI, Android Auto, বা Wear OS, ব্যবহারকারীকে কোন বোতামগুলি দেখাতে হবে সে সম্পর্কে নিজস্ব সিদ্ধান্ত নিতে পারে৷ আপনি কোন প্লেব্যাক নিয়ন্ত্রণগুলি ব্যবহারকারীর কাছে প্রকাশ করতে চান তা নির্দেশ করতে, আপনি MediaSessionমিডিয়া বোতাম পছন্দগুলি নির্দিষ্ট করতে পারেন। এই পছন্দগুলির মধ্যে CommandButton দৃষ্টান্তগুলির একটি অর্ডার করা তালিকা থাকে, প্রতিটি ব্যবহারকারী ইন্টারফেসে একটি বোতামের জন্য একটি পছন্দ নির্ধারণ করে।

কমান্ড বোতাম সংজ্ঞায়িত করুন

CommandButton দৃষ্টান্ত মিডিয়া বোতাম পছন্দ সংজ্ঞায়িত করতে ব্যবহার করা হয়. প্রতিটি বোতাম পছন্দসই UI উপাদানের তিনটি দিক সংজ্ঞায়িত করে:

  1. আইকন , চাক্ষুষ চেহারা সংজ্ঞায়িত. CommandButton.Builder তৈরি করার সময় আইকনটিকে অবশ্যই পূর্বনির্ধারিত ধ্রুবকগুলির একটিতে সেট করতে হবে। মনে রাখবেন এটি একটি প্রকৃত বিটম্যাপ বা চিত্র সম্পদ নয়। একটি জেনেরিক ধ্রুবক কন্ট্রোলারদের তাদের নিজস্ব UI এর মধ্যে সামঞ্জস্যপূর্ণ চেহারা এবং অনুভূতির জন্য একটি উপযুক্ত সংস্থান চয়ন করতে সহায়তা করে। যদি পূর্বনির্ধারিত আইকন ধ্রুবকগুলির একটিও আপনার ব্যবহারের ক্ষেত্রে উপযুক্ত না হয় তবে আপনি পরিবর্তে setCustomIconResId ব্যবহার করতে পারেন।
  2. কমান্ড , ব্যবহারকারী যখন বোতামের সাথে ইন্টারঅ্যাক্ট করে তখন ট্রিগার হওয়া ক্রিয়া সংজ্ঞায়িত করে। আপনি একটি Player.Command এর জন্য setPlayerCommand , অথবা একটি পূর্বনির্ধারিত বা কাস্টম SessionCommand এর জন্য setSessionCommand ব্যবহার করতে পারেন।
  3. স্লট , কন্ট্রোলার UI-তে বোতামটি কোথায় রাখা উচিত তা নির্ধারণ করে। এই ক্ষেত্রটি ঐচ্ছিক এবং আইকন এবং কমান্ডের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে সেট করা হয়। উদাহরণস্বরূপ, এটি নির্দিষ্ট করার অনুমতি দেয় যে ডিফল্ট 'ওভারফ্লো' এলাকার পরিবর্তে UI এর 'ফরোয়ার্ড' নেভিগেশন এলাকায় একটি বোতাম প্রদর্শিত হবে।

কোটলিন

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

জাভা

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

মিডিয়া বোতাম পছন্দগুলি সমাধান করা হলে, নিম্নলিখিত অ্যালগরিদম প্রয়োগ করা হয়:

  1. মিডিয়া বোতাম পছন্দের প্রতিটি CommandButton জন্য, প্রথম উপলব্ধ এবং অনুমোদিত স্লটে বোতামটি রাখুন।
  2. যদি কেন্দ্রীয়, ফরোয়ার্ড এবং পশ্চাদগামী স্লটগুলির মধ্যে একটি বোতাম দিয়ে পূর্ণ না হয় তবে এই স্লটের জন্য ডিফল্ট বোতামগুলি যুক্ত করুন৷

আপনি CommandButton.DisplayConstraints ব্যবহার করতে পারেন কিভাবে UI প্রদর্শনের সীমাবদ্ধতার উপর নির্ভর করে মিডিয়া বোতাম পছন্দগুলি সমাধান করা হবে তার একটি পূর্বরূপ তৈরি করতে।

মিডিয়া বোতাম পছন্দ সেট করুন

মিডিয়া বোতাম পছন্দগুলি সেট করার সবচেয়ে সহজ উপায় হল MediaSession তৈরি করার সময় তালিকাটি সংজ্ঞায়িত করা। বিকল্পভাবে, আপনি প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য মিডিয়া বোতাম পছন্দগুলি কাস্টমাইজ করতে MediaSession.Callback.onConnect ওভাররাইড করতে পারেন৷

কোটলিন

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

জাভা

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

ব্যবহারকারীর ইন্টারঅ্যাকশনের পর মিডিয়া বোতামের পছন্দ আপডেট করুন

আপনার প্লেয়ারের সাথে একটি মিথস্ক্রিয়া পরিচালনা করার পরে, আপনি কন্ট্রোলার UI এ প্রদর্শিত বোতামগুলি আপডেট করতে চাইতে পারেন। একটি সাধারণ উদাহরণ হল একটি টগল বোতাম যা এই বোতামের সাথে যুক্ত ক্রিয়াটি ট্রিগার করার পরে এর আইকন এবং ক্রিয়া পরিবর্তন করে। মিডিয়া বোতাম পছন্দ আপডেট করতে, আপনি MediaSession.setMediaButtonPreferences ব্যবহার করতে পারেন সব কন্ট্রোলার বা একটি নির্দিষ্ট কন্ট্রোলারের পছন্দ আপডেট করতে:

কোটলিন

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

জাভা

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

কাস্টম কমান্ড যোগ করুন এবং ডিফল্ট আচরণ কাস্টমাইজ করুন

উপলব্ধ প্লেয়ার কমান্ডগুলি কাস্টম কমান্ড দ্বারা প্রসারিত করা যেতে পারে এবং ডিফল্ট আচরণ পরিবর্তন করতে ইনকামিং প্লেয়ার কমান্ড এবং মিডিয়া বোতামগুলিকে আটকানোও সম্ভব।

কাস্টম কমান্ড ঘোষণা এবং পরিচালনা করুন

মিডিয়া অ্যাপ্লিকেশনগুলি কাস্টম কমান্ডগুলিকে সংজ্ঞায়িত করতে পারে যা উদাহরণস্বরূপ মিডিয়া বোতাম পছন্দগুলিতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনি এমন বোতামগুলি প্রয়োগ করতে চাইতে পারেন যা ব্যবহারকারীকে পছন্দের আইটেমগুলির একটি তালিকায় একটি মিডিয়া আইটেম সংরক্ষণ করতে দেয়। MediaController কাস্টম কমান্ড পাঠায় এবং MediaSession.Callback সেগুলি গ্রহণ করে।

কাস্টম কমান্ড সংজ্ঞায়িত করার জন্য, প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য উপলব্ধ কাস্টম কমান্ড সেট করতে আপনাকে MediaSession.Callback.onConnect() ওভাররাইড করতে হবে।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

একটি MediaController থেকে কাস্টম কমান্ডের অনুরোধ পেতে, Callback onCustomCommand() পদ্ধতিটি ওভাররাইড করুন।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

আপনি MediaSession.ControllerInfo অবজেক্টের packageName প্রপার্টি ব্যবহার করে কোন মিডিয়া কন্ট্রোলার একটি অনুরোধ করছে তা ট্র্যাক করতে পারেন যা Callback পদ্ধতিতে পাস করা হয়। এটি আপনাকে একটি প্রদত্ত কমান্ডের প্রতিক্রিয়া হিসাবে আপনার অ্যাপের আচরণকে উপযোগী করার অনুমতি দেয় যদি এটি সিস্টেম, আপনার নিজের অ্যাপ বা অন্যান্য ক্লায়েন্ট অ্যাপ থেকে উদ্ভূত হয়।

ডিফল্ট প্লেয়ার কমান্ড কাস্টমাইজ করুন

সমস্ত ডিফল্ট কমান্ড এবং স্টেট হ্যান্ডলিং MediaSession থাকা Player কাছে অর্পণ করা হয়। Player ইন্টারফেসে সংজ্ঞায়িত একটি কমান্ডের আচরণ কাস্টমাইজ করতে, যেমন play() বা seekToNext() , আপনার Player MediaSession এ পাস করার আগে একটি ForwardingSimpleBasePlayer এ মুড়ে দিন:

কোটলিন

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

ForwardingSimpleBasePlayer সম্পর্কে আরও তথ্যের জন্য, কাস্টমাইজেশনের উপর ExoPlayer গাইড দেখুন।

একটি প্লেয়ার কমান্ডের অনুরোধকারী নিয়ামক সনাক্ত করুন

যখন একটি Player পদ্ধতিতে একটি কল একটি MediaController দ্বারা উদ্ভূত হয়, তখন আপনি MediaSession.controllerForCurrentRequest এর মাধ্যমে উত্সের উত্স সনাক্ত করতে পারেন এবং বর্তমান অনুরোধের জন্য ControllerInfo অর্জন করতে পারেন:

কোটলিন

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

জাভা

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

মিডিয়া বোতাম হ্যান্ডলিং কাস্টমাইজ করুন

মিডিয়া বোতাম হল হার্ডওয়্যার বোতাম যা অ্যান্ড্রয়েড ডিভাইস এবং অন্যান্য পেরিফেরাল ডিভাইসে পাওয়া যায়, যেমন একটি ব্লুটুথ হেডসেটে প্লে/পজ বোতাম। Media3 আপনার জন্য মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করে যখন তারা সেশনে আসে এবং সেশন প্লেয়ারে উপযুক্ত Player পদ্ধতিতে কল করে৷

সংশ্লিষ্ট Player পদ্ধতিতে সমস্ত আগত মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করার পরামর্শ দেওয়া হয়। আরও উন্নত ব্যবহারের ক্ষেত্রে, মিডিয়া বোতাম ইভেন্টগুলিকে MediaSession.Callback.onMediaButtonEvent(Intent) এ আটকানো যেতে পারে।

ত্রুটি হ্যান্ডলিং এবং রিপোর্টিং

একটি অধিবেশন নির্গত এবং নিয়ন্ত্রকদের রিপোর্ট যে দুটি ধরনের ত্রুটি আছে. মারাত্মক ত্রুটিগুলি সেশন প্লেয়ারের একটি প্রযুক্তিগত প্লেব্যাক ব্যর্থতার রিপোর্ট করে যা প্লেব্যাকে বাধা দেয়। মারাত্মক ত্রুটিগুলি যখন ঘটে তখন স্বয়ংক্রিয়ভাবে নিয়ামককে রিপোর্ট করা হয়। অপ্রয়োজনীয় ত্রুটিগুলি অ-প্রযুক্তিগত বা নীতিগত ত্রুটি যা প্লেব্যাকে বাধা দেয় না এবং ম্যানুয়ালি অ্যাপ্লিকেশন দ্বারা নিয়ন্ত্রকদের কাছে পাঠানো হয়।

মারাত্মক প্লেব্যাক ত্রুটি

প্লেয়ারের দ্বারা সেশনে একটি মারাত্মক প্লেব্যাক ত্রুটি রিপোর্ট করা হয় এবং তারপরে Player.Listener.onPlayerError(PlaybackException) এবং Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) এর মাধ্যমে কল করার জন্য কন্ট্রোলারদের রিপোর্ট করা হয়।

এই ধরনের ক্ষেত্রে, প্লেব্যাক স্টেটটি STATE_IDLE তে স্থানান্তরিত হয় এবং MediaController.getPlaybackError() PlaybackException প্রদান করে যা স্থানান্তর ঘটায়। ত্রুটির কারণ সম্পর্কে তথ্য পেতে একজন নিয়ামক PlayerException.errorCode পরিদর্শন করতে পারেন।

ইন্টারঅপারেবিলিটির জন্য, একটি মারাত্মক ত্রুটি প্ল্যাটফর্ম সেশনে প্রতিলিপি করা হয় তার অবস্থা STATE_ERROR এ রূপান্তর করে এবং PlaybackException অনুযায়ী ত্রুটি কোড এবং বার্তা সেট করে।

মারাত্মক ত্রুটির কাস্টমাইজেশন

ব্যবহারকারীকে স্থানীয় এবং অর্থপূর্ণ তথ্য প্রদানের জন্য, একটি মারাত্মক প্লেব্যাক ত্রুটির ত্রুটি কোড, ত্রুটি বার্তা এবং ত্রুটি অতিরিক্তগুলি সেশন তৈরি করার সময় একটি ForwardingPlayer ব্যবহার করে কাস্টমাইজ করা যেতে পারে:

কোটলিন

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

ফরওয়ার্ডিং প্লেয়ার ForwardingSimpleBasePlayer ব্যবহার করে ত্রুটিটি আটকাতে এবং ত্রুটি কোড, বার্তা বা অতিরিক্ত কাস্টমাইজ করতে পারে। একইভাবে, আপনি নতুন ত্রুটিও তৈরি করতে পারেন যা আসল প্লেয়ারে বিদ্যমান নেই:

কোটলিন

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

জাভা

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

অপ্রত্যাশিত ত্রুটি

প্রযুক্তিগত ব্যতিক্রম থেকে উদ্ভূত নয় এমন অপ্রত্যাশিত ত্রুটিগুলি একটি অ্যাপ দ্বারা সকলকে বা একটি নির্দিষ্ট নিয়ামকের কাছে পাঠানো যেতে পারে:

কোটলিন

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

জাভা

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

যখন একটি অপ্রত্যাশিত ত্রুটি মিডিয়া বিজ্ঞপ্তি কন্ট্রোলারে পাঠানো হয়, তখন ত্রুটি কোড এবং ত্রুটি বার্তাটি প্ল্যাটফর্ম মিডিয়া সেশনে প্রতিলিপি করা হয়, যখন PlaybackState.state STATE_ERROR এ পরিবর্তিত হয় না।

মারাত্মক ত্রুটিগুলি পান

একটি MediaController MediaController.Listener.onError প্রয়োগ করে একটি অপ্রত্যাশিত ত্রুটি পায়:

কোটলিন

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

জাভা

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });

,

মিডিয়া সেশনগুলি একটি অডিও বা ভিডিও প্লেয়ারের সাথে ইন্টারঅ্যাক্ট করার একটি সর্বজনীন উপায় প্রদান করে। Media3 এ, ডিফল্ট প্লেয়ার হল ExoPlayer ক্লাস, যা Player ইন্টারফেস প্রয়োগ করে। প্লেয়ারের সাথে মিডিয়া সেশন সংযুক্ত করা একটি অ্যাপকে বাহ্যিকভাবে মিডিয়া প্লেব্যাকের বিজ্ঞাপন দিতে এবং বাহ্যিক উত্স থেকে প্লেব্যাক কমান্ড পেতে অনুমতি দেয়৷

কমান্ডগুলি হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতামের মতো শারীরিক বোতামগুলি থেকে উদ্ভূত হতে পারে। এগুলি ক্লায়েন্ট অ্যাপ থেকেও আসতে পারে যেগুলির একটি মিডিয়া কন্ট্রোলার রয়েছে, যেমন Google সহকারীকে "পজ" নির্দেশ দেওয়া। মিডিয়া সেশন এই কমান্ডগুলি মিডিয়া অ্যাপের প্লেয়ারে অর্পণ করে।

কখন মিডিয়া সেশন বেছে নেবেন

আপনি যখন MediaSession প্রয়োগ করেন, আপনি ব্যবহারকারীদের প্লেব্যাক নিয়ন্ত্রণ করার অনুমতি দেন:

  • তাদের হেডফোনের মাধ্যমে। মিডিয়া প্লে বা পজ করতে বা পরবর্তী বা পূর্ববর্তী ট্র্যাকে যাওয়ার জন্য ব্যবহারকারীরা তাদের হেডফোনে প্রায়শই বোতাম বা টাচ ইন্টারঅ্যাকশন করতে পারেন।
  • গুগল সহকারীর সাথে কথা বলে। একটি সাধারণ প্যাটার্ন হল "ওকে গুগল, পজ" বলার জন্য যে কোনো মিডিয়া বর্তমানে ডিভাইসে চলছে।
  • তাদের Wear OS ঘড়ির মাধ্যমে। এটি তাদের ফোনে খেলার সময় সবচেয়ে সাধারণ প্লেব্যাক নিয়ন্ত্রণগুলিতে সহজে অ্যাক্সেসের অনুমতি দেয়।
  • মিডিয়া নিয়ন্ত্রণের মাধ্যমে। এই ক্যারাউজেল প্রতিটি চলমান মিডিয়া সেশনের জন্য নিয়ন্ত্রণ দেখায়।
  • টিভিতে । ফিজিক্যাল প্লেব্যাক বোতাম, প্ল্যাটফর্ম প্লেব্যাক কন্ট্রোল এবং পাওয়ার ম্যানেজমেন্ট সহ অ্যাকশনের অনুমতি দেয় (উদাহরণস্বরূপ যদি টিভি, সাউন্ডবার বা A/V রিসিভার বন্ধ হয়ে যায় বা ইনপুট সুইচ করা হয়, প্লেব্যাক অ্যাপে বন্ধ করা উচিত)।
  • এবং অন্য কোনো বাহ্যিক প্রক্রিয়া যা প্লেব্যাককে প্রভাবিত করতে হবে।

এটি অনেক ব্যবহারের ক্ষেত্রে দুর্দান্ত। বিশেষ করে, আপনার MediaSession ব্যবহার করার বিষয়ে দৃঢ়ভাবে বিবেচনা করা উচিত যখন:

  • আপনি সিনেমা বা লাইভ টিভির মতো দীর্ঘ-ফর্মের ভিডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি পডকাস্ট বা সঙ্গীত প্লেলিস্টের মতো দীর্ঘ-ফর্মের অডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি একটি টিভি অ্যাপ তৈরি করছেন।

যাইহোক, সমস্ত ব্যবহারের ক্ষেত্রে MediaSession সাথে ভালভাবে মানানসই নয়। আপনি নিম্নলিখিত ক্ষেত্রে শুধুমাত্র Player ব্যবহার করতে চাইতে পারেন:

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

যদি আপনার ব্যবহারের ক্ষেত্রে উপরে তালিকাভুক্ত কোনটির সাথে মানানসই না হয়, তাহলে ব্যবহারকারী যখন সক্রিয়ভাবে বিষয়বস্তুর সাথে জড়িত না হয় তখন আপনার অ্যাপ চালিয়ে যাওয়া প্লেব্যাক আপনি ঠিক আছেন কিনা তা বিবেচনা করুন। উত্তর যদি হ্যাঁ হয়, আপনি সম্ভবত MediaSession বেছে নিতে চান। উত্তর না হলে, আপনি সম্ভবত Player ব্যবহার করতে চান।

একটি মিডিয়া সেশন তৈরি করুন

একটি মিডিয়া সেশন প্লেয়ারের সাথে থাকে যা এটি পরিচালনা করে। আপনি একটি Context এবং একটি Player অবজেক্ট সহ একটি মিডিয়া সেশন তৈরি করতে পারেন। আপনার প্রয়োজন হলে একটি মিডিয়া সেশন তৈরি এবং আরম্ভ করা উচিত, যেমন onStart() বা onResume() লাইফসাইকেল পদ্ধতি Activity বা Fragment , অথবা Service onCreate() পদ্ধতি যা মিডিয়া সেশন এবং এর সাথে সম্পর্কিত প্লেয়ারের মালিক।

একটি মিডিয়া সেশন তৈরি করতে, একটি Player শুরু করুন এবং এটিকে MediaSession.Builder এ এইভাবে সরবরাহ করুন:

কোটলিন

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

জাভা

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

স্বয়ংক্রিয় রাষ্ট্র পরিচালনা

Media3 লাইব্রেরি স্বয়ংক্রিয়ভাবে প্লেয়ারের অবস্থা ব্যবহার করে মিডিয়া সেশন আপডেট করে। যেমন, আপনাকে প্লেয়ার থেকে সেশনে ম্যাপিং ম্যানুয়ালি পরিচালনা করতে হবে না।

এটি প্ল্যাটফর্ম মিডিয়া সেশন থেকে ভিন্ন যেখানে আপনাকে PlaybackState তৈরি এবং বজায় রাখার প্রয়োজন ছিল প্লেয়ার থেকে স্বাধীনভাবে, উদাহরণস্বরূপ কোনো ত্রুটি নির্দেশ করার জন্য।

অনন্য সেশন আইডি

ডিফল্টরূপে, MediaSession.Builder সেশন আইডি হিসাবে একটি খালি স্ট্রিং সহ একটি সেশন তৈরি করে। এটি যথেষ্ট যদি একটি অ্যাপ শুধুমাত্র একটি একক সেশনের উদাহরণ তৈরি করতে চায়, যা সবচেয়ে সাধারণ ক্ষেত্রে।

যদি একটি অ্যাপ একই সময়ে একাধিক সেশন ইনস্ট্যান্স পরিচালনা করতে চায়, তবে অ্যাপটিকে নিশ্চিত করতে হবে যে প্রতিটি সেশনের সেশন আইডি অনন্য। MediaSession.Builder.setId(String id) দিয়ে সেশন তৈরি করার সময় সেশন আইডি সেট করা যেতে পারে।

আপনি যদি দেখেন একটি IllegalStateException ত্রুটি বার্তা সহ আপনার অ্যাপ ক্র্যাশ করছে IllegalStateException: Session ID must be unique. ID= তাহলে সম্ভবত একই আইডি সহ পূর্বে তৈরি করা ইন্সট্যান্স রিলিজ হওয়ার আগে একটি সেশন অপ্রত্যাশিতভাবে তৈরি হয়েছে। একটি প্রোগ্রামিং ত্রুটি দ্বারা ফাঁস করা সেশন এড়াতে, এই ধরনের কেস সনাক্ত করা হয় এবং একটি ব্যতিক্রম নিক্ষেপ করে বিজ্ঞপ্তি দেওয়া হয়।

অন্যান্য ক্লায়েন্টদের নিয়ন্ত্রণ মঞ্জুর করুন

মিডিয়া সেশন প্লেব্যাক নিয়ন্ত্রণের চাবিকাঠি। এটি আপনাকে বাহ্যিক উত্স থেকে প্লেয়ারের কাছে কমান্ড রুট করতে সক্ষম করে যা আপনার মিডিয়া চালানোর কাজ করে। এই উত্সগুলি হতে পারে ফিজিক্যাল বোতাম যেমন হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতাম বা পরোক্ষ কমান্ড যেমন Google অ্যাসিস্ট্যান্টকে "পজ" নির্দেশ দেওয়া। একইভাবে, আপনি বিজ্ঞপ্তি এবং লক স্ক্রিন নিয়ন্ত্রণের সুবিধার্থে অ্যান্ড্রয়েড সিস্টেমে বা Wear OS ঘড়িতে অ্যাক্সেস দিতে চাইতে পারেন যাতে আপনি ওয়াচফেস থেকে প্লেব্যাক নিয়ন্ত্রণ করতে পারেন। বাহ্যিক ক্লায়েন্টরা আপনার মিডিয়া অ্যাপে প্লেব্যাক কমান্ড ইস্যু করতে একটি মিডিয়া কন্ট্রোলার ব্যবহার করতে পারে। এগুলি আপনার মিডিয়া সেশন দ্বারা গৃহীত হয়, যা শেষ পর্যন্ত মিডিয়া প্লেয়ারকে কমান্ড অর্পণ করে।

একটি ডায়াগ্রাম যা একটি MediaSession এবং MediaController এর মধ্যে মিথস্ক্রিয়া প্রদর্শন করে।
চিত্র 1 : মিডিয়া কন্ট্রোলার বাহ্যিক উত্স থেকে মিডিয়া সেশনে কমান্ড পাস করার সুবিধা দেয়৷

যখন একটি নিয়ামক আপনার মিডিয়া সেশনের সাথে সংযোগ করতে চলেছে, onConnect() পদ্ধতিটি বলা হয়। অনুরোধ গ্রহণ বা প্রত্যাখ্যান করার সিদ্ধান্ত নিতে আপনি প্রদত্ত ControllerInfo ব্যবহার করতে পারেন। কাস্টম কমান্ড ঘোষণা বিভাগে সংযোগের অনুরোধ গ্রহণ করার একটি উদাহরণ দেখুন।

সংযোগ করার পরে, একটি নিয়ামক সেশনে প্লেব্যাক কমান্ড পাঠাতে পারে। সেশন তারপর সেই কমান্ডগুলি প্লেয়ারের কাছে অর্পণ করে। Player ইন্টারফেসে সংজ্ঞায়িত প্লেব্যাক এবং প্লেলিস্ট কমান্ডগুলি সেশন দ্বারা স্বয়ংক্রিয়ভাবে পরিচালনা করা হয়।

অন্যান্য কলব্যাক পদ্ধতি আপনাকে পরিচালনা করতে দেয়, উদাহরণস্বরূপ, কাস্টম কমান্ডের অনুরোধ এবং প্লেলিস্ট পরিবর্তন করা । এই কলব্যাকগুলিতে একইভাবে একটি ControllerInfo অবজেক্ট অন্তর্ভুক্ত থাকে যাতে আপনি প্রতি-নিয়ন্ত্রকের ভিত্তিতে প্রতিটি অনুরোধে কীভাবে প্রতিক্রিয়া জানান তা আপনি সংশোধন করতে পারেন।

প্লেলিস্ট পরিবর্তন করুন

প্লেলিস্টের জন্য ExoPlayer গাইডে ব্যাখ্যা করা একটি মিডিয়া সেশন সরাসরি তার প্লেয়ারের প্লেলিস্ট পরিবর্তন করতে পারে। যদি COMMAND_SET_MEDIA_ITEM বা COMMAND_CHANGE_MEDIA_ITEMS কন্ট্রোলারের কাছে উপলব্ধ থাকে তাহলে কন্ট্রোলাররাও প্লেলিস্ট সংশোধন করতে সক্ষম৷

প্লেলিস্টে নতুন আইটেম যোগ করার সময়, প্লেয়ারের সাধারণত একটি সংজ্ঞায়িত URI সহ MediaItem দৃষ্টান্ত প্রয়োজন হয় যাতে সেগুলিকে প্লে করা যায়৷ ডিফল্টরূপে, নতুন যোগ করা আইটেমগুলি প্লেয়ার পদ্ধতিতে স্বয়ংক্রিয়ভাবে ফরোয়ার্ড হয় যেমন player.addMediaItem যদি তাদের একটি URI সংজ্ঞায়িত থাকে।

আপনি যদি প্লেয়ারে যোগ করা MediaItem দৃষ্টান্তগুলি কাস্টমাইজ করতে চান, তাহলে আপনি onAddMediaItems() ওভাররাইড করতে পারেন। এই পদক্ষেপটি প্রয়োজন হয় যখন আপনি একটি সংজ্ঞায়িত URI ছাড়া মিডিয়ার অনুরোধকারী নিয়ামকদের সমর্থন করতে চান। পরিবর্তে, MediaItem সাধারণত অনুরোধ করা মিডিয়া বর্ণনা করতে নিম্নলিখিত ক্ষেত্রগুলির মধ্যে এক বা একাধিক সেট থাকে:

  • MediaItem.id : একটি জেনেরিক আইডি যা মিডিয়াকে চিহ্নিত করে।
  • MediaItem.RequestMetadata.mediaUri : একটি অনুরোধ URI যা একটি কাস্টম স্কিমা ব্যবহার করতে পারে এবং প্লেয়ার দ্বারা সরাসরি খেলার যোগ্য নয়৷
  • MediaItem.RequestMetadata.searchQuery : একটি পাঠ্য অনুসন্ধান ক্যোয়ারী, উদাহরণস্বরূপ Google সহকারী থেকে।
  • MediaItem.MediaMetadata : কাঠামোবদ্ধ মেটাডেটা যেমন 'শিরোনাম' বা 'শিল্পী'।

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

মিডিয়া বোতাম পছন্দগুলি পরিচালনা করুন

প্রতিটি কন্ট্রোলার, উদাহরণস্বরূপ সিস্টেম UI, Android Auto, বা Wear OS, ব্যবহারকারীকে কোন বোতামগুলি দেখাতে হবে সে সম্পর্কে নিজস্ব সিদ্ধান্ত নিতে পারে৷ আপনি কোন প্লেব্যাক নিয়ন্ত্রণগুলি ব্যবহারকারীর কাছে প্রকাশ করতে চান তা নির্দেশ করতে, আপনি MediaSessionমিডিয়া বোতাম পছন্দগুলি নির্দিষ্ট করতে পারেন। এই পছন্দগুলির মধ্যে CommandButton দৃষ্টান্তগুলির একটি অর্ডার করা তালিকা থাকে, প্রতিটি ব্যবহারকারী ইন্টারফেসে একটি বোতামের জন্য একটি পছন্দ নির্ধারণ করে।

কমান্ড বোতাম সংজ্ঞায়িত করুন

CommandButton দৃষ্টান্ত মিডিয়া বোতাম পছন্দ সংজ্ঞায়িত করতে ব্যবহার করা হয়. প্রতিটি বোতাম পছন্দসই UI উপাদানের তিনটি দিক সংজ্ঞায়িত করে:

  1. আইকন , চাক্ষুষ চেহারা সংজ্ঞায়িত. CommandButton.Builder তৈরি করার সময় আইকনটিকে অবশ্যই পূর্বনির্ধারিত ধ্রুবকগুলির একটিতে সেট করতে হবে। মনে রাখবেন এটি একটি প্রকৃত বিটম্যাপ বা চিত্র সম্পদ নয়। একটি জেনেরিক ধ্রুবক কন্ট্রোলারদের তাদের নিজস্ব UI এর মধ্যে সামঞ্জস্যপূর্ণ চেহারা এবং অনুভূতির জন্য একটি উপযুক্ত সংস্থান চয়ন করতে সহায়তা করে। যদি পূর্বনির্ধারিত আইকন ধ্রুবকগুলির একটিও আপনার ব্যবহারের ক্ষেত্রে উপযুক্ত না হয় তবে আপনি পরিবর্তে setCustomIconResId ব্যবহার করতে পারেন।
  2. কমান্ড , ব্যবহারকারী যখন বোতামের সাথে ইন্টারঅ্যাক্ট করে তখন ট্রিগার হওয়া ক্রিয়া সংজ্ঞায়িত করে। আপনি একটি Player.Command এর জন্য setPlayerCommand , অথবা একটি পূর্বনির্ধারিত বা কাস্টম SessionCommand এর জন্য setSessionCommand ব্যবহার করতে পারেন।
  3. স্লট , কন্ট্রোলার UI-তে বোতামটি কোথায় রাখা উচিত তা নির্ধারণ করে। এই ক্ষেত্রটি ঐচ্ছিক এবং আইকন এবং কমান্ডের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে সেট করা হয়। উদাহরণস্বরূপ, এটি নির্দিষ্ট করার অনুমতি দেয় যে ডিফল্ট 'ওভারফ্লো' এলাকার পরিবর্তে UI এর 'ফরোয়ার্ড' নেভিগেশন এলাকায় একটি বোতাম প্রদর্শিত হবে।

কোটলিন

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

জাভা

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

মিডিয়া বোতাম পছন্দগুলি সমাধান করা হলে, নিম্নলিখিত অ্যালগরিদম প্রয়োগ করা হয়:

  1. মিডিয়া বোতাম পছন্দের প্রতিটি CommandButton জন্য, প্রথম উপলব্ধ এবং অনুমোদিত স্লটে বোতামটি রাখুন।
  2. যদি কেন্দ্রীয়, ফরোয়ার্ড এবং পশ্চাদগামী স্লটগুলির মধ্যে একটি বোতাম দিয়ে পূর্ণ না হয় তবে এই স্লটের জন্য ডিফল্ট বোতামগুলি যুক্ত করুন৷

আপনি CommandButton.DisplayConstraints ব্যবহার করতে পারেন কিভাবে UI প্রদর্শনের সীমাবদ্ধতার উপর নির্ভর করে মিডিয়া বোতাম পছন্দগুলি সমাধান করা হবে তার একটি পূর্বরূপ তৈরি করতে।

মিডিয়া বোতাম পছন্দ সেট করুন

মিডিয়া বোতাম পছন্দগুলি সেট করার সবচেয়ে সহজ উপায় হল MediaSession তৈরি করার সময় তালিকাটি সংজ্ঞায়িত করা। বিকল্পভাবে, আপনি প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য মিডিয়া বোতাম পছন্দগুলি কাস্টমাইজ করতে MediaSession.Callback.onConnect ওভাররাইড করতে পারেন৷

কোটলিন

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

জাভা

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

ব্যবহারকারীর ইন্টারঅ্যাকশনের পর মিডিয়া বোতামের পছন্দ আপডেট করুন

আপনার প্লেয়ারের সাথে একটি মিথস্ক্রিয়া পরিচালনা করার পরে, আপনি কন্ট্রোলার UI এ প্রদর্শিত বোতামগুলি আপডেট করতে চাইতে পারেন। একটি সাধারণ উদাহরণ হল একটি টগল বোতাম যা এই বোতামের সাথে যুক্ত ক্রিয়াটি ট্রিগার করার পরে এর আইকন এবং ক্রিয়া পরিবর্তন করে। মিডিয়া বোতাম পছন্দ আপডেট করতে, আপনি MediaSession.setMediaButtonPreferences ব্যবহার করতে পারেন সব কন্ট্রোলার বা একটি নির্দিষ্ট কন্ট্রোলারের পছন্দ আপডেট করতে:

কোটলিন

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

জাভা

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

কাস্টম কমান্ড যোগ করুন এবং ডিফল্ট আচরণ কাস্টমাইজ করুন

উপলব্ধ প্লেয়ার কমান্ডগুলি কাস্টম কমান্ড দ্বারা প্রসারিত করা যেতে পারে এবং ডিফল্ট আচরণ পরিবর্তন করতে ইনকামিং প্লেয়ার কমান্ড এবং মিডিয়া বোতামগুলিকে আটকানোও সম্ভব।

কাস্টম কমান্ড ঘোষণা এবং পরিচালনা করুন

মিডিয়া অ্যাপ্লিকেশনগুলি কাস্টম কমান্ডগুলিকে সংজ্ঞায়িত করতে পারে যা উদাহরণস্বরূপ মিডিয়া বোতাম পছন্দগুলিতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনি এমন বোতামগুলি প্রয়োগ করতে চাইতে পারেন যা ব্যবহারকারীকে পছন্দের আইটেমগুলির একটি তালিকায় একটি মিডিয়া আইটেম সংরক্ষণ করতে দেয়। MediaController কাস্টম কমান্ড পাঠায় এবং MediaSession.Callback সেগুলি গ্রহণ করে।

কাস্টম কমান্ড সংজ্ঞায়িত করার জন্য, প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য উপলব্ধ কাস্টম কমান্ড সেট করতে আপনাকে MediaSession.Callback.onConnect() ওভাররাইড করতে হবে।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

একটি MediaController থেকে কাস্টম কমান্ডের অনুরোধ পেতে, Callback onCustomCommand() পদ্ধতিটি ওভাররাইড করুন।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

আপনি MediaSession.ControllerInfo অবজেক্টের packageName প্রপার্টি ব্যবহার করে কোন মিডিয়া কন্ট্রোলার একটি অনুরোধ করছে তা ট্র্যাক করতে পারেন যা Callback পদ্ধতিতে পাস করা হয়। এটি আপনাকে একটি প্রদত্ত কমান্ডের প্রতিক্রিয়া হিসাবে আপনার অ্যাপের আচরণকে উপযোগী করার অনুমতি দেয় যদি এটি সিস্টেম, আপনার নিজের অ্যাপ বা অন্যান্য ক্লায়েন্ট অ্যাপ থেকে উদ্ভূত হয়।

ডিফল্ট প্লেয়ার কমান্ড কাস্টমাইজ করুন

সমস্ত ডিফল্ট কমান্ড এবং স্টেট হ্যান্ডলিং MediaSession থাকা Player কাছে অর্পণ করা হয়। Player ইন্টারফেসে সংজ্ঞায়িত একটি কমান্ডের আচরণ কাস্টমাইজ করতে, যেমন play() বা seekToNext() , আপনার Player MediaSession এ পাস করার আগে একটি ForwardingSimpleBasePlayer এ মুড়ে দিন:

কোটলিন

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

ForwardingSimpleBasePlayer সম্পর্কে আরও তথ্যের জন্য, কাস্টমাইজেশনের উপর ExoPlayer গাইড দেখুন।

একটি প্লেয়ার কমান্ডের অনুরোধকারী নিয়ামক সনাক্ত করুন

যখন একটি Player পদ্ধতিতে একটি কল একটি MediaController দ্বারা উদ্ভূত হয়, তখন আপনি MediaSession.controllerForCurrentRequest এর মাধ্যমে উত্সের উত্স সনাক্ত করতে পারেন এবং বর্তমান অনুরোধের জন্য ControllerInfo অর্জন করতে পারেন:

কোটলিন

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

জাভা

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

মিডিয়া বোতাম হ্যান্ডলিং কাস্টমাইজ করুন

মিডিয়া বোতাম হল হার্ডওয়্যার বোতাম যা অ্যান্ড্রয়েড ডিভাইস এবং অন্যান্য পেরিফেরাল ডিভাইসে পাওয়া যায়, যেমন একটি ব্লুটুথ হেডসেটে প্লে/পজ বোতাম। Media3 আপনার জন্য মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করে যখন তারা সেশনে আসে এবং সেশন প্লেয়ারে উপযুক্ত Player পদ্ধতিতে কল করে৷

সংশ্লিষ্ট Player পদ্ধতিতে সমস্ত আগত মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করার পরামর্শ দেওয়া হয়। আরও উন্নত ব্যবহারের ক্ষেত্রে, মিডিয়া বোতাম ইভেন্টগুলিকে MediaSession.Callback.onMediaButtonEvent(Intent) এ আটকানো যেতে পারে।

ত্রুটি হ্যান্ডলিং এবং রিপোর্টিং

একটি অধিবেশন নির্গত এবং নিয়ন্ত্রকদের রিপোর্ট যে দুটি ধরনের ত্রুটি আছে. মারাত্মক ত্রুটিগুলি সেশন প্লেয়ারের একটি প্রযুক্তিগত প্লেব্যাক ব্যর্থতার রিপোর্ট করে যা প্লেব্যাকে বাধা দেয়। মারাত্মক ত্রুটিগুলি যখন ঘটে তখন স্বয়ংক্রিয়ভাবে নিয়ামককে রিপোর্ট করা হয়। অপ্রয়োজনীয় ত্রুটিগুলি অ-প্রযুক্তিগত বা নীতিগত ত্রুটি যা প্লেব্যাকে বাধা দেয় না এবং ম্যানুয়ালি অ্যাপ্লিকেশন দ্বারা নিয়ন্ত্রকদের কাছে পাঠানো হয়।

মারাত্মক প্লেব্যাক ত্রুটি

প্লেয়ারের দ্বারা সেশনে একটি মারাত্মক প্লেব্যাক ত্রুটি রিপোর্ট করা হয় এবং তারপরে Player.Listener.onPlayerError(PlaybackException) এবং Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) এর মাধ্যমে কল করার জন্য কন্ট্রোলারদের রিপোর্ট করা হয়।

এই ধরনের ক্ষেত্রে, প্লেব্যাক স্টেটটি STATE_IDLE তে স্থানান্তরিত হয় এবং MediaController.getPlaybackError() PlaybackException প্রদান করে যা স্থানান্তর ঘটায়। ত্রুটির কারণ সম্পর্কে তথ্য পেতে একজন নিয়ামক PlayerException.errorCode পরিদর্শন করতে পারেন।

ইন্টারঅপারেবিলিটির জন্য, একটি মারাত্মক ত্রুটি প্ল্যাটফর্ম সেশনে প্রতিলিপি করা হয় তার অবস্থা STATE_ERROR এ রূপান্তর করে এবং PlaybackException অনুযায়ী ত্রুটি কোড এবং বার্তা সেট করে।

মারাত্মক ত্রুটির কাস্টমাইজেশন

ব্যবহারকারীকে স্থানীয় এবং অর্থপূর্ণ তথ্য প্রদানের জন্য, একটি মারাত্মক প্লেব্যাক ত্রুটির ত্রুটি কোড, ত্রুটি বার্তা এবং ত্রুটি অতিরিক্তগুলি সেশন তৈরি করার সময় একটি ForwardingPlayer ব্যবহার করে কাস্টমাইজ করা যেতে পারে:

কোটলিন

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

ফরওয়ার্ডিং প্লেয়ার ForwardingSimpleBasePlayer ব্যবহার করে ত্রুটিটি আটকাতে এবং ত্রুটি কোড, বার্তা বা অতিরিক্ত কাস্টমাইজ করতে পারে। একইভাবে, আপনি নতুন ত্রুটিও তৈরি করতে পারেন যা আসল প্লেয়ারে বিদ্যমান নেই:

কোটলিন

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

জাভা

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

অপ্রত্যাশিত ত্রুটি

প্রযুক্তিগত ব্যতিক্রম থেকে উদ্ভূত নয় এমন অপ্রত্যাশিত ত্রুটিগুলি একটি অ্যাপ দ্বারা সকলকে বা একটি নির্দিষ্ট নিয়ামকের কাছে পাঠানো যেতে পারে:

কোটলিন

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

জাভা

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

যখন একটি অপ্রত্যাশিত ত্রুটি মিডিয়া বিজ্ঞপ্তি কন্ট্রোলারে পাঠানো হয়, তখন ত্রুটি কোড এবং ত্রুটি বার্তাটি প্ল্যাটফর্ম মিডিয়া সেশনে প্রতিলিপি করা হয়, যখন PlaybackState.state STATE_ERROR এ পরিবর্তিত হয় না।

মারাত্মক ত্রুটিগুলি পান

একটি MediaController MediaController.Listener.onError প্রয়োগ করে একটি অপ্রত্যাশিত ত্রুটি পায়:

কোটলিন

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

জাভা

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });

,

মিডিয়া সেশনগুলি একটি অডিও বা ভিডিও প্লেয়ারের সাথে ইন্টারঅ্যাক্ট করার একটি সর্বজনীন উপায় প্রদান করে। Media3 এ, ডিফল্ট প্লেয়ার হল ExoPlayer ক্লাস, যা Player ইন্টারফেস প্রয়োগ করে। প্লেয়ারের সাথে মিডিয়া সেশন সংযুক্ত করা একটি অ্যাপকে বাহ্যিকভাবে মিডিয়া প্লেব্যাকের বিজ্ঞাপন দিতে এবং বাহ্যিক উত্স থেকে প্লেব্যাক কমান্ড পেতে অনুমতি দেয়৷

কমান্ডগুলি হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতামের মতো শারীরিক বোতামগুলি থেকে উদ্ভূত হতে পারে। এগুলি ক্লায়েন্ট অ্যাপ থেকেও আসতে পারে যেগুলির একটি মিডিয়া কন্ট্রোলার রয়েছে, যেমন Google সহকারীকে "পজ" নির্দেশ দেওয়া। মিডিয়া সেশন এই কমান্ডগুলি মিডিয়া অ্যাপের প্লেয়ারে অর্পণ করে।

কখন মিডিয়া সেশন বেছে নেবেন

আপনি যখন MediaSession প্রয়োগ করেন, আপনি ব্যবহারকারীদের প্লেব্যাক নিয়ন্ত্রণ করার অনুমতি দেন:

  • তাদের হেডফোনের মাধ্যমে। মিডিয়া প্লে বা পজ করতে বা পরবর্তী বা পূর্ববর্তী ট্র্যাকে যাওয়ার জন্য ব্যবহারকারীরা তাদের হেডফোনে প্রায়শই বোতাম বা টাচ ইন্টারঅ্যাকশন করতে পারেন।
  • গুগল সহকারীর সাথে কথা বলে। একটি সাধারণ প্যাটার্ন হল "ওকে গুগল, পজ" বলার জন্য যে কোনো মিডিয়া বর্তমানে ডিভাইসে চলছে।
  • তাদের Wear OS ঘড়ির মাধ্যমে। এটি তাদের ফোনে খেলার সময় সবচেয়ে সাধারণ প্লেব্যাক নিয়ন্ত্রণগুলিতে সহজে অ্যাক্সেসের অনুমতি দেয়।
  • মিডিয়া নিয়ন্ত্রণের মাধ্যমে। এই ক্যারাউজেল প্রতিটি চলমান মিডিয়া সেশনের জন্য নিয়ন্ত্রণ দেখায়।
  • টিভিতে । ফিজিক্যাল প্লেব্যাক বোতাম, প্ল্যাটফর্ম প্লেব্যাক কন্ট্রোল এবং পাওয়ার ম্যানেজমেন্ট সহ অ্যাকশনের অনুমতি দেয় (উদাহরণস্বরূপ যদি টিভি, সাউন্ডবার বা A/V রিসিভার বন্ধ হয়ে যায় বা ইনপুট সুইচ করা হয়, প্লেব্যাক অ্যাপে বন্ধ করা উচিত)।
  • এবং অন্য কোনো বাহ্যিক প্রক্রিয়া যা প্লেব্যাককে প্রভাবিত করতে হবে।

এটি অনেক ব্যবহারের ক্ষেত্রে দুর্দান্ত। বিশেষ করে, আপনার MediaSession ব্যবহার করার বিষয়ে দৃঢ়ভাবে বিবেচনা করা উচিত যখন:

  • আপনি সিনেমা বা লাইভ টিভির মতো দীর্ঘ-ফর্মের ভিডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি পডকাস্ট বা সঙ্গীত প্লেলিস্টের মতো দীর্ঘ-ফর্মের অডিও সামগ্রী স্ট্রিম করছেন৷
  • আপনি একটি টিভি অ্যাপ তৈরি করছেন।

যাইহোক, সমস্ত ব্যবহারের ক্ষেত্রে MediaSession সাথে ভালভাবে মানানসই নয়। আপনি নিম্নলিখিত ক্ষেত্রে শুধুমাত্র Player ব্যবহার করতে চাইতে পারেন:

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

যদি আপনার ব্যবহারের ক্ষেত্রে উপরে তালিকাভুক্ত কোনটির সাথে মানানসই না হয়, তাহলে ব্যবহারকারী যখন সক্রিয়ভাবে বিষয়বস্তুর সাথে জড়িত না হয় তখন আপনার অ্যাপ চালিয়ে যাওয়া প্লেব্যাক আপনি ঠিক আছেন কিনা তা বিবেচনা করুন। উত্তর যদি হ্যাঁ হয়, আপনি সম্ভবত MediaSession বেছে নিতে চান। উত্তর না হলে, আপনি সম্ভবত Player ব্যবহার করতে চান।

একটি মিডিয়া সেশন তৈরি করুন

একটি মিডিয়া সেশন প্লেয়ারের সাথে থাকে যা এটি পরিচালনা করে। আপনি একটি Context এবং একটি Player অবজেক্ট সহ একটি মিডিয়া সেশন তৈরি করতে পারেন। আপনার প্রয়োজন হলে একটি মিডিয়া সেশন তৈরি এবং আরম্ভ করা উচিত, যেমন onStart() বা onResume() লাইফসাইকেল পদ্ধতি Activity বা Fragment , অথবা Service onCreate() পদ্ধতি যা মিডিয়া সেশন এবং এর সাথে সম্পর্কিত প্লেয়ারের মালিক।

একটি মিডিয়া সেশন তৈরি করতে, একটি Player শুরু করুন এবং এটিকে MediaSession.Builder এ এইভাবে সরবরাহ করুন:

কোটলিন

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

জাভা

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

স্বয়ংক্রিয় রাষ্ট্র পরিচালনা

Media3 লাইব্রেরি স্বয়ংক্রিয়ভাবে প্লেয়ারের অবস্থা ব্যবহার করে মিডিয়া সেশন আপডেট করে। যেমন, আপনাকে প্লেয়ার থেকে সেশনে ম্যাপিং ম্যানুয়ালি পরিচালনা করতে হবে না।

এটি প্ল্যাটফর্ম মিডিয়া সেশন থেকে ভিন্ন যেখানে আপনাকে PlaybackState তৈরি এবং বজায় রাখার প্রয়োজন ছিল প্লেয়ার থেকে স্বাধীনভাবে, উদাহরণস্বরূপ কোনো ত্রুটি নির্দেশ করার জন্য।

অনন্য সেশন আইডি

ডিফল্টরূপে, MediaSession.Builder সেশন আইডি হিসাবে একটি খালি স্ট্রিং সহ একটি সেশন তৈরি করে। এটি যথেষ্ট যদি একটি অ্যাপ শুধুমাত্র একটি একক সেশনের উদাহরণ তৈরি করতে চায়, যা সবচেয়ে সাধারণ ক্ষেত্রে।

যদি একটি অ্যাপ একই সময়ে একাধিক সেশন ইনস্ট্যান্স পরিচালনা করতে চায়, তবে অ্যাপটিকে নিশ্চিত করতে হবে যে প্রতিটি সেশনের সেশন আইডি অনন্য। MediaSession.Builder.setId(String id) দিয়ে সেশন তৈরি করার সময় সেশন আইডি সেট করা যেতে পারে।

আপনি যদি দেখেন একটি IllegalStateException ত্রুটি বার্তা সহ আপনার অ্যাপ ক্র্যাশ করছে IllegalStateException: Session ID must be unique. ID= তাহলে সম্ভবত একই আইডি সহ পূর্বে তৈরি করা ইন্সট্যান্স রিলিজ হওয়ার আগে একটি সেশন অপ্রত্যাশিতভাবে তৈরি হয়েছে। একটি প্রোগ্রামিং ত্রুটি দ্বারা ফাঁস করা সেশন এড়াতে, এই ধরনের কেস সনাক্ত করা হয় এবং একটি ব্যতিক্রম নিক্ষেপ করে বিজ্ঞপ্তি দেওয়া হয়।

অন্যান্য ক্লায়েন্টদের নিয়ন্ত্রণ মঞ্জুর করুন

মিডিয়া সেশন প্লেব্যাক নিয়ন্ত্রণের চাবিকাঠি। এটি আপনাকে বাহ্যিক উত্স থেকে প্লেয়ারের কাছে কমান্ড রুট করতে সক্ষম করে যা আপনার মিডিয়া চালানোর কাজ করে। এই উত্সগুলি হতে পারে ফিজিক্যাল বোতাম যেমন হেডসেট বা টিভি রিমোট কন্ট্রোলের প্লে বোতাম বা পরোক্ষ কমান্ড যেমন Google অ্যাসিস্ট্যান্টকে "পজ" নির্দেশ দেওয়া। একইভাবে, আপনি বিজ্ঞপ্তি এবং লক স্ক্রিন নিয়ন্ত্রণের সুবিধার্থে অ্যান্ড্রয়েড সিস্টেমে বা Wear OS ঘড়িতে অ্যাক্সেস দিতে চাইতে পারেন যাতে আপনি ওয়াচফেস থেকে প্লেব্যাক নিয়ন্ত্রণ করতে পারেন। বাহ্যিক ক্লায়েন্টরা আপনার মিডিয়া অ্যাপে প্লেব্যাক কমান্ড ইস্যু করতে একটি মিডিয়া কন্ট্রোলার ব্যবহার করতে পারে। এগুলি আপনার মিডিয়া সেশন দ্বারা গৃহীত হয়, যা শেষ পর্যন্ত মিডিয়া প্লেয়ারকে কমান্ড অর্পণ করে।

একটি ডায়াগ্রাম যা একটি MediaSession এবং MediaController এর মধ্যে মিথস্ক্রিয়া প্রদর্শন করে।
চিত্র 1 : মিডিয়া কন্ট্রোলার বাহ্যিক উত্স থেকে মিডিয়া সেশনে কমান্ড পাস করার সুবিধা দেয়৷

যখন একটি নিয়ামক আপনার মিডিয়া সেশনের সাথে সংযোগ করতে চলেছে, onConnect() পদ্ধতিটি বলা হয়। অনুরোধ গ্রহণ বা প্রত্যাখ্যান করার সিদ্ধান্ত নিতে আপনি প্রদত্ত ControllerInfo ব্যবহার করতে পারেন। কাস্টম কমান্ড ঘোষণা বিভাগে সংযোগের অনুরোধ গ্রহণ করার একটি উদাহরণ দেখুন।

সংযোগ করার পরে, একটি নিয়ামক সেশনে প্লেব্যাক কমান্ড পাঠাতে পারে। সেশন তারপর সেই কমান্ডগুলি প্লেয়ারের কাছে অর্পণ করে। Player ইন্টারফেসে সংজ্ঞায়িত প্লেব্যাক এবং প্লেলিস্ট কমান্ডগুলি সেশন দ্বারা স্বয়ংক্রিয়ভাবে পরিচালনা করা হয়।

অন্যান্য কলব্যাক পদ্ধতি আপনাকে পরিচালনা করতে দেয়, উদাহরণস্বরূপ, কাস্টম কমান্ডের অনুরোধ এবং প্লেলিস্ট পরিবর্তন করা । এই কলব্যাকগুলিতে একইভাবে একটি ControllerInfo অবজেক্ট অন্তর্ভুক্ত থাকে যাতে আপনি প্রতি-নিয়ন্ত্রকের ভিত্তিতে প্রতিটি অনুরোধে কীভাবে প্রতিক্রিয়া জানান তা আপনি সংশোধন করতে পারেন।

প্লেলিস্ট পরিবর্তন করুন

প্লেলিস্টের জন্য ExoPlayer গাইডে ব্যাখ্যা করা একটি মিডিয়া সেশন সরাসরি তার প্লেয়ারের প্লেলিস্ট পরিবর্তন করতে পারে। যদি COMMAND_SET_MEDIA_ITEM বা COMMAND_CHANGE_MEDIA_ITEMS কন্ট্রোলারের কাছে উপলব্ধ থাকে তাহলে কন্ট্রোলাররাও প্লেলিস্ট সংশোধন করতে সক্ষম৷

প্লেলিস্টে নতুন আইটেম যোগ করার সময়, প্লেয়ারের সাধারণত একটি সংজ্ঞায়িত URI সহ MediaItem দৃষ্টান্ত প্রয়োজন হয় যাতে সেগুলিকে প্লে করা যায়৷ ডিফল্টরূপে, নতুন যোগ করা আইটেমগুলি প্লেয়ার পদ্ধতিতে স্বয়ংক্রিয়ভাবে ফরোয়ার্ড হয় যেমন player.addMediaItem যদি তাদের একটি URI সংজ্ঞায়িত থাকে।

আপনি যদি প্লেয়ারে যোগ করা MediaItem দৃষ্টান্তগুলি কাস্টমাইজ করতে চান, তাহলে আপনি onAddMediaItems() ওভাররাইড করতে পারেন। এই পদক্ষেপটি প্রয়োজন হয় যখন আপনি একটি সংজ্ঞায়িত URI ছাড়া মিডিয়ার অনুরোধকারী নিয়ামকদের সমর্থন করতে চান। পরিবর্তে, MediaItem সাধারণত অনুরোধ করা মিডিয়া বর্ণনা করতে নিম্নলিখিত ক্ষেত্রগুলির মধ্যে এক বা একাধিক সেট থাকে:

  • MediaItem.id : একটি জেনেরিক আইডি যা মিডিয়াকে চিহ্নিত করে।
  • MediaItem.RequestMetadata.mediaUri : একটি অনুরোধ URI যা একটি কাস্টম স্কিমা ব্যবহার করতে পারে এবং প্লেয়ার দ্বারা সরাসরি খেলার যোগ্য নয়৷
  • MediaItem.RequestMetadata.searchQuery : একটি পাঠ্য অনুসন্ধান ক্যোয়ারী, উদাহরণস্বরূপ Google সহকারী থেকে।
  • MediaItem.MediaMetadata : কাঠামোবদ্ধ মেটাডেটা যেমন 'শিরোনাম' বা 'শিল্পী'।

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

মিডিয়া বোতাম পছন্দগুলি পরিচালনা করুন

প্রতিটি কন্ট্রোলার, উদাহরণস্বরূপ সিস্টেম UI, Android Auto, বা Wear OS, ব্যবহারকারীকে কোন বোতামগুলি দেখাতে হবে সে সম্পর্কে নিজস্ব সিদ্ধান্ত নিতে পারে৷ আপনি কোন প্লেব্যাক নিয়ন্ত্রণগুলি ব্যবহারকারীর কাছে প্রকাশ করতে চান তা নির্দেশ করতে, আপনি MediaSessionমিডিয়া বোতাম পছন্দগুলি নির্দিষ্ট করতে পারেন। এই পছন্দগুলির মধ্যে CommandButton দৃষ্টান্তগুলির একটি অর্ডার করা তালিকা থাকে, প্রতিটি ব্যবহারকারী ইন্টারফেসে একটি বোতামের জন্য একটি পছন্দ নির্ধারণ করে।

কমান্ড বোতাম সংজ্ঞায়িত করুন

CommandButton দৃষ্টান্ত মিডিয়া বোতাম পছন্দ সংজ্ঞায়িত করতে ব্যবহার করা হয়. প্রতিটি বোতাম পছন্দসই UI উপাদানের তিনটি দিক সংজ্ঞায়িত করে:

  1. আইকন , চাক্ষুষ চেহারা সংজ্ঞায়িত. CommandButton.Builder তৈরি করার সময় আইকনটিকে অবশ্যই পূর্বনির্ধারিত ধ্রুবকগুলির একটিতে সেট করতে হবে। মনে রাখবেন এটি একটি প্রকৃত বিটম্যাপ বা চিত্র সম্পদ নয়। একটি জেনেরিক ধ্রুবক কন্ট্রোলারদের তাদের নিজস্ব UI এর মধ্যে সামঞ্জস্যপূর্ণ চেহারা এবং অনুভূতির জন্য একটি উপযুক্ত সংস্থান চয়ন করতে সহায়তা করে। যদি পূর্বনির্ধারিত আইকন ধ্রুবকগুলির একটিও আপনার ব্যবহারের ক্ষেত্রে উপযুক্ত না হয় তবে আপনি পরিবর্তে setCustomIconResId ব্যবহার করতে পারেন।
  2. কমান্ড , ব্যবহারকারী যখন বোতামের সাথে ইন্টারঅ্যাক্ট করে তখন ট্রিগার হওয়া ক্রিয়া সংজ্ঞায়িত করে। আপনি একটি Player.Command এর জন্য setPlayerCommand , অথবা একটি পূর্বনির্ধারিত বা কাস্টম SessionCommand এর জন্য setSessionCommand ব্যবহার করতে পারেন।
  3. স্লট , কন্ট্রোলার UI-তে বোতামটি কোথায় রাখা উচিত তা নির্ধারণ করে। এই ক্ষেত্রটি ঐচ্ছিক এবং আইকন এবং কমান্ডের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে সেট করা হয়। উদাহরণস্বরূপ, এটি নির্দিষ্ট করার অনুমতি দেয় যে ডিফল্ট 'ওভারফ্লো' এলাকার পরিবর্তে UI এর 'ফরোয়ার্ড' নেভিগেশন এলাকায় একটি বোতাম প্রদর্শিত হবে।

কোটলিন

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

জাভা

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

মিডিয়া বোতাম পছন্দগুলি সমাধান করা হলে, নিম্নলিখিত অ্যালগরিদম প্রয়োগ করা হয়:

  1. মিডিয়া বোতাম পছন্দের প্রতিটি CommandButton জন্য, প্রথম উপলব্ধ এবং অনুমোদিত স্লটে বোতামটি রাখুন।
  2. যদি কেন্দ্রীয়, ফরোয়ার্ড এবং পশ্চাদগামী স্লটগুলির মধ্যে একটি বোতাম দিয়ে পূর্ণ না হয় তবে এই স্লটের জন্য ডিফল্ট বোতামগুলি যুক্ত করুন৷

আপনি CommandButton.DisplayConstraints ব্যবহার করতে পারেন কিভাবে UI প্রদর্শনের সীমাবদ্ধতার উপর নির্ভর করে মিডিয়া বোতাম পছন্দগুলি সমাধান করা হবে তার একটি পূর্বরূপ তৈরি করতে।

মিডিয়া বোতাম পছন্দ সেট করুন

মিডিয়া বোতাম পছন্দগুলি সেট করার সবচেয়ে সহজ উপায় হল MediaSession তৈরি করার সময় তালিকাটি সংজ্ঞায়িত করা। বিকল্পভাবে, আপনি প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য মিডিয়া বোতাম পছন্দগুলি কাস্টমাইজ করতে MediaSession.Callback.onConnect ওভাররাইড করতে পারেন৷

কোটলিন

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

জাভা

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

ব্যবহারকারীর ইন্টারঅ্যাকশনের পর মিডিয়া বোতামের পছন্দ আপডেট করুন

আপনার প্লেয়ারের সাথে একটি মিথস্ক্রিয়া পরিচালনা করার পরে, আপনি কন্ট্রোলার UI এ প্রদর্শিত বোতামগুলি আপডেট করতে চাইতে পারেন। একটি সাধারণ উদাহরণ হল একটি টগল বোতাম যা এই বোতামের সাথে যুক্ত ক্রিয়াটি ট্রিগার করার পরে এর আইকন এবং ক্রিয়া পরিবর্তন করে। মিডিয়া বোতাম পছন্দ আপডেট করতে, আপনি MediaSession.setMediaButtonPreferences ব্যবহার করতে পারেন সব কন্ট্রোলার বা একটি নির্দিষ্ট কন্ট্রোলারের পছন্দ আপডেট করতে:

কোটলিন

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

জাভা

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

কাস্টম কমান্ড যোগ করুন এবং ডিফল্ট আচরণ কাস্টমাইজ করুন

উপলব্ধ প্লেয়ার কমান্ডগুলি কাস্টম কমান্ড দ্বারা প্রসারিত করা যেতে পারে এবং ডিফল্ট আচরণ পরিবর্তন করতে ইনকামিং প্লেয়ার কমান্ড এবং মিডিয়া বোতামগুলিকে আটকানোও সম্ভব।

কাস্টম কমান্ড ঘোষণা এবং পরিচালনা করুন

মিডিয়া অ্যাপ্লিকেশনগুলি কাস্টম কমান্ডগুলিকে সংজ্ঞায়িত করতে পারে যা উদাহরণস্বরূপ মিডিয়া বোতাম পছন্দগুলিতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনি এমন বোতামগুলি প্রয়োগ করতে চাইতে পারেন যা ব্যবহারকারীকে পছন্দের আইটেমগুলির একটি তালিকায় একটি মিডিয়া আইটেম সংরক্ষণ করতে দেয়। MediaController কাস্টম কমান্ড পাঠায় এবং MediaSession.Callback সেগুলি গ্রহণ করে।

কাস্টম কমান্ড সংজ্ঞায়িত করার জন্য, প্রতিটি সংযুক্ত কন্ট্রোলারের জন্য উপলব্ধ কাস্টম কমান্ড সেট করতে আপনাকে MediaSession.Callback.onConnect() ওভাররাইড করতে হবে।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): MediaSession.ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

একটি MediaController থেকে কাস্টম কমান্ডের অনুরোধ পেতে, Callback onCustomCommand() পদ্ধতিটি ওভাররাইড করুন।

কোটলিন

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

জাভা

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

আপনি MediaSession.ControllerInfo অবজেক্টের packageName প্রপার্টি ব্যবহার করে কোন মিডিয়া কন্ট্রোলার একটি অনুরোধ করছে তা ট্র্যাক করতে পারেন যা Callback পদ্ধতিতে পাস করা হয়। এটি আপনাকে একটি প্রদত্ত কমান্ডের প্রতিক্রিয়া হিসাবে আপনার অ্যাপের আচরণকে উপযোগী করার অনুমতি দেয় যদি এটি সিস্টেম, আপনার নিজের অ্যাপ বা অন্যান্য ক্লায়েন্ট অ্যাপ থেকে উদ্ভূত হয়।

ডিফল্ট প্লেয়ার কমান্ড কাস্টমাইজ করুন

সমস্ত ডিফল্ট কমান্ড এবং স্টেট হ্যান্ডলিং MediaSession থাকা Player কাছে অর্পণ করা হয়। Player ইন্টারফেসে সংজ্ঞায়িত একটি কমান্ডের আচরণ কাস্টমাইজ করতে, যেমন play() বা seekToNext() , আপনার Player MediaSession এ পাস করার আগে একটি ForwardingSimpleBasePlayer এ মুড়ে দিন:

কোটলিন

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

ForwardingSimpleBasePlayer সম্পর্কে আরও তথ্যের জন্য, কাস্টমাইজেশনের উপর ExoPlayer গাইড দেখুন।

একটি প্লেয়ার কমান্ডের অনুরোধকারী নিয়ামক সনাক্ত করুন

যখন একটি Player পদ্ধতিতে একটি কল একটি MediaController দ্বারা উদ্ভূত হয়, তখন আপনি MediaSession.controllerForCurrentRequest এর মাধ্যমে উত্সের উত্স সনাক্ত করতে পারেন এবং বর্তমান অনুরোধের জন্য ControllerInfo অর্জন করতে পারেন:

কোটলিন

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

জাভা

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

মিডিয়া বোতাম হ্যান্ডলিং কাস্টমাইজ করুন

মিডিয়া বোতাম হল হার্ডওয়্যার বোতাম যা অ্যান্ড্রয়েড ডিভাইস এবং অন্যান্য পেরিফেরাল ডিভাইসে পাওয়া যায়, যেমন একটি ব্লুটুথ হেডসেটে প্লে/পজ বোতাম। Media3 আপনার জন্য মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করে যখন তারা সেশনে আসে এবং সেশন প্লেয়ারে উপযুক্ত Player পদ্ধতিতে কল করে৷

সংশ্লিষ্ট Player পদ্ধতিতে সমস্ত আগত মিডিয়া বোতাম ইভেন্টগুলি পরিচালনা করার পরামর্শ দেওয়া হয়। আরও উন্নত ব্যবহারের ক্ষেত্রে, মিডিয়া বোতাম ইভেন্টগুলিকে MediaSession.Callback.onMediaButtonEvent(Intent) এ আটকানো যেতে পারে।

ত্রুটি হ্যান্ডলিং এবং রিপোর্টিং

একটি অধিবেশন নির্গত এবং নিয়ন্ত্রকদের রিপোর্ট যে দুটি ধরনের ত্রুটি আছে. মারাত্মক ত্রুটিগুলি সেশন প্লেয়ারের একটি প্রযুক্তিগত প্লেব্যাক ব্যর্থতার রিপোর্ট করে যা প্লেব্যাকে বাধা দেয়। মারাত্মক ত্রুটিগুলি যখন ঘটে তখন স্বয়ংক্রিয়ভাবে নিয়ামককে রিপোর্ট করা হয়। অপ্রয়োজনীয় ত্রুটিগুলি অ-প্রযুক্তিগত বা নীতিগত ত্রুটি যা প্লেব্যাকে বাধা দেয় না এবং ম্যানুয়ালি অ্যাপ্লিকেশন দ্বারা নিয়ন্ত্রকদের কাছে পাঠানো হয়।

মারাত্মক প্লেব্যাক ত্রুটি

প্লেয়ারের দ্বারা সেশনে একটি মারাত্মক প্লেব্যাক ত্রুটি রিপোর্ট করা হয় এবং তারপরে Player.Listener.onPlayerError(PlaybackException) এবং Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) এর মাধ্যমে কল করার জন্য কন্ট্রোলারদের রিপোর্ট করা হয়।

এই ধরনের ক্ষেত্রে, প্লেব্যাক স্টেটটি STATE_IDLE তে স্থানান্তরিত হয় এবং MediaController.getPlaybackError() PlaybackException প্রদান করে যা স্থানান্তর ঘটায়। ত্রুটির কারণ সম্পর্কে তথ্য পেতে একজন নিয়ামক PlayerException.errorCode পরিদর্শন করতে পারেন।

ইন্টারঅপারেবিলিটির জন্য, একটি মারাত্মক ত্রুটি প্ল্যাটফর্ম সেশনে প্রতিলিপি করা হয় তার অবস্থা STATE_ERROR এ রূপান্তর করে এবং PlaybackException অনুযায়ী ত্রুটি কোড এবং বার্তা সেট করে।

মারাত্মক ত্রুটির কাস্টমাইজেশন

ব্যবহারকারীকে স্থানীয় এবং অর্থপূর্ণ তথ্য প্রদানের জন্য, একটি মারাত্মক প্লেব্যাক ত্রুটির ত্রুটি কোড, ত্রুটি বার্তা এবং ত্রুটি অতিরিক্তগুলি সেশন তৈরি করার সময় একটি ForwardingPlayer ব্যবহার করে কাস্টমাইজ করা যেতে পারে:

কোটলিন

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

জাভা

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

ফরওয়ার্ডিং প্লেয়ার ForwardingSimpleBasePlayer ব্যবহার করে ত্রুটিটি আটকাতে এবং ত্রুটি কোড, বার্তা বা অতিরিক্ত কাস্টমাইজ করতে পারে। একইভাবে, আপনি নতুন ত্রুটিও তৈরি করতে পারেন যা আসল প্লেয়ারে বিদ্যমান নেই:

কোটলিন

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

জাভা

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

অপ্রত্যাশিত ত্রুটি

প্রযুক্তিগত ব্যতিক্রম থেকে উদ্ভূত নয় এমন অপ্রত্যাশিত ত্রুটিগুলি একটি অ্যাপ দ্বারা সকলকে বা একটি নির্দিষ্ট নিয়ামকের কাছে পাঠানো যেতে পারে:

কোটলিন

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

জাভা

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

যখন একটি অপ্রত্যাশিত ত্রুটি মিডিয়া বিজ্ঞপ্তি কন্ট্রোলারে পাঠানো হয়, তখন ত্রুটি কোড এবং ত্রুটি বার্তাটি প্ল্যাটফর্ম মিডিয়া সেশনে প্রতিলিপি করা হয়, যখন PlaybackState.state STATE_ERROR এ পরিবর্তিত হয় না।

মারাত্মক ত্রুটিগুলি পান

একটি MediaController MediaController.Listener.onError প্রয়োগ করে একটি অপ্রত্যাশিত ত্রুটি পায়:

কোটলিন

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

জাভা

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });