diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 5eca3ff7..6c0ab802 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -109,7 +109,7 @@ func handleFlags() revid.Config { cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") inputCodecPtr = flag.String("InputCodec", "H264", "The codec of the input: H264, Mjpeg, PCM, ADPCM") inputPtr = flag.String("Input", "", "The input type: Raspivid, File, v4l, Audio, RTSP") - rtspURLPtr = flag.String("RTSPURL", "", "The URL for an RTSP server.") + cameraIPPtr = flag.String("CameraIP", "", "The the IP of the RTSP server") verbosityPtr = flag.String("Verbosity", "Info", "Verbosity: Debug, Info, Warning, Error, Fatal") rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: : (port is generally 6970-6999)") logPathPtr = flag.String("LogPath", defaultLogPath, "The log path") @@ -231,7 +231,7 @@ func handleFlags() revid.Config { netsender.ConfigFile = *configFilePtr } - cfg.RTSPURL = *rtspURLPtr + cfg.CameraIP = *cameraIPPtr cfg.Rotation = *rotationPtr cfg.FlipHorizontal = *horizontalFlipPtr cfg.FlipVertical = *verticalFlipPtr diff --git a/revid/config.go b/revid/config.go index 29acb42c..d02b65bc 100644 --- a/revid/config.go +++ b/revid/config.go @@ -63,6 +63,19 @@ var AutoWhiteBalanceModes = [...]string{ "horizon", } +// quality represents video quality. +type quality int + +// The different video qualities that can be used for variable bitrate when +// using the GeoVision camera. +const ( + qualityStandard quality = iota + qualityFair + qualityGood + qualityGreat + qualityExcellent +) + // Enums to define inputs, outputs and codecs. const ( // Indicates no option has been set. @@ -100,8 +113,11 @@ const ( defaultInputCodec = codecutil.H264 defaultVerbosity = logger.Error defaultRtpAddr = "localhost:6970" - defaultRTSPURL = "rtsp://admin:admin@192.168.1.50:8554/CH001.sdp" - defaultBurstPeriod = 10 // Seconds + defaultCameraIP = "192.168.1.50" + defaultVBR = false + defaultVBRQuality = qualityStandard + defaultBurstPeriod = 10 // Seconds + defaultVBRBitrate = 500 // kbps // Raspivid video defaults. defaultBrightness = 50 @@ -113,7 +129,7 @@ const ( defaultMinFrames = 100 defaultClipDuration = 0 defaultQuantization = 30 - defaultBitrate = 400000 + defaultBitrate = 400 // Audio defaults. defaultAudioInputCodec = codecutil.ADPCM @@ -152,7 +168,7 @@ type Config struct { // File: // Location must be specified in InputPath field. // RTSP: - // RTSPURL must also be defined. + // CameraIP should also be defined. Input uint8 // InputCodec defines the input codec we wish to use, and therefore defines the @@ -181,9 +197,9 @@ type Config struct { // RTMP is to be used as an output. RTMPURL string - // RTSPURL specifies the RTSP server URL for RTSP input. This must be defined - // when Input is RTSP. - RTSPURL string + // CameraIP is the IP address of the camera in the case of the input camera + // being an IP camera. + CameraIP string // OutputPath defines the output destination for File output. This must be // defined if File output is to be used. @@ -204,11 +220,27 @@ type Config struct { // defined in /etc/netsender.conf. HTTPAddress string - // Quantization defines the quantization level, which may be a value between - // 0-40. This will only take effect if the Quantize field is true and if we - // are using Raspivid input. + // VBR indicates whether we wish to use constant or variable bitrate. If VBR + // is true then we will use variable bitrate, and constant bitrate otherwise. + // In the case of the Pi camera, variable bitrate quality is controlled by + // the Quantization parameter below. In the case of the GeoVision camera, + // variable bitrate quality is controlled by firstly the VBRQuality parameter + // and second the VBRBitrate parameter. + VBR bool + + // Quantization defines the quantization level, which will determine variable + // bitrate quality in the case of input from the Pi Camera. Quantization uint + // VBRQuality describes the general quality of video from the GeoVision camera + // under variable bitrate. VBRQuality can be one 5 consts defined: + // qualityStandard, qualityFair, qualityGood, qualityGreat and qualityExcellent. + VBRQuality quality + + // VBRBitrate describes maximal bitrate for the GeoVision camera when under + // variable bitrate. + VBRBitrate int + // MinFrames defines the frequency of key NAL units SPS, PPS and IDR in // number of NAL units. This will also determine the frequency of PSI if the // output container is MPEG-TS. If ClipDuration is less than MinFrames, @@ -251,7 +283,7 @@ type Config struct { Rotation uint // Rotation defines the video rotation angle in degrees Raspivid input. Height uint // Height defines the input video height Raspivid input. Width uint // Width defines the input video width Raspivid input. - Bitrate uint // Bitrate specifies the input bitrate for Raspivid input. + Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps. FlipHorizontal bool // FlipHorizontal flips video horizontally for Raspivid input. FlipVertical bool // FlipVertial flips video vertically for Raspivid input. @@ -288,9 +320,9 @@ func (c *Config) Validate() error { switch c.Input { case Raspivid, V4L, File, Audio: case RTSP: - if c.RTSPURL == "" { - c.Logger.Log(logger.Info, pkg+"no RTSPURL defined, defaulting", "RTSPURL", defaultRTSPURL) - c.RTSPURL = defaultRTSPURL + if c.CameraIP == "" { + c.Logger.Log(logger.Info, pkg+"no CameraIP defined, defaulting", "CameraIP", defaultCameraIP) + c.CameraIP = defaultCameraIP } case NothingDefined: c.Logger.Log(logger.Info, pkg+"no input type defined, defaulting", "input", defaultInput) @@ -481,6 +513,18 @@ func (c *Config) Validate() error { c.MTSRBWriteTimeout = defaultMTSRBWriteTimeout } + switch c.VBRQuality { + case qualityStandard, qualityFair, qualityGood, qualityGreat, qualityExcellent: + default: + c.Logger.Log(logger.Info, pkg+"VBRQuality bad or unset, defaulting", "VBRQuality", defaultVBRQuality) + c.VBRQuality = defaultVBRQuality + } + + if c.VBRBitrate <= 0 { + c.Logger.Log(logger.Info, pkg+"VBRBitrate bad or unset, defaulting", "VBRBitrate", defaultVBRBitrate) + c.VBRBitrate = defaultVBRBitrate + } + return nil } diff --git a/revid/inputs.go b/revid/inputs.go index 4df3abb3..9d1d2198 100644 --- a/revid/inputs.go +++ b/revid/inputs.go @@ -40,12 +40,19 @@ import ( "time" "bitbucket.org/ausocean/av/codec/codecutil" + "bitbucket.org/ausocean/av/input/gvctrl" "bitbucket.org/ausocean/av/protocol/rtcp" "bitbucket.org/ausocean/av/protocol/rtp" "bitbucket.org/ausocean/av/protocol/rtsp" "bitbucket.org/ausocean/utils/logger" ) +// TODO: remove this when gvctrl has configurable user and pass. +const ( + ipCamUser = "admin" + ipCamPass = "pass" +) + // startRaspivid sets up things for input from raspivid i.e. starts // a raspivid process and pipes it's data output. func (r *Revid) startRaspivid() (func() error, error) { @@ -58,7 +65,7 @@ func (r *Revid) startRaspivid() (func() error, error) { "--timeout", disabled, "--width", fmt.Sprint(r.config.Width), "--height", fmt.Sprint(r.config.Height), - "--bitrate", fmt.Sprint(r.config.Bitrate), + "--bitrate", fmt.Sprint(r.config.Bitrate * 1000), // Convert from kbps to bps. "--framerate", fmt.Sprint(r.config.FrameRate), "--rotation", fmt.Sprint(r.config.Rotation), "--brightness", fmt.Sprint(r.config.Brightness), @@ -87,7 +94,7 @@ func (r *Revid) startRaspivid() (func() error, error) { "--inline", "--intra", fmt.Sprint(r.config.MinFrames), ) - if r.config.Quantization != 0 { + if r.config.VBR { args = append(args, "-qp", fmt.Sprint(r.config.Quantization)) } case codecutil.MJPEG: @@ -173,7 +180,36 @@ func (r *Revid) setupInputForFile() (func() error, error) { // client is created from which RTP packets containing either h264/h265 can be // read by the selected lexer. func (r *Revid) startRTSPCamera() (func() error, error) { - rtspClt, local, remote, err := rtsp.NewClient(r.config.RTSPURL) + err := gvctrl.Set( + r.config.CameraIP, + gvctrl.CodecOut( + map[uint8]gvctrl.Codec{ + codecutil.H264: gvctrl.CodecH264, + codecutil.H265: gvctrl.CodecH265, + codecutil.MJPEG: gvctrl.CodecMJPEG, + }[r.config.InputCodec], + ), + gvctrl.Height(int(r.config.Height)), + gvctrl.FrameRate(int(r.config.FrameRate)), + gvctrl.VariableBitrate(r.config.VBR), + gvctrl.VBRQuality( + map[quality]gvctrl.Quality{ + qualityStandard: gvctrl.QualityStandard, + qualityFair: gvctrl.QualityFair, + qualityGood: gvctrl.QualityGood, + qualityGreat: gvctrl.QualityGreat, + qualityExcellent: gvctrl.QualityExcellent, + }[r.config.VBRQuality], + ), + gvctrl.VBRBitrate(r.config.VBRBitrate), + gvctrl.CBRBitrate(int(r.config.Bitrate)/1000), + gvctrl.Refresh(float64(r.config.MinFrames)/float64(r.config.FrameRate)), + ) + if err != nil { + return nil, fmt.Errorf("could not set IPCamera settings: %w", err) + } + + rtspClt, local, remote, err := rtsp.NewClient("rtsp://" + ipCamUser + ":" + ipCamPass + "@" + r.config.CameraIP + ":8554/" + "CH002.sdp") if err != nil { return nil, err } diff --git a/revid/revid.go b/revid/revid.go index 7a867bcd..b5b23ceb 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -618,6 +618,27 @@ func (r *Revid) Update(vars map[string]string) error { break } r.config.MTSRBWriteTimeout = v + case "VBR": + v, ok := map[string]bool{"true": true, "false": false}[strings.ToLower(value)] + if !ok { + r.config.Logger.Log(logger.Warning, pkg+"invalid VBR var", "value", value) + break + } + r.config.VBR = v + case "VBRQuality": + v, ok := map[string]quality{"standard": qualityStandard, "fair": qualityFair, "good": qualityGood, "great": qualityGreat, "excellent": qualityExcellent}[strings.ToLower(value)] + if !ok { + r.config.Logger.Log(logger.Warning, pkg+"invalid VBRQuality var", "value", value) + break + } + r.config.VBRQuality = v + case "VBRBitrate": + v, err := strconv.Atoi(value) + if err != nil || v <= 0 { + r.config.Logger.Log(logger.Warning, pkg+"invalid VBRBitrate var", "value", value) + break + } + r.config.VBRBitrate = v } } r.config.Logger.Log(logger.Info, pkg+"revid config changed", "config", fmt.Sprintf("%+v", r.config))