servo-media-thread 0.1.0

A component of the servo web-engine.
Documentation
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::thread;

use ipc_channel::ipc::{IpcSender, channel};
use log::{trace, warn};
use paint_api::{WebRenderExternalImageIdManager, WebRenderImageHandlerType};
use rustc_hash::FxHashMap;
use webrender_api::ExternalImageId;

/// GL player threading API entry point that lives in the
/// constellation.
use crate::{GLPlayerMsg, GLPlayerMsgForward};

/// A GLPlayerThread manages the life cycle and message demultiplexing of
/// a set of video players with GL render.
pub struct GLPlayerThread {
    /// Map of live players.
    players: FxHashMap<u64, IpcSender<GLPlayerMsgForward>>,
    /// List of registered webrender external images.
    /// We use it to get an unique ID for new players.
    external_image_id_manager: WebRenderExternalImageIdManager,
}

impl GLPlayerThread {
    pub fn new(external_image_id_manager: WebRenderExternalImageIdManager) -> Self {
        GLPlayerThread {
            players: Default::default(),
            external_image_id_manager,
        }
    }

    pub fn start(
        external_image_id_manager: WebRenderExternalImageIdManager,
    ) -> IpcSender<GLPlayerMsg> {
        let (sender, receiver) = channel().unwrap();
        thread::Builder::new()
            .name("GLPlayer".to_owned())
            .spawn(move || {
                let mut renderer = GLPlayerThread::new(external_image_id_manager);
                loop {
                    let msg = receiver.recv().unwrap();
                    let exit = renderer.handle_msg(msg);
                    if exit {
                        return;
                    }
                }
            })
            .expect("Thread spawning failed");

        sender
    }

    /// Handles a generic GLPlayerMsg message
    #[inline]
    fn handle_msg(&mut self, msg: GLPlayerMsg) -> bool {
        trace!("processing {:?}", msg);
        match msg {
            GLPlayerMsg::RegisterPlayer(sender) => {
                let id = self
                    .external_image_id_manager
                    .next_id(WebRenderImageHandlerType::Media);
                self.players.insert(id.0, sender.clone());
                sender.send(GLPlayerMsgForward::PlayerId(id.0)).unwrap();
            },
            GLPlayerMsg::UnregisterPlayer(id) => {
                self.external_image_id_manager.remove(&ExternalImageId(id));
                if self.players.remove(&id).is_none() {
                    warn!("Tried to remove an unknown player");
                }
            },
            GLPlayerMsg::Lock(id, handler_sender) => {
                if let Some(sender) = self.players.get(&id) {
                    sender.send(GLPlayerMsgForward::Lock(handler_sender)).ok();
                }
            },
            GLPlayerMsg::Unlock(id) => {
                if let Some(sender) = self.players.get(&id) {
                    sender.send(GLPlayerMsgForward::Unlock()).ok();
                }
            },
            GLPlayerMsg::Exit => return true,
        }

        false
    }
}