Casting from Google Chome is a multi-step process where:
- The user clicks play on the target device in the Cast selection dialog.
There are different ways in which Chromium can play via an external device:
- Tab or screen casting, where the audio/video content is streamed directly from the browser to the Cast device (supported via the Cast protocol only).
- Communication with a receiver application hosted on a Cast device, where an identifying token is sent to the receiver application and is then used to initiate playback directly via a backend service (supported via the Cast and DIAL protocols).
Using the existing Google Chrome cast implementation from CEF is problematic for the following reasons:
- The Media Router component extension works in Chromium (enable “Media Router Component Extension” via chrome://flags), but it has dependencies on the Chrome extension system that are not currently supported by CEF (see issue #1947).
- The user experience described above is not ideal for desktop applications that embed CEF and would like to develop a fully integrated and streamlined cast experience. For example, a client application may wish to integrate the list of devices into an application-specific device picker UI and trigger playback via that UI without additional intermediate steps.
While it would be nice to support all casting methods, there is substantial complexity involved with tab or screen casting. We will therefore focus at this time on only supporting communication with a receiver application.
Chromium provides the following interfaces for device discovery, control and communication:
- MediaRouter, retrieved via MediaRouterFactory::GetApiForBrowserContext(brower_context)
-> CreateRoute(source_id, sink_id, web_contents, origin, callback) => callback receives async MediaRoute
-> GetCurrentRoutes() => returns vector<MediaRoute>
-> SendRouteMessage(route_id, message)
-> AddPresentationConnectionStateChangedCallback(route_id, callback) => callback receives async PresentationConnectionStateChangeInfo (states include connecting, closed, etc)
-> GetMediaController(route_id, MediaController, MediaStatusObserver) => ability to control/observe playback state
- MediaSinksObserver(MediaRouter, source_id, origin), registered with MediaRouter via the constructor
-> OnSinksUpdated(vector<MediaSink> sinks, vector<Origin> origins)
- MediaRoutesObserver(MediaRouter, source_id), registered with MediaRouter via the constructor
-> OnRoutesUpdated(vector<MediaRoute> routes, vector<route_id> joinable_route_ids)
- RouteMessageObserver(MediaRouter, source_id), registered with MediaRouter via the constructor
-> OnMessagesReceived(vector<RouteMessage> messages)
Identifiers are as follows:
source_id(MediaSource::Id) is an application-specific media source URN which will be already known to the client application (e.g.
urn:x-cast:com.mycompany.cast) or which can be retrieved using methods like MediaSource::FromPresentationUrl.
sink_id(MediaSink::Id) uniquely identifies a MediaSink; retrieved via MediaSinksObserver::OnSinksUpdated.
route_id(MediaRoute::Id) uniquely identifies a MediaRoute; retrieved via MediaRoute::CreateRoute, MediaRoute::GetCurrentRoutes or MediaRoutesObserver::OnRoutesUpdated.
CEF will expose this functionality via a new C/C++ API that generally follows the Chromium organization and naming patterns (e.g.
CefMediaSource). Control and callback interfaces will be distributed among these objects as seems most intuitive.