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 ứng dụng gửi trên Android bằng SDK gửi trên Android.
Thiết bị di động hoặc máy tính xách tay là thiết bị gửi kiểm soát chế độ phát và thiết bị truyền Google Cast là thiết bị nhận hiển thị nội dung trên TV.
Khung thiết bị gửi đề cập đến tệp nhị phân thư viện lớp Cast và các tài nguyên liên kết có trong thời gian chạy trên thiết bị gửi. Ứng dụng gửi hoặc ứng dụng Cast đề cập đến một ứng dụng cũng đang chạy trên thiết bị gửi. Ứng dụng Web Receiver đề cập đến ứng dụng HTML chạy trên thiết bị hỗ trợ Cast.
Khung thiết bị gửi sử dụng thiết kế gọi lại không đồng bộ để thông báo cho ứng dụng gửi về các sự kiện và chuyển đổi giữa nhiều trạng thái của vòng đời ứng dụng Cast.
Quy trình ứ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 ứng dụng Android gửi:
- Khung Cast tự động bắt đầu
MediaRouterquá trình 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ẽ hiển thị hộp thoại Truyền có 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ố gắng chạy ứng dụng Web Receiver trên thiết bị Truyền.
- Khung gọi các lệnh gọi lại trong ứng dụng gửi để xác nhận rằng ứng dụng Web Receiver đã được chạy.
- Khung tạo kênh giao tiếp giữa ứng dụng gửi và ứng dụng Web Receiver.
- Khung sử dụng kênh giao tiếp để tải và kiểm soát chế độ phát nội dung đa phương tiện trên Web Receiver.
- Khung đồng bộ hoá trạng thái phát nội dung đa phương tiện giữa thiết bị gửi và Web Receiver: khi người dùng thực hiện các thao tác trên giao diện người dùng của thiết bị gửi, khung sẽ chuyển các yêu cầu kiểm soát nội dung đa phương tiện đó đến Web Receiver và khi Web Receiver gửi thông tin cập nhật về trạng thái nội dung đa phương tiện, khung sẽ cập nhật trạng thái của giao diện người dùng của thiết bị 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 sẽ ngắt kết nối ứng dụng 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 SDK Android của Google Cast, hãy xem Tài liệu tham khảo API 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 phần tử sau cho SDK Cast:
uses-sdk
Đặt cấp độ API Android tối thiểu và mục tiêu mà SDK Cast hỗ trợ. Hiện tại, mức tối thiểu là cấp độ API 24 và mục tiêu là cấp độ API 35.
<uses-sdk
android:minSdkVersion="24"
android:targetSdkVersion="35" />
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 bạn không triển khai giao diện của riêng mình, bạn nên sử dụng một biến thể của Theme.AppCompat khi nhắm đến phiên bản SDK Android tối thiểu là phiên bản 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 có một đối tượng singleton chung, CastContext, điều phối tất cả các tương tác của khung.
Ứng dụng của bạn phải triển khai giao diện
OptionsProvider
để cung cấp các lựa chọn cần thiết để khởi chạy singleton
CastContext. OptionsProvider cung cấp một thực thể của
CastOptions
chứa các lựa chọn ảnh hưởng đến hành vi của khung. Quan trọng nhất trong số này là mã ứng dụng Web Receiver, được dùng để lọc kết quả khám phá và chạy ứng dụng Web Receiver khi phiên Truyền bắt đầ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 dưới dạng trường siêu dữ liệu trong tệp AndroidManifest.xml của ứng dụng 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 một cách trì hoãn khi CastContext.getSharedInstance()
đượ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); } }
Tiện ích UX Truyền
Khung Truyền cung cấp các tiện ích tuân thủ Danh sách kiểm tra thiết kế Truyền:
Lớp phủ giới thiệu: Khung cung cấp một Chế độ xem tuỳ chỉnh,
IntroductoryOverlay, được hiển thị cho người dùng để thu hút sự chú ý đến nút Truyền vào lần đầu tiên có thiết bị nhận. Ứng dụng gửi có thể tuỳ chỉnh văn bản và vị trí của văn bản tiêu đề text.Nút Truyền: Nút Truyền hiển thị bất kể thiết bị Truyền có hay không. Khi người dùng nhấp vào nút Truyền lần đầu tiên, một hộp thoại Truyền sẽ xuất hiện, 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 trong khi thiết bị được kết nối, nút này sẽ hiển thị siêu dữ liệu nội dung đa phương tiện hiện tại (chẳng hạn như tiêu đề, tên của phòng thu âm 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à "biểu tượng Truyền".
Bộ điều khiển thu nhỏ: Khi người dùng đang truyền nội dung và đã rời khỏi trang nội dung hiện tại hoặc bộ điều khiển mở rộng sang màn hình khác trong ứng dụng gửi, bộ điều khiển thu nhỏ sẽ xuất hiện ở cuối màn hình để cho phép người dùng xem siêu dữ liệu nội dung đa phương tiện đang truyền và kiểm soát chế độ 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 nội dung đa phương tiện hoặc bộ điều khiển thu nhỏ, bộ điều khiển mở rộng sẽ chạy, hiển thị siêu dữ liệu nội dung đa phương tiện đang phát và cung cấp một số nút để kiểm soát chế độ phát nội dung đa phương tiệ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 đa phương tiện sẽ xuất hiện, cho biết siêu dữ liệu nội dung đa phương tiện đang truyền và các nút đ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ờ) đến màn hình khoá, một nút điều khiển màn hình khoá nội dung đa phương tiện sẽ xuất hiện, cho biết siêu dữ liệu nội dung đa phương tiện đang truyền và các nút điều khiển chế độ phát.
Hướng dẫn sau đây bao gồm nội dung mô tả 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
API
MediaRouter
của Android được thiết kế để cho phép hiển thị và phát nội dung đa phương tiện trên các thiết bị phụ.
Các ứng dụng Android sử dụng API MediaRouter phải có nút Truyền trong 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 giúp bạn thêm a
MediaRouteButton
làm a
Cast button
rất dễ dàng. Trước tiên, bạn nên thêm một mục trình đơn hoặc MediaRouteButton vào tệp xml
xác định trình đơn của bạn và sử dụng
CastButtonFactory
để kết nối 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 kế thừa từ
FragmentActivity,
bạn có thể thêm
MediaRouteButton
vào bố cục của mình.
// 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); }
Để đặt giao diện của nút Truyền bằng một giao diện, hãy xem bài viết Tuỳ chỉnh nút Truyền.
Định cấu hình quá trình khám phá thiết bị
Quá trình khám phá thiết bị hoàn toàn do the
CastContext quản lý.
Khi khởi chạy CastContext, ứng dụng gửi sẽ chỉ định mã ứng dụng Web Receiver
và có thể tuỳ ý yêu cầu lọc không gian tên bằng cách đặt
supportedNamespaces trong
CastOptions.
CastContext giữ một tham chiếu đến MediaRouter bên trong 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 độ trễ khám phá thiết bị và mức sử dụng pin, quá trình khám phá đôi khi sẽ tự động bắt đầu khi ứng dụng gửi chuyển sang nền trước.
- Hộp thoại Truyền đang mở.
- SDK Cast đang cố gắng khôi phục 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 gửi chuyển sang 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 Cast giới thiệu khái niệm về phiên Truyền, việc thiết lập phiên này kết hợp các bước kết nối với thiết bị, chạy (hoặc tham gia) ứng dụng Web Receiver, kết nối với ứng dụng đó và khởi chạy kênh kiểm soát nội dung đa phương tiện. Hãy xem hướng dẫn về Vòng đời ứng dụng Web Receiver để biết thêm thông tin về các phiên Truyền và vòng đời Web Receiver.
Các phiên được quản lý bởi lớp
SessionManager,
mà ứng dụng của bạn có thể truy cập thông 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 hiện đang hoạt động
thông qua
SessionManager.getCurrentCastSession().
Ứng dụng của bạn có thể sử dụng lớp
SessionManagerListener
để theo dõi các sự kiện phiên, chẳng hạn như tạo, tạm ngưng, tiếp tục và
chấm dứt. Khung sẽ tự động cố gắng tiếp tục từ quá trình chấm dứt bất thường/đột ngột trong khi phiên đang hoạt động.
Các phiên được tạo và huỷ tự động để phản hồi các cử chỉ của người dùng từ hộp thoại MediaRouter.
Để hiểu rõ hơn về các lỗi khi bắt đầu Truyền, ứ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 được ghi lại dưới dạng lỗi.
Nếu cần biết về các thay đổi về trạng thái của phiên, bạn có thể triển khai SessionManagerListener. Ví dụ này theo dõi tính khả dụng 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 đổi phiên phát trực tuyến
Việc duy trì trạng thái phiên là cơ sở của quá trình chuyển đổi phiên phát trực tuyến, trong đó người dùng có thể di chuyển các luồng âm thanh và video hiện có trên các thiết bị bằng lệnh thoại, ứng dụng Google Home hoặc màn hình thông minh. Nội dung đa phương tiện ngừng phát trên một thiết bị (nguồn) và tiếp tục trên một thiết bị khác (đích). Mọi thiết bị Truyền có chương trình cơ sở mới nhất đều có thể đóng vai trò là nguồn hoặc đích trong quá trình chuyển đổi phiên phát trực tuyến.
Để nhận thiết bị đích mới trong quá trình chuyển đổi phiên phát trực tuyến hoặc mở rộng, hãy đăng ký Cast.Listener bằng CastSession#addCastListener.
Sau đó, hãy gọi
CastSession#getCastDevice()
trong lệnh gọi lại onDeviceNameChanged.
Hãy xem bài viết Chuyển đổi phiên phát trực tuyến trên Web Receiver để biết thêm thông tin.
Tự động kết nối lại
Khung cung cấp
ReconnectionService
mà ứng dụng gửi có thể bật để xử lý việc kết nối lại trong nhiều trường hợp nhỏ, chẳng hạn như:
- Khôi phục sau khi mất kết nối Wi-Fi tạm thời
- Khôi phục sau khi thiết bị chuyển sang chế độ ngủ
- Khôi phục sau khi ứng dụng chuyển sang 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à có thể tắt trong
CastOptions.Builder.
Dịch vụ này có thể tự động hợp nhất vào tệp kê khai của ứng dụng nếu tính năng tự động hợp nhất được bật trong tệp gradle.
Khung sẽ bắt đầu dịch vụ khi có phiên nội dung đa phương tiện và dừng dịch vụ này khi phiên nội dung đa phương tiện kết thúc.
Cách hoạt động của tính năng Kiểm soát nội dung đa phương tiện
Khung Truyền không dùng lớp
RemoteMediaPlayer
từ Cast 2.x mà dùng lớp mới
RemoteMediaClient,
cung cấp cùng chức năng trong một tập hợp API thuận tiện hơn và
tránh phải truyền vào GoogleApiClient.
Khi ứng dụng của bạn thiết lập
CastSession
với một ứng dụng Web Receiver hỗ trợ không gian tên nội dung đa phương tiện, một thực thể của
RemoteMediaClient sẽ tự động được khung tạo; ứng dụng của bạn có thể
truy cập vào thực thể này bằng cách gọi phương thức getRemoteMediaClient() trên thực thể CastSession.
Tất cả các phương thức của RemoteMediaClient đưa ra yêu cầu đến Web Receiver sẽ trả về đối tượng PendingResult có thể dùng để theo dõi yêu cầu đó.
Dự kiến thực thể của RemoteMediaClient có thể được
nhiều phần của ứng dụng chia sẻ và thực sự là một số thành phần nội bộ của
khung, chẳng hạn như bộ điều khiển thu nhỏ liên tục và
dịch vụ thông báo.
Để đạt được mục tiêu đó, thực thể này hỗ trợ việc đăng ký nhiều thực thể của RemoteMediaClient.Listener.
Đặt siêu dữ liệu nội dung đa phương tiện
Lớp
MediaMetadata
biểu thị thông tin về một mục nội dung đa phương tiện mà bạn muốn Truyền. Ví dụ sau đây tạo một thực thể MediaMetadata mới của một bộ phim và đặt 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))));
Hãy xem phần Chọn hình ảnh để biết cách sử dụng hình ảnh có siêu dữ liệu nội dung đa phương tiện.
Tải nội dung đa phương tiệ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. Trước tiên, hãy sử dụng
MediaInfo.Builder
với siêu dữ liệu của nội dung đa phương tiện để tạo thực thể
MediaInfo. Lấy
RemoteMediaClient
từ CastSession hiện tại, sau đó tải MediaInfo vào
RemoteMediaClient đó. Sử dụng RemoteMediaClient để phát, tạm dừng và kiểm soát ứng dụng trình phát nội dung đa phương tiện đang chạy trên Web Receiver.
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 về cách sử dụng bản nhạc.
Định dạng video 4K
Để kiểm tra định dạng video của nội dung đa phương tiện, hãy sử dụng
getVideoInfo()
trong MediaStatus để lấy thực thể
VideoInfohiện tại.
Thực thể này chứa loại định dạng TV HDR và chiều cao cũng như chiều rộng hiển thị tính theo pixel. Các biến thể của định dạng 4K được biểu thị bằng các 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ạng sẽ nhận được thông báo để cho phép họ kiểm soát chế độ phát. Bất kỳ ai có thiết bị nhận được thông báo như vậy đều có thể tắt thông báo cho thiết bị đó trong ứng dụng Cài đặt tại Google > Google Cast > Hiện thông báo đ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 chi tiết, hãy xem bài viết Thông báo điều khiển từ xa của tính năng Truyền.
Thêm bộ điều khiển thu nhỏ
Theo Danh sách kiểm tra thiết kế Truyền, ứng dụng gửi phải cung cấp một nút điều khiển liên tục được gọi là bộ điều khiển thu nhỏ sẽ xuất hiện khi người dùng rời khỏi trang nội dung hiện tại để chuyển đến một phần khác của ứng dụng gửi. Bộ điều khiển thu nhỏ cung cấp lời nhắc hữu hình cho người dùng về phiên Truyền hiện tại. Bằng cách nhấn vào bộ điều khiển thu 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 tính năng Truyền.
Khung cung cấp một Chế độ xem tuỳ chỉnh, MiniControllerFragment, mà bạn có thể thêm vào cuối tệp bố cục của từng hoạt động mà bạn muốn hiển thị bộ đ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 gửi của bạn đang phát video hoặc luồng âm thanh trực tiếp, SDK sẽ tự động hiển thị nút phát/dừng thay cho nút phát/tạm dừng trong bộ điều khiển thu nhỏ.
Để đặt giao diện văn bản của tiêu đề và phụ đề của chế độ xem tuỳ chỉnh này, cũng như chọn các nút, hãy xem Tuỳ chỉnh bộ đ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ế Google Cast yêu cầu ứng dụng gửi cung cấp một bộ điều khiển mở rộng cho nội dung đa phương tiệ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 bộ điều khiển thu nhỏ.
SDK Cast cung cấp một tiện ích cho bộ điều khiển mở rộng có tên là
ExpandedControllerActivity.
Đây là một 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 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 đích thành 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 của bạn khi nội dung đa phương tiện từ xa được tải:
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 gửi của bạn đang phát video hoặc luồng âm thanh trực tiếp, SDK sẽ tự động hiển thị nút phát/dừng thay 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ác giao diện, hãy chọn các nút cần hiển thị, và thêm các nút tuỳ chỉnh, hãy xem bài viết Tuỳ chỉnh bộ điều khiển mở rộng.
Điều chỉnh âm lượng
Khung sẽ tự động quản lý âm lượng cho ứng dụng gửi. Khung sẽ tự động đồng bộ hoá ứng dụng gửi và ứng dụng Web Receiver để giao diện người dùng của thiết bị gửi 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, các nút vật lý trên thiết bị gửi có thể được dùng để thay đổi âm lượng của phiên Truyền trên Web Receiver theo mặc định cho mọi thiết bị sử dụng Jelly Bean trở lên.
Điều chỉnh â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 khiển âm lượng thiết bị Web Receiver trên
các thiết bị Android cũ hơn Jelly Bean, ứng dụng gửi phải ghi đè
dispatchKeyEvent
trong Hoạt động của chúng 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 nút điều khiển nội dung đa phương tiệ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ế Google Cast yêu cầu ứng dụng gửi
triển khai các nút điều khiển nội dung đa phương tiện trong
thông báo
và trên màn hình
khoá,
trong đó thiết bị gửi đang truyền nhưng ứng dụng gửi không có tiêu điểm. Khung
cung cấp
MediaNotificationService
và
MediaIntentReceiver
để giúp ứng dụng gửi tạo các nút điều khiển nội dung đa phương tiện trong thông báo và trên màn hình
khoá.
MediaNotificationService chạy khi thiết bị gửi đang truyền và sẽ hiển thị thông báo có hình thu nhỏ và thông tin về mục đang truyền, nút phát/tạm dừng và nút dừng.
MediaIntentReceiver là BroadcastReceiver xử lý các thao tác của người dùng từ thông báo.
Ứng dụng của bạn có thể định cấu hình thông báo và nút điều khiển nội dung đa phương tiện trên 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 cần hiển thị trong thông báo và Activity cần mở khi người dùng nhấn vào thông báo. Nếu bạn không cung cấp các thao tác một cách rõ ràng, thì các giá trị mặc định, MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK và MediaIntentReceiver.ACTION_STOP_CASTING sẽ được sử 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();
Các nút điều khiển nội dung đa phương tiện trong thông báo và trên màn hình khoá được bật theo mặc định và có thể tắt bằng cách gọi
setNotificationOptions
với giá trị null trong
CastMediaOptions.Builder.
Hiện tại, tính năng màn hình khoá được bật miễn là thông báo được 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 gửi của bạn đang phát video hoặc luồng âm thanh trực tiếp, SDK sẽ tự động hiển thị nút phát/dừng thay cho nút phát/tạm dừng trên nút điều khiển thông báo nhưng không phải nút điều khiển màn hình khoá.
Lưu ý: Để hiển thị các nút điều khiển màn hình khoá trên các thiết bị trước Lollipop,
RemoteMediaClient sẽ tự động yêu cầu tiêu điểm âm thanh thay cho bạn.
Xử lý lỗi
Các ứng dụng gửi cần xử lý tất cả các lệnh gọi lại lỗi và quyết định phản hồi tốt nhất cho từng giai đoạn của vòng đời Truyền. Ứng dụng có thể hiển thị hộp thoại lỗi cho người dùng hoặc có thể quyết định huỷ kết nối với Web Receiver.