Skip to main content

rtc_rtp/extension/video_orientation_extension/
mod.rs

1#[cfg(test)]
2mod video_orientation_extension_test;
3
4use std::convert::{TryFrom, TryInto};
5
6use bytes::BufMut;
7use serde::{Deserialize, Serialize};
8use shared::{
9    error::{Error, Result},
10    marshal::{Marshal, MarshalSize, Unmarshal},
11};
12
13// One byte header size
14/// The extension's encoded size in bytes.
15pub const VIDEO_ORIENTATION_EXTENSION_SIZE: usize = 1;
16
17/// Coordination of Video Orientation in RTP streams.
18///
19/// Coordination of Video Orientation consists in signaling of the current
20/// orientation of the image captured on the sender side to the receiver for
21/// appropriate rendering and displaying.
22///
23/// C = Camera: indicates the direction of the camera used for this video
24///     stream. It can be used by the MTSI client in receiver to e.g. display
25///     the received video differently depending on the source camera.
26///
27/// 0: Front-facing camera, facing the user. If camera direction is
28///    unknown by the sending MTSI client in the terminal then this is the
29///    default value used.
30/// 1: Back-facing camera, facing away from the user.
31///
32/// F = Flip: indicates a horizontal (left-right flip) mirror operation on
33///     the video as sent on the link.
34///
35///    0                   1
36///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
37///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38///   |  ID   | len=0 |0 0 0 0 C F R R|
39///   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Serialize, Deserialize)]
41pub struct VideoOrientationExtension {
42    /// Which camera produced the frame.
43    pub direction: CameraDirection,
44    /// Whether the image is horizontally mirrored, as front cameras usually are.
45    pub flip: bool,
46    /// How far the receiver must rotate the image to display it upright.
47    pub rotation: VideoRotation,
48}
49
50#[derive(Default, PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize)]
51/// Which camera a frame came from.
52pub enum CameraDirection {
53    #[default]
54    /// The front-facing (user-facing) camera.
55    Front = 0,
56    /// The rear-facing camera.
57    Back = 1,
58}
59
60#[derive(Default, PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize)]
61/// Clockwise rotation to apply when displaying the frame.
62pub enum VideoRotation {
63    #[default]
64    /// No rotation.
65    Degree0 = 0,
66    /// Rotate 90° clockwise.
67    Degree90 = 1,
68    /// Rotate 180°.
69    Degree180 = 2,
70    /// Rotate 270° clockwise.
71    Degree270 = 3,
72}
73
74impl MarshalSize for VideoOrientationExtension {
75    fn marshal_size(&self) -> usize {
76        VIDEO_ORIENTATION_EXTENSION_SIZE
77    }
78}
79
80impl Unmarshal for VideoOrientationExtension {
81    fn unmarshal<B>(buf: &mut B) -> Result<Self>
82    where
83        Self: Sized,
84        B: bytes::Buf,
85    {
86        if buf.remaining() < VIDEO_ORIENTATION_EXTENSION_SIZE {
87            return Err(Error::ErrBufferTooSmall);
88        }
89
90        let b = buf.get_u8();
91
92        let c = (b & 0b1000) >> 3;
93        let f = b & 0b0100;
94        let r = b & 0b0011;
95
96        Ok(VideoOrientationExtension {
97            direction: c.try_into()?,
98            flip: f > 0,
99            rotation: r.try_into()?,
100        })
101    }
102}
103
104impl Marshal for VideoOrientationExtension {
105    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
106        let c = (self.direction as u8) << 3;
107        let f = if self.flip { 0b0100 } else { 0 };
108        let r = self.rotation as u8;
109
110        buf.put_u8(c | f | r);
111
112        Ok(VIDEO_ORIENTATION_EXTENSION_SIZE)
113    }
114}
115
116impl TryFrom<u8> for CameraDirection {
117    type Error = shared::error::Error;
118
119    fn try_from(value: u8) -> Result<Self> {
120        match value {
121            0 => Ok(CameraDirection::Front),
122            1 => Ok(CameraDirection::Back),
123            _ => Err(shared::error::Error::Other(format!(
124                "Unhandled camera direction: {value}"
125            ))),
126        }
127    }
128}
129
130impl TryFrom<u8> for VideoRotation {
131    type Error = shared::error::Error;
132
133    fn try_from(value: u8) -> Result<Self> {
134        match value {
135            0 => Ok(VideoRotation::Degree0),
136            1 => Ok(VideoRotation::Degree90),
137            2 => Ok(VideoRotation::Degree180),
138            3 => Ok(VideoRotation::Degree270),
139            _ => Err(shared::error::Error::Other(format!(
140                "Unhandled video rotation: {value}"
141            ))),
142        }
143    }
144}