Hướng dẫn dành cho nhà phát triển này mô tả cách thêm tính năng hỗ trợ Google Cast vào thiết bị Android bằng Android Sender SDK.
Thiết bị di động hoặc máy tính xách tay là người gửi điều khiển quá trình phát và Thiết bị Google Cast là Bộ nhận hiển thị nội dung trên TV.
Khung trình gửi đề cập đến tệp nhị phân của thư viện lớp Cast và tệp liên kết có trong thời gian chạy trên trình gửi. Ứng dụng người gửi hoặc Ứng dụng truyền đề cập đến một ứng dụng cũng chạy trên trình gửi. Ứng dụng Trình nhận web đề cập đến ứng dụng HTML chạy trên thiết bị hỗ trợ Truyền.
Khung người gửi sử dụng thiết kế lệnh gọi lại không đồng bộ để thông báo cho người gửi ứng dụng của các sự kiện và để chuyển đổi giữa các trạng thái khác nhau của thời gian hoạt động của ứng dụng Truyền chu kỳ.
Luồng ứng dụng
Các bước sau đây mô tả quy trình thực thi cấp cao điển hình cho một người gửi Ứng dụng Android:
- Khung Truyền sẽ tự động khởi động
MediaRouter
khám phá thiết bị dựa trên vòng đờiActivity
. - Khi người dùng nhấp vào nút Truyền, khung sẽ trình bày giao diện Truyền hộp thoại với danh sách các Thiết bị truyền được phát hiện.
- Khi người dùng chọn một Thiết bị truyền, khung sẽ cố khởi chạy Ứng dụng Trình nhận web trên Thiết bị truyền.
- Khung này gọi các lệnh gọi lại trong ứng dụng gửi để xác nhận rằng Web Đã chạy ứng dụng receiver.
- Khung này tạo ra một kênh liên lạc giữa người gửi và Web Ứng dụng đầu nhận.
- Khung này sử dụng kênh liên lạc để tải và điều khiển nội dung nghe nhìn phát trên Web receiver.
- Khung này sẽ đồng bộ hoá trạng thái phát nội dung nghe nhìn giữa người gửi và Web receiver: khi người dùng thực hiện thao tác trên giao diện người dùng của người gửi, khung này sẽ chuyển các yêu cầu điều khiển nội dung nghe nhìn đó đến Web receiver cũng như khi Web receiver gửi thông tin cập nhật trạng thái nội dung nghe nhìn, khung sẽ cập nhật trạng thái của giao diện người dùng gửi.
- Khi người dùng nhấp vào nút Truyền để ngắt kết nối khỏi Thiết bị truyền, khung này sẽ ngắt kết nối ứng dụng của người gửi khỏi Web receiver.
Để xem danh sách đầy đủ tất cả các lớp, phương thức và sự kiện trong Google Cast Android SDK, xem Tài liệu tham khảo API người gửi của Google Cast cho Android. Các phần sau đây trình bày các bước để bạn thêm tính năng Truyền vào ứng dụng Android.
Định cấu hình tệp kê khai Android
Tệp AndroidManifest.xml của ứng dụng yêu cầu bạn định cấu hình các mục sau các phần tử cho SDK Truyền:
uses-sdk
Đặt các cấp độ API Android tối thiểu và mục tiêu mà SDK Truyền hỗ trợ. Hiện tại, mức tối thiểu là API cấp 23 và mục tiêu là API cấp 34.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Đặt giao diện của ứng dụng dựa trên phiên bản SDK Android tối thiểu. Ví dụ: nếu
nếu không triển khai giao diện của riêng mình, thì bạn nên sử dụng biến thể của
Theme.AppCompat
khi nhắm đến một phiên bản SDK Android tối thiểu
trước Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
Khởi chạy Ngữ cảnh truyền
Khung này có một đối tượng singleton toàn cầu (CastContext
) phối hợp
tất cả các tương tác của khung.
Ứng dụng của bạn phải triển khai
OptionsProvider
để cung cấp các tuỳ chọn cần thiết để khởi chạy
CastContext
singleton. OptionsProvider
cung cấp một bản sao của
CastOptions
chứa các tuỳ chọn ảnh hưởng đến hành vi của khung này. Nhiều nhất
quan trọng trong số này là ID ứng dụng Trình nhận web, được dùng để lọc
kết quả khám phá và khởi chạy ứng dụng Web receiver khi một phiên Truyền
đầu.
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; } }
Bạn phải khai báo tên đủ điều kiện của OptionsProvider
đã triển khai
làm trường siêu dữ liệu trong tệp AndroidManifest.xml của ứng dụng của người gửi:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext
được khởi chạy từng phần khi CastContext.getSharedInstance()
sẽ được gọi.
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); } }
Các tiện ích Cast UX
Khung Truyền cung cấp các tiện ích tuân thủ Thiết kế truyền Danh sách kiểm tra:
Lớp phủ giới thiệu: Khung này cung cấp một Khung hiển thị tuỳ chỉnh,
IntroductoryOverlay
! được hiển thị với người dùng để thu hút sự chú ý đến nút Truyền khi có người nhận. Ứng dụng Người gửi có thể tuỳ chỉnh văn bản và vị trí của tiêu đề văn bản.Nút truyền: Nút Truyền sẽ hiển thị bất kể các thiết bị Truyền có sẵn có hay không. Khi người dùng nhấp vào nút Truyền lần đầu tiên, hộp thoại Truyền sẽ hiển thị Bảng này liệt kê các thiết bị được phát hiện. Khi người dùng nhấp vào nút Truyền khi thiết bị được kết nối, thiết bị sẽ hiển thị siêu dữ liệu phương tiện hiện tại (chẳng hạn như tiêu đề, tên phòng thu và hình thu nhỏ) hoặc cho phép người dùng để ngắt kết nối khỏi Thiết bị truyền. "Nút truyền" đôi khi được gọi là làm "biểu tượng Truyền".
Tay điều khiển thu nhỏ: Khi người dùng đang truyền nội dung và đã rời khỏi trang hiện tại trang nội dung hoặc bộ điều khiển mở rộng sang một màn hình khác trong ứng dụng dành cho người gửi, bộ điều khiển mini hiển thị ở cuối màn hình để cho phép người dùng xem siêu dữ liệu nội dung nghe nhìn đang truyền và điều khiển việc phát.
Bộ điều khiển mở rộng: Khi người dùng đang truyền nội dung, nếu họ nhấp vào thông báo về nội dung nghe nhìn hoặc bộ điều khiển mini, bộ điều khiển mở rộng sẽ khởi chạy, hiển thị hiện đang phát siêu dữ liệu đa phương tiện và cung cấp một số nút để điều khiển việc phát nội dung nghe nhìn.
Thông báo: Chỉ dành cho Android. Khi người dùng đang truyền nội dung và rời khỏi ứng dụng gửi, một thông báo nội dung nghe nhìn sẽ được hiển thị cho biết chế độ truyền siêu dữ liệu đa phương tiện và bộ điều khiển chế độ phát.
Màn hình khoá: Chỉ dành cho Android. Khi người dùng đang truyền nội dung và di chuyển (hoặc thiết bị hết thời gian chờ) vào màn hình khóa, điều khiển màn hình khóa phương tiện được hiển thị hiển thị siêu dữ liệu nội dung nghe nhìn đang truyền và bộ điều khiển chế độ phát.
Hướng dẫn sau bao gồm mô tả về cách thêm các tiện ích này vào ứng dụng của bạn.
Thêm nút Truyền
Hệ điều hành Android
MediaRouter
Các API được thiết kế để cho phép hiển thị và phát nội dung nghe nhìn trên các thiết bị phụ.
Ứng dụng Android sử dụng API MediaRouter
phải bao gồm nút Truyền.
của giao diện người dùng, để cho phép người dùng chọn một tuyến nội dung đa phương tiện để phát nội dung đa phương tiện trên
một thiết bị phụ, chẳng hạn như Thiết bị truyền.
Khung này giúp việc thêm
MediaRouteButton
với tư cách là
Cast button
rất dễ dàng. Trước tiên, bạn phải thêm một mục trong trình đơn hoặc một MediaRouteButton
vào tệp xml
tệp xác định trình đơn của bạn và sử dụng
CastButtonFactory
để kết nối nó với khung.
// 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; }
Sau đó, nếu Activity
của bạn kế thừa từ
FragmentActivity
,
bạn có thể thêm
MediaRouteButton
vào bố cục của bạn.
// 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); }
Để thiết lập giao diện của nút Truyền bằng cách sử dụng một chủ đề, hãy xem Tuỳ chỉnh nút truyền.
Định cấu hình chế độ khám phá thiết bị
Quá trình khám phá thiết bị được quản lý hoàn toàn bằng
CastContext
.
Khi khởi chạy CastContext, ứng dụng gửi sẽ chỉ định Web receiver
mã ứng dụng và có thể tuỳ ý yêu cầu lọc không gian tên theo chế độ cài đặt
supportedNamespaces
inch
CastOptions
.
CastContext
lưu giữ tham chiếu đến MediaRouter
trong nội bộ và sẽ bắt đầu
quá trình khám phá trong các điều kiện sau:
- Dựa trên một thuật toán được thiết kế để cân bằng giữa độ trễ phát hiện thiết bị và mức sử dụng pin, tính năng khám phá đôi khi sẽ tự động bắt đầu khi ứng dụng của người gửi được đưa vào nền trước.
- Hộp thoại Truyền đang mở.
- SDK Truyền đang tìm cách khôi phục một phiên Truyền.
Quá trình khám phá sẽ dừng lại khi hộp thoại Truyền đóng hoặc ứng dụng của người gửi sẽ vào chế độ nền.
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; } }
Cách hoạt động của tính năng quản lý phiên
SDK truyền giới thiệu khái niệm về phiên Truyền, việc thiết lập kết hợp các bước kết nối với một thiết bị, khởi chạy (hoặc tham gia) một Web ứng dụng receiver, kết nối với ứng dụng đó và khởi tạo một kênh điều khiển nội dung nghe nhìn. Xem Trình nhận web Hướng dẫn về vòng đời của ứng dụng để biết thêm thông tin về các phiên Truyền và vòng đời của Trình thu phát web.
Các phiên hoạt động do lớp học quản lý
SessionManager
!
mà ứng dụng của bạn có thể truy cập qua
CastContext.getSessionManager()
.
Các phiên riêng lẻ được biểu thị bằng các lớp con của lớp
Session
.
Ví dụ:
CastSession
biểu thị các phiên có Thiết bị truyền. Ứng dụng của bạn có thể truy cập vào
Phiên truyền qua
SessionManager.getCurrentCastSession()
.
Ứng dụng của bạn có thể sử dụng
SessionManagerListener
để theo dõi các sự kiện trong phiên hoạt động, chẳng hạn như tạo, tạm ngưng, tiếp tục và
chấm dứt tài khoản. Khung này sẽ tự động tìm cách tiếp tục từ một
chấm dứt bất thường/đột ngột trong khi một phiên đang hoạt động.
Phiên hoạt động được tạo và thu gọn tự động theo cử chỉ của người dùng
khỏi hộp thoại MediaRouter
.
Để hiểu rõ hơn về lỗi khởi động tính năng Truyền, các ứng dụng có thể sử dụng
CastContext#getCastReasonCodeForCastStatusCode(int)
để chuyển đổi lỗi bắt đầu phiên thành
CastReasonCodes
.
Xin lưu ý rằng một số lỗi bắt đầu phiên (ví dụ: CastReasonCodes#CAST_CANCELLED
)
là hành vi dự kiến và không nên được ghi nhật ký dưới dạng lỗi.
Nếu cần lưu ý về các thay đổi trạng thái trong phiên, bạn có thể triển khai
SessionManagerListener
. Ví dụ này giải thích về tính sẵn có của
CastSession
trong Activity
.
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); } }
Chuyển sự kiện phát trực tiếp
Việc bảo toàn trạng thái phiên là cơ sở của quá trình chuyển luồng, trong đó người dùng có thể di chuyển luồng âm thanh và video hiện có giữa các thiết bị bằng lệnh thoại, Google Home Ứng dụng hoặc màn hình thông minh. Phương tiện ngừng phát trên một thiết bị (nguồn) và tiếp tục phát trên một thiết bị khác (nguồn) đích). Bất kỳ thiết bị Truyền nào có chương trình cơ sở mới nhất đều có thể đóng vai trò là nguồn hoặc đích trong một truyền trực tuyến.
Để có được thiết bị đích mới trong khi chuyển hoặc mở rộng luồng,
đăng ký một
Cast.Listener
sử dụng
CastSession#addCastListener
.
Sau đó gọi
CastSession#getCastDevice()
trong lệnh gọi lại onDeviceNameChanged
.
Xem Chuyển sự kiện phát trực tiếp trên Web receiver để biết thêm thông tin.
Tự động kết nối lại
Khung này cung cấp
ReconnectionService
có thể được ứng dụng người gửi cho phép xử lý việc kết nối lại theo nhiều cách
trường hợp không liên quan, chẳng hạn như:
- Khôi phục sau khi mất Wi-Fi tạm thời
- Khôi phục từ chế độ ngủ của thiết bị
- Khôi phục sau khi chạy ứng dụng ở chế độ nền
- Khôi phục nếu ứng dụng gặp sự cố
Dịch vụ này được bật theo mặc định và bạn có thể tắt trong
CastOptions.Builder
.
Dịch vụ này có thể tự động được hợp nhất vào tệp kê khai của ứng dụng nếu bạn tự động hợp nhất được bật trong tệp gradle của bạn.
Khung này sẽ bắt đầu dịch vụ khi có một phiên phát nội dung đa phương tiện và dừng dịch vụ đó khi phiên phát nội dung nghe nhìn kết thúc.
Cách hoạt động của tính năng Điều khiển nội dung nghe nhìn
Khung Cast sẽ ngừng sử dụng
RemoteMediaPlayer
một lớp từ Cast 2.x để chuyển sang một lớp mới
RemoteMediaClient
!
cung cấp chức năng tương tự trong một tập hợp API tiện lợi hơn và
tránh phải truyền GoogleApiClient.
Khi ứng dụng của bạn thiết lập một
CastSession
thông qua ứng dụng Web receiver hỗ trợ không gian tên đa phương tiện, ví dụ:
RemoteMediaClient
sẽ do khung này tạo tự động; ứng dụng của bạn có thể
truy cập bằng cách gọi phương thức getRemoteMediaClient()
trên CastSession
thực thể.
Tất cả các phương thức của RemoteMediaClient
đưa ra yêu cầu đến Trình nhận web sẽ
trả về một đối tượng PendingResult có thể dùng để theo dõi yêu cầu đó.
Theo dự kiến, thực thể của RemoteMediaClient
có thể được chia sẻ bởi
nhiều phần của ứng dụng và thực sự một số thành phần nội bộ của
khung, chẳng hạn như bộ điều khiển mini cố định và
dịch vụ thông báo.
Do đó, phiên bản này hỗ trợ đăng ký nhiều phiên bản
RemoteMediaClient.Listener
.
Thiết lập siêu dữ liệu đa phương tiện
Chiến lược phát hành đĩa đơn
MediaMetadata
cho biết thông tin về mục nội dung đa phương tiện bạn muốn Truyền. Chiến lược phát hành đĩa đơn
ví dụ sau đây sẽ tạo một phiên bản MediaMetadata mới của một bộ phim và thiết lập giá trị
tiêu đề, phụ đề và hai hình ảnh.
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))));
Xem Lựa chọn hình ảnh về việc sử dụng hình ảnh cùng với siêu dữ liệu phương tiện.
Tải nội dung nghe nhìn
Ứng dụng của bạn có thể tải một mục nội dung đa phương tiện, như minh hoạ trong mã sau. Lần sử dụng đầu tiên
MediaInfo.Builder
với siêu dữ liệu của phương tiện truyền thông để tạo
MediaInfo
thực thể. Tải
RemoteMediaClient
từ CastSession
hiện tại, rồi tải MediaInfo
vào đó
RemoteMediaClient
. Sử dụng RemoteMediaClient
để phát, tạm dừng và làm những việc khác
điều khiển ứng dụng trình phát nội dung đa phương tiện chạy trên Bộ nhận web.
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());
Ngoài ra, hãy xem phần này trên bằng các bản nhạc nội dung nghe nhìn.
Định dạng video 4K
Để kiểm tra xem nội dung nghe nhìn của bạn có định dạng video nào, hãy sử dụng
getVideoInfo()
trong MediaStatus để nhận bản sao hiện tại của
VideoInfo
.
Phiên bản này chứa loại định dạng TV HDR và chiều cao hiển thị
và chiều rộng tính bằng pixel. Biến thể của định dạng 4K được biểu thị bằng hằng số
HDR_TYPE_*
.
Thông báo điều khiển từ xa đến nhiều thiết bị
Khi người dùng đang truyền, các thiết bị Android khác trên cùng một mạng sẽ nhận được một thông báo để cho phép họ điều khiển quá trình phát. Bất cứ ai có thiết bị nhận được các thông báo như vậy có thể tắt chúng cho thiết bị đó trong Cài đặt ứng dụng của Google > Google Cast > Hiển thị thông báo trên điều khiển từ xa. (Thông báo bao gồm lối tắt đến ứng dụng Cài đặt.) Để biết thêm thông tin, hãy xem Truyền thông báo điều khiển từ xa.
Thêm tay điều khiển thu nhỏ
Theo Cast Design Danh sách kiểm tra, ứng dụng của người gửi sẽ cung cấp chế độ kiểm soát liên tục được gọi là cơ chế tay điều khiển sẽ xuất hiện khi người dùng điều hướng khỏi trang nội dung hiện tại để một phần khác của ứng dụng dành cho người gửi. Bộ điều khiển mini đưa ra một lời nhắc rõ ràng tới người dùng của phiên Truyền hiện tại. Khi nhấn vào trình điều khiển nhỏ, người dùng có thể quay lại chế độ xem bộ điều khiển mở rộng toàn màn hình của chế độ Truyền.
Khung này cung cấp một Thành phần hiển thị tuỳ chỉnh, MiniControllerFragment mà bạn có thể thêm vào cuối tệp bố cục của mỗi hoạt động mà bạn muốn hiển thị tay điều khiển thu nhỏ.
<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" />
Khi ứng dụng của người gửi đang phát video hoặc âm thanh trực tiếp, SDK tự động hiển thị nút phát/dừng ở vị trí cho nút phát/tạm dừng trên trình điều khiển mini.
Để đặt giao diện văn bản cho tiêu đề và phụ đề của chế độ xem tuỳ chỉnh này, và để chọn các nút, hãy xem Tuỳ chỉnh Tay điều khiển thu nhỏ.
Thêm bộ điều khiển mở rộng
Danh sách kiểm tra thiết kế của Google Cast yêu cầu ứng dụng gửi yêu cầu ứng dụng gửi được mở rộng tay điều khiển cho nội dung nghe nhìn đang được Truyền. Bộ điều khiển mở rộng là phiên bản toàn màn hình của trình điều khiển mini.
Cast SDK cung cấp một tiện ích cho bộ điều khiển mở rộng có tên là
ExpandedControllerActivity
.
Đây là lớp trừu tượng mà bạn phải tạo lớp con để thêm nút Truyền.
Trước tiên, hãy tạo một tệp tài nguyên trình đơn mới cho bộ điều khiển mở rộng để cung cấp nút Truyền:
<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>
Tạo một lớp mới mở rộng 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; } }
Bây giờ, hãy khai báo hoạt động mới của bạn trong tệp kê khai ứng dụng bên trong thẻ 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>
Chỉnh sửa CastOptionsProvider
và thay đổi NotificationOptions
và
CastMediaOptions
để đặt hoạt động mục tiêu cho hoạt động mới của bạn:
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(); }
Cập nhật phương thức LocalPlayerActivity
loadRemoteMedia
để hiển thị
hoạt động mới khi tải nội dung nghe nhìn từ xa:
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()); }
Khi ứng dụng của người gửi đang phát video hoặc âm thanh trực tiếp, SDK tự động hiển thị nút phát/dừng ở vị trí cho nút phát/tạm dừng trong bộ điều khiển mở rộng.
Để đặt giao diện bằng cách sử dụng chủ đề, hãy chọn nút để hiển thị, và thêm các nút tuỳ chỉnh, hãy xem Tuỳ chỉnh Bộ điều khiển mở rộng.
Điều chỉnh âm lượng
Khung này tự động quản lý âm lượng cho ứng dụng dành cho người gửi. Khung tự động đồng bộ hoá người gửi và các ứng dụng Trình nhận trên web để người gửi Giao diện người dùng luôn báo cáo âm lượng do Web receiver chỉ định.
Điều chỉnh âm lượng bằng nút vật lý
Trên Android, bạn có thể sử dụng các nút vật lý trên thiết bị gửi để thay đổi âm lượng của phiên Truyền trên Bộ nhận web theo mặc định đối với mọi thiết bị sử dụng Jelly Bean trở lên.
Kiểm soát âm lượng bằng nút vật lý trước Jelly Bean
Để sử dụng các phím âm lượng vật lý để điều chỉnh âm lượng của thiết bị Web nhận
Thiết bị Android cũ hơn Jelly Bean, ứng dụng của người gửi sẽ ghi đè
dispatchKeyEvent
trong Hoạt động của họ và gọi
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); } }
Thêm các chế độ điều khiển nội dung nghe nhìn vào thông báo và màn hình khoá
Chỉ trên Android, Danh sách kiểm tra thiết kế của Google Cast yêu cầu ứng dụng gửi
triển khai các chế độ kiểm soát nội dung nghe nhìn trong
thông báo
và trong khoá
màn hình,
trong đó người gửi đang truyền nhưng ứng dụng của người gửi không có tâm điểm. Chiến lược phát hành đĩa đơn
khung cung cấp
MediaNotificationService
và
MediaIntentReceiver
để giúp ứng dụng của người gửi tạo các chế độ điều khiển nội dung nghe nhìn trong một thông báo và trong khoá
màn hình.
MediaNotificationService
sẽ chạy khi người gửi đang truyền và sẽ hiển thị một
thông báo kèm theo hình thu nhỏ của hình ảnh và thông tin về quá trình truyền hiện tại
mục, nút phát/tạm dừng và nút dừng.
MediaIntentReceiver
là một BroadcastReceiver
xử lý các thao tác của người dùng
thông báo đó.
Ứng dụng của bạn có thể định cấu hình chế độ điều khiển thông báo và nội dung nghe nhìn từ màn hình khoá thông qua
NotificationOptions
.
Ứng dụng của bạn có thể định cấu hình các nút điều khiển sẽ hiển thị trong thông báo, và
Activity
để mở khi người dùng nhấn vào thông báo. Nếu hành động
không được cung cấp rõ ràng, các giá trị mặc định,
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
và
MediaIntentReceiver.ACTION_STOP_CASTING
sẽ được dùng.
// 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();
Hiện các chế độ điều khiển nội dung nghe nhìn từ thông báo và màn hình khoá được bật bằng
mặc định và có thể tắt bằng cách gọi
setNotificationOptions
có giá trị rỗng trong
CastMediaOptions.Builder
.
Hiện tại, tính năng màn hình khoá sẽ bật, miễn là bạn có thông báo
đã bật.
// ... 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();
Khi ứng dụng của người gửi đang phát video hoặc âm thanh trực tiếp, SDK tự động hiển thị nút phát/dừng ở vị trí cho nút phát/tạm dừng trên điều khiển thông báo chứ không phải điều khiển màn hình khoá.
Lưu ý: Để hiển thị điều khiển màn hình khóa trên các thiết bị sử dụng Lollipop,
RemoteMediaClient
sẽ thay mặt bạn tự động yêu cầu quyền phát âm thanh.
Xử lý lỗi
Điều quan trọng là ứng dụng của người gửi phải xử lý tất cả các lệnh gọi lại báo lỗi và quyết định phản hồi tốt nhất cho từng giai đoạn trong vòng đời của tính năng Truyền. Ứng dụng có thể hiển thị hộp thoại báo lỗi cho người dùng hoặc có thể quyết định chia nhỏ kết nối Web receiver.