Skip to content

Commit

Permalink
Merge pull request #331 from webrtc-rs/feature/video-orientation
Browse files Browse the repository at this point in the history
Video orientation extension
  • Loading branch information
algesten authored Oct 26, 2022
2 parents 6904064 + 6e2124d commit 3bd134f
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 0 deletions.
1 change: 1 addition & 0 deletions rtp/src/extension/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod abs_send_time_extension;
pub mod audio_level_extension;
pub mod transport_cc_extension;
pub mod video_orientation_extension;
142 changes: 142 additions & 0 deletions rtp/src/extension/video_orientation_extension/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#[cfg(test)]
mod video_orientation_extension_test;

use std::convert::{TryFrom, TryInto};

use bytes::BufMut;
use util::{marshal::Unmarshal, Marshal, MarshalSize};

use crate::Error;

// One byte header size
pub const VIDEO_ORIENTATION_EXTENSION_SIZE: usize = 1;

/// Coordination of Video Orientation in RTP streams.
///
/// Coordination of Video Orientation consists in signaling of the current
/// orientation of the image captured on the sender side to the receiver for
/// appropriate rendering and displaying.
///
/// C = Camera: indicates the direction of the camera used for this video
/// stream. It can be used by the MTSI client in receiver to e.g. display
/// the received video differently depending on the source camera.
///
/// 0: Front-facing camera, facing the user. If camera direction is
/// unknown by the sending MTSI client in the terminal then this is the
/// default value used.
/// 1: Back-facing camera, facing away from the user.
///
/// F = Flip: indicates a horizontal (left-right flip) mirror operation on
/// the video as sent on the link.
///
/// 0 1
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | ID | len=0 |0 0 0 0 C F R R|
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)]
pub struct VideoOrientationExtension {
pub direction: CameraDirection,
pub flip: bool,
pub rotation: VideoRotation,
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum CameraDirection {
Front = 0,
Back = 1,
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum VideoRotation {
Degree0 = 0,
Degree90 = 1,
Degree180 = 2,
Degree270 = 3,
}

impl MarshalSize for VideoOrientationExtension {
fn marshal_size(&self) -> usize {
VIDEO_ORIENTATION_EXTENSION_SIZE
}
}

impl Unmarshal for VideoOrientationExtension {
fn unmarshal<B>(buf: &mut B) -> util::Result<Self>
where
Self: Sized,
B: bytes::Buf,
{
if buf.remaining() < VIDEO_ORIENTATION_EXTENSION_SIZE {
return Err(Error::ErrBufferTooSmall.into());
}

let b = buf.get_u8();

let c = (b & 0b1000) >> 3;
let f = b & 0b0100;
let r = b & 0b0011;

Ok(VideoOrientationExtension {
direction: c.try_into()?,
flip: f > 0,
rotation: r.try_into()?,
})
}
}

impl Marshal for VideoOrientationExtension {
fn marshal_to(&self, mut buf: &mut [u8]) -> util::Result<usize> {
let c = (self.direction as u8) << 3;
let f = if self.flip { 0b0100 } else { 0 };
let r = self.rotation as u8;

buf.put_u8(c | f | r);

Ok(VIDEO_ORIENTATION_EXTENSION_SIZE)
}
}

impl TryFrom<u8> for CameraDirection {
type Error = util::Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(CameraDirection::Front),
1 => Ok(CameraDirection::Back),
_ => Err(util::Error::Other(format!(
"Unhandled camera direction: {}",
value
))),
}
}
}

impl TryFrom<u8> for VideoRotation {
type Error = util::Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(VideoRotation::Degree0),
1 => Ok(VideoRotation::Degree90),
2 => Ok(VideoRotation::Degree180),
3 => Ok(VideoRotation::Degree270),
_ => Err(util::Error::Other(format!(
"Unhandled video rotation: {}",
value
))),
}
}
}

impl Default for CameraDirection {
fn default() -> Self {
CameraDirection::Front
}
}

impl Default for VideoRotation {
fn default() -> Self {
VideoRotation::Degree0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use super::*;
use crate::error::Result;
use bytes::{Bytes, BytesMut};

#[test]
fn test_video_orientation_extension_too_small() -> Result<()> {
let mut buf = &vec![0u8; 0][..];
let result = VideoOrientationExtension::unmarshal(&mut buf);
assert!(result.is_err());

Ok(())
}

#[test]
fn test_video_orientation_extension_back_facing_camera() -> Result<()> {
let raw = Bytes::from_static(&[0b1000]);
let buf = &mut raw.clone();
let a1 = VideoOrientationExtension::unmarshal(buf)?;
let a2 = VideoOrientationExtension {
direction: CameraDirection::Back,
flip: false,
rotation: VideoRotation::Degree0,
};
assert_eq!(a1, a2);

let mut dst = BytesMut::with_capacity(a2.marshal_size());
dst.resize(a2.marshal_size(), 0);
a2.marshal_to(&mut dst)?;
assert_eq!(raw, dst.freeze());

Ok(())
}

#[test]
fn test_video_orientation_extension_flip_true() -> Result<()> {
let raw = Bytes::from_static(&[0b0100]);
let buf = &mut raw.clone();
let a1 = VideoOrientationExtension::unmarshal(buf)?;
let a2 = VideoOrientationExtension {
direction: CameraDirection::Front,
flip: true,
rotation: VideoRotation::Degree0,
};
assert_eq!(a1, a2);

let mut dst = BytesMut::with_capacity(a2.marshal_size());
dst.resize(a2.marshal_size(), 0);
a2.marshal_to(&mut dst)?;
assert_eq!(raw, dst.freeze());

Ok(())
}

#[test]
fn test_video_orientation_extension_degree_90() -> Result<()> {
let raw = Bytes::from_static(&[0b0001]);
let buf = &mut raw.clone();
let a1 = VideoOrientationExtension::unmarshal(buf)?;
let a2 = VideoOrientationExtension {
direction: CameraDirection::Front,
flip: false,
rotation: VideoRotation::Degree90,
};
assert_eq!(a1, a2);

let mut dst = BytesMut::with_capacity(a2.marshal_size());
dst.resize(a2.marshal_size(), 0);
a2.marshal_to(&mut dst)?;
assert_eq!(raw, dst.freeze());

Ok(())
}

#[test]
fn test_video_orientation_extension_degree_180() -> Result<()> {
let raw = Bytes::from_static(&[0b0010]);
let buf = &mut raw.clone();
let a1 = VideoOrientationExtension::unmarshal(buf)?;
let a2 = VideoOrientationExtension {
direction: CameraDirection::Front,
flip: false,
rotation: VideoRotation::Degree180,
};
assert_eq!(a1, a2);

let mut dst = BytesMut::with_capacity(a2.marshal_size());
dst.resize(a2.marshal_size(), 0);
a2.marshal_to(&mut dst)?;
assert_eq!(raw, dst.freeze());

Ok(())
}

#[test]
fn test_video_orientation_extension_degree_270() -> Result<()> {
let raw = Bytes::from_static(&[0b0011]);
let buf = &mut raw.clone();
let a1 = VideoOrientationExtension::unmarshal(buf)?;
let a2 = VideoOrientationExtension {
direction: CameraDirection::Front,
flip: false,
rotation: VideoRotation::Degree270,
};
assert_eq!(a1, a2);

let mut dst = BytesMut::with_capacity(a2.marshal_size());
dst.resize(a2.marshal_size(), 0);
a2.marshal_to(&mut dst)?;
assert_eq!(raw, dst.freeze());

Ok(())
}
1 change: 1 addition & 0 deletions sdp/src/extmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub const TRANSPORT_CC_URI: &str =
pub const SDES_MID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:mid";
pub const SDES_RTP_STREAM_ID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id";
pub const AUDIO_LEVEL_URI: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
pub const VIDEO_ORIENTATION_URI: &str = "urn:3gpp:video-orientation";

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

0 comments on commit 3bd134f

Please sign in to comment.