easy_ffprobe/
streams.rs

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