Skip to main content

easy_ffprobe/
streams.rs

1use std::time::Duration;
2
3use chrono::{DateTime, FixedOffset, NaiveDateTime, NaiveTime, Timelike};
4use serde::{
5    de::{self, IntoDeserializer},
6    Deserialize, Deserializer, Serialize, Serializer,
7};
8use serde_value::Value;
9
10use crate::{
11    attachment_stream::AttachmentStream, audio_stream::AudioStream, data_stream::DataStream,
12    disposition::Disposition, ratio::Ratio, subtitle_stream::SubtitleStream,
13    video_stream::VideoStream,
14};
15
16#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
17/// Stream parsed
18pub struct Stream {
19    /// The identifier of the stream, if available.
20    pub id: Option<String>,
21    // The index of the stream.
22    pub index: u64,
23    /// Disposition flags indicating various properties of the stream.
24    pub disposition: Disposition,
25    /// The average frame rate of the stream.
26    pub avg_frame_rate: Ratio,
27    /// The codec_tag field represents a numeric identifier associated with the codec used in the stream. It is often an integer value assigned to a specific codec format, allowing programs to quickly identify the codec type without needing to parse through codec-specific headers extensively. These tags are usually defined by standards organizations or codec developers.
28    /// For example, in the context of video codecs, a codec tag might represent the codec used for encoding the video stream, such as H.264 (codec tag 0x21) or MPEG-4 Visual (codec tag 0x20).
29    #[serde(deserialize_with = "string_to_bytes")]
30    pub codec_tag: Vec<u8>,
31    /// The time base of the stream. eg. 1/1000
32    pub time_base: Ratio,
33    /// The start presentation timestamp (PTS) of the stream.
34    /// ptr * timebase = start in seconds
35    pub start_pts: i64,
36    #[serde(default)]
37    /// A list of side data associated with the stream.
38    pub side_data_list: Vec<SideData>,
39    /// The size of the extra data associated with the stream, if available.
40    pub extradata_size: Option<i64>,
41    /// The real frame rate of the stream.
42    pub r_frame_rate: Ratio,
43    /// The total number of frames in the stream, if available.
44    #[serde(deserialize_with = "option_string_to_int", default)]
45    pub nb_frames: Option<i64>,
46    /// Number of frames seen by the decoder.
47    /// Requires full decoding and is only available if the 'count_frames'
48    /// setting was enabled.
49    #[serde(deserialize_with = "option_string_to_int", default)]
50    pub nb_read_frames: Option<i64>,
51    #[cfg(feature = "__internal_deny_unknown_fields")]
52    codec_tag_string: Value,
53    #[serde(flatten)]
54    pub stream: StreamKinds,
55}
56
57impl Stream {
58    pub fn start_time(&self) -> f64 {
59        (self.start_pts * self.time_base.numerator() as i64) as f64
60            / (self.time_base.denominator() as f64)
61    }
62    pub fn duration(&self) -> Option<Duration> {
63        match &self.stream {
64            StreamKinds::Audio(v) => v.duration_ts,
65            StreamKinds::Video(v) => v.duration_ts,
66            StreamKinds::Subtitle(sub) => sub.duration_ts,
67            StreamKinds::Attachment(attach) => Some(attach.duration_ts),
68            StreamKinds::Data(v) => Some(v.duration_ts),
69            StreamKinds::Unknown => None,
70            StreamKinds::Nb => None,
71        }
72        .map(|d| {
73            Duration::from_millis(
74                ((d * self.time_base.numerator()) as f64 / self.time_base.denominator() as f64
75                    * 1000.) as u64,
76            )
77        })
78    }
79}
80#[derive(Debug, Clone, PartialEq, Eq)]
81/// codec_type as enum
82pub enum StreamKinds {
83    Audio(AudioStream),
84    Video(VideoStream),
85    Subtitle(SubtitleStream),
86    Attachment(AttachmentStream),
87    Data(DataStream),
88    Unknown,
89    Nb,
90}
91
92impl Serialize for StreamKinds {
93    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94    where
95        S: Serializer,
96    {
97        match *self {
98            StreamKinds::Audio(ref __field0) => Serialize::serialize(__field0, serializer),
99            StreamKinds::Video(ref __field0) => Serialize::serialize(__field0, serializer),
100            StreamKinds::Subtitle(ref __field0) => Serialize::serialize(__field0, serializer),
101            StreamKinds::Attachment(ref __field0) => Serialize::serialize(__field0, serializer),
102            StreamKinds::Data(ref __field0) => Serialize::serialize(__field0, serializer),
103            StreamKinds::Unknown => serializer.serialize_none(),
104            StreamKinds::Nb => serializer.serialize_none(),
105        }
106    }
107}
108
109#[derive(Deserialize, Serialize)]
110struct StreamKindInfo {
111    pub codec_type: Option<MediaType>,
112}
113
114//https://ffmpeg.org/doxygen/trunk/group__lavu__misc.html#ga9a84bba4713dfced21a1a56163be1f48
115#[derive(
116    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
117)]
118#[serde(rename_all = "lowercase")]
119pub enum MediaType {
120    Unknown,
121    Video,
122    Audio,
123    Data,
124    Subtitle,
125    Attachment,
126    Nb,
127}
128
129impl<'de> Deserialize<'de> for StreamKinds {
130    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131    where
132        D: Deserializer<'de>,
133    {
134        let v = Value::deserialize(deserializer)?;
135        let deserializer = || v.clone().into_deserializer();
136        let mut err = None;
137        if let Ok(kind) = StreamKindInfo::deserialize(deserializer()) {
138            match kind.codec_type {
139                Some(MediaType::Audio) => {
140                    match Result::map(
141                        <AudioStream as Deserialize>::deserialize(deserializer()),
142                        StreamKinds::Audio,
143                    ) {
144                        Ok(__ok) => return Ok(__ok),
145                        Err(e) => err = Some(e.to_string()),
146                    }
147                }
148                Some(MediaType::Video) => {
149                    match Result::map(
150                        <VideoStream as Deserialize>::deserialize(deserializer()),
151                        StreamKinds::Video,
152                    ) {
153                        Ok(__ok) => return Ok(__ok),
154                        Err(e) => err = Some(e.to_string()),
155                    }
156                }
157                Some(MediaType::Attachment) => {
158                    match Result::map(
159                        <AttachmentStream as Deserialize>::deserialize(deserializer()),
160                        StreamKinds::Attachment,
161                    ) {
162                        Ok(__ok) => return Ok(__ok),
163                        Err(e) => err = Some(e.to_string()),
164                    }
165                }
166                Some(MediaType::Data) => {
167                    match Result::map(
168                        <DataStream as Deserialize>::deserialize(deserializer()),
169                        StreamKinds::Data,
170                    ) {
171                        Ok(__ok) => return Ok(__ok),
172                        Err(e) => err = Some(e.to_string()),
173                    }
174                }
175                Some(MediaType::Subtitle) => {
176                    match Result::map(
177                        <SubtitleStream as Deserialize>::deserialize(deserializer()),
178                        StreamKinds::Subtitle,
179                    ) {
180                        Ok(__ok) => return Ok(__ok),
181                        Err(e) => err = Some(e.to_string()),
182                    };
183                }
184                Some(MediaType::Nb) => return Ok(Self::Nb),
185                Some(MediaType::Unknown) => return Ok(Self::Unknown),
186                None => {}
187            }
188        }
189        let msg = Value::deserialize(deserializer());
190
191        let msg = match err {
192            Some(v) => match msg {
193                Ok(vv) => format!("StreamKinds: {} {:#?}", v, vv),
194                Err(_) => format!("StreamKinds: {}", v),
195            },
196            None => match msg {
197                Ok(v) => format!(
198                    "data did not match any variant of untagged enum StreamKinds: {:#?}",
199                    v
200                ),
201                Err(_) => "data did not match any variant of untagged enum StreamKinds".to_string(),
202            },
203        };
204
205        Err(de::Error::custom(msg))
206    }
207}
208
209#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
210#[cfg_attr(feature = "__internal_deny_unknown_fields", serde(deny_unknown_fields))]
211pub struct SideData {
212    pub side_data_type: Option<String>,
213    pub service_type: Option<i64>,
214    pub dv_version_major: Option<i64>,
215    pub dv_version_minor: Option<i64>,
216    pub dv_profile: Option<i64>,
217    pub dv_level: Option<i64>,
218    pub rpu_present_flag: Option<i64>,
219    pub el_present_flag: Option<i64>,
220    pub bl_present_flag: Option<i64>,
221    pub dv_bl_signal_compatibility_id: Option<i64>,
222    pub rotation: Option<i16>,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226/// Stream tags for video, audio, subtitle
227pub struct StreamTags {
228    #[serde(rename = "BPS")]
229    #[serde(deserialize_with = "option_string_to_int", default)]
230    pub bps: Option<i64>,
231    #[serde(rename = "DURATION")]
232    #[serde(deserialize_with = "option_chronostring_to_duration", default)]
233    pub duration: Option<Duration>,
234    #[serde(rename = "NUMBER_OF_BYTES")]
235    #[serde(deserialize_with = "option_string_to_int", default)]
236    pub number_of_bytes: Option<i64>,
237    #[serde(rename = "NUMBER_OF_FRAMES")]
238    #[serde(deserialize_with = "option_string_to_int", default)]
239    pub number_of_frames: Option<i64>,
240    #[serde(rename = "_STATISTICS_TAGS")]
241    pub statistics_tags: Option<String>,
242    #[serde(rename = "_STATISTICS_WRITING_APP")]
243    pub statistics_writing_app: Option<String>,
244    #[serde(rename = "_STATISTICS_WRITING_DATE_UTC")]
245    #[serde(deserialize_with = "option_string_to_naivedatetime", default)]
246    pub statistics_writing_date_utc: Option<NaiveDateTime>,
247    #[serde(alias = "HANDLER_NAME")]
248    pub handler_name: Option<String>,
249    #[serde(deserialize_with = "option_string_to_datetime", default)]
250    pub creation_time: Option<DateTime<FixedOffset>>,
251    #[serde(alias = "ENCODER")]
252    pub encoder: Option<String>,
253    #[serde(alias = "VENDOR_ID")]
254    pub vendor_id: Option<String>,
255    pub title: Option<String>,
256    pub language: Option<String>,
257    pub timecode: Option<String>,
258    pub reel_name: Option<String>,
259}
260
261pub fn option_string_to_int<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
262where
263    D: Deserializer<'de>,
264{
265    let s: Option<String> = Option::deserialize(deserializer)?;
266    match s {
267        Some(s) => s.parse::<i64>().map(Some).map_err(serde::de::Error::custom),
268        None => Ok(None),
269    }
270}
271
272pub fn string_to_int<'de, D>(deserializer: D) -> Result<i64, D::Error>
273where
274    D: Deserializer<'de>,
275{
276    let s = String::deserialize(deserializer)?;
277    s.parse::<i64>().map_err(serde::de::Error::custom)
278}
279
280pub fn string_to_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
281where
282    D: Deserializer<'de>,
283{
284    let hex_str = String::deserialize(deserializer)?;
285
286    let trimmed_str = if let Some(stripped) = hex_str.strip_prefix("0x") {
287        stripped
288    } else {
289        &hex_str
290    };
291
292    if trimmed_str.len() % 2 != 0 {
293        return Err(serde::de::Error::custom(
294            "Hex string must have an even number of digits",
295        ));
296    }
297
298    trimmed_str
299        .as_bytes()
300        .chunks(2)
301        .map(|chunk| {
302            let hex_digit = std::str::from_utf8(chunk).map_err(|_| "Invalid UTF-8 sequence")?;
303
304            u8::from_str_radix(hex_digit, 16).map_err(|_| "Conversion error")
305        })
306        .collect::<Result<Vec<_>, _>>()
307        .map_err(serde::de::Error::custom)
308}
309
310pub fn option_string_to_bool<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
311where
312    D: Deserializer<'de>,
313{
314    let s: Option<String> = Option::deserialize(deserializer)?;
315    match s {
316        Some(s) => s
317            .parse::<bool>()
318            .map(Some)
319            .map_err(serde::de::Error::custom),
320        None => Ok(None),
321    }
322}
323
324pub fn option_chronostring_to_duration<'de, D>(
325    deserializer: D,
326) -> Result<Option<Duration>, D::Error>
327where
328    D: Deserializer<'de>,
329{
330    let s: Option<String> = Option::deserialize(deserializer)?;
331    match s {
332        Some(s) => NaiveTime::parse_from_str(&s, "%H:%M:%S%.f")
333            .map(|time| {
334                let seconds = time.hour() * 3600 + time.minute() * 60 + time.second();
335                Duration::new(seconds as u64, time.nanosecond())
336            })
337            .map(Some)
338            .map_err(serde::de::Error::custom),
339        None => Ok(None),
340    }
341}
342
343pub fn option_string_to_naivedatetime<'de, D>(
344    deserializer: D,
345) -> Result<Option<NaiveDateTime>, D::Error>
346where
347    D: Deserializer<'de>,
348{
349    let s: Option<String> = Option::deserialize(deserializer)?;
350    match s {
351        Some(s) => NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S")
352            .map(Some)
353            .map_err(serde::de::Error::custom),
354        None => Ok(None),
355    }
356}
357
358pub fn option_string_to_datetime<'de, D>(
359    deserializer: D,
360) -> Result<Option<DateTime<FixedOffset>>, D::Error>
361where
362    D: Deserializer<'de>,
363{
364    let s: Option<String> = Option::deserialize(deserializer)?;
365    match s {
366        Some(s) => DateTime::parse_from_rfc3339(&s)
367            .map(Some)
368            .map_err(serde::de::Error::custom),
369        None => Ok(None),
370    }
371}