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
#[cfg(test)]
mod video_orientation_extension_test;

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

use bytes::BufMut;
use serde::{Deserialize, Serialize};
use util::marshal::Unmarshal;
use util::{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, Serialize, Deserialize)]
pub struct VideoOrientationExtension {
    pub direction: CameraDirection,
    pub flip: bool,
    pub rotation: VideoRotation,
}

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

#[derive(Default, PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize)]
pub enum VideoRotation {
    #[default]
    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}"
            ))),
        }
    }
}