1use std::path::Path;
30use std::process::Command;
31use std::time::Duration;
32
33use error::FfProbeError;
34#[cfg(feature = "streams")]
35mod attachment_stream;
36#[cfg(feature = "streams")]
37mod audio_stream;
38#[cfg(feature = "chapters")]
39mod chapter;
40mod config;
41#[cfg(feature = "streams")]
42mod data_stream;
43#[cfg(feature = "streams")]
44mod disposition;
45pub mod error;
46mod ffprobe;
47#[cfg(feature = "format")]
48mod format;
49mod ratio;
50#[cfg(feature = "streams")]
51mod streams;
52#[cfg(feature = "streams")]
53mod subtitle_stream;
54#[cfg(feature = "streams")]
55mod video_stream;
56
57#[cfg(feature = "streams")]
58pub use attachment_stream::AttachmentStream;
59#[cfg(feature = "streams")]
60pub use attachment_stream::AttachmentTags;
61#[cfg(feature = "streams")]
62pub use audio_stream::AudioStream;
63#[cfg(feature = "streams")]
64pub use audio_stream::AudioTags;
65#[cfg(feature = "chapters")]
66pub use chapter::Chapter;
67#[cfg(feature = "chapters")]
68pub use chapter::ChapterTags;
69pub use config::Config;
70#[cfg(feature = "streams")]
71pub use data_stream::DataStream;
72#[cfg(feature = "streams")]
73pub use data_stream::DataTags;
74#[cfg(feature = "streams")]
75pub use disposition::Disposition;
76pub use ffprobe::FfProbe;
77#[cfg(feature = "format")]
78pub use format::Format;
79#[cfg(feature = "format")]
80pub use format::FormatTags;
81pub use ratio::Ratio;
82use serde::Deserialize;
83use serde::Deserializer;
84#[cfg(feature = "streams")]
85pub use streams::SideData;
86#[cfg(feature = "streams")]
87pub use streams::Stream;
88#[cfg(feature = "streams")]
89pub use streams::StreamKinds;
90#[cfg(feature = "streams")]
91pub use streams::StreamTags;
92#[cfg(feature = "streams")]
93pub use subtitle_stream::SubtititleTags;
94#[cfg(feature = "streams")]
95pub use subtitle_stream::SubtitleStream;
96#[cfg(feature = "streams")]
97pub use video_stream::VideoStream;
98#[cfg(feature = "streams")]
99pub use video_stream::VideoTags;
100pub fn ffprobe(path: impl AsRef<Path>) -> Result<FfProbe, FfProbeError> {
104 ffprobe_config(Config::new(), path)
105}
106
107pub fn ffprobe_config(config: Config, path: impl AsRef<Path>) -> Result<FfProbe, FfProbeError> {
110 let path = path.as_ref();
111 let mut cmd = Command::new(config.ffprobe_bin);
112 cmd.args(["-v", "quiet", "-print_format", "json"]);
114 #[cfg(feature = "chapters")]
115 cmd.arg("-show_chapters");
116 #[cfg(feature = "format")]
117 cmd.arg("-show_format");
118 #[cfg(feature = "streams")]
119 cmd.arg("-show_streams");
120
121 if config.count_frames {
122 cmd.arg("-count_frames");
123 }
124
125 cmd.arg(path);
126
127 let out = cmd.output().map_err(FfProbeError::Io)?;
128
129 if !out.status.success() {
130 return Err(FfProbeError::Status(out));
131 }
132
133 serde_json::from_slice::<FfProbe>(&out.stdout).map_err(FfProbeError::Deserialize)
134}
135
136#[cfg(feature = "async")]
137pub async fn ffprobe_async(path: impl AsRef<std::path::Path>) -> Result<FfProbe, FfProbeError> {
138 ffprobe_async_config(Config::new(), path).await
139}
140
141#[cfg(feature = "async")]
142pub async fn ffprobe_async_config(
143 config: Config,
144 path: impl AsRef<Path>,
145) -> Result<FfProbe, FfProbeError> {
146 let mut cmd = tokio::process::Command::new("ffprobe");
147 let path = path.as_ref();
148 cmd.args(["-v", "quiet", "-print_format", "json"]);
149 #[cfg(feature = "chapters")]
150 cmd.arg("-show_chapters");
151 #[cfg(feature = "format")]
152 cmd.arg("-show_format");
153 #[cfg(feature = "streams")]
154 cmd.arg("-show_streams");
155
156 if config.count_frames {
157 cmd.arg("-count_frames");
158 }
159
160 cmd.arg(path);
161
162 let out = cmd.output().await.map_err(FfProbeError::Io)?;
163
164 if !out.status.success() {
165 return Err(FfProbeError::Status(out));
166 }
167
168 serde_json::from_slice::<FfProbe>(&out.stdout).map_err(FfProbeError::Deserialize)
169}
170
171pub fn option_string_to_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
172where
173 D: Deserializer<'de>,
174{
175 let s: Option<String> = Option::deserialize(deserializer)?;
176 match s {
177 Some(s) => s
178 .parse::<f64>()
179 .map(|v| v.max(0.0))
180 .map(Duration::from_secs_f64)
181 .map(Some)
182 .map_err(serde::de::Error::custom),
183 None => Ok(None),
184 }
185}