1pub mod decoder;
13pub mod sync;
14pub mod output;
15pub mod drm;
16
17use anyhow::Result;
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct PlayerConfig {
23 pub source: String,
25 pub width: u32,
27 pub height: u32,
29 pub volume: f32,
31 pub looping: bool,
33 pub hw_accel: bool,
35}
36
37impl Default for PlayerConfig {
38 fn default() -> Self {
39 Self {
40 source: String::new(),
41 width: 1920,
42 height: 1080,
43 volume: 1.0,
44 looping: false,
45 hw_accel: true,
46 }
47 }
48}
49
50pub struct AcePlayer {
52 config: PlayerConfig,
53}
54
55impl AcePlayer {
56 pub fn new(config: PlayerConfig) -> Self {
57 Self { config }
58 }
59
60 pub async fn load(&mut self) -> Result<MediaInfo> {
62 decoder::probe(&self.config.source).await
63 }
64
65 pub async fn play(&self) -> Result<()> {
67 let config = self.config.clone();
68 tokio::spawn(async move {
69 if let Err(e) = _run_playback(config).await {
70 tracing::error!("Playback error: {e}");
71 }
72 });
73 Ok(())
74 }
75}
76
77async fn _run_playback(config: PlayerConfig) -> Result<()> {
78 let (video_tx, video_rx) = tokio::sync::mpsc::channel(32);
79 let (audio_tx, audio_rx) = tokio::sync::mpsc::channel(32);
80
81 let source = config.source.clone();
83 let decode_video = tokio::spawn(decoder::video::decode_stream(source.clone(), video_tx));
84 let decode_audio = tokio::spawn(decoder::audio::decode_stream(source, audio_tx));
85
86 let render = tokio::spawn(sync::av_sync::run(video_rx, audio_rx));
88
89 let _ = tokio::join!(decode_video, decode_audio, render);
90 Ok(())
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct MediaInfo {
96 pub duration_secs: f64,
97 pub width: u32,
98 pub height: u32,
99 pub has_video: bool,
100 pub has_audio: bool,
101 pub codec_video: Option<String>,
102 pub codec_audio: Option<String>,
103 pub fps: f64,
104}
105
106#[cfg(feature = "python-bindings")]
109use pyo3::prelude::*;
110
111#[cfg(feature = "python-bindings")]
112#[pymodule]
113fn ace_player_py(m: &Bound<'_, PyModule>) -> PyResult<()> {
114 m.add_class::<PyAcePlayer>()?;
115 Ok(())
116}
117
118#[cfg(feature = "python-bindings")]
119#[pyclass(name = "AcePlayer")]
120struct PyAcePlayer {
121 inner: AcePlayer,
122}
123
124#[cfg(feature = "python-bindings")]
125#[pymethods]
126impl PyAcePlayer {
127 #[new]
128 fn new(source: String) -> Self {
129 Self {
130 inner: AcePlayer::new(PlayerConfig {
131 source,
132 ..Default::default()
133 }),
134 }
135 }
136
137 fn play(&self) -> PyResult<()> {
138 let rt = tokio::runtime::Runtime::new().unwrap();
139 rt.block_on(self.inner.play()).map_err(|e| {
140 pyo3::exceptions::PyRuntimeError::new_err(e.to_string())
141 })
142 }
143}