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

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

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

কখন মিডিয়া সেশন নির্বাচন করবেন

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

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

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

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

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

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

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

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

একটি মিডিয়া সেশন সেই প্লেয়ারের সাথে থাকে যা এটি পরিচালনা করে। আপনি একটি Context এবং একটি Player অবজেক্ট দিয়ে একটি মিডিয়া সেশন তৈরি করতে পারেন। প্রয়োজনে আপনার একটি মিডিয়া সেশন তৈরি এবং আরম্ভ করা উচিত, যেমন Activity বা Fragment এর onStart() বা onResume() লাইফসাইকেল পদ্ধতি, অথবা মিডিয়া সেশন এবং এর সাথে সম্পর্কিত প্লেয়ারের মালিকানাধীন 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= ত্রুটি বার্তা সহ ক্র্যাশ করছে, তাহলে সম্ভবত একই আইডি সহ পূর্বে তৈরি করা একটি ইনস্ট্যান্স প্রকাশের আগে একটি সেশন অপ্রত্যাশিতভাবে তৈরি করা হয়েছে। প্রোগ্রামিং ত্রুটির মাধ্যমে সেশন ফাঁস হওয়া এড়াতে, এই ধরনের ঘটনা সনাক্ত করা হয় এবং একটি ব্যতিক্রম নিক্ষেপ করে অবহিত করা হয়।

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

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

একটি মিডিয়াসেশন এবং মিডিয়াকন্ট্রোলারের মধ্যে মিথস্ক্রিয়া প্রদর্শনকারী একটি চিত্র।
চিত্র ১ : মিডিয়া কন্ট্রোলার বাহ্যিক উৎস থেকে মিডিয়া সেশনে কমান্ড প্রেরণের সুবিধা প্রদান করে।

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

সংযোগ স্থাপনের পর, একটি নিয়ামক সেশনে প্লেব্যাক কমান্ড পাঠাতে পারে। এরপর সেশনটি সেই কমান্ডগুলি প্লেয়ারের কাছে অর্পণ করে। 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 : একটি টেক্সটুয়াল সার্চ কোয়েরি, উদাহরণস্বরূপ গুগল অ্যাসিস্ট্যান্ট থেকে।
  • MediaItem.MediaMetadata : 'শিরোনাম' বা 'শিল্পী'-এর মতো স্ট্রাকচার্ড মেটাডেটা।

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

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

প্রতিটি কন্ট্রোলার, উদাহরণস্বরূপ System 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. যদি কেন্দ্রীয়, সামনের এবং পিছনের কোনও স্লটে বোতাম না থাকে, তাহলে এই স্লটের জন্য ডিফল্ট বোতাম যোগ করুন।

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

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

মিডিয়া বোতামের পছন্দগুলি সেট করার সবচেয়ে সহজ উপায় হল 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
  ): 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)
      );
    }
    ...
  }
}

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

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

সকল ডিফল্ট কমান্ড এবং স্টেট হ্যান্ডলিং 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);
  }
}

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

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

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

ত্রুটি পরিচালনা এবং প্রতিবেদন করা

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

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

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

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

একটি কাস্টম প্লেয়ার ত্রুটি সেট করা হচ্ছে

প্লেয়ার দ্বারা রিপোর্ট করা মারাত্মক ত্রুটি ছাড়াও, একটি অ্যাপ্লিকেশন MediaSession.setPlaybackException(PlaybackException) ব্যবহার করে MediaSession স্তরে একটি কাস্টম PlaybackException সেট করতে পারে। এটি অ্যাপ্লিকেশনটিকে সংযুক্ত কন্ট্রোলারগুলিতে একটি ত্রুটির অবস্থা সংকেত দিতে দেয়। ব্যতিক্রমটি সমস্ত সংযুক্ত কন্ট্রোলার বা একটি নির্দিষ্ট ControllerInfo এর জন্য সেট করা যেতে পারে।

যখন কোনও অ্যাপ এই API ব্যবহার করে একটি PlaybackException সেট করে:

  • সংযুক্ত MediaController ইনস্ট্যান্সগুলিকে অবহিত করা হবে। কন্ট্রোলারের Listener.onPlayerError(PlaybackException) এবং Listener.onPlayerErrorChanged(@Nullable PlaybackException) কলব্যাকগুলি প্রদত্ত ব্যতিক্রমের সাথে আহ্বান করা হবে।

  • MediaController.getPlayerError() পদ্ধতিটি অ্যাপ্লিকেশন দ্বারা সেট করা PlaybackException ফেরত দেবে।

  • প্রভাবিত কন্ট্রোলারগুলির প্লেব্যাক অবস্থা Player.STATE_IDLE তে পরিবর্তিত হবে।

  • উপলব্ধ কমান্ডগুলি সরানো হবে এবং যদি COMMAND_GET_TIMELINE এর মতো পঠন কমান্ডগুলি ইতিমধ্যেই মঞ্জুর করা হয়ে থাকে তবে সেগুলি অবশিষ্ট থাকবে। উদাহরণস্বরূপ, Timeline অবস্থা, যখন ব্যতিক্রমটি নিয়ামকটিতে প্রয়োগ করা হয়েছিল তখন সেই অবস্থায় স্থির থাকে। প্লেয়ারের অবস্থা পরিবর্তন করার চেষ্টা করে এমন কমান্ডগুলি, যেমন COMMAND_PLAY , অ্যাপ দ্বারা প্রদত্ত কন্ট্রোলারের প্লেব্যাক ব্যতিক্রম অপসারণ না করা পর্যন্ত সরানো হয়।

পূর্বে সেট করা কাস্টম PlaybackException সাফ করতে এবং স্বাভাবিক প্লেয়ার স্টেট রিপোর্টিং পুনরুদ্ধার করতে, একটি অ্যাপ MediaSession.setPlaybackException(/* playbackException= */ null) অথবা MediaSession.setPlaybackException(ControllerInfo, /* playbackException= */ null) কল করতে পারে।

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

ব্যবহারকারীকে স্থানীয় এবং অর্থপূর্ণ তথ্য প্রদানের জন্য, আপনি প্রকৃত প্লেয়ার থেকে আসা মারাত্মক প্লেব্যাক ত্রুটির ত্রুটি কোড, ত্রুটি বার্তা এবং ত্রুটি অতিরিক্তগুলি কাস্টমাইজ করতে পারেন। সেশন তৈরি করার সময় একটি 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.
              }
            });