इस डेवलपर गाइड में, Android Sender SDK का इस्तेमाल करके, अपने Android डिवाइस से स्क्रीन शेयर करने वाले ऐप्लिकेशन में Google Cast की सुविधा जोड़ने का तरीका बताया गया है.
मोबाइल डिवाइस या लैपटॉप, स्रोत होता है, जो वीडियो चलाने की सुविधा को कंट्रोल करता है. वहीं, Google Cast डिवाइस रिसीवर होता है, जो टीवी पर कॉन्टेंट दिखाता है.
संडर फ़्रेमवर्क, भेजने वाले डिवाइस पर रनटाइम के दौरान मौजूद Cast क्लास लाइब्रेरी बाइनरी और उससे जुड़े संसाधनों को दिखाता है. ऐप्लिकेशन भेजने वाले व्यक्ति के डिवाइस पर मौजूद ऐप्लिकेशन या कास्ट करने वाला ऐप्लिकेशन, दोनों एक ही ऐप्लिकेशन होते हैं. वेब रिसीवर ऐप्लिकेशन का मतलब, Cast की सुविधा वाले डिवाइस पर चल रहे एचटीएमएल ऐप्लिकेशन से है.
Sender फ़्रेमवर्क, इवेंट के बारे में Sender ऐप्लिकेशन को बताने और Cast ऐप्लिकेशन के लाइफ़साइकल की अलग-अलग स्थितियों के बीच ट्रांज़िशन करने के लिए, असाइनोक्रोनस कॉलबैक डिज़ाइन का इस्तेमाल करता है.
ऐप्लिकेशन फ़्लो
यहां दिए गए चरणों में, ईमेल भेजने वाले Android ऐप्लिकेशन के लिए, हाई-लेवल पर ईमेल भेजने की प्रोसेस के बारे में बताया गया है:
- Cast फ़्रेमवर्क,
Activity
के लाइफ़साइकल के आधार पर,MediaRouter
डिवाइस खोजने की सुविधा को अपने-आप शुरू करता है. - जब उपयोगकर्ता 'कास्ट करें' बटन पर क्लिक करता है, तो फ़्रेमवर्क, 'कास्ट करें' डायलॉग बॉक्स के साथ, ढूंढे गए Cast डिवाइसों की सूची दिखाता है.
- जब कोई उपयोगकर्ता कोई Cast डिवाइस चुनता है, तो फ़्रेमवर्क उस डिवाइस पर Web Receiver ऐप्लिकेशन को लॉन्च करने की कोशिश करता है.
- फ़्रेमवर्क, वेब रिसीवर ऐप्लिकेशन के लॉन्च होने की पुष्टि करने के लिए, ईमेल भेजने वाले ऐप्लिकेशन में कॉलबैक को ट्रिगर करता है.
- यह फ़्रेमवर्क, ईमेल भेजने वाले और वेब रिसीवर ऐप्लिकेशन के बीच कम्यूनिकेशन चैनल बनाता है.
- यह फ़्रेमवर्क, वेब रिसीवर पर मीडिया चलाने की सुविधा को लोड और कंट्रोल करने के लिए, कम्यूनिकेशन चैनल का इस्तेमाल करता है.
- फ़्रेमवर्क, मीडिया चलाने की स्थिति को भेजने वाले और वेब रिसीवर के बीच सिंक करता है: जब उपयोगकर्ता, भेजने वाले के यूज़र इंटरफ़ेस (यूआई) पर कोई कार्रवाई करता है, तो फ़्रेमवर्क उन मीडिया कंट्रोल के अनुरोधों को वेब रिसीवर को भेजता है. साथ ही, जब वेब रिसीवर मीडिया की स्थिति के अपडेट भेजता है, तो फ़्रेमवर्क, भेजने वाले के यूज़र इंटरफ़ेस की स्थिति को अपडेट करता है.
- जब उपयोगकर्ता, Cast डिवाइस से डिसकनेक्ट करने के लिए Cast बटन पर क्लिक करता है, तो फ़्रेमवर्क, वेब रिसीवर से भेजने वाले ऐप्लिकेशन को डिसकनेक्ट कर देगा.
Google Cast के Android SDK टूल में मौजूद सभी क्लास, तरीकों, और इवेंट की पूरी सूची के लिए, Android के लिए Google Cast Sender API का रेफ़रंस देखें. यहां दिए गए सेक्शन में, अपने Android ऐप्लिकेशन में Cast को जोड़ने का तरीका बताया गया है.
Android मेनिफ़ेस्ट कॉन्फ़िगर करना
आपको अपने ऐप्लिकेशन की AndroidManifest.xml फ़ाइल में, Cast SDK टूल के लिए ये एलिमेंट कॉन्फ़िगर करने होंगे:
uses-sdk
Android के उन एपीआई लेवल को टारगेट और कम से कम लेवल के तौर पर सेट करें जिन पर Cast SDK टूल काम करता है. फ़िलहाल, ऐप्लिकेशन के लिए कम से कम एपीआई लेवल 23 और टारगेट एपीआई लेवल 34 होना चाहिए.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Android SDK टूल के कम से कम वर्शन के आधार पर, अपने ऐप्लिकेशन की थीम सेट करें. उदाहरण के लिए, अगर आपने अपनी थीम लागू नहीं की है, तो आपको Lollipop से पहले के Android SDK टूल के कम से कम वर्शन को टारगेट करते समय, Theme.AppCompat
के वैरिएंट का इस्तेमाल करना चाहिए.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
कास्ट कॉन्टेक्स्ट को शुरू करना
फ़्रेमवर्क में एक ग्लोबल सिंगलटन ऑब्जेक्ट, CastContext
होता है, जो फ़्रेमवर्क के सभी इंटरैक्शन को मैनेज करता है.
आपके ऐप्लिकेशन में CastContext
सिंगलटन को शुरू करने के लिए ज़रूरी विकल्प देने के लिए, OptionsProvider
इंटरफ़ेस लागू करना ज़रूरी है. OptionsProvider
, CastOptions
का एक इंस्टेंस उपलब्ध कराता है. इसमें ऐसे विकल्प होते हैं जिनसे फ़्रेमवर्क के काम करने के तरीके पर असर पड़ता है. इनमें से सबसे अहम है वेब रिसीवर ऐप्लिकेशन आईडी. इसका इस्तेमाल, डिस्कवरी के नतीजों को फ़िल्टर करने और कास्ट सेशन शुरू होने पर वेब रिसीवर ऐप्लिकेशन को लॉन्च करने के लिए किया जाता है.
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
आपको मेटाडेटा फ़ील्ड के तौर पर, लागू किए गए OptionsProvider
का पूरा नाम बताना होगा. यह नाम, ईमेल भेजने वाले ऐप्लिकेशन की AndroidManifest.xml फ़ाइल में दिया जाना चाहिए:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext.getSharedInstance()
को कॉल करने पर, CastContext
को धीरे-धीरे शुरू किया जाता है.
class MyActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val castContext = CastContext.getSharedInstance(this) } }
public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { CastContext castContext = CastContext.getSharedInstance(this); } }
Cast के यूज़र इंटरफ़ेस (यूएक्स) विजेट
Cast फ़्रेमवर्क, ऐसे विजेट उपलब्ध कराता है जो Cast डिज़ाइन की चेकलिस्ट के मुताबिक होते हैं:
शुरुआती ओवरले: फ़्रेमवर्क, उपयोगकर्ता को एक कस्टम व्यू,
IntroductoryOverlay
दिखाता है. यह व्यू, पहली बार डिवाइस पर रिसीवर उपलब्ध होने पर, कास्ट बटन पर ध्यान खींचने के लिए दिखाया जाता है. ईमेल भेजने वाला ऐप्लिकेशन, टाइटल टेक्स्ट के टेक्स्ट और उसकी जगह को पसंद के मुताबिक बना सकता है.कास्ट बटन: कास्ट बटन तब भी दिखता है, जब कास्ट करने के लिए कोई डिवाइस उपलब्ध न हो. जब कोई उपयोगकर्ता पहली बार कास्ट बटन पर क्लिक करता है, तो कास्ट डायलॉग दिखता है. इसमें, डिस्कवर किए गए डिवाइसों की सूची होती है. जब डिवाइस कनेक्ट होता है, तो उपयोगकर्ता Cast बटन पर क्लिक करता है. इससे, मौजूदा मीडिया का मेटाडेटा दिखता है. जैसे, टाइटल, रिकॉर्डिंग स्टूडियो का नाम, और थंबनेल इमेज. इसके अलावा, उपयोगकर्ता को Cast डिवाइस से डिसकनेक्ट करने की अनुमति भी मिलती है. "कास्ट बटन" को कभी-कभी "कास्ट आइकॉन" भी कहा जाता है.
मिनी कंट्रोलर: जब उपयोगकर्ता किसी कॉन्टेंट को कास्ट कर रहा होता है और वह कॉन्टेंट भेजने वाले ऐप्लिकेशन के मौजूदा कॉन्टेंट पेज या बड़े किए गए कंट्रोलर से किसी दूसरी स्क्रीन पर चला जाता है, तो स्क्रीन पर सबसे नीचे मिनी कंट्रोलर दिखता है. इससे उपयोगकर्ता, कास्ट किए जा रहे मौजूदा मीडिया का मेटाडेटा देख सकता है और उसे चला सकता है.
बड़ा किया गया कंट्रोल: जब कोई उपयोगकर्ता कॉन्टेंट कास्ट कर रहा होता है, तो अगर वह मीडिया की सूचना या मिनी कंट्रोलर पर क्लिक करता है, तो बड़ा किया गया कंट्रोलर लॉन्च होता है. इसमें, फ़िलहाल चल रहे मीडिया का मेटाडेटा दिखता है. साथ ही, मीडिया के प्लेबैक को कंट्रोल करने के लिए कई बटन भी होते हैं.
सूचना: सिर्फ़ Android के लिए. जब कोई उपयोगकर्ता कॉन्टेंट कास्ट कर रहा होता है और वह भेजने वाले ऐप्लिकेशन से बाहर निकल जाता है, तो उसे मीडिया की सूचना दिखती है. इसमें, फ़िलहाल कास्ट किया जा रहा मीडिया का मेटाडेटा और उसे चलाने के कंट्रोल दिखते हैं.
लॉक स्क्रीन: सिर्फ़ Android के लिए. जब कोई उपयोगकर्ता कॉन्टेंट कास्ट कर रहा होता है और लॉक स्क्रीन पर जाता है या डिवाइस का टाइम आउट हो जाता है, तो मीडिया लॉक स्क्रीन कंट्रोल दिखता है. इसमें, फ़िलहाल कास्ट किया जा रहा मीडिया का मेटाडेटा और वीडियो चलाने के कंट्रोल दिखते हैं.
इस गाइड में, अपने ऐप्लिकेशन में इन विजेट को जोड़ने का तरीका बताया गया है.
कास्ट बटन जोड़ना
Android
MediaRouter
एपीआई को सेकंडरी डिवाइसों पर मीडिया डिसप्ले और चलाने की सुविधा देने के लिए डिज़ाइन किया गया है.
MediaRouter
API का इस्तेमाल करने वाले Android ऐप्लिकेशन में, यूज़र इंटरफ़ेस के हिस्से के तौर पर कास्ट बटन शामिल होना चाहिए. इससे उपयोगकर्ता, किसी दूसरे डिवाइस पर मीडिया चलाने के लिए, मीडिया का कोई रूट चुन पाएंगे. जैसे, Cast डिवाइस.
फ़्रेमवर्क की मदद से, MediaRouteButton
को Cast button
के तौर पर जोड़ना बहुत आसान है. आपको सबसे पहले, अपने मेन्यू की जानकारी देने वाली एक्सएमएल फ़ाइल में कोई मेन्यू आइटम या MediaRouteButton
जोड़ना चाहिए. इसके बाद, फ़्रेमवर्क के साथ जोड़ने के लिए, CastButtonFactory
का इस्तेमाल करें.
// To add a Cast button, add the following snippet.
// menu.xml
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always" />
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.kt override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.main, menu) CastButtonFactory.setUpMediaRouteButton( applicationContext, menu, R.id.media_route_menu_item ) return true }
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); return true; }
इसके बाद, अगर आपका Activity
, FragmentActivity
से इनहेरिट करता है, तो अपने लेआउट में MediaRouteButton
जोड़ा जा सकता है.
// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:mediaRouteTypes="user"
android:visibility="gone" />
</LinearLayout>
// MyActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_layout) mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton) mCastContext = CastContext.getSharedInstance(this) }
// MyActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton); mCastContext = CastContext.getSharedInstance(this); }
किसी थीम का इस्तेमाल करके, कास्ट बटन का रंग सेट करने के लिए, कास्ट बटन को पसंद के मुताबिक बनाना लेख पढ़ें.
डिवाइस खोजे जाने की सुविधा को कॉन्फ़िगर करना
डिवाइस डिस्कवरी को पूरी तरह से CastContext
मैनेज करता है.
CastContext को शुरू करते समय, भेजने वाला ऐप्लिकेशन वेब रिसीवर ऐप्लिकेशन आईडी बताता है. साथ ही, CastOptions
में supportedNamespaces
सेट करके, नेमस्पेस फ़िल्टर करने का अनुरोध भी कर सकता है.
CastContext
में MediaRouter
का रेफ़रंस होता है. साथ ही, यह इन स्थितियों में डिस्कवरी प्रोसेस शुरू करेगा:
- डिवाइस डिस्कवरी में लगने वाले समय और बैटरी खर्च को संतुलित करने के लिए, एक एल्गोरिदम डिज़ाइन किया गया है. इस एल्गोरिदम के आधार पर, डिवाइस डिस्कवरी कभी-कभी अपने-आप शुरू हो जाएगी. ऐसा तब होगा, जब मैसेज भेजने वाला ऐप्लिकेशन फ़ोरग्राउंड में आएगा.
- कास्ट करने का डायलॉग बॉक्स खुला है.
- Cast SDK, Cast सेशन को वापस लाने की कोशिश कर रहा है.
डिस्कवरी की प्रोसेस तब बंद हो जाएगी, जब कास्ट डायलॉग बंद हो जाएगा या भेजने वाला ऐप्लिकेशन बैकग्राउंड में चला जाएगा.
class CastOptionsProvider : OptionsProvider { companion object { const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace" } override fun getCastOptions(appContext: Context): CastOptions { val supportedNamespaces: MutableList<String> = ArrayList() supportedNamespaces.add(CUSTOM_NAMESPACE) return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
class CastOptionsProvider implements OptionsProvider { public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"; @Override public CastOptions getCastOptions(Context appContext) { List<String> supportedNamespaces = new ArrayList<>(); supportedNamespaces.add(CUSTOM_NAMESPACE); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
सेशन मैनेजमेंट के काम करने का तरीका
Cast SDK टूल, Cast सेशन के कॉन्सेप्ट को पेश करता है. इसे सेट अप करने के लिए, किसी डिवाइस से कनेक्ट करना, वेब रिसीवर ऐप्लिकेशन को लॉन्च करना (या उसमें शामिल होना), उस ऐप्लिकेशन से कनेक्ट करना, और मीडिया कंट्रोल चैनल को शुरू करना ज़रूरी है. कास्ट सेशन और वेब रिसीवर के लाइफ़साइकल के बारे में ज़्यादा जानने के लिए, वेब रिसीवर के ऐप्लिकेशन लाइफ़साइकल की गाइड देखें.
सेशन को क्लास SessionManager
मैनेज करता है. आपका ऐप्लिकेशन, CastContext.getSessionManager()
के ज़रिए इसे ऐक्सेस कर सकता है.
अलग-अलग सेशन को क्लास के सबक्लास Session
से दिखाया जाता है.
उदाहरण के लिए,
CastSession
कास्ट डिवाइसों पर, Google खाते में की गई गतिविधियों को दिखाता है. आपका ऐप्लिकेशन, SessionManager.getCurrentCastSession()
के ज़रिए, फ़िलहाल चालू Cast सेशन को ऐक्सेस कर सकता है.
आपका ऐप्लिकेशन, सेशन के इवेंट पर नज़र रखने के लिए, SessionManagerListener
क्लास का इस्तेमाल कर सकता है. जैसे, सेशन बनाना, निलंबित करना, फिर से शुरू करना, और खत्म करना. अगर कोई सेशन चालू होने के दौरान, अचानक या गड़बड़ी की वजह से बंद हो जाता है, तो फ़्रेमवर्क उसे अपने-आप फिर से शुरू करने की कोशिश करता है.
MediaRouter
डायलॉग में उपयोगकर्ता के जेस्चर के जवाब में, सेशन अपने-आप बनते और बंद होते हैं.
कास्ट शुरू करने से जुड़ी गड़बड़ियों को बेहतर तरीके से समझने के लिए, ऐप्लिकेशन CastContext#getCastReasonCodeForCastStatusCode(int)
का इस्तेमाल कर सकते हैं. इससे, सेशन शुरू करने से जुड़ी गड़बड़ी को CastReasonCodes
में बदला जा सकता है.
कृपया ध्यान दें कि सेशन शुरू होने से जुड़ी कुछ गड़बड़ियां (जैसे, CastReasonCodes#CAST_CANCELLED
) सामान्य हैं और इन्हें गड़बड़ी के तौर पर लॉग नहीं किया जाना चाहिए.
अगर आपको सेशन के स्टेटस में होने वाले बदलावों के बारे में पता करना है, तो SessionManagerListener
लागू करें. इस उदाहरण में, Activity
में CastSession
की उपलब्धता के बारे में जानकारी दी गई है.
class MyActivity : Activity() { private var mCastSession: CastSession? = null private lateinit var mCastContext: CastContext private lateinit var mSessionManager: SessionManager private val mSessionManagerListener: SessionManagerListener<CastSession> = SessionManagerListenerImpl() private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { invalidateOptionsMenu() } override fun onSessionStartFailed(session: CastSession?, error: Int) { val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error) // Handle error } override fun onSessionSuspended(session: CastSession?, reason Int) {} override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { invalidateOptionsMenu() } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { finish() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession } override fun onDestroy() { super.onDestroy() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) } }
public class MyActivity extends Activity { private CastContext mCastContext; private CastSession mCastSession; private SessionManager mSessionManager; private SessionManagerListener<CastSession> mSessionManagerListener = new SessionManagerListenerImpl(); private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { invalidateOptionsMenu(); } @Override public void onSessionStartFailed(CastSession session, int error) { int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error); // Handle error } @Override public void onSessionSuspended(CastSession session, int reason) {} @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { invalidateOptionsMenu(); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { finish(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
संगीत को दूसरे स्पीकर पर चलाना
सेशन की स्थिति को बनाए रखना, स्ट्रीम ट्रांसफ़र करने का आधार है. इसकी मदद से, उपयोगकर्ता बोलकर दिए गए निर्देशों, Google Home ऐप्लिकेशन या स्मार्ट डिसप्ले का इस्तेमाल करके, मौजूदा ऑडियो और वीडियो स्ट्रीम को एक से दूसरे डिवाइस पर ले जा सकते हैं. मीडिया एक डिवाइस (सोर्स) पर चलना बंद हो जाता है और दूसरे डिवाइस (डेस्टिनेशन) पर चलता रहता है. नए फ़र्मवेयर वाला कोई भी Cast डिवाइस, स्ट्रीम ट्रांसफ़र में सोर्स या डेस्टिनेशन के तौर पर काम कर सकता है.
स्ट्रीम ट्रांसफ़र या एक्सपैंशन के दौरान नया डेस्टिनेशन डिवाइस पाने के लिए, CastSession#addCastListener
का इस्तेमाल करके Cast.Listener
रजिस्टर करें.
इसके बाद, onDeviceNameChanged
कॉलबैक के दौरान CastSession#getCastDevice()
को कॉल करें.
ज़्यादा जानकारी के लिए, वेब रिसीवर पर स्ट्रीम ट्रांसफ़र करना देखें.
अपने-आप फिर से कनेक्ट होना
फ़्रेमवर्क में एक ReconnectionService
होता है. इसे मैसेज भेजने वाले ऐप्लिकेशन से चालू किया जा सकता है, ताकि कई मुश्किल मामलों में फिर से कनेक्ट किया जा सके. जैसे:
- वाई-फ़ाई के कुछ समय के लिए बंद होने पर उसे वापस चालू करना
- डिवाइस के स्लीप मोड से वापस आना
- ऐप्लिकेशन को बैकग्राउंड में भेजने के बाद उसे वापस लाना
- ऐप्लिकेशन क्रैश होने पर उसे वापस लाना
यह सेवा डिफ़ॉल्ट रूप से चालू होती है. इसे CastOptions.Builder
में जाकर बंद किया जा सकता है.
अगर आपकी gradle फ़ाइल में अपने-आप मर्ज होने की सुविधा चालू है, तो यह सेवा आपके ऐप्लिकेशन के मेनिफ़ेस्ट में अपने-आप मर्ज हो सकती है.
मीडिया सेशन होने पर, फ़्रेमवर्क सेवा शुरू कर देगा और मीडिया सेशन खत्म होने पर उसे बंद कर देगा.
मीडिया कंट्रोल की सुविधा कैसे काम करती है
Cast फ़्रेमवर्क, Cast 2.x की RemoteMediaPlayer
क्लास को बंद कर रहा है. इसकी जगह, एक नई क्लास RemoteMediaClient
का इस्तेमाल किया जा रहा है. यह क्लास, ज़्यादा सुविधाजनक एपीआई के सेट में वही फ़ंक्शन उपलब्ध कराती है. साथ ही, GoogleApiClient को पास करने की ज़रूरत नहीं होती.
जब आपका ऐप्लिकेशन, मीडिया नेमस्पेस के साथ काम करने वाले वेब रिसीवर ऐप्लिकेशन के साथ CastSession
सेटअप करता है, तो फ़्रेमवर्क अपने-आप RemoteMediaClient
का एक इंस्टेंस बना देगा. आपका ऐप्लिकेशन, CastSession
इंस्टेंस पर getRemoteMediaClient()
मेथड को कॉल करके इसे ऐक्सेस कर सकता है.
वेब रिसीवर को अनुरोध करने वाले RemoteMediaClient
के सभी तरीके, PendingResult ऑब्जेक्ट दिखाएंगे. इस ऑब्जेक्ट का इस्तेमाल, अनुरोध को ट्रैक करने के लिए किया जा सकता है.
ऐसा हो सकता है कि RemoteMediaClient
का इंस्टेंस, आपके ऐप्लिकेशन के कई हिस्सों और फ़्रेमवर्क के कुछ इंटरनल कॉम्पोनेंट के साथ शेयर किया जाए. जैसे, लगातार काम करने वाले मिनी कंट्रोलर और सूचना सेवा.
इसलिए, यह इंस्टेंस RemoteMediaClient.Listener
के कई इंस्टेंस रजिस्टर करने की सुविधा देता है.
मीडिया का मेटाडेटा सेट करना
MediaMetadata
क्लास, उस मीडिया आइटम की जानकारी दिखाती है जिसे आपको कास्ट करना है. यहां दिए गए उदाहरण में, किसी फ़िल्म का नया MediaMetadata इंस्टेंस बनाया गया है. साथ ही, टाइटल, सबटाइटल, और दो इमेज सेट की गई हैं.
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()) movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0)))) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0)))); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));
मीडिया मेटाडेटा वाली इमेज के इस्तेमाल के बारे में जानने के लिए, इमेज चुनना लेख पढ़ें.
मीडिया लोड करना
आपका ऐप्लिकेशन, मीडिया आइटम लोड कर सकता है. इसकी जानकारी नीचे दिए गए कोड में दी गई है. सबसे पहले, मीडिया के मेटाडेटा के साथ
MediaInfo.Builder
का इस्तेमाल करके,
MediaInfo
का उदाहरण बनाएं. मौजूदा CastSession
से RemoteMediaClient
पाएं. इसके बाद, उस RemoteMediaClient
में MediaInfo
लोड करें. वेब रिसीवर पर चल रहे मीडिया प्लेयर ऐप्लिकेशन को चलाने, रोकने, और कंट्रोल करने के लिए, RemoteMediaClient
का इस्तेमाल करें.
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build() val remoteMediaClient = mCastSession.getRemoteMediaClient() remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build(); RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());
मीडिया ट्रैक इस्तेमाल करने के बारे में भी सेक्शन देखें.
4K वीडियो फ़ॉर्मैट
यह देखने के लिए कि आपका मीडिया किस फ़ॉर्मैट में है, MediaStatus में getVideoInfo()
का इस्तेमाल करें. इससे आपको VideoInfo
का मौजूदा इंस्टेंस मिलेगा.
इस इंस्टेंस में, एचडीआर टीवी फ़ॉर्मैट का टाइप और डिसप्ले की ऊंचाई और चौड़ाई, पिक्सल में शामिल होती है. 4K फ़ॉर्मैट के वैरिएंट को स्थिर वैल्यू HDR_TYPE_*
से दिखाया जाता है.
कई डिवाइसों पर सूचनाएं रिमोट कंट्रोल करना
जब कोई उपयोगकर्ता कास्ट कर रहा होता है, तो उसी नेटवर्क पर मौजूद अन्य Android डिवाइसों पर एक सूचना मिलेगी. इससे वे भी वीडियो चलाने की सुविधा को कंट्रोल कर पाएंगे. जिस डिवाइस पर ऐसी सूचनाएं मिलती हैं उसके लिए, सेटिंग ऐप्लिकेशन में जाकर इन्हें बंद किया जा सकता है. इसके लिए, Google > Google Cast > रिमोट कंट्रोल की सूचनाएं दिखाएं पर जाएं. (सूचनाओं में, Settings ऐप्लिकेशन का शॉर्टकट शामिल होता है.) ज़्यादा जानकारी के लिए, कास्ट किए गए रिमोट कंट्रोल की सूचनाएं देखें.
मिनी कंट्रोलर जोड़ना
Cast डिज़ाइन की चेकलिस्ट के मुताबिक, भेजने वाले ऐप्लिकेशन में मिनी कंट्रोलर नाम का एक कंट्रोल होना चाहिए. यह कंट्रोल तब दिखना चाहिए, जब उपयोगकर्ता मौजूदा कॉन्टेंट पेज से, भेजने वाले ऐप्लिकेशन के किसी दूसरे हिस्से पर जाए. यह मिनी कंट्रोलर, उपयोगकर्ता को मौजूदा Cast सेशन की याद दिलाता है. मिनी कंट्रोलर पर टैप करके, उपयोगकर्ता, कास्ट के फ़ुल-स्क्रीन में बड़े किए गए कंट्रोलर व्यू पर वापस जा सकता है.
फ़्रेमवर्क, MiniControllerFragment नाम का कस्टम व्यू उपलब्ध कराता है. इसे हर उस गतिविधि की लेआउट फ़ाइल में सबसे नीचे जोड़ा जा सकता है जिसमें आपको मिनी कंट्रोलर दिखाना है.
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
जब आपका ऐप्लिकेशन वीडियो या ऑडियो लाइव स्ट्रीम चला रहा होता है, तो एसडीके, मिनी कंट्रोलर में 'चलाएं/रोकें' बटन के बजाय, 'चलाएं/रोकें' बटन को अपने-आप दिखाता है.
इस कस्टम व्यू के टाइटल और सबटाइटल के टेक्स्ट के दिखने का तरीका सेट करने के लिए, और बटन चुनने के लिए, मिनी कंट्रोलर को पसंद के मुताबिक बनाना लेख पढ़ें.
बड़ा किया गया कंट्रोल जोड़ना
Google Cast डिज़ाइन की चेकलिस्ट के मुताबिक, भेजने वाले ऐप्लिकेशन को कास्ट किए जा रहे मीडिया के लिए, बड़ा किया गया कंट्रोल उपलब्ध कराना ज़रूरी है. बड़ा किया गया कंट्रोल पैनल, मिनी कंट्रोल पैनल का फ़ुल स्क्रीन वर्शन होता है.
Cast SDK टूल, बड़े किए गए कंट्रोलर के लिए एक विजेट उपलब्ध कराता है. इसे ExpandedControllerActivity
कहा जाता है.
यह एक ऐसी एब्स्ट्रैक्ट क्लास है जिसे कास्ट बटन जोड़ने के लिए सबक्लास बनाना होगा.
सबसे पहले, बड़ा किए गए कंट्रोलर के लिए एक नई मेन्यू रिसॉर्स फ़ाइल बनाएं, ताकि उसमें कास्ट बटन जोड़ा जा सके:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
ExpandedControllerActivity
को एक्सटेंड करने वाली नई क्लास बनाएं.
class ExpandedControlsActivity : ExpandedControllerActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.expanded_controller, menu) CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item) return true } }
public class ExpandedControlsActivity extends ExpandedControllerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.expanded_controller, menu); CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); return true; } }
अब application
टैग में, ऐप्लिकेशन मेनिफ़ेस्ट में अपनी नई गतिविधि का एलान करें:
<application>
...
<activity
android:name=".expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
...
</application>
टारगेट की गई गतिविधि को अपनी नई गतिविधि पर सेट करने के लिए, CastOptionsProvider
में बदलाव करें और NotificationOptions
और
CastMediaOptions
को बदलें:
override fun getCastOptions(context: Context): CastOptions? { val notificationOptions = NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity::class.java.name) .build() val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name) .build() return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build() }
public CastOptions getCastOptions(Context context) { NotificationOptions notificationOptions = new NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) .build(); CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) .build(); return new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build(); }
रिमोट मीडिया लोड होने पर अपनी नई गतिविधि दिखाने के लिए, LocalPlayerActivity
loadRemoteMedia
का तरीका अपडेट करें:
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { val remoteMediaClient = mCastSession?.remoteMediaClient ?: return remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java) startActivity(intent) remoteMediaClient.unregisterCallback(this) } }) remoteMediaClient.load( MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position.toLong()).build() ) }
private void loadRemoteMedia(int position, boolean autoPlay) { if (mCastSession == null) { return; } final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); if (remoteMediaClient == null) { return; } remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() { @Override public void onStatusUpdated() { Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class); startActivity(intent); remoteMediaClient.unregisterCallback(this); } }); remoteMediaClient.load(new MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position).build()); }
जब आपका ऐप्लिकेशन किसी वीडियो या ऑडियो लाइव स्ट्रीम को चला रहा होता है, तो SDK, बड़े किए गए कंट्रोलर में चलाएं/रोकें बटन के बजाय, अपने-आप चलाएं/रोकें बटन दिखाता है.
थीम का इस्तेमाल करके, डिवाइस के दिखने का तरीका सेट करने के लिए, चुनें कि कौनसे बटन दिखाने हैं और कस्टम बटन जोड़ें. इसके लिए, बड़े किए गए कंट्रोलर को पसंद के मुताबिक बनाना लेख पढ़ें.
आवाज़ कंट्रोल करें
यह फ़्रेमवर्क, भेजने वाले ऐप्लिकेशन के वॉल्यूम को अपने-आप मैनेज करता है. साथ ही, यह फ़्रेमवर्क, भेजने वाले ऐप्लिकेशन और वेब रिसीवर ऐप्लिकेशन को अपने-आप सिंक करता है, ताकि भेजने वाले ऐप्लिकेशन का यूज़र इंटरफ़ेस (यूआई) हमेशा वेब रिसीवर के तय किए गए वॉल्यूम की जानकारी दिखाता रहे.
बटन की मदद से वॉल्यूम कंट्रोल करना
Android डिवाइस पर, भेजने वाले डिवाइस के फ़िज़िकल बटन का इस्तेमाल करके, वेब रिसीवर पर Cast सेशन का वॉल्यूम बदला जा सकता है. यह सुविधा, Jelly Bean या उसके बाद के वर्शन वाले किसी भी डिवाइस पर डिफ़ॉल्ट रूप से काम करती है.
Jelly Bean से पहले, फ़िज़िकल बटन से वॉल्यूम कंट्रोल करने की सुविधा
Jelly Bean से पहले के Android डिवाइसों पर, वेब रिसीवर डिवाइस के वॉल्यूम को कंट्रोल करने के लिए, फ़िज़िकल वॉल्यूम बटन का इस्तेमाल करने के लिए, भेजने वाले ऐप्लिकेशन को अपनी गतिविधियों में dispatchKeyEvent
को बदलना होगा और CastContext.onDispatchVolumeKeyEventBeforeJellyBean()
को कॉल करना होगा:
class MyActivity : FragmentActivity() { override fun dispatchKeyEvent(event: KeyEvent): Boolean { return (CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event)) } }
class MyActivity extends FragmentActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { return CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event); } }
सूचना और लॉक स्क्रीन पर मीडिया कंट्रोल जोड़ना
सिर्फ़ Android पर, Google Cast डिज़ाइन की चेकलिस्ट के मुताबिक, मीडिया भेजने वाले ऐप्लिकेशन को सूचना में मीडिया कंट्रोल लागू करने और लॉक स्क्रीन पर ऐसा करने की ज़रूरत होती है. ऐसा तब करना होता है, जब मीडिया भेजने वाले ऐप्लिकेशन पर फ़ोकस न हो, लेकिन कास्टिंग की जा रही हो. फ़्रेमवर्क में MediaNotificationService
और MediaIntentReceiver
एलिमेंट होते हैं. इनकी मदद से, मैसेज भेजने वाले ऐप्लिकेशन को सूचना और लॉक स्क्रीन में मीडिया कंट्रोल बनाने में मदद मिलती है.
MediaNotificationService
, तब चलता है, जब कोई व्यक्ति कास्ट कर रहा हो. साथ ही, यह इमेज के थंबनेल और कास्ट किए जा रहे मौजूदा आइटम की जानकारी के साथ सूचना दिखाएगा. साथ ही, इसमें 'चलाएं/रोकें' बटन और 'बंद करें' बटन भी दिखेगा.
MediaIntentReceiver
एक BroadcastReceiver
है, जो सूचना से उपयोगकर्ता की कार्रवाइयों को मैनेज करता है.
आपका ऐप्लिकेशन, NotificationOptions
की मदद से, लॉक स्क्रीन पर सूचना और मीडिया कंट्रोल को कॉन्फ़िगर कर सकता है.
आपका ऐप्लिकेशन यह कॉन्फ़िगर कर सकता है कि सूचना में कौनसे कंट्रोल बटन दिखाए जाएं और उपयोगकर्ता के सूचना पर टैप करने पर कौनसा Activity
खुले. अगर ऐक्शन के बारे में साफ़ तौर पर नहीं बताया गया है, तो डिफ़ॉल्ट वैल्यू,
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
और
MediaIntentReceiver.ACTION_STOP_CASTING
का इस्तेमाल किया जाएगा.
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". val buttonActions: MutableList<String> = ArrayList() buttonActions.add(MediaIntentReceiver.ACTION_REWIND) buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK) buttonActions.add(MediaIntentReceiver.ACTION_FORWARD) buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING) // Showing "play/pause" and "stop casting" in the compat view of the notification. val compatButtonActionsIndices = intArrayOf(1, 3) // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. val notificationOptions = NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity::class.java.name) .build()
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". List<String> buttonActions = new ArrayList<>(); buttonActions.add(MediaIntentReceiver.ACTION_REWIND); buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); buttonActions.add(MediaIntentReceiver.ACTION_FORWARD); buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); // Showing "play/pause" and "stop casting" in the compat view of the notification. int[] compatButtonActionsIndices = new int[]{1, 3}; // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. NotificationOptions notificationOptions = new NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity.class.getName()) .build();
सूचना और लॉक स्क्रीन पर मीडिया कंट्रोल दिखाने की सुविधा डिफ़ॉल्ट रूप से चालू होती है. इसे बंद करने के लिए, CastMediaOptions.Builder
में null के साथ setNotificationOptions
को कॉल करें.
फ़िलहाल, सूचनाएं दिखने की सुविधा चालू होने पर ही लॉक स्क्रीन की सुविधा चालू रहती है.
// ... continue with the NotificationOptions built above val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build() val castOptions: CastOptions = Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build()
// ... continue with the NotificationOptions built above CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build(); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build();
जब आपका ऐप्लिकेशन वीडियो या ऑडियो लाइव स्ट्रीम चला रहा होता है, तो SDK, सूचना कंट्रोल पर 'चलाएं/रोकें' बटन के बजाय 'चलाएं/रोकें' बटन को अपने-आप दिखाता है. हालांकि, यह लॉक स्क्रीन कंट्रोल पर नहीं दिखता.
ध्यान दें: Lollipop से पहले के वर्शन वाले डिवाइसों पर लॉक स्क्रीन के कंट्रोल दिखाने के लिए,
RemoteMediaClient
आपकी ओर से ऑडियो फ़ोकस का अनुरोध अपने-आप करेगा.
गड़बड़ियां ठीक करना
भेजने वाले ऐप्लिकेशन के लिए, गड़बड़ी के सभी कॉलबैक मैनेज करना और Cast के लाइफ़ साइकल के हर चरण के लिए सबसे सही जवाब तय करना बहुत ज़रूरी है. ऐप्लिकेशन, उपयोगकर्ता को गड़बड़ी के डायलॉग दिखा सकता है या वेब रिसीवर से कनेक्शन को बंद कर सकता है.