이 페이지에는 Android TV 수신기 앱 맞춤설정
라이브러리 구성
Android TV 앱에서 Cast Connect API를 사용할 수 있도록 하려면 다음 단계를 따르세요.
<ph type="x-smartling-placeholder">-
애플리케이션 모듈 디렉터리 내에서
build.gradle
파일을 엽니다. -
나열된
repositories
에google()
가 포함되어 있는지 확인합니다.repositories { google() }
-
앱의 대상 기기 유형에 따라 최신 버전을 추가합니다.
를 종속 항목에 추가합니다.
<ph type="x-smartling-placeholder">
- </ph>
-
Android 수신기 앱:
dependencies { implementation 'com.google.android.gms:play-services-cast-tv:21.1.0' implementation 'com.google.android.gms:play-services-cast:21.5.0' }
-
Android Sender 앱의 경우:
dependencies { implementation 'com.google.android.gms:play-services-cast:21.1.0' implementation 'com.google.android.gms:play-services-cast-framework:21.5.0' }
-
Android 수신기 앱:
-
변경사항을 저장하고
Sync Project with Gradle Files
를 클릭합니다. 를 클릭합니다.
-
Podfile
에서google-cast-sdk
4.8.3을 타겟팅하는지 확인합니다. 이상 -
iOS 14 이상을 타겟팅합니다. 출시 노트를 참고하세요.
를 참조하세요.
platform: ios, '14' def target_pods pod 'google-cast-sdk', '~>4.8.3' end
- Chromium 브라우저 버전 M87 이상이 필요합니다.
-
프로젝트에 Web Sender API 라이브러리 추가
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
AndroidX 요구사항
Google Play 서비스의 새 버전을 사용하려면 앱을 업데이트해야 합니다.
androidx
네임스페이스 다음 안내를 따르세요.
AndroidX로 이전에 관해 자세히 알아보세요.
Android TV 앱 - 기본 요건
Android TV 앱에서 Cast Connect를 지원하려면 미디어 세션의 지원 이벤트입니다. 미디어 세션에서 제공하는 데이터입니다. 는 동영상의 위치, 재생 상태 등과 같은 기본 정보를 확인할 수 있습니다 미디어 세션은 Cast Connect 라이브러리에서도 사용됩니다. 보내는 사람이 보내는 특정 메시지(예: 일시중지)를 받으면 신호를 보냅니다.
미디어 세션 및 미디어 세션 초기화 방법에 관한 자세한 내용은 자세한 내용은 미디어 세션 가이드 작업
미디어 세션 수명 주기
앱은 재생이 시작될 때 미디어 세션을 만들고 세션이 시작되면 이를 해제해야 합니다. 더 이상 제어할 수 없습니다. 예를 들어 앱이 동영상 앱인 경우 사용자가 재생 활동을 종료할 때 세션을 해제해야 합니다. '뒤로' 선택 앱을 백그라운드로 재생하여 다른 콘텐츠를 탐색할 수도 있습니다 만약 앱이 음악 앱인 경우 앱에서 더 이상 재생하지 않는 경우 삭제해야 합니다. 있습니다.
세션 상태 업데이트 중
미디어 세션의 데이터는 있습니다. 예를 들어 재생이 일시중지되면 지원되는 작업이 있습니다. 다음 표에는 사용자는 최신 정보를 확인해야 합니다
MediaMetadataCompat
메타데이터 필드 | 설명 |
---|---|
METADATA_KEY_TITLE (필수) | 미디어 제목입니다. |
METADATA_KEY_DISPLAY_SUBTITLE | 부제목 |
METADATA_KEY_DISPLAY_ICON_URI | 아이콘 URL입니다. |
METADATA_KEY_DURATION (필수) | 미디어 재생 시간 |
METADATA_KEY_MEDIA_URI | Content ID |
METADATA_KEY_ARTIST | 아티스트 |
METADATA_KEY_ALBUM | 앨범 |
PlaybackStateCompat
필수 방법 | 설명 |
---|---|
setActions() | 지원되는 미디어 명령어를 설정합니다. |
setState() | 재생 상태와 현재 위치를 설정합니다. |
MediaSessionCompat
필수 방법 | 설명 |
---|---|
setRepeatMode() | 반복 모드를 설정합니다. |
setShuffleMode() | 셔플 모드를 설정합니다. |
setMetadata() | 미디어 메타데이터를 설정합니다. |
setPlaybackState() | 재생 상태를 설정합니다. |
private fun updateMediaSession() { val metadata = MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, mMovie.getCardImageUrl()) .build() val playbackState = PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis() ) .build() mediaSession.setMetadata(metadata) mediaSession.setPlaybackState(playbackState) }
private void updateMediaSession() { MediaMetadataCompat metadata = new MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,mMovie.getCardImageUrl()) .build(); PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis()) .build(); mediaSession.setMetadata(metadata); mediaSession.setPlaybackState(playbackState); }
전송 제어 처리
앱은 미디어 세션 전송 제어 콜백을 구현해야 합니다. 이 다음 표는 이들이 처리해야 하는 전송 제어 작업을 보여줍니다.
MediaSessionCompat.Callback
작업 | 설명 |
---|---|
onPlay() | 재개 |
onPause() | 일시중지 |
onSeekTo() | 위치 탐색 |
onStop() | 현재 미디어 중지 |
class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. ... } override fun onPlay() { // Resume the player and update the play state. ... } override fun onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback( MyMediaSessionCallback() );
public MyMediaSessionCallback extends MediaSessionCompat.Callback { public void onPause() { // Pause the player and update the play state. ... } public void onPlay() { // Resume the player and update the play state. ... } public void onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback(new MyMediaSessionCallback());
Cast 지원 구성
발신자 애플리케이션에서 실행 요청을 전송하면 인텐트가 생성됩니다.
애플리케이션 네임스페이스로 대체해야 합니다 애플리케이션은 이를 처리할 책임이 있습니다.
애플리케이션의 인스턴스를 만들어
CastReceiverContext
드림
객체가 됩니다. CastReceiverContext
객체가 필요합니다.
TV 앱이 실행되는 동안 Cast와 상호작용합니다. 이 객체는
애플리케이션을 사용하여 연결된 모든 발신자가 보낸 Cast 미디어 메시지를 수락해야 합니다.
Android TV 설정
실행 인텐트 필터 추가
출시를 처리하려는 활동에 새 인텐트 필터 추가 인텐트를 전송합니다.
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
수신자 옵션 제공업체 지정
다음과 같이
ReceiverOptionsProvider
드림
제공하기 위해
CastReceiverOptions
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setStatusText("My App") .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setStatusText("My App") .build(); } }
그런 다음 AndroidManifest
에서 옵션 제공자를 지정합니다.
<meta-data
android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.example.mysimpleatvapplication.MyReceiverOptionsProvider" />
ReceiverOptionsProvider
는 다음과 같은 경우 CastReceiverOptions
를 제공하는 데 사용됩니다.
CastReceiverContext
가 초기화되었습니다.
캐스트 수신기 컨텍스트
초기화:
CastReceiverContext
드림
다음 안내를 따르세요.
override fun onCreate() { CastReceiverContext.initInstance(this) ... }
@Override public void onCreate() { CastReceiverContext.initInstance(this); ... }
앱이 포그라운드로 이동하면 CastReceiverContext
를 시작합니다.
CastReceiverContext.getInstance().start()
CastReceiverContext.getInstance().start();
전화걸기
stop()
드림
에
CastReceiverContext
동영상 앱 또는 이를 지원하지 않는 앱의 경우 앱이 백그라운드로 전환된 후
백그라운드 재생:
// Player has stopped. CastReceiverContext.getInstance().stop()
// Player has stopped. CastReceiverContext.getInstance().stop();
또한 앱이 백그라운드 재생을 지원한다면 stop()
를 호출합니다.
백그라운드에서 재생이 중지될 때 CastReceiverContext
에서 발생합니다.
Google은 이 애플리케이션의 LifecycleObserver를
androidx.lifecycle
드림
통화 관리 라이브러리
CastReceiverContext.start()
및
CastReceiverContext.stop()
,
특히 네이티브 앱에 여러 활동이 있는 경우에 그렇습니다. 이렇게 하면 인종을 피할 수 있습니다.
여러 활동에서 start()
및 stop()
를 호출할 때의 조건
// Create a LifecycleObserver class. class MyLifecycleObserver : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start() } override fun onStop(owner: LifecycleOwner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop() } } // Add the observer when your application is being created. class MyApplication : Application() { fun onCreate() { super.onCreate() // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */) // Register LifecycleObserver ProcessLifecycleOwner.get().lifecycle.addObserver( MyLifecycleObserver()) } }
// Create a LifecycleObserver class. public class MyLifecycleObserver implements DefaultLifecycleObserver { @Override public void onStart(LifecycleOwner owner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start(); } @Override public void onStop(LifecycleOwner owner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop(); } } // Add the observer when your application is being created. public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */); // Register LifecycleObserver ProcessLifecycleOwner.get().getLifecycle().addObserver( new MyLifecycleObserver()); } }
// In AndroidManifest.xml set MyApplication as the application class
<application
...
android:name=".MyApplication">
MediaManager에 MediaSession 연결
배포를 만들 때
MediaSession
님,
현재 MediaSession
토큰도 제공해야 합니다.
CastReceiverContext
명령을 전송하고 미디어 재생 상태를 검색할 위치를 알 수 있습니다.
val mediaManager: MediaManager = receiverContext.getMediaManager() mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken())
MediaManager mediaManager = receiverContext.getMediaManager(); mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken());
재생 비활성화로 인해 MediaSession
을(를) 해제할 때 다음과 같이 설정해야 합니다.
null 토큰 사용
MediaManager
:
myPlayer.stop() mediaSession.release() mediaManager.setSessionCompatToken(null)
myPlayer.stop(); mediaSession.release(); mediaManager.setSessionCompatToken(null);
앱이 백그라운드에 있는 동안 미디어 재생을 지원하는 경우
통화 중
CastReceiverContext.stop()
드림
백그라운드로 전송될 때는 앱이 실행될 때만 호출해야 합니다.
백그라운드에 있어 더 이상 미디어를 재생하지 않습니다. 예를 들면 다음과 같습니다.
class MyLifecycleObserver : DefaultLifecycleObserver { ... // App has moved to the background. override fun onPause(owner: LifecycleOwner) { mIsBackground = true myStopCastReceiverContextIfNeeded() } } // Stop playback on the player. private fun myStopPlayback() { myPlayer.stop() myStopCastReceiverContextIfNeeded() } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private fun myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop() } }
public class MyLifecycleObserver implements DefaultLifecycleObserver { ... // App has moved to the background. @Override public void onPause(LifecycleOwner owner) { mIsBackground = true; myStopCastReceiverContextIfNeeded(); } } // Stop playback on the player. private void myStopPlayback() { myPlayer.stop(); myStopCastReceiverContextIfNeeded(); } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private void myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop(); } }
Cast Connect로 Exoplayer 사용
Exoplayer
를 사용하는 경우
MediaSessionConnector
세션과 관련 정보를 포함한 모든 관련 정보가 자동으로 유지관리되도록
재생 상태를 수동으로 추적해야 합니다.
MediaSessionConnector.MediaButtonEventHandler
드림
다음과 같은 방법으로 MediaButton 이벤트를 처리하는 데 사용할 수 있습니다.
setMediaButtonEventHandler(MediaButtonEventHandler)
다른 방식으로 처리되며
MediaSessionCompat.Callback
기본적으로 제공됩니다
통합하려면
MediaSessionConnector
드림
앱에서 플레이어 활동 클래스나
미디어 세션 관리:
class PlayerActivity : Activity() { private var mMediaSession: MediaSessionCompat? = null private var mMediaSessionConnector: MediaSessionConnector? = null private var mMediaManager: MediaManager? = null override fun onCreate(savedInstanceState: Bundle?) { ... mMediaSession = MediaSessionCompat(this, LOG_TAG) mMediaSessionConnector = MediaSessionConnector(mMediaSession!!) ... } override fun onStart() { ... mMediaManager = receiverContext.getMediaManager() mMediaManager!!.setSessionCompatToken(currentMediaSession.getSessionToken()) mMediaSessionConnector!!.setPlayer(mExoPlayer) mMediaSessionConnector!!.setMediaMetadataProvider(mMediaMetadataProvider) mMediaSession!!.isActive = true ... } override fun onStop() { ... mMediaSessionConnector!!.setPlayer(null) mMediaSession!!.release() mMediaManager!!.setSessionCompatToken(null) ... } }
public class PlayerActivity extends Activity { private MediaSessionCompat mMediaSession; private MediaSessionConnector mMediaSessionConnector; private MediaManager mMediaManager; @Override protected void onCreate(Bundle savedInstanceState) { ... mMediaSession = new MediaSessionCompat(this, LOG_TAG); mMediaSessionConnector = new MediaSessionConnector(mMediaSession); ... } @Override protected void onStart() { ... mMediaManager = receiverContext.getMediaManager(); mMediaManager.setSessionCompatToken(currentMediaSession.getSessionToken()); mMediaSessionConnector.setPlayer(mExoPlayer); mMediaSessionConnector.setMediaMetadataProvider(mMediaMetadataProvider); mMediaSession.setActive(true); ... } @Override protected void onStop() { ... mMediaSessionConnector.setPlayer(null); mMediaSession.release(); mMediaManager.setSessionCompatToken(null); ... } }
발신자 앱 설정
Cast Connect 지원 사용 설정
Cast Connect 지원으로 발신기 앱을 업데이트한 후
준비 상태를
androidReceiverCompatible
드림
깃발 켜기
LaunchOptions
true로 설정합니다.
play-services-cast-framework
버전 필요
19.0.0
이상
androidReceiverCompatible
플래그는
LaunchOptions
(CastOptions
의 일부):
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context?): CastOptions { val launchOptions: LaunchOptions = Builder() .setAndroidReceiverCompatible(true) .build() return CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { LaunchOptions launchOptions = new LaunchOptions.Builder() .setAndroidReceiverCompatible(true) .build(); return new CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build(); } }
google-cast-sdk
버전 v4.4.8
또는
더 높습니다.
androidReceiverCompatible
플래그는
GCKLaunchOptions
(
GCKCastOptions
).
let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID)) ... let launchOptions = GCKLaunchOptions() launchOptions.androidReceiverCompatible = true options.launchOptions = launchOptions GCKCastContext.setSharedInstanceWith(options)
Chromium 브라우저 버전 필요
M87
이상
const context = cast.framework.CastContext.getInstance(); const castOptions = new cast.framework.CastOptions(); castOptions.receiverApplicationId = kReceiverAppID; castOptions.androidReceiverCompatible = true; context.setOptions(castOptions);
Cast Developer Console 설정
Android TV 앱 구성
Android TV 앱의 패키지 이름을 다음 위치에 추가합니다. Cast 개발자 콘솔 Cast 앱 ID와 연결합니다.
개발자 기기 등록
사용하려는 Android TV 기기의 일련번호를 등록합니다. 생성형 AI 모델의 프로토타입을 Cast 개발자 콘솔을 참고하세요.
등록하지 않으면 Cast Connect는 Google Play 스토어로 이동합니다.
Cast용 Cast 또는 Android TV 기기 등록에 관한 자세한 내용 등록 페이지를 참조하세요.
미디어 로드
Android TV 앱에서 이미 딥 링크 지원을 구현했다면 Android TV 매니페스트에도 유사한 정의가 구성되어 있어야 합니다.
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
<data android:host="www.example.com"/>
<data android:pathPattern=".*"/>
</intent-filter>
</activity>
발신자의 항목별 로드
발신자의 경우, 미디어에서 entity
를 설정하여 딥 링크를 전달할 수 있습니다.
정보를 제공합니다.
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
Chromium 브라우저 버전 필요
M87
이상
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
로드 명령어는 딥 링크 및 패키지 이름이 있는 인텐트를 통해 전송됩니다. 확인할 수 있습니다.
발신기에 ATV 사용자 인증 정보 설정
웹 수신기 앱과 Android TV 앱이 서로 다른 지원을 제공할 수도 있습니다.
딥 링크 및 credentials
(예: 인증을 처리하는 경우)
다르게 나타납니다. 이 문제를 해결하려면
Android TV용 entity
및 credentials
:
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") mediaInfoBuilder.atvEntity = "myscheme://example.com/atv/some-id" ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" mediaLoadRequestDataBuilder.atvCredentials = "atv-user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
Chromium 브라우저 버전 필요
M87
이상
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; mediaInfo.atvEntity = 'myscheme://example.com/atv/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; request.atvCredentials = 'atv-user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
웹 수신기 앱이 실행되면 다음에서 entity
및 credentials
를 사용합니다.
지정할 수 있습니다 하지만 Android TV 앱이 실행되면 SDK가
atvEntity
및 atvCredentials
의 entity
및 credentials
(지정된 경우)입니다.
Content ID 또는 MediaQueueData로 로드
entity
또는 atvEntity
를 사용하지 않고 Content ID 또는
미디어 정보의 콘텐츠 URL 또는 더 자세한 미디어 로드 사용
데이터를 요청하려면 다음과 같은 사전 정의된 인텐트 필터를
Android TV 앱:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
발신자 측에서는 항목별 로드와 마찬가지로
내 콘텐츠 정보로 로드 요청을 만들고 load()
를 호출할 수 있습니다.
val mediaToLoad = MediaInfo.Builder("some-id").build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id").build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(contentId: "some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
Chromium 브라우저 버전 필요
M87
이상
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); ... let request = new chrome.cast.media.LoadRequest(mediaInfo); ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
로드 요청 처리
활동에서 이러한 로드 요청을 처리하려면 다음과 같이 설정합니다.
class MyActivity : Activity() { override fun onStart() { super.onStart() val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). override fun onNewIntent(intent: Intent) { val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
public class MyActivity extends Activity { @Override protected void onStart() { super.onStart(); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(getIntent())) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). @Override protected void onNewIntent(Intent intent) { MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
MediaManager
인 경우
해당 인텐트가 로드 인텐트임을 감지하면
MediaLoadRequestData
객체를 삭제하고
MediaLoadCommandCallback.onLoad()
:
로드 요청을 처리하려면 이 메서드를 재정의해야 합니다. 콜백은
다음 날짜 이전에 등록할 수 있습니다.
MediaManager.onNewIntent()
드림
(활동 또는 애플리케이션에 있는 것이 좋음) onCreate()
메서드를 사용하세요.
class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback()) } } class MyMediaLoadCommandCallback : MediaLoadCommandCallback() { override fun onLoad( senderId: String?, loadRequestData: MediaLoadRequestData ): Task{ return Tasks.call { // Resolve the entity into your data structure and load media. val mediaInfo = loadRequestData.getMediaInfo() if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw MediaException( MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build() ) } myFillMediaInfo(MediaInfoWriter(mediaInfo)) myPlayerLoad(mediaInfo.getContentUrl()) // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData) ... castReceiverContext.getMediaManager().broadcastMediaStatus() // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData } } private fun myPlayerLoad(contentURL: String) { myPlayer.load(contentURL) // Update the MediaSession state. val playbackState: PlaybackStateCompat = Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis() ) ... .build() mediaSession.setPlaybackState(playbackState) }
public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaLoadCommandCallback(new MyMediaLoadCommandCallback()); } } public class MyMediaLoadCommandCallback extends MediaLoadCommandCallback { @Override public TaskonLoad(String senderId, MediaLoadRequestData loadRequestData) { return Tasks.call(() -> { // Resolve the entity into your data structure and load media. MediaInfo mediaInfo = loadRequestData.getMediaInfo(); if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw new MediaException( new MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build()); } myFillMediaInfo(new MediaInfoWriter(mediaInfo)); myPlayerLoad(mediaInfo.getContentUrl()); // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData); ... castReceiverContext.getMediaManager().broadcastMediaStatus(); // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData; }); } private void myPlayerLoad(String contentURL) { myPlayer.load(contentURL); // Update the MediaSession state. PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis()) ... .build(); mediaSession.setPlaybackState(playbackState); }
로드 인텐트를 처리하려면 인텐트를 데이터 구조로 파싱하세요.
인코더-디코더 아키텍처를
(MediaLoadRequestData
입니다.
미디어 명령어 지원
기본 재생 컨트롤 지원
기본 통합 명령어에는 미디어와 호환되는 명령어가 포함됩니다. 세션입니다. 이러한 명령어는 미디어 세션 콜백을 통해 알림을 받습니다. 해야 할 일 이를 지원하기 위해 미디어 세션에 콜백을 등록합니다 (이 작업을 있습니다.
private class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. myPlayer.pause() } override fun onPlay() { // Resume the player and update the play state. myPlayer.play() } override fun onSeekTo(pos: Long) { // Seek and update the play state. myPlayer.seekTo(pos) } ... } mediaSession.setCallback(MyMediaSessionCallback())
private class MyMediaSessionCallback extends MediaSessionCompat.Callback { @Override public void onPause() { // Pause the player and update the play state. myPlayer.pause(); } @Override public void onPlay() { // Resume the player and update the play state. myPlayer.play(); } @Override public void onSeekTo(long pos) { // Seek and update the play state. myPlayer.seekTo(pos); } ... } mediaSession.setCallback(new MyMediaSessionCallback());
Cast 제어 명령어 지원
다음에서 사용할 수 없는 몇 가지 Cast 명령어가 있습니다.
MediaSession
님,
예:
skipAd()
또는
setActiveMediaTracks()
:
또한 일부 대기열 명령어를 여기에 구현해야 하는 이유는 전송 대기열이
MediaSession
큐와 완전히 호환되지 않습니다.
class MyMediaCommandCallback : MediaCommandCallback() { override fun onSkipAd(requestData: RequestData?): Task{ // Skip your ad ... return Tasks.forResult(null) } } val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
public class MyMediaCommandCallback extends MediaCommandCallback { @Override public TaskonSkipAd(RequestData requestData) { // Skip your ad ... return Tasks.forResult(null); } } MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaCommandCallback(new MyMediaCommandCallback());
지원되는 미디어 명령어 지정
Cast 수신기와 마찬가지로 Android TV 앱에서도 사용할 명령어를 지정해야 합니다.
발신자가 특정 UI 컨트롤을 사용 또는 사용 중지할 수 있습니다. 대상
Kubernetes의 일부인
MediaSession
님,
명령어를 사용하여
PlaybackStateCompat
추가 명령어는
MediaStatusModifier
// Set media session supported commands val playbackState: PlaybackStateCompat = PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build() mediaSession.setPlaybackState(playbackState) // Set additional commands in MediaStatusModifier val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT)
// Set media session supported commands PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build(); mediaSession.setPlaybackState(playbackState); // Set additional commands in MediaStatusModifier MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT);
지원되지 않는 버튼 숨기기
Android TV 앱이 기본 미디어 컨트롤만 지원하지만 웹 수신기는 지원하지 않는 경우 고급 제어 기능을 지원하는 앱이므로 발신기 앱이 제대로 작동하는지 올바르게 연결해야 합니다. 예를 들어 Android TV가 웹 수신기 앱이 지원하는 동안 앱은 재생 속도 변경을 지원하지 않습니다. 각 플랫폼에서 지원되는 작업을 올바르게 설정하고 발신자 앱이 UI를 올바르게 렌더링합니다.
MediaStatus 수정
트랙, 광고, 실시간, 현재 재생목록과 같은 고급 기능을 지원하려면
TV 앱이 다음을 통해 확인할 수 없는 추가 정보를 제공해야 함
MediaSession
Google은
MediaStatusModifier
드림
이 작업을 수행할 수 있습니다. MediaStatusModifier
은(는) 항상
MediaSession
:
CastReceiverContext
만들고 브로드캐스트하기
MediaStatus
:
val mediaManager: MediaManager = castReceiverContext.getMediaManager() val statusModifier: MediaStatusModifier = mediaManager.getMediaStatusModifier() statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData) mediaManager.broadcastMediaStatus()
MediaManager mediaManager = castReceiverContext.getMediaManager(); MediaStatusModifier statusModifier = mediaManager.getMediaStatusModifier(); statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData); mediaManager.broadcastMediaStatus();
클라이언트 라이브러리는 MediaSession
에서 기본 MediaStatus
를 가져옵니다.
Android TV 앱은
MediaStatus
수정자.
일부 상태와 메타데이터는 MediaSession
및
MediaStatusModifier
적극:
MediaSession
입니다. 여전히 수정자를 사용하여
MediaSession
: 수정자의 상태가 항상
MediaSession
에서 제공하는 값보다 우선순위가 높습니다.
전송하기 전에 MediaStatus 가로채기
웹 수신기 SDK와 동일하며,
보낼 때는
MediaStatusInterceptor
드림
사용하여
MediaStatus
도착
전송됩니다 여기서
MediaStatusWriter
드림
전송되기 전에 MediaStatus
를 조작합니다.
mediaManager.setMediaStatusInterceptor(object : MediaStatusInterceptor { override fun intercept(mediaStatusWriter: MediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(JSONObject("{data: \"my Hello\"}")) } })
mediaManager.setMediaStatusInterceptor(new MediaStatusInterceptor() { @Override public void intercept(MediaStatusWriter mediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(new JSONObject("{data: \"my Hello\"}")); } });
사용자 인증 정보 처리
Android TV 앱에서 특정 사용자만 앱을 실행하거나 참여하도록 허용할 수 있음 세션입니다. 예를 들어 다음과 같은 경우에만 발신자가 앱을 시작하거나 참여하도록 허용합니다.
- 발신기 앱이 ATV 앱과 동일한 계정 및 프로필에 로그인되어 있습니다.
- 발신기 앱이 ATV 앱과 동일한 계정에 로그인되어 있지만 프로필이 다릅니다.
앱에서 여러 명 또는 익명의 사용자를 처리할 수 있는 경우 모든 사용자를 추가로 허용할 수 있습니다. ATV 세션에 참여해야 합니다. 사용자가 사용자 인증 정보를 제공하면 ATV 앱이 진행 상황과 다른 사용자 데이터가 있습니다.
발신기 앱이 Android TV 앱을 실행하거나 Android TV 앱에 가입하면 발신기 앱 세션에 참여하는 사람을 나타내는 사용자 인증 정보를 제공해야 합니다.
발신자가 Android TV 앱을 실행하고 참여하기 전에 검사기를 실행하여 발신자 사용자 인증 정보가 허용되는지 확인합니다. 그렇지 않은 경우 Cast Connect SDK는 웹 수신기 실행으로 대체됩니다.
발신자 앱 실행 사용자 인증 정보 데이터
발신자 측에서는 수신자를 나타내는 CredentialsData
를 지정할 수 있습니다.
세션에 참여해야 합니다.
credentials
은 ATV의 경우 사용자가 정의할 수 있는 문자열입니다.
이해할 수 있습니다. credentialsType
는
CredentialsData
의 출처이거나 맞춤 값일 수 있습니다. 기본적으로
데이터를 보내는 데 사용합니다
CredentialsData
는 실행 중에만 Android TV 앱으로 전달됩니다.
참여할 수 있습니다. 연결된 상태에서 다시 설정하면 다음 기기로 전달되지 않습니다.
Android TV 앱 연결된 상태에서 발신자가 프로필을 전환하면
세션에 머무르거나
SessionManager.endCurrentCastSession(boolean stopCasting)
드림
새 프로필이 세션과 호환되지 않는다고 생각되는 경우
이
CredentialsData
드림
각 발신자에 대한 가져오기는
getSenders
에
CastReceiverContext
SenderInfo
,
getCastLaunchRequest()
다음을 얻으세요.
CastLaunchRequest
님,
그런 다음
getCredentialsData()
play-services-cast-framework
버전 필요
19.0.0
이상
CastContext.getSharedInstance().setLaunchCredentialsData( CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build() )
CastContext.getSharedInstance().setLaunchCredentialsData( new CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build());
google-cast-sdk
버전 v4.8.3
또는
더 높습니다.
옵션을 설정한 후 언제든지 호출할 수 있습니다.
GCKCastContext.setSharedInstanceWith(options)
GCKCastContext.sharedInstance().setLaunch( GCKCredentialsData(credentials: "{\"userId\": \"abc\"}")
Chromium 브라우저 버전 필요
M87
이상
옵션을 설정한 후 언제든지 호출할 수 있습니다.
cast.framework.CastContext.getInstance().setOptions(options);
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}"); cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
ATV 출시 요청 검사기 구현
이
CredentialsData
드림
발신자가 앱을 시작하거나 참여하려고 하면 Android TV 앱으로 전달됩니다. 다음과 같은 작업을 할 수 있습니다.
구현
LaunchRequestChecker
이 요청을 허용하거나 거부합니다.
요청이 거부되면 웹 수신기가 실행되지 않고 로드됨 ATV 앱에 기본적으로 통합되어 있습니다. ATV에서 다음 작업을 할 수 없는 경우 요청을 거부해야 합니다. 시작 또는 참여를 요청하는 사용자를 처리합니다. 예를 들어 사용자가 요청한 것보다 ATV 앱에 로그인되어 있고 앱에서 사용자 인증 정보 전환을 처리할 수 없거나, 현재 계정에 ATV 앱
요청이 허용되면 ATV 앱이 실행됩니다. 이 설정은
사용자가 로드 요청 전송을 지원하는지에 따라 다른 동작
ATV 앱에 로그인되어 있지 않거나 사용자 불일치가 있는 경우입니다. 이 동작은
LaunchRequestChecker
에서 완전히 맞춤설정할 수 있습니다.
다음을 구현하는 클래스를 만듭니다.
CastReceiverOptions.LaunchRequestChecker
드림
인터페이스에 추가되었습니다.
class MyLaunchRequestChecker : LaunchRequestChecker { override fun checkLaunchRequestSupported(launchRequest: CastLaunchRequest): Task{ return Tasks.call { myCheckLaunchRequest( launchRequest ) } } } private fun myCheckLaunchRequest(launchRequest: CastLaunchRequest): Boolean { val credentialsData = launchRequest.getCredentialsData() ?: return false // or true if you allow anonymous users to join. // The request comes from a mobile device, e.g. checking user match. return if (credentialsData.credentialsType == CredentialsData.CREDENTIALS_TYPE_ANDROID) { myCheckMobileCredentialsAllowed(credentialsData.getCredentials()) } else false // Unrecognized credentials type. }
public class MyLaunchRequestChecker implements CastReceiverOptions.LaunchRequestChecker { @Override public TaskcheckLaunchRequestSupported(CastLaunchRequest launchRequest) { return Tasks.call(() -> myCheckLaunchRequest(launchRequest)); } } private boolean myCheckLaunchRequest(CastLaunchRequest launchRequest) { CredentialsData credentialsData = launchRequest.getCredentialsData(); if (credentialsData == null) { return false; // or true if you allow anonymous users to join. } // The request comes from a mobile device, e.g. checking user match. if (credentialsData.getCredentialsType().equals(CredentialsData.CREDENTIALS_TYPE_ANDROID)) { return myCheckMobileCredentialsAllowed(credentialsData.getCredentials()); } // Unrecognized credentials type. return false; }
그런 다음
ReceiverOptionsProvider
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(MyLaunchRequestChecker()) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(new MyLaunchRequestChecker()) .build(); } }
다음에서 true
해결
LaunchRequestChecker
ATV 앱을 실행하고 false
는 Web Receiver 앱을 실행합니다.
전송 및 사용자 지정 메시지 수신
Cast 프로토콜을 사용하면 보낸 사람과 맞춤 문자열 메시지를
할 수 있습니다. 전송하려면 네임스페이스 (채널)를 등록해야 합니다.
메시지 전체의
CastReceiverContext
Android TV - 맞춤 네임스페이스 지정
지원되는 네임스페이스를
CastReceiverOptions
드림
다음 단계를 따르세요.
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace") ) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace")) .build(); } }
Android TV—메시지 전송
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString)
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString);
Android TV: 맞춤 네임스페이스 메시지 수신
class MyCustomMessageListener : MessageReceivedListener { override fun onMessageReceived( namespace: String, senderId: String?, message: String ) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());
class MyCustomMessageListener implements CastReceiverContext.MessageReceivedListener { @Override public void onMessageReceived( String namespace, String senderId, String message) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());