Introduction
User bocorps pointed me in the direction of gst-rpicamsrc. This is “… a GStreamer wrapper around the raspivid/raspistill functionality of the RaspberryPi, providing a GStreamer source element capturing from the Rpi camera.”
What this means is that instead of piping the output of raspivid into gstreamer, gstreamer has a source element to read the camera directly. This is similar to using the video4linux (v4l) source element, but negates the need for a v4l driver.
My hope was that by integrating the camera functionality into a gstreamer source element the latency would be reduced. Unfortunately, I actually saw an 18% increase in latency.
Installation
Before I could use the gst-rpicamsrc element, I needed to download the source and build it. As I was working with a minimal install of Raspbian Jessie, I needed to install the git package before I could do anything else
sudo apt-get install git
With git installed I could download the latest sources for gst-rpicamsrc.
git clone https://github.com/thaytan/gst-rpicamsrc.git
With that done a look in the REQUIREMENTS file indicated what other packages were needed in order to accomplish the build.
sudo apt-get install autoconf automake libtool libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libraspberrypi-dev
Finally, I was able to complete the build and install.
./autogen --prefix=/usr --libdir=/usr/lib/arm-linux-gnueabihf/ make sudo make install
The command `gst-inspect-1.0 rpicamsrc’ produces a list of the available parameters.
Factory Details: Rank none (0) Long-name Raspberry Pi Camera Source Klass Source/Video Description Raspberry Pi camera module source Author Jan Schmidt <jan@centricular.com> Plugin Details: Name rpicamsrc Description Raspberry Pi Camera Source Filename /usr/lib/arm-linux-gnueabihf/gstreamer-1.0/libgstrpicamsrc.so Version 1.0.0 License LGPL Source module gstrpicamsrc Binary package GStreamer Origin URL http://gstreamer.net/ GObject +----GInitiallyUnowned +----GstObject +----GstElement +----GstBaseSrc +----GstPushSrc +----GstRpiCamSrc Pad Templates: SRC template: 'src' Availability: Always Capabilities: video/x-h264 width: [ 1, 2147483647 ] height: [ 1, 2147483647 ] framerate: [ 0/1, 2147483647/1 ] stream-format: byte-stream alignment: au profile: { baseline, main, high } Element Flags: no flags set Element Implementation: Has change_state() function: gst_base_src_change_state Element has no clocking capabilities. Element has no URI handling capabilities. Pads: SRC: 'src' Implementation: Has getrangefunc(): gst_base_src_getrange Has custom eventfunc(): gst_base_src_event Has custom queryfunc(): gst_base_src_query Has custom iterintlinkfunc(): gst_pad_iterate_internal_links_default Pad Template: 'src' Element Properties: name : The name of the object flags: readable, writable String. Default: "rpicamsrc0" parent : The parent of the object flags: readable, writable Object of type "GstObject" blocksize : Size in bytes to read per buffer (-1 = default) flags: readable, writable Unsigned Integer. Range: 0 - 4294967295 Default: 4096 num-buffers : Number of buffers to output before sending EOS (-1 = unlimited) flags: readable, writable Integer. Range: -1 - 2147483647 Default: -1 typefind : Run typefind before negotiating flags: readable, writable Boolean. Default: false do-timestamp : Apply current stream time to buffers flags: readable, writable Boolean. Default: true bitrate : Bitrate for encoding flags: readable, writable Integer. Range: 1 - 25000000 Default: 17000000 preview : Display preview window overlay flags: readable, writable Boolean. Default: true preview-encoded : Display encoder output in the preview flags: readable, writable Boolean. Default: true preview-opacity : Opacity to use for the preview window flags: readable, writable Integer. Range: 0 - 255 Default: 255 fullscreen : Display preview window full screen flags: readable, writable Boolean. Default: true sharpness : Image capture sharpness flags: readable, writable Integer. Range: -100 - 100 Default: 0 contrast : Image capture contrast flags: readable, writable Integer. Range: -100 - 100 Default: 0 brightness : Image capture brightness flags: readable, writable Integer. Range: 0 - 100 Default: 50 saturation : Image capture saturation flags: readable, writable Integer. Range: -100 - 100 Default: 0 iso : ISO value to use (0 = Auto) flags: readable, writable Integer. Range: 0 - 3200 Default: 0 video-stabilisation : Enable or disable video stabilisation flags: readable, writable Boolean. Default: false exposure-compensation: Exposure Value compensation flags: readable, writable Integer. Range: -10 - 10 Default: 0 exposure-mode : Camera exposure mode to use flags: readable, writable Enum "GstRpiCamSrcExposureMode" Default: 1, "auto" (0): off - GST_RPI_CAM_SRC_EXPOSURE_MODE_OFF (1): auto - GST_RPI_CAM_SRC_EXPOSURE_MODE_AUTO (2): night - GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHT (3): nightpreview - GST_RPI_CAM_SRC_EXPOSURE_MODE_NIGHTPREVIEW (4): backlight - GST_RPI_CAM_SRC_EXPOSURE_MODE_BACKLIGHT (5): spotlight - GST_RPI_CAM_SRC_EXPOSURE_MODE_SPOTLIGHT (6): sports - GST_RPI_CAM_SRC_EXPOSURE_MODE_SPORTS (7): snow - GST_RPI_CAM_SRC_EXPOSURE_MODE_SNOW (8): beach - GST_RPI_CAM_SRC_EXPOSURE_MODE_BEACH (9): verylong - GST_RPI_CAM_SRC_EXPOSURE_MODE_VERYLONG (10): fixedfps - GST_RPI_CAM_SRC_EXPOSURE_MODE_FIXEDFPS (11): antishake - GST_RPI_CAM_SRC_EXPOSURE_MODE_ANTISHAKE (12): fireworks - GST_RPI_CAM_SRC_EXPOSURE_MODE_FIREWORKS metering-mode : Camera exposure metering mode to use flags: readable, writable Enum "GstRpiCamSrcExposureMeteringMode" Default: 0, "average" (0): average - GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_AVERAGE (1): spot - GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_SPOT (2): backlist - GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_BACKLIST (3): matrix - GST_RPI_CAM_SRC_EXPOSURE_METERING_MODE_MATRIX awb-mode : White Balance mode flags: readable, writable Enum "GstRpiCamSrcAWBMode" Default: 1, "auto" (0): off - GST_RPI_CAM_SRC_AWB_MODE_OFF (1): auto - GST_RPI_CAM_SRC_AWB_MODE_AUTO (2): sunlight - GST_RPI_CAM_SRC_AWB_MODE_SUNLIGHT (3): cloudy - GST_RPI_CAM_SRC_AWB_MODE_CLOUDY (4): shade - GST_RPI_CAM_SRC_AWB_MODE_SHADE (5): tungsten - GST_RPI_CAM_SRC_AWB_MODE_TUNGSTEN (6): fluorescent - GST_RPI_CAM_SRC_AWB_MODE_FLUORESCENT (7): incandescent - GST_RPI_CAM_SRC_AWB_MODE_INCANDESCENT (8): flash - GST_RPI_CAM_SRC_AWB_MODE_FLASH (9): horizon - GST_RPI_CAM_SRC_AWB_MODE_HORIZON image-effect : Visual FX to apply to the image flags: readable, writable Enum "GstRpiCamSrcImageEffect" Default: 0, "none" (0): none - GST_RPI_CAM_SRC_IMAGEFX_NONE (1): negative - GST_RPI_CAM_SRC_IMAGEFX_NEGATIVE (2): solarize - GST_RPI_CAM_SRC_IMAGEFX_SOLARIZE (3): posterize - GST_RPI_CAM_SRC_IMAGEFX_POSTERIZE (4): whiteboard - GST_RPI_CAM_SRC_IMAGEFX_WHITEBOARD (5): blackboard - GST_RPI_CAM_SRC_IMAGEFX_BLACKBOARD (6): sketch - GST_RPI_CAM_SRC_IMAGEFX_SKETCH (7): denoise - GST_RPI_CAM_SRC_IMAGEFX_DENOISE (8): emboss - GST_RPI_CAM_SRC_IMAGEFX_EMBOSS (9): oilpaint - GST_RPI_CAM_SRC_IMAGEFX_OILPAINT (10): hatch - GST_RPI_CAM_SRC_IMAGEFX_HATCH (11): gpen - GST_RPI_CAM_SRC_IMAGEFX_GPEN (12): pastel - GST_RPI_CAM_SRC_IMAGEFX_PASTEL (13): watercolour - GST_RPI_CAM_SRC_IMAGEFX_WATERCOLOUR (14): film - GST_RPI_CAM_SRC_IMAGEFX_FILM (15): blur - GST_RPI_CAM_SRC_IMAGEFX_BLUR (16): saturation - GST_RPI_CAM_SRC_IMAGEFX_SATURATION (17): colourswap - GST_RPI_CAM_SRC_IMAGEFX_COLOURSWAP (18): washedout - GST_RPI_CAM_SRC_IMAGEFX_WASHEDOUT (19): posterise - GST_RPI_CAM_SRC_IMAGEFX_POSTERISE (20): colourpoint - GST_RPI_CAM_SRC_IMAGEFX_COLOURPOINT (21): colourbalance - GST_RPI_CAM_SRC_IMAGEFX_COLOURBALANCE (22): cartoon - GST_RPI_CAM_SRC_IMAGEFX_CARTOON rotation : Rotate captured image (0, 90, 180, 270 degrees) flags: readable, writable Integer. Range: 0 - 270 Default: 0 hflip : Flip capture horizontally flags: readable, writable Boolean. Default: false vflip : Flip capture vertically flags: readable, writable Boolean. Default: false roi-x : Normalised region-of-interest X coord flags: readable, writable Float. Range: 0 - 1 Default: 0 roi-y : Normalised region-of-interest Y coord flags: readable, writable Float. Range: 0 - 1 Default: 0 roi-w : Normalised region-of-interest W coord flags: readable, writable Float. Range: 0 - 1 Default: 1 roi-h : Normalised region-of-interest H coord flags: readable, writable Float. Range: 0 - 1 Default: 1
Usage
Because of the way gstreamer works, the parameters for the feed needed to be split and re-arranged in the streamer pipeline. Previously all the parameters are specified as part of raspivid.
/opt/vc/bin/raspivid -t $DURATION -w $WIDTH -h $HEIGHT -fps $FRAMERATE -b $BITRATE -n -pf high -o - | gst-launch-1.0 -v fdsrc !
GStreamer parameters like width, height and frame rate are configured through capabilities (caps) negotiation with the next element. Other parameters like the bit rate and preview screen are controlled as part of the source element.
gst-launch-1.0 rpicamsrc bitrate=$BITRATE preview=0 ! video/x-h264,width=$WIDTH,height=$HEIGHT,framerate=$FRAMERATE/1 !
The new stream script is
#!/bin/bash source remote.conf if [ "$1" != "" ] then export FRAMERATE=$1 fi NOW=`date +%Y%m%d%H%M%S` FILENAME=$NOW-Tx.h264 gst-launch-1.0 rpicamsrc bitrate=$BITRATE preview=0 ! video/x-h264,width=$WIDTH,height=$HEIGHT,framereate=$FRAMERATE/1,profile=high ! h264parse ! rtph264pay config-interval=1 pt=96 ! udpsink host=$RX_IP port=$UDPPORT
Tests
This test was a straight comparison between the old and new scripts using the same settings and the same testing methodology as previously. The resolution was 1280 x 720 pixels with a 6Mbps bitrate.
Update : Since the original article was published use Jan Schmitt spotted that I had misspelled “framerate” as “framereate” in the gst-rpicamsrc script. He also suggested I should try using the baseline profile and a queue element to decouple the video capture from the UDP transmission. With this in mind I have re-run the tests.
Results
Almost immediately I had the feeling that gst-rpicamsrc has slower. Analysis of the video showed I was correct. The latency using gst-rpicamsrc was 18% higher than using raspivid.
Update:
Running the original erroneous script with debugging on showed that the capture was running at 30fps instead of the intended 48 fps. Here are the new results averaged from 10 cycles.
Script | gst-rpicamsrc @ 48 fps |
raspivid @ 48 fps |
||
Profile | No queue | With queue | No queue | With queue |
baseline | 184.2 | 175.4 | 153.9 | 156.9 |
high | 186.2 | 185 | 154.1 | 159.7 |
gst-rpicamsrc @ 30 fps, high profile, no queue = 198.2 ms
Analysis
The first thing to note is that the raspivid latency (no queue, high profile) has risen from the 126ms found in the last tests to 154ms. The only difference was that I cloned the Sandisk microSDHC card onto a Transcend 8GB. I’ll set up some more tests to compare the cards. As these tests were run from the same card and from the same boot, they are still valid for comparison.
It is immediately obvious that the gst-rpicamsrc latency is about 20% higher than the raspivid script, so the conclusion from the first publish of this article still stands.
What can be added is that using the baseline profile, does reduce the latency a little: 1 to 3ms in most cases.
Adding a queue element does provide a benefit for the gst-rpicamsrc script, especially with the baseline profile where a 9ms reduction in latency was observed. For the raspivid script adding a queue element actually increased the latency by 3 to 4ms. I suspect this is because the video stream is already decoupled from gstreamer by being piped in from an external process.
Conclusion
Using gst-rpicamsrc provides no benefit for reducing latency over raspivid. That is not to say gst-rpicamsrc provides no other benefits. For any use other than FPV, I would definitely use gst-rpicamsrc instead of having to pipe the video in through stdin. It provides plenty of options for setting up the video stream as the command `gst-inspect-1.0 rpicamsrc’ above showed.
The problem here is that I am targeting this development for FPV use where low latency is the driving factor. At the moment my lowest latency for a adequate quality HD stream is 125ms and I really need to get this under 100ms to compete with current analog standard definition systems. Whether it is possible to shave of another 25ms remains to be seen.
Update: Following the additional tests I would add that it is better to use the baseline profile over the high profile.