Register media instance with session and prepare communication with embedder

This commit is contained in:
Fernando Jiménez Moreno 2019-10-08 10:18:13 +02:00
parent 9c329a7935
commit 4d147d2c56
8 changed files with 107 additions and 4 deletions

View file

@ -474,6 +474,9 @@ pub struct Constellation<Message, LTF, STF> {
/// Mechanism to force the compositor to process events.
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
/// Browser ID of the active media session, if any.
active_media_session: Option<TopLevelBrowsingContextId>,
}
/// State needed to construct a constellation.
@ -843,6 +846,7 @@ where
glplayer_threads: state.glplayer_threads,
player_context: state.player_context,
event_loop_waker: state.event_loop_waker,
active_media_session: None,
};
constellation.run();
@ -1774,6 +1778,9 @@ where
new_value,
);
},
FromScriptMsg::MediaSessionEventMsg(browser_id, event) => {
// TODO
},
}
}

View file

@ -15,8 +15,10 @@ use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaE
use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods;
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId};
use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId};
use crate::dom::bindings::codegen::UnionTypes::{
@ -78,6 +80,7 @@ use net_traits::request::{Destination, Referrer};
use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata};
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
use script_layout_interface::HTMLMediaData;
use script_traits::MediaSessionEvent;
use servo_config::pref;
use servo_media::player::audio::AudioRenderer;
use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
@ -592,7 +595,6 @@ impl HTMLMediaElement {
match (old_ready_state, ready_state) {
(ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
task_source.queue_simple_event(self.upcast(), atom!("loadedmetadata"), &window);
// No other steps are applicable in this case.
return;
},
@ -1883,6 +1885,12 @@ impl HTMLMediaElement {
self.media_element_load_algorithm();
}
}
fn send_media_session_event(&self, event: MediaSessionEvent) {
let global = self.global();
let media_session = global.as_window().Navigator().MediaSession();
media_session.send_event(event);
}
}
// XXX Placeholder for [https://github.com/servo/servo/issues/22293]

View file

@ -11,12 +11,13 @@ use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionMe
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionPlaybackState;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::htmlmediaelement::HTMLMediaElement;
use crate::dom::mediametadata::MediaMetadata;
use crate::dom::window::Window;
use crate::script_thread::ScriptThread;
use dom_struct::dom_struct;
use msg::constellation_msg::TopLevelBrowsingContextId;
use script_traits::MediaSessionActionType;
use script_traits::{MediaSessionActionType, MediaSessionEvent, ScriptMsg};
use std::collections::HashMap;
use std::rc::Rc;
@ -30,6 +31,9 @@ pub struct MediaSession {
/// https://w3c.github.io/mediasession/#supported-media-session-actions
#[ignore_malloc_size_of = "Rc"]
action_handlers: DomRefCell<HashMap<MediaSessionActionType, Rc<MediaSessionActionHandler>>>,
/// The media instance controlled by this media session.
/// For now only HTMLMediaElements are controlled by media sessions.
media_instance: MutNullableDom<HTMLMediaElement>,
}
impl MediaSession {
@ -40,6 +44,7 @@ impl MediaSession {
metadata: Default::default(),
playback_state: DomRefCell::new(MediaSessionPlaybackState::None),
action_handlers: DomRefCell::new(HashMap::new()),
media_instance: Default::default(),
};
ScriptThread::register_media_session(&media_session, browsing_context_id);
media_session
@ -63,6 +68,18 @@ impl MediaSession {
}
// TODO default action.
}
pub fn send_event(&self, event: MediaSessionEvent) {
let global = self.global();
let browser_id = global
.as_window()
.window_proxy()
.top_level_browsing_context_id();
let _ = global
.script_to_constellation_chan()
.send(ScriptMsg::MediaSessionEventMsg(browser_id, event))
.unwrap();
}
}
impl MediaSessionMethods for MediaSession {

View file

@ -21,6 +21,7 @@ use crate::dom::promise::Promise;
use crate::dom::serviceworkercontainer::ServiceWorkerContainer;
use crate::dom::window::Window;
use crate::dom::xr::XR;
use crate::script_thread::ScriptThread;
use dom_struct::dom_struct;
use std::rc::Rc;
@ -192,7 +193,24 @@ impl NavigatorMethods for Navigator {
/// https://w3c.github.io/mediasession/#dom-navigator-mediasession
fn MediaSession(&self) -> DomRoot<MediaSession> {
self.mediasession
.or_init(|| MediaSession::new(self.global().as_window()))
self.mediasession.or_init(|| {
// There is a single MediaSession instance per top level browsing context
// and only one active MediaSession globally.
//
// MediaSession creation can happen in two cases:
//
// - If content gets `navigator.mediaSession`
// - If a media instance (HTMLMediaElement so far) starts playing media.
//
// The MediaSession constructor is in charge of registering itself with
// the script thread.
let global = self.global();
let window = global.as_window();
let browsing_context_id = window.window_proxy().top_level_browsing_context_id();
match ScriptThread::get_media_session(browsing_context_id) {
Some(session) => session,
None => MediaSession::new(window),
}
})
}
}

View file

@ -3993,6 +3993,19 @@ impl ScriptThread {
.remove(&browsing_context_id);
})
}
pub fn get_media_session(
browsing_context_id: TopLevelBrowsingContextId,
) -> Option<DomRoot<MediaSession>> {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = unsafe { &*root.get().unwrap() };
script_thread
.media_sessions
.borrow()
.get(&browsing_context_id)
.map(|s| DomRoot::from_ref(&**s))
})
}
}
impl Drop for ScriptThread {

View file

@ -1087,3 +1087,34 @@ pub enum MediaSessionActionType {
/// The action intent is to move the playback time to a specific time.
SeekTo,
}
/// https://w3c.github.io/mediasession/#mediametadata
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MediaMetadata {
/// Title
pub title: String,
/// Artist
pub artist: Option<String>,
/// Album
pub album: Option<String>,
}
/// https://w3c.github.io/mediasession/#enumdef-mediasessionplaybackstate
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum MediaSessionPlaybackState {
/// The browsing context does not specify whether its playing or paused.
None_,
/// The browsing context has paused media and it can be resumed.
Playing,
/// The browsing context is currently playing media and it can be paused.
Paused,
}
/// Type of events sent from script to the constellation about the media session.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum MediaSessionEvent {
/// Indicates that the media metadata is available.
SetMetadata(MediaMetadata),
/// Indicates that the playback state has changed.
PlaybackStateChange(MediaSessionPlaybackState),
}

View file

@ -8,6 +8,7 @@ use crate::DocumentState;
use crate::IFrameLoadInfoWithData;
use crate::LayoutControlMsg;
use crate::LoadData;
use crate::MediaSessionEvent;
use crate::MessagePortMsg;
use crate::PortMessageTask;
use crate::StructuredSerializedData;
@ -254,6 +255,9 @@ pub enum ScriptMsg {
GetScreenSize(IpcSender<DeviceIntSize>),
/// Get the available screen size (pixel)
GetScreenAvailSize(IpcSender<DeviceIntSize>),
/// Notifies the constellation about media session events
/// (i.e. when there is metadata for the active media session, playback state changes...).
MediaSessionEventMsg(TopLevelBrowsingContextId, MediaSessionEvent),
}
impl fmt::Debug for ScriptMsg {
@ -305,6 +309,7 @@ impl fmt::Debug for ScriptMsg {
GetClientWindow(..) => "GetClientWindow",
GetScreenSize(..) => "GetScreenSize",
GetScreenAvailSize(..) => "GetScreenAvailSize",
MediaSessionEventMsg(..) => "MediaSessionEventMsg",
};
write!(formatter, "ScriptMsg::{}", variant)
}

View file

@ -449,6 +449,10 @@ where
error!("Failed to store profile: {}", e);
}
},
EmbedderMsg::MediaSession(_) => {
debug!("MediaSession received");
// TODO(ferjm)
},
}
}
}