Skip to content

Commit 7ceeeeb

Browse files
committed
Video orientation extension
1 parent 6904064 commit 7ceeeeb

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

rtp/src/extension/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod abs_send_time_extension;
22
pub mod audio_level_extension;
33
pub mod transport_cc_extension;
4+
pub mod video_orientation_extension;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#[cfg(test)]
2+
mod video_orientation_extension_test;
3+
4+
use std::convert::{TryFrom, TryInto};
5+
6+
use bytes::BufMut;
7+
use util::{marshal::Unmarshal, Marshal, MarshalSize};
8+
9+
use crate::Error;
10+
11+
// One byte header size
12+
pub const VIDEO_ORIENTATION_EXTENSION_SIZE: usize = 1;
13+
14+
/// Coordination of Video Orientation in RTP streams.
15+
///
16+
/// Coordination of Video Orientation consists in signaling of the current
17+
/// orientation of the image captured on the sender side to the receiver for
18+
/// appropriate rendering and displaying.
19+
///
20+
/// C = Camera: indicates the direction of the camera used for this video
21+
/// stream. It can be used by the MTSI client in receiver to e.g. display
22+
/// the received video differently depending on the source camera.
23+
///
24+
/// 0: Front-facing camera, facing the user. If camera direction is
25+
/// unknown by the sending MTSI client in the terminal then this is the
26+
/// default value used.
27+
/// 1: Back-facing camera, facing away from the user.
28+
///
29+
/// F = Flip: indicates a horizontal (left-right flip) mirror operation on
30+
/// the video as sent on the link.
31+
///
32+
/// 0 1
33+
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
34+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35+
/// | ID | len=0 |0 0 0 0 C F R R|
36+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37+
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)]
38+
pub struct VideoOrientationExtension {
39+
pub direction: CameraDirection,
40+
pub flip: bool,
41+
pub rotation: VideoRotation,
42+
}
43+
44+
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)]
45+
pub enum CameraDirection {
46+
#[default]
47+
Front = 0,
48+
Back = 1,
49+
}
50+
51+
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)]
52+
pub enum VideoRotation {
53+
#[default]
54+
Degree0 = 0,
55+
Degree90 = 1,
56+
Degree180 = 2,
57+
Degree270 = 3,
58+
}
59+
60+
impl MarshalSize for VideoOrientationExtension {
61+
fn marshal_size(&self) -> usize {
62+
VIDEO_ORIENTATION_EXTENSION_SIZE
63+
}
64+
}
65+
66+
impl Unmarshal for VideoOrientationExtension {
67+
fn unmarshal<B>(buf: &mut B) -> util::Result<Self>
68+
where
69+
Self: Sized,
70+
B: bytes::Buf,
71+
{
72+
if buf.remaining() < VIDEO_ORIENTATION_EXTENSION_SIZE {
73+
return Err(Error::ErrBufferTooSmall.into());
74+
}
75+
76+
let b = buf.get_u8();
77+
78+
let c = (b & 0b1000) >> 3;
79+
let f = b & 0b0100;
80+
let r = b & 0b0011;
81+
82+
Ok(VideoOrientationExtension {
83+
direction: c.try_into()?,
84+
flip: f > 0,
85+
rotation: r.try_into()?,
86+
})
87+
}
88+
}
89+
90+
impl Marshal for VideoOrientationExtension {
91+
fn marshal_to(&self, mut buf: &mut [u8]) -> util::Result<usize> {
92+
let c = (self.direction as u8) << 3;
93+
let f = if self.flip { 0b0100 } else { 0 };
94+
let r = self.rotation as u8;
95+
96+
buf.put_u8(c | f | r);
97+
98+
Ok(VIDEO_ORIENTATION_EXTENSION_SIZE)
99+
}
100+
}
101+
102+
impl TryFrom<u8> for CameraDirection {
103+
type Error = util::Error;
104+
105+
fn try_from(value: u8) -> Result<Self, Self::Error> {
106+
match value {
107+
0 => Ok(CameraDirection::Front),
108+
1 => Ok(CameraDirection::Back),
109+
_ => Err(util::Error::Other(format!(
110+
"Unhandled camera direction: {}",
111+
value
112+
))),
113+
}
114+
}
115+
}
116+
117+
impl TryFrom<u8> for VideoRotation {
118+
type Error = util::Error;
119+
120+
fn try_from(value: u8) -> Result<Self, Self::Error> {
121+
match value {
122+
0 => Ok(VideoRotation::Degree0),
123+
1 => Ok(VideoRotation::Degree90),
124+
2 => Ok(VideoRotation::Degree180),
125+
3 => Ok(VideoRotation::Degree270),
126+
_ => Err(util::Error::Other(format!(
127+
"Unhandled video rotation: {}",
128+
value
129+
))),
130+
}
131+
}
132+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use super::*;
2+
use crate::error::Result;
3+
use bytes::{Bytes, BytesMut};
4+
5+
#[test]
6+
fn test_video_orientation_extension_too_small() -> Result<()> {
7+
let mut buf = &vec![0u8; 0][..];
8+
let result = VideoOrientationExtension::unmarshal(&mut buf);
9+
assert!(result.is_err());
10+
11+
Ok(())
12+
}
13+
14+
#[test]
15+
fn test_video_orientation_extension_back_facing_camera() -> Result<()> {
16+
let raw = Bytes::from_static(&[0b1000]);
17+
let buf = &mut raw.clone();
18+
let a1 = VideoOrientationExtension::unmarshal(buf)?;
19+
let a2 = VideoOrientationExtension {
20+
direction: CameraDirection::Back,
21+
flip: false,
22+
rotation: VideoRotation::Degree0,
23+
};
24+
assert_eq!(a1, a2);
25+
26+
let mut dst = BytesMut::with_capacity(a2.marshal_size());
27+
dst.resize(a2.marshal_size(), 0);
28+
a2.marshal_to(&mut dst)?;
29+
assert_eq!(raw, dst.freeze());
30+
31+
Ok(())
32+
}
33+
34+
#[test]
35+
fn test_video_orientation_extension_flip_true() -> Result<()> {
36+
let raw = Bytes::from_static(&[0b0100]);
37+
let buf = &mut raw.clone();
38+
let a1 = VideoOrientationExtension::unmarshal(buf)?;
39+
let a2 = VideoOrientationExtension {
40+
direction: CameraDirection::Front,
41+
flip: true,
42+
rotation: VideoRotation::Degree0,
43+
};
44+
assert_eq!(a1, a2);
45+
46+
let mut dst = BytesMut::with_capacity(a2.marshal_size());
47+
dst.resize(a2.marshal_size(), 0);
48+
a2.marshal_to(&mut dst)?;
49+
assert_eq!(raw, dst.freeze());
50+
51+
Ok(())
52+
}
53+
54+
#[test]
55+
fn test_video_orientation_extension_degree_90() -> Result<()> {
56+
let raw = Bytes::from_static(&[0b0001]);
57+
let buf = &mut raw.clone();
58+
let a1 = VideoOrientationExtension::unmarshal(buf)?;
59+
let a2 = VideoOrientationExtension {
60+
direction: CameraDirection::Front,
61+
flip: false,
62+
rotation: VideoRotation::Degree90,
63+
};
64+
assert_eq!(a1, a2);
65+
66+
let mut dst = BytesMut::with_capacity(a2.marshal_size());
67+
dst.resize(a2.marshal_size(), 0);
68+
a2.marshal_to(&mut dst)?;
69+
assert_eq!(raw, dst.freeze());
70+
71+
Ok(())
72+
}
73+
74+
#[test]
75+
fn test_video_orientation_extension_degree_180() -> Result<()> {
76+
let raw = Bytes::from_static(&[0b0010]);
77+
let buf = &mut raw.clone();
78+
let a1 = VideoOrientationExtension::unmarshal(buf)?;
79+
let a2 = VideoOrientationExtension {
80+
direction: CameraDirection::Front,
81+
flip: false,
82+
rotation: VideoRotation::Degree180,
83+
};
84+
assert_eq!(a1, a2);
85+
86+
let mut dst = BytesMut::with_capacity(a2.marshal_size());
87+
dst.resize(a2.marshal_size(), 0);
88+
a2.marshal_to(&mut dst)?;
89+
assert_eq!(raw, dst.freeze());
90+
91+
Ok(())
92+
}
93+
94+
#[test]
95+
fn test_video_orientation_extension_degree_270() -> Result<()> {
96+
let raw = Bytes::from_static(&[0b0011]);
97+
let buf = &mut raw.clone();
98+
let a1 = VideoOrientationExtension::unmarshal(buf)?;
99+
let a2 = VideoOrientationExtension {
100+
direction: CameraDirection::Front,
101+
flip: false,
102+
rotation: VideoRotation::Degree270,
103+
};
104+
assert_eq!(a1, a2);
105+
106+
let mut dst = BytesMut::with_capacity(a2.marshal_size());
107+
dst.resize(a2.marshal_size(), 0);
108+
a2.marshal_to(&mut dst)?;
109+
assert_eq!(raw, dst.freeze());
110+
111+
Ok(())
112+
}

0 commit comments

Comments
 (0)