use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use serde_json::json;
use tokio::task::AbortHandle;
use tokio::time::sleep;
use crate::cdp::core::CdpCore;
use crate::protocol::Connection;
use crate::{Error, Result};
#[derive(Default)]
pub(crate) struct ScreencastShared {
pub(crate) running: bool,
pub(crate) abort: Option<AbortHandle>,
pub(crate) dir: Option<PathBuf>,
}
pub struct ChromiumScreencast {
core: Arc<CdpCore>,
}
impl ChromiumScreencast {
pub(crate) fn new(core: Arc<CdpCore>) -> Self {
Self { core }
}
pub async fn start(&self, save_dir: impl Into<PathBuf>, fps: u32) -> Result<()> {
self.stop().await?;
let dir = save_dir.into();
std::fs::create_dir_all(&dir)
.map_err(|e| Error::msg(format!("CDP: 建录像目录失败: {e}")))?;
let interval = Duration::from_secs_f64(1.0 / (fps.max(1) as f64));
let task = tokio::spawn(cast_pump(
self.core.conn.clone(),
self.core.session_id.clone(),
dir.clone(),
interval,
));
let mut g = self.core.screencast.lock().await;
g.running = true;
g.abort = Some(task.abort_handle());
g.dir = Some(dir);
Ok(())
}
pub async fn is_recording(&self) -> bool {
self.core.screencast.lock().await.running
}
pub async fn stop(&self) -> Result<PathBuf> {
let mut g = self.core.screencast.lock().await;
g.running = false;
if let Some(a) = g.abort.take() {
a.abort();
}
Ok(g.dir.clone().unwrap_or_default())
}
}
async fn cast_pump(conn: Connection, session_id: String, dir: PathBuf, interval: Duration) {
let mut n: u64 = 0;
loop {
if let Ok(r) = conn
.send(
"Page.captureScreenshot",
json!({ "format": "png" }),
Some(&session_id),
)
.await
{
if let Some(data) = r["data"].as_str() {
if let Some(bytes) = crate::util::base64_decode(data) {
let path = dir.join(format!("frame_{n:05}.png"));
let _ = std::fs::write(&path, &bytes);
n += 1;
}
}
}
sleep(interval).await;
}
}