Skip to content

Commit 3bd134f

Browse files
authored
Merge pull request #331 from webrtc-rs/feature/video-orientation
Video orientation extension
2 parents 6904064 + 6e2124d commit 3bd134f

File tree

4 files changed

+256
-0
lines changed

4 files changed

+256
-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: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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, Copy, Clone)]
45+
pub enum CameraDirection {
46+
Front = 0,
47+
Back = 1,
48+
}
49+
50+
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
51+
pub enum VideoRotation {
52+
Degree0 = 0,
53+
Degree90 = 1,
54+
Degree180 = 2,
55+
Degree270 = 3,
56+
}
57+
58+
impl MarshalSize for VideoOrientationExtension {
59+
fn marshal_size(&self) -> usize {
60+
VIDEO_ORIENTATION_EXTENSION_SIZE
61+
}
62+
}
63+
64+
impl Unmarshal for VideoOrientationExtension {
65+
fn unmarshal<B>(buf: &mut B) -> util::Result<Self>
66+
where
67+
Self: Sized,
68+
B: bytes::Buf,
69+
{
70+
if buf.remaining() < VIDEO_ORIENTATION_EXTENSION_SIZE {
71+
return Err(Error::ErrBufferTooSmall.into());
72+
}
73+
74+
let b = buf.get_u8();
75+
76+
let c = (b & 0b1000) >> 3;
77+
let f = b & 0b0100;
78+
let r = b & 0b0011;
79+
80+
Ok(VideoOrientationExtension {
81+
direction: c.try_into()?,
82+
flip: f > 0,
83+
rotation: r.try_into()?,
84+
})
85+
}
86+
}
87+
88+
impl Marshal for VideoOrientationExtension {
89+
fn marshal_to(&self, mut buf: &mut [u8]) -> util::Result<usize> {
90+
let c = (self.direction as u8) << 3;
91+
let f = if self.flip { 0b0100 } else { 0 };
92+
let r = self.rotation as u8;
93+
94+
buf.put_u8(c | f | r);
95+
96+
Ok(VIDEO_ORIENTATION_EXTENSION_SIZE)
97+
}
98+
}
99+
100+
impl TryFrom<u8> for CameraDirection {
101+
type Error = util::Error;
102+
103+
fn try_from(value: u8) -> Result<Self, Self::Error> {
104+
match value {
105+
0 => Ok(CameraDirection::Front),
106+
1 => Ok(CameraDirection::Back),
107+
_ => Err(util::Error::Other(format!(
108+
"Unhandled camera direction: {}",
109+
value
110+
))),
111+
}
112+
}
113+
}
114+
115+
impl TryFrom<u8> for VideoRotation {
116+
type Error = util::Error;
117+
118+
fn try_from(value: u8) -> Result<Self, Self::Error> {
119+
match value {
120+
0 => Ok(VideoRotation::Degree0),
121+
1 => Ok(VideoRotation::Degree90),
122+
2 => Ok(VideoRotation::Degree180),
123+
3 => Ok(VideoRotation::Degree270),
124+
_ => Err(util::Error::Other(format!(
125+
"Unhandled video rotation: {}",
126+
value
127+
))),
128+
}
129+
}
130+
}
131+
132+
impl Default for CameraDirection {
133+
fn default() -> Self {
134+
CameraDirection::Front
135+
}
136+
}
137+
138+
impl Default for VideoRotation {
139+
fn default() -> Self {
140+
VideoRotation::Degree0
141+
}
142+
}
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+
}

sdp/src/extmap/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub const TRANSPORT_CC_URI: &str =
2121
pub const SDES_MID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:mid";
2222
pub const SDES_RTP_STREAM_ID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id";
2323
pub const AUDIO_LEVEL_URI: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
24+
pub const VIDEO_ORIENTATION_URI: &str = "urn:3gpp:video-orientation";
2425

2526
/// ExtMap represents the activation of a single RTP header extension
2627
#[derive(Debug, Clone, Default)]

0 commit comments

Comments
 (0)