@@ -127,93 +127,78 @@ GstElement * CameraDevice::CreateSnapshotPipeline(const std::string & device, in
127
127
if (!pipeline || !source || !jpeg_caps || !videorate || !videorate_caps || !queue || !filesink)
128
128
{
129
129
ChipLogError (Camera, " Not all elements could be created." );
130
- gst_object_unref (pipeline);
130
+
131
+ if (pipeline)
132
+ gst_object_unref (pipeline);
133
+ if (source)
134
+ gst_object_unref (source);
135
+ if (jpeg_caps)
136
+ gst_object_unref (jpeg_caps);
137
+ if (videorate)
138
+ gst_object_unref (videorate);
139
+ if (videorate_caps)
140
+ gst_object_unref (videorate_caps);
141
+ if (queue)
142
+ gst_object_unref (queue);
143
+ if (filesink)
144
+ gst_object_unref (filesink);
145
+
131
146
error = CameraError::ERROR_INIT_FAILED;
132
147
return nullptr ;
133
148
}
134
149
150
+ // Set the source device and caps
151
+ g_object_set (source, " device" , device.c_str (), " do-timestamp" , TRUE , nullptr );
152
+
153
+ GstCaps * caps = gst_caps_new_simple (" image/jpeg" , " width" , G_TYPE_INT, width, " height" , G_TYPE_INT, height, " framerate" ,
154
+ GST_TYPE_FRACTION, framerate, 1 , " quality" , G_TYPE_INT, quality, nullptr );
155
+ g_object_set (jpeg_caps, " caps" , caps, nullptr );
156
+ gst_caps_unref (caps);
157
+
158
+ // Set the output file location
159
+ g_object_set (filesink, " location" , filename.c_str (), nullptr );
160
+
135
161
// Add elements to the pipeline
136
- gst_bin_add_many (GST_BIN (pipeline), source, jpeg_caps, videorate, videorate_caps, queue, filesink, NULL );
162
+ gst_bin_add_many (GST_BIN (pipeline), source, jpeg_caps, videorate, videorate_caps, queue, filesink, nullptr );
137
163
138
164
// Link the elements
139
- if (gst_element_link_many (source, jpeg_caps, videorate, videorate_caps, queue, filesink, NULL ) != TRUE )
165
+ if (gst_element_link_many (source, jpeg_caps, videorate, videorate_caps, queue, filesink, nullptr ) != TRUE )
140
166
{
141
167
ChipLogError (Camera, " Elements could not be linked." );
168
+
169
+ // The pipeline will unref all added elements automatically when you unref the pipeline.
142
170
gst_object_unref (pipeline);
143
171
error = CameraError::ERROR_INIT_FAILED;
144
172
return nullptr ;
145
173
}
146
174
147
- // Set the source device and caps
148
- g_object_set (source, " device" , device.c_str (), " do-timestamp" , TRUE , NULL );
149
-
150
- GstCaps * caps = gst_caps_new_simple (" image/jpeg" , " width" , G_TYPE_INT, width, " height" , G_TYPE_INT, height, " framerate" ,
151
- GST_TYPE_FRACTION, framerate, 1 , " quality" , G_TYPE_INT, quality, NULL );
152
- g_object_set (jpeg_caps, " caps" , caps, NULL );
153
- gst_caps_unref (caps);
154
-
155
- // Set the output file location
156
- g_object_set (filesink, " location" , filename.c_str (), NULL );
157
-
158
175
return pipeline;
159
176
}
160
177
161
- // Helper function to create a GStreamer pipeline
178
+ // Helper function to create a GStreamer pipeline that ingests MJPEG frames coming
179
+ // from the camera, converted to H.264, and sent out on UDP port over RTP/UDP.
162
180
GstElement * CameraDevice::CreateVideoPipeline (const std::string & device, int width, int height, int framerate,
163
181
CameraError & error)
164
182
{
165
- GstElement * pipeline = nullptr ;
183
+ GstElement * pipeline = gst_pipeline_new (" video-pipeline" );
184
+ GstElement * capsfilter = gst_element_factory_make (" capsfilter" , " mjpeg_caps" );
185
+ GstElement * jpegdec = gst_element_factory_make (" jpegdec" , " jpegdec" );
186
+ GstElement * videoconvert = gst_element_factory_make (" videoconvert" , " videoconvert" );
187
+ GstElement * x264enc = gst_element_factory_make (" x264enc" , " encoder" );
188
+ GstElement * rtph264pay = gst_element_factory_make (" rtph264pay" , " rtph264" );
189
+ GstElement * udpsink = gst_element_factory_make (" udpsink" , " udpsink" );
166
190
GstElement * source = nullptr ;
167
- GstElement * capsfilter = nullptr ;
168
- GstElement * videoconvert = nullptr ;
169
- GstElement * x264enc = nullptr ;
170
- GstElement * rtph264pay = nullptr ;
171
- GstElement * udpsink = nullptr ;
172
-
173
- // Create the pipeline elements
174
- pipeline = gst_pipeline_new (" video-pipeline" );
175
191
176
- // Create elements
177
192
#ifdef AV_STREAM_GST_USE_TEST_SRC
178
193
source = gst_element_factory_make (" videotestsrc" , " source" );
179
194
#else
180
195
source = gst_element_factory_make (" v4l2src" , " source" );
181
196
#endif
182
- capsfilter = gst_element_factory_make (" capsfilter" , " filter" );
183
- videoconvert = gst_element_factory_make (" videoconvert" , " videoconvert" );
184
- x264enc = gst_element_factory_make (" x264enc" , " encoder" );
185
- rtph264pay = gst_element_factory_make (" rtph264pay" , " rtph264" );
186
- udpsink = gst_element_factory_make (" udpsink" , " udpsink" );
187
197
188
- if (pipeline == nullptr || source == nullptr || capsfilter == nullptr || videoconvert == nullptr || x264enc == nullptr ||
189
- rtph264pay == nullptr || udpsink == nullptr )
198
+ if (!pipeline || !source || !capsfilter || !jpegdec || !videoconvert || !x264enc || !rtph264pay || !udpsink)
190
199
{
191
200
ChipLogError (Camera, " Not all elements could be created." );
192
- if (pipeline)
193
- gst_object_unref (pipeline);
194
- if (source)
195
- gst_object_unref (source);
196
- if (capsfilter)
197
- gst_object_unref (capsfilter);
198
- if (videoconvert)
199
- gst_object_unref (videoconvert);
200
- if (x264enc)
201
- gst_object_unref (x264enc);
202
- if (rtph264pay)
203
- gst_object_unref (rtph264pay);
204
- if (udpsink)
205
- gst_object_unref (udpsink);
206
- error = CameraError::ERROR_INIT_FAILED;
207
- return nullptr ;
208
- }
209
-
210
- // Add elements to the pipeline
211
- gst_bin_add_many (GST_BIN (pipeline), source, capsfilter, videoconvert, x264enc, rtph264pay, udpsink, NULL );
212
201
213
- // Link the elements
214
- if (!gst_element_link_many (source, capsfilter, videoconvert, x264enc, rtph264pay, udpsink, NULL ))
215
- {
216
- ChipLogError (Camera, " Elements could not be linked." );
217
202
if (pipeline)
218
203
gst_object_unref (pipeline);
219
204
if (source)
@@ -222,68 +207,76 @@ GstElement * CameraDevice::CreateVideoPipeline(const std::string & device, int w
222
207
gst_object_unref (capsfilter);
223
208
if (videoconvert)
224
209
gst_object_unref (videoconvert);
210
+ if (jpegdec)
211
+ gst_object_unref (jpegdec);
225
212
if (x264enc)
226
213
gst_object_unref (x264enc);
227
214
if (rtph264pay)
228
215
gst_object_unref (rtph264pay);
229
216
if (udpsink)
230
217
gst_object_unref (udpsink);
218
+
231
219
error = CameraError::ERROR_INIT_FAILED;
232
220
return nullptr ;
233
221
}
234
222
235
- // Create GstCaps for the video source
236
- GstCaps * caps = gst_caps_new_simple (" video/x-raw" , " width" , G_TYPE_INT, width, " height" , G_TYPE_INT, height, " format" ,
237
- G_TYPE_STRING, " NV12" , // Adjust format as needed
238
- " framerate" , GST_TYPE_FRACTION, framerate, 1 , NULL );
223
+ // Caps asking the camera for MJPEG at the requested resolution / rate
224
+ GstCaps * caps = gst_caps_new_simple (" image/jpeg" , " width" , G_TYPE_INT, width, " height" , G_TYPE_INT, height, " framerate" ,
225
+ GST_TYPE_FRACTION, framerate, 1 , nullptr );
226
+ g_object_set (capsfilter, " caps" , caps, nullptr );
227
+ gst_caps_unref (caps);
239
228
240
- // Set video test src pattern
229
+ // Configure source device
241
230
#ifdef AV_STREAM_GST_USE_TEST_SRC
242
- g_object_set (source, " pattern" , kBallAnimationPattern , NULL );
231
+ g_object_set (source, " pattern" , kBallAnimationPattern , nullptr );
232
+ #else
233
+ g_object_set (source, " device" , device.c_str (), nullptr );
243
234
#endif
244
235
245
- // Set the caps on the capsfilter element
246
- g_object_set (capsfilter, " caps" , caps, NULL );
236
+ // Configure encoder / sink for low‑latency RTP
237
+ gst_util_set_object_arg (G_OBJECT (x264enc), " tune" , " zerolatency" );
238
+ g_object_set (udpsink, " host" , STREAM_GST_DEST_IP, " port" , VIDEO_STREAM_GST_DEST_PORT, " sync" , FALSE , " async" , FALSE , nullptr );
247
239
248
- // Set udpsink properties
249
- g_object_set (udpsink, " host " , STREAM_GST_DEST_IP, " port " , VIDEO_STREAM_GST_DEST_PORT, NULL );
240
+ // Build pipeline: v4l2src → capsfilter → jpegdec → videoconvert → x264enc → rtph264pay → udpsink
241
+ gst_bin_add_many ( GST_BIN (pipeline), source, capsfilter, jpegdec, videoconvert, x264enc, rtph264pay, udpsink, nullptr );
250
242
251
- // Unref the caps to free memory
252
- gst_caps_unref (caps);
243
+ // Link the elements
244
+ if (!gst_element_link_many (source, capsfilter, jpegdec, videoconvert, x264enc, rtph264pay, udpsink, nullptr ))
245
+ {
246
+ ChipLogError (Camera, " CreateVideoPipeline: link failed" );
247
+
248
+ // The bin (pipeline) will unref all added elements automatically when you unref the bin.
249
+ gst_object_unref (pipeline);
250
+ error = CameraError::ERROR_INIT_FAILED;
251
+ return nullptr ;
252
+ }
253
253
254
254
return pipeline;
255
255
}
256
256
257
257
// Helper function to create a GStreamer pipeline
258
258
GstElement * CameraDevice::CreateAudioPipeline (const std::string & device, int channels, int sampleRate, CameraError & error)
259
259
{
260
- GstElement * pipeline = nullptr ;
261
- GstElement * source = nullptr ;
262
- GstElement * capsfilter = nullptr ;
263
- GstElement * audioconvert = nullptr ;
264
- GstElement * opusenc = nullptr ;
265
- GstElement * rtpopuspay = nullptr ;
266
- GstElement * udpsink = nullptr ;
260
+ GstElement * pipeline = gst_pipeline_new (" audio-pipeline" );
267
261
268
- // Create the pipeline elements
269
- pipeline = gst_pipeline_new (" audio-pipeline" );
262
+ GstElement * capsfilter = gst_element_factory_make (" capsfilter" , " filter" );
263
+ GstElement * audioconvert = gst_element_factory_make (" audioconvert" , " audio-convert" );
264
+ GstElement * opusenc = gst_element_factory_make (" opusenc" , " opus-encoder" );
265
+ GstElement * rtpopuspay = gst_element_factory_make (" rtpopuspay" , " rtpopuspay" );
266
+ GstElement * udpsink = gst_element_factory_make (" udpsink" , " udpsink" );
270
267
268
+ GstElement * source = nullptr ;
271
269
// Create elements
272
270
#ifdef AV_STREAM_GST_USE_TEST_SRC
273
271
source = gst_element_factory_make (" audiotestsrc" , " source" );
274
272
#else
275
273
source = gst_element_factory_make (" pulsesrc" , " source" );
276
274
#endif
277
- capsfilter = gst_element_factory_make (" capsfilter" , " filter" );
278
- audioconvert = gst_element_factory_make (" audioconvert" , " audio-convert" );
279
- opusenc = gst_element_factory_make (" opusenc" , " opus-encoder" );
280
- rtpopuspay = gst_element_factory_make (" rtpopuspay" , " rtpopuspay" );
281
- udpsink = gst_element_factory_make (" udpsink" , " udpsink" );
282
275
283
- if (pipeline == nullptr || source == nullptr || capsfilter == nullptr || audioconvert == nullptr || opusenc == nullptr ||
284
- rtpopuspay == nullptr || udpsink == nullptr )
276
+ if (!pipeline || !source || !capsfilter || !audioconvert || !opusenc || !rtpopuspay || !udpsink)
285
277
{
286
278
ChipLogError (Camera, " Not all elements could be created." );
279
+
287
280
if (pipeline)
288
281
gst_object_unref (pipeline);
289
282
if (source)
@@ -298,43 +291,29 @@ GstElement * CameraDevice::CreateAudioPipeline(const std::string & device, int c
298
291
gst_object_unref (rtpopuspay);
299
292
if (udpsink)
300
293
gst_object_unref (udpsink);
294
+
301
295
error = CameraError::ERROR_INIT_FAILED;
302
296
return nullptr ;
303
297
}
304
298
305
299
// Create GstCaps for the audio source
306
- GstCaps * caps = gst_caps_new_simple (" audio/x-raw" , " channels" , G_TYPE_INT, channels, " rate" , G_TYPE_INT, sampleRate, NULL );
307
-
308
- // Set the caps on the capsfilter element
309
- g_object_set (capsfilter, " caps" , caps, NULL );
300
+ GstCaps * caps = gst_caps_new_simple (" audio/x-raw" , " channels" , G_TYPE_INT, channels, " rate" , G_TYPE_INT, sampleRate, nullptr );
301
+ g_object_set (capsfilter, " caps" , caps, nullptr );
302
+ gst_caps_unref (caps);
310
303
311
304
// Set udpsink properties
312
- g_object_set (udpsink, " host" , STREAM_GST_DEST_IP, " port" , AUDIO_STREAM_GST_DEST_PORT, NULL );
313
-
314
- // Unref the caps to free memory
315
- gst_caps_unref (caps);
305
+ g_object_set (udpsink, " host" , STREAM_GST_DEST_IP, " port" , AUDIO_STREAM_GST_DEST_PORT, nullptr );
316
306
317
307
// Add elements to the pipeline
318
- gst_bin_add_many (GST_BIN (pipeline), source, capsfilter, audioconvert, opusenc, rtpopuspay, udpsink, NULL );
308
+ gst_bin_add_many (GST_BIN (pipeline), source, capsfilter, audioconvert, opusenc, rtpopuspay, udpsink, nullptr );
319
309
320
310
// Link elements
321
- if (!gst_element_link_many (source, capsfilter, audioconvert, opusenc, rtpopuspay, udpsink, NULL ))
311
+ if (!gst_element_link_many (source, capsfilter, audioconvert, opusenc, rtpopuspay, udpsink, nullptr ))
322
312
{
323
313
ChipLogError (Camera, " Elements could not be linked." );
324
- if (pipeline)
325
- gst_object_unref (pipeline);
326
- if (source)
327
- gst_object_unref (source);
328
- if (capsfilter)
329
- gst_object_unref (capsfilter);
330
- if (audioconvert)
331
- gst_object_unref (audioconvert);
332
- if (opusenc)
333
- gst_object_unref (opusenc);
334
- if (rtpopuspay)
335
- gst_object_unref (rtpopuspay);
336
- if (udpsink)
337
- gst_object_unref (udpsink);
314
+
315
+ // The pipeline will unref all added elements automatically when you unref the pipeline.
316
+ gst_object_unref (pipeline);
338
317
error = CameraError::ERROR_INIT_FAILED;
339
318
return nullptr ;
340
319
}
@@ -428,18 +407,12 @@ CameraError CameraDevice::StartVideoStream(uint16_t streamID)
428
407
return CameraError::ERROR_VIDEO_STREAM_START_FAILED;
429
408
}
430
409
431
- // Start the pipeline
432
- GstStateChangeReturn result = gst_element_set_state (videoPipeline, GST_STATE_PLAYING);
433
- if (result == GST_STATE_CHANGE_FAILURE)
434
- {
435
- ChipLogError (Camera, " Failed to start video pipeline." );
436
- gst_object_unref (videoPipeline);
437
- it->videoContext = nullptr ;
438
- return CameraError::ERROR_VIDEO_STREAM_START_FAILED;
439
- }
410
+ ChipLogProgress (Camera, " Starting video stream (id=%u): %u×%u @ %ufps" , streamID, it->videoStreamParams .minResolution .width ,
411
+ it->videoStreamParams .minResolution .height , it->videoStreamParams .minFrameRate );
440
412
441
413
// Start the pipeline
442
- result = gst_element_set_state (videoPipeline, GST_STATE_PLAYING);
414
+ ChipLogProgress (Camera, " Requesting PLAYING …" );
415
+ GstStateChangeReturn result = gst_element_set_state (videoPipeline, GST_STATE_PLAYING);
443
416
if (result == GST_STATE_CHANGE_FAILURE)
444
417
{
445
418
ChipLogError (Camera, " Failed to start video pipeline." );
@@ -450,7 +423,7 @@ CameraError CameraDevice::StartVideoStream(uint16_t streamID)
450
423
451
424
// Wait for the pipeline to reach the PLAYING state
452
425
GstState state;
453
- gst_element_get_state (videoPipeline, &state, nullptr , GST_CLOCK_TIME_NONE );
426
+ gst_element_get_state (videoPipeline, &state, nullptr , 5 * GST_SECOND );
454
427
if (state != GST_STATE_PLAYING)
455
428
{
456
429
ChipLogError (Camera, " Video pipeline did not reach PLAYING state." );
0 commit comments