forked from mavlink/mavlink-camera-manager
-
Notifications
You must be signed in to change notification settings - Fork 0
/
v4l_pipeline.rs
140 lines (126 loc) · 5.72 KB
/
v4l_pipeline.rs
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
133
134
135
136
137
138
139
140
use crate::{
stream::types::CaptureConfiguration,
video::types::{VideoEncodeType, VideoSourceType},
video_stream::types::VideoAndStreamInformation,
};
use super::{
PipelineGstreamerInterface, PipelineState, PIPELINE_FILTER_NAME, PIPELINE_RTP_TEE_NAME,
PIPELINE_VIDEO_TEE_NAME,
};
use anyhow::{anyhow, Result};
use tracing::*;
use gst::prelude::*;
#[derive(Debug)]
pub struct V4lPipeline {
pub state: PipelineState,
}
impl V4lPipeline {
#[instrument(level = "debug")]
pub fn try_new(
pipeline_id: &uuid::Uuid,
video_and_stream_information: &VideoAndStreamInformation,
) -> Result<gst::Pipeline> {
let configuration = match &video_and_stream_information
.stream_information
.configuration
{
CaptureConfiguration::Video(configuration) => configuration,
unsupported => return Err(anyhow!("{unsupported:?} is not supported as V4l Pipeline")),
};
let video_source = match &video_and_stream_information.video_source {
VideoSourceType::Local(source) => source,
unsupported => {
return Err(anyhow!(
"SourceType {unsupported:?} is not supported as V4l Pipeline"
))
}
};
let device = video_source.device_path.as_str();
let width = configuration.width;
let height = configuration.height;
let interval_numerator = configuration.frame_interval.numerator;
let interval_denominator = configuration.frame_interval.denominator;
let filter_name = format!("{PIPELINE_FILTER_NAME}-{pipeline_id}");
let video_tee_name = format!("{PIPELINE_VIDEO_TEE_NAME}-{pipeline_id}");
let rtp_tee_name = format!("{PIPELINE_RTP_TEE_NAME}-{pipeline_id}");
let description = match &configuration.encode {
VideoEncodeType::H264 => {
format!(
concat!(
"v4l2src device={device} do-timestamp=true",
" ! h264parse", // Here we need the parse to help the stream-format and alignment part, which is being fixed here because avc/au seems to reduce the CPU usage in the RTP payloading part.
" ! capsfilter name={filter_name} caps=video/x-h264,stream-format=avc,alignment=au,width={width},height={height},framerate={interval_denominator}/{interval_numerator}",
" ! tee name={video_tee_name} allow-not-linked=true",
" ! rtph264pay aggregate-mode=zero-latency config-interval=10 pt=96",
" ! tee name={rtp_tee_name} allow-not-linked=true"
),
device = device,
width = width,
height = height,
interval_denominator = interval_denominator,
interval_numerator = interval_numerator,
filter_name = filter_name,
video_tee_name = video_tee_name,
rtp_tee_name = rtp_tee_name,
)
}
VideoEncodeType::Yuyv => {
format!(
concat!(
"v4l2src device={device} do-timestamp=true",
" ! videoconvert",
" ! capsfilter name={filter_name} caps=video/x-raw,format=I420,width={width},height={height},framerate={interval_denominator}/{interval_numerator}",
" ! tee name={video_tee_name} allow-not-linked=true",
" ! rtpvrawpay pt=96",
" ! tee name={rtp_tee_name} allow-not-linked=true"
),
device = device,
width = width,
height = height,
interval_denominator = interval_denominator,
interval_numerator = interval_numerator,
filter_name = filter_name,
video_tee_name = video_tee_name,
rtp_tee_name = rtp_tee_name,
)
}
VideoEncodeType::Mjpg => {
format!(
concat!(
"v4l2src device={device} do-timestamp=true",
// We don't need a jpegparse, as it leads to incompatible caps, spoiling the negotiation.
" ! capsfilter name={filter_name} caps=image/jpeg,width={width},height={height},framerate={interval_denominator}/{interval_numerator}",
" ! tee name={video_tee_name} allow-not-linked=true",
" ! rtpjpegpay pt=96",
" ! tee name={rtp_tee_name} allow-not-linked=true"
),
device = device,
width = width,
height = height,
interval_denominator = interval_denominator,
interval_numerator = interval_numerator,
filter_name = filter_name,
video_tee_name = video_tee_name,
rtp_tee_name = rtp_tee_name,
)
}
unsupported => {
return Err(anyhow!(
"Encode {unsupported:?} is not supported for V4L2 Pipeline"
))
}
};
debug!("pipeline_description: {description:#?}");
let pipeline = gst::parse::launch(&description)?;
let pipeline = pipeline
.downcast::<gst::Pipeline>()
.expect("Couldn't downcast pipeline");
Ok(pipeline)
}
}
impl PipelineGstreamerInterface for V4lPipeline {
#[instrument(level = "trace")]
fn is_running(&self) -> bool {
self.state.pipeline_runner.is_running()
}
}