1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! Packet types for compressed media data.
use bitflags::bitflags;
use bytes::Bytes;
use oximedia_core::Timestamp;
bitflags! {
/// Flags indicating packet properties.
///
/// These flags provide information about the packet's role in the stream
/// and whether it can be decoded independently.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PacketFlags: u32 {
/// Packet contains a keyframe (can be decoded independently).
///
/// For video, this typically indicates an I-frame.
/// For audio, most packets are effectively keyframes.
const KEYFRAME = 0x0001;
/// Packet data may be corrupt.
///
/// Set when the demuxer detects potential corruption but
/// still provides the data for attempted recovery.
const CORRUPT = 0x0002;
/// Packet should be discarded.
///
/// Used for packets that are part of the stream but should
/// not be decoded (e.g., during seeking).
const DISCARD = 0x0004;
}
}
impl Default for PacketFlags {
fn default() -> Self {
Self::empty()
}
}
/// A compressed media packet from a container.
///
/// Packets are the fundamental unit of compressed data in a container.
/// Each packet typically contains one or more compressed frames from
/// a single stream.
///
/// # Examples
///
/// ```
/// use oximedia_container::{Packet, PacketFlags};
/// use oximedia_core::{Timestamp, Rational};
/// use bytes::Bytes;
///
/// let packet = Packet::new(
/// 0,
/// Bytes::from_static(&[0, 1, 2, 3]),
/// Timestamp::new(1000, Rational::new(1, 1000)),
/// PacketFlags::KEYFRAME,
/// );
///
/// assert!(packet.is_keyframe());
/// assert_eq!(packet.size(), 4);
/// ```
#[derive(Clone, Debug)]
pub struct Packet {
/// Index of the stream this packet belongs to.
pub stream_index: usize,
/// Compressed packet data.
pub data: Bytes,
/// Presentation and decode timestamps.
pub timestamp: Timestamp,
/// Packet flags.
pub flags: PacketFlags,
}
impl Packet {
/// Creates a new packet.
///
/// # Arguments
///
/// * `stream_index` - Index of the stream this packet belongs to
/// * `data` - Compressed packet data
/// * `timestamp` - Presentation/decode timestamps
/// * `flags` - Packet flags (keyframe, corrupt, etc.)
#[must_use]
pub const fn new(
stream_index: usize,
data: Bytes,
timestamp: Timestamp,
flags: PacketFlags,
) -> Self {
Self {
stream_index,
data,
timestamp,
flags,
}
}
/// Returns true if this packet is a keyframe.
///
/// Keyframes can be decoded independently without reference to
/// other frames in the stream.
#[must_use]
pub const fn is_keyframe(&self) -> bool {
self.flags.contains(PacketFlags::KEYFRAME)
}
/// Returns true if this packet may be corrupt.
#[must_use]
pub const fn is_corrupt(&self) -> bool {
self.flags.contains(PacketFlags::CORRUPT)
}
/// Returns true if this packet should be discarded.
#[must_use]
pub const fn should_discard(&self) -> bool {
self.flags.contains(PacketFlags::DISCARD)
}
/// Returns the size of the packet data in bytes.
#[must_use]
pub fn size(&self) -> usize {
self.data.len()
}
/// Returns true if the packet data is empty.
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// Returns the presentation timestamp in the stream's timebase.
#[must_use]
pub const fn pts(&self) -> i64 {
self.timestamp.pts
}
/// Returns the decode timestamp if available.
#[must_use]
pub const fn dts(&self) -> Option<i64> {
self.timestamp.dts
}
/// Returns the packet duration if available.
#[must_use]
pub const fn duration(&self) -> Option<i64> {
self.timestamp.duration
}
}
#[cfg(test)]
mod tests {
use super::*;
use oximedia_core::Rational;
#[test]
fn test_packet_new() {
let data = Bytes::from_static(&[1, 2, 3, 4, 5]);
let timestamp = Timestamp::new(1000, Rational::new(1, 1000));
let packet = Packet::new(0, data, timestamp, PacketFlags::KEYFRAME);
assert_eq!(packet.stream_index, 0);
assert_eq!(packet.size(), 5);
assert!(packet.is_keyframe());
assert!(!packet.is_corrupt());
}
#[test]
fn test_packet_flags() {
let data = Bytes::new();
let timestamp = Timestamp::new(0, Rational::new(1, 1000));
let keyframe = Packet::new(0, data.clone(), timestamp, PacketFlags::KEYFRAME);
assert!(keyframe.is_keyframe());
assert!(!keyframe.is_corrupt());
let corrupt = Packet::new(
0,
data.clone(),
timestamp,
PacketFlags::CORRUPT | PacketFlags::KEYFRAME,
);
assert!(corrupt.is_keyframe());
assert!(corrupt.is_corrupt());
let discard = Packet::new(0, data, timestamp, PacketFlags::DISCARD);
assert!(discard.should_discard());
}
#[test]
fn test_packet_timestamps() {
let data = Bytes::new();
let mut timestamp = Timestamp::new(1000, Rational::new(1, 48000));
timestamp.dts = Some(999);
timestamp.duration = Some(1024);
let packet = Packet::new(0, data, timestamp, PacketFlags::empty());
assert_eq!(packet.pts(), 1000);
assert_eq!(packet.dts(), Some(999));
assert_eq!(packet.duration(), Some(1024));
}
#[test]
fn test_packet_is_empty() {
let timestamp = Timestamp::new(0, Rational::new(1, 1000));
let empty = Packet::new(0, Bytes::new(), timestamp, PacketFlags::empty());
assert!(empty.is_empty());
assert_eq!(empty.size(), 0);
let non_empty = Packet::new(0, Bytes::from_static(&[1]), timestamp, PacketFlags::empty());
assert!(!non_empty.is_empty());
assert_eq!(non_empty.size(), 1);
}
}