Repository: bluenviron/mediamtx

Issue:
RPi camera H.264 software encoder ignores codec config
### Which version are you using?

v1.9.3

### Which operating system are you using?

Linux arm64 standard

### Describe how to replicate the issue

I'm using a Raspberry Pi 5 Bookworm + HQ Camera to stream with the following config:
```
paths:
  cam:
    source: rpiCamera
    rpiCameraWidth: 1920
    rpiCameraHeight: 1080
    rpiCameraFPS: 25
    rpiCameraMode: "2028:1080:12"
    rpiCameraCodec: softwareH264
    rpiCameraBitrate: 5000000
    rpiCameraProfile: main
    rpiCameraLevel: "4.1"
```
When starting the server the following output is logged:
```
2024/11/19 13:13:30 INF MediaMTX v1.9.3
2024/11/19 13:13:30 INF configuration loaded from /usr/local/etc/mediamtx.yml
2024/11/19 13:13:30 INF [path cam] [RPI Camera source] started
2024/11/19 13:13:30 INF [RTSP] listener opened on :8554 (TCP)
2024/11/19 13:13:30 INF [SRT] listener opened on :8890 (UDP)
[23:07:52.467317250] [4752]  INFO Camera camera_manager.cpp:313 libcamera v0.3.0+65-6ddd79b5-dirty (2024-10-20T12:49:50+00:00)
[23:07:52.468428324] [4753]  INFO RPI pisp.cpp:695 libpisp version v1.0.6 b567f0455680-dirty 20-10-2024 (12:35:35)
[23:07:52.482498473] [4753]  INFO RPI pisp.cpp:1154 Registered camera /base/axi/pcie@120000/rp1/i2c@88000/imx477@1a to CFE device /dev/media0 and ISP device /dev/media2 using PiSP variant BCM2712_C0
[23:07:52.483069806] [4752]  INFO Camera camera.cpp:1183 configuring streams: (0) 1920x1080-YUV420 (1) 2028x1080-BGGR_PISP_COMP1
[23:07:52.483308121] [4753]  INFO RPI pisp.cpp:1450 Sensor: /base/axi/pcie@120000/rp1/i2c@88000/imx477@1a - Selected sensor format: 2028x1080-SBGGR12_1X12 - Selected CFE format: 2028x1080-PC1B
using software H264 encoder
x264 [info]: using cpu capabilities:
x264 [info]: profile Constrained Baseline, level 4.0, 4:2:0, 8-bit
2024/11/19 13:13:31 INF [path cam] [RPI Camera source] ready: 1 track (H264)
```

The selected profile is `Constrained Baseline, level 4.0` despite `main` being specified in the config.

Do the codec parameters not get correctly passed to x264?

### Server logs

_No response_

### Network dump

_No response_

Discussion:
@aler9: Thanks for reporting the issue, PR #4786 introduces two new parameters (rpiCameraSoftwareH264Profile and rpiCameraSoftwareH264Level) that allow to tune the H264 software encoder.@github-actions: This issue is mentioned in release v1.14.0 🚀
Check out the entire changelog by [clicking here](https://github.com/bluenviron/mediamtx/releases/tag/v1.14.0)

Solution patch:
diff --git a/apidocs/openapi.yaml b/apidocs/openapi.yaml
index 9c7108f5..2a80c765 100644
--- a/apidocs/openapi.yaml
+++ b/apidocs/openapi.yaml
@@ -456,6 +456,10 @@ components:
           type: string
         rpiCameraHardwareH264Level:
           type: string
+        rpiCameraSoftwareH264Profile:
+          type: string
+        rpiCameraSoftwareH264Level:
+          type: string
         rpiCameraMJPEGQuality:
           type: integer
 
diff --git a/internal/conf/path.go b/internal/conf/path.go
index 84bad9d7..1da4c7af 100644
--- a/internal/conf/path.go
+++ b/internal/conf/path.go
@@ -195,6 +195,8 @@ type Path struct {
 	RPICameraLevel                 *string   `json:"rpiCameraLevel,omitempty"`   // deprecated
 	RPICameraHardwareH264Profile   string    `json:"rpiCameraHardwareH264Profile"`
 	RPICameraHardwareH264Level     string    `json:"rpiCameraHardwareH264Level"`
+	RPICameraSoftwareH264Profile   string    `json:"rpiCameraSoftwareH264Profile"`
+	RPICameraSoftwareH264Level     string    `json:"rpiCameraSoftwareH264Level"`
 	RPICameraJPEGQuality           *uint     `json:"rpiCameraJPEGQuality,omitempty"` // deprecated
 	RPICameraMJPEGQuality          uint      `json:"rpiCameraMJPEGQuality"`
 	RPICameraPrimaryName           string    `json:"-"` // filled by Check()
@@ -259,6 +261,8 @@ func (pconf *Path) setDefaults() {
 	pconf.RPICameraBitrate = 5000000
 	pconf.RPICameraHardwareH264Profile = "main"
 	pconf.RPICameraHardwareH264Level = "4.1"
+	pconf.RPICameraSoftwareH264Profile = "baseline"
+	pconf.RPICameraSoftwareH264Level = "4.1"
 	pconf.RPICameraMJPEGQuality = 60
 
 	// Hooks
@@ -521,6 +525,18 @@ func (pconf *Path) validate(
 			return fmt.Errorf("invalid 'rpiCameraHardwareH264Level' value")
 		}
 
+		switch pconf.RPICameraSoftwareH264Profile {
+		case "baseline", "main", "high":
+		default:
+			return fmt.Errorf("invalid 'rpiCameraSoftwareH264Profile' value")
+		}
+
+		switch pconf.RPICameraSoftwareH264Level {
+		case "4.0", "4.1", "4.2":
+		default:
+			return fmt.Errorf("invalid 'rpiCameraSoftwareH264Level' value")
+		}
+
 		if pconf.RPICameraJPEGQuality != nil {
 			l.Log(logger.Warn, "parameter 'rpiCameraJPEGQuality' is deprecated"+
 				" and has been replaced with 'rpiCameraMJPEGQuality'")
diff --git a/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_32_TAR_GZ b/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_32_TAR_GZ
index 90b041fe..4e9a3cb8 100644
--- a/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_32_TAR_GZ
+++ b/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_32_TAR_GZ
@@ -1 +1 @@
-db9310fe114fa09d8427e93331fe6aec9ff3ba4aa486c7e7edbcbab9eae043cf
+fd406e8484e00a51a645bff468a4d1187875a3ab5f3302ee00fdbeed06495367
diff --git a/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_64_TAR_GZ b/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_64_TAR_GZ
index a1aa25cc..73382719 100644
--- a/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_64_TAR_GZ
+++ b/internal/staticsources/rpicamera/mtxrpicamdownloader/HASH_MTXRPICAM_64_TAR_GZ
@@ -1 +1 @@
-f6073cad9fd6c8570141f08aed72237ec062d28883b8ab62a37dbce190409f0d
+89e6865c7f3ae0f2e0e7788d8a10c54c5370ae97f16ae122e28673d3ccf12c1b
diff --git a/internal/staticsources/rpicamera/mtxrpicamdownloader/VERSION b/internal/staticsources/rpicamera/mtxrpicamdownloader/VERSION
index 4d86c300..6bd4da99 100644
--- a/internal/staticsources/rpicamera/mtxrpicamdownloader/VERSION
+++ b/internal/staticsources/rpicamera/mtxrpicamdownloader/VERSION
@@ -1 +1 @@
-v2.4.3
+v2.4.4
diff --git a/internal/staticsources/rpicamera/params.go b/internal/staticsources/rpicamera/params.go
index 487dba57..eed96b54 100644
--- a/internal/staticsources/rpicamera/params.go
+++ b/internal/staticsources/rpicamera/params.go
@@ -1,45 +1,47 @@
 package rpicamera
 
 type params struct {
-	LogLevel          string
-	CameraID          uint32
-	Width             uint32
-	Height            uint32
-	HFlip             bool
-	VFlip             bool
-	Brightness        float32
-	Contrast          float32
-	Saturation        float32
-	Sharpness         float32
-	Exposure          string
-	AWB               string
-	AWBGainRed        float32
-	AWBGainBlue       float32
-	Denoise           string
-	Shutter           uint32
-	Metering          string
-	Gain              float32
-	EV                float32
-	ROI               string
-	HDR               bool
-	TuningFile        string
-	Mode              string
-	FPS               float32
-	AfMode            string
-	AfRange           string
-	AfSpeed           string
-	LensPosition      float32
-	AfWindow          string
-	FlickerPeriod     uint32
-	TextOverlayEnable bool
-	TextOverlay       string
-	Codec             string
-	IDRPeriod         uint32
-	Bitrate           uint32
-	Profile           string
-	Level             string
-	SecondaryWidth    uint32
-	SecondaryHeight   uint32
-	SecondaryFPS      float32
-	SecondaryQuality  uint32
+	LogLevel              string
+	CameraID              uint32
+	Width                 uint32
+	Height                uint32
+	HFlip                 bool
+	VFlip                 bool
+	Brightness            float32
+	Contrast              float32
+	Saturation            float32
+	Sharpness             float32
+	Exposure              string
+	AWB                   string
+	AWBGainRed            float32
+	AWBGainBlue           float32
+	Denoise               string
+	Shutter               uint32
+	Metering              string
+	Gain                  float32
+	EV                    float32
+	ROI                   string
+	HDR                   bool
+	TuningFile            string
+	Mode                  string
+	FPS                   float32
+	AfMode                string
+	AfRange               string
+	AfSpeed               string
+	LensPosition          float32
+	AfWindow              string
+	FlickerPeriod         uint32
+	TextOverlayEnable     bool
+	TextOverlay           string
+	Codec                 string
+	IDRPeriod             uint32
+	Bitrate               uint32
+	HardwareH264Profile   string
+	HardwareH264Level     string
+	SoftwareH264Profile   string
+	SoftwareH264Level     string
+	SecondaryWidth        uint32
+	SecondaryHeight       uint32
+	SecondaryFPS          float32
+	SecondaryMJPEGQuality uint32
 }
diff --git a/internal/staticsources/rpicamera/source.go b/internal/staticsources/rpicamera/source.go
index d9cbf4f6..9d9dc5a4 100644
--- a/internal/staticsources/rpicamera/source.go
+++ b/internal/staticsources/rpicamera/source.go
@@ -37,46 +37,48 @@ func paramsFromConf(logLevel conf.LogLevel, cnf *conf.Path) params {
 			}
 			return "error"
 		}(),
-		CameraID:          uint32(cnf.RPICameraCamID),
-		Width:             uint32(cnf.RPICameraWidth),
-		Height:            uint32(cnf.RPICameraHeight),
-		HFlip:             cnf.RPICameraHFlip,
-		VFlip:             cnf.RPICameraVFlip,
-		Brightness:        float32(cnf.RPICameraBrightness),
-		Contrast:          float32(cnf.RPICameraContrast),
-		Saturation:        float32(cnf.RPICameraSaturation),
-		Sharpness:         float32(cnf.RPICameraSharpness),
-		Exposure:          cnf.RPICameraExposure,
-		AWB:               cnf.RPICameraAWB,
-		AWBGainRed:        float32(cnf.RPICameraAWBGains[0]),
-		AWBGainBlue:       float32(cnf.RPICameraAWBGains[1]),
-		Denoise:           cnf.RPICameraDenoise,
-		Shutter:           uint32(cnf.RPICameraShutter),
-		Metering:          cnf.RPICameraMetering,
-		Gain:              float32(cnf.RPICameraGain),
-		EV:                float32(cnf.RPICameraEV),
-		ROI:               cnf.RPICameraROI,
-		HDR:               cnf.RPICameraHDR,
-		TuningFile:        cnf.RPICameraTuningFile,
-		Mode:              cnf.RPICameraMode,
-		FPS:               float32(cnf.RPICameraFPS),
-		AfMode:            cnf.RPICameraAfMode,
-		AfRange:           cnf.RPICameraAfRange,
-		AfSpeed:           cnf.RPICameraAfSpeed,
-		LensPosition:      float32(cnf.RPICameraLensPosition),
-		AfWindow:          cnf.RPICameraAfWindow,
-		FlickerPeriod:     uint32(cnf.RPICameraFlickerPeriod),
-		TextOverlayEnable: cnf.RPICameraTextOverlayEnable,
-		TextOverlay:       cnf.RPICameraTextOverlay,
-		Codec:             cnf.RPICameraCodec,
-		IDRPeriod:         uint32(cnf.RPICameraIDRPeriod),
-		Bitrate:           uint32(cnf.RPICameraBitrate),
-		Profile:           cnf.RPICameraHardwareH264Profile,
-		Level:             cnf.RPICameraHardwareH264Level,
-		SecondaryWidth:    uint32(cnf.RPICameraSecondaryWidth),
-		SecondaryHeight:   uint32(cnf.RPICameraSecondaryHeight),
-		SecondaryFPS:      float32(cnf.RPICameraSecondaryFPS),
-		SecondaryQuality:  uint32(cnf.RPICameraSecondaryMJPEGQuality),
+		CameraID:              uint32(cnf.RPICameraCamID),
+		Width:                 uint32(cnf.RPICameraWidth),
+		Height:                uint32(cnf.RPICameraHeight),
+		HFlip:                 cnf.RPICameraHFlip,
+		VFlip:                 cnf.RPICameraVFlip,
+		Brightness:            float32(cnf.RPICameraBrightness),
+		Contrast:              float32(cnf.RPICameraContrast),
+		Saturation:            float32(cnf.RPICameraSaturation),
+		Sharpness:             float32(cnf.RPICameraSharpness),
+		Exposure:              cnf.RPICameraExposure,
+		AWB:                   cnf.RPICameraAWB,
+		AWBGainRed:            float32(cnf.RPICameraAWBGains[0]),
+		AWBGainBlue:           float32(cnf.RPICameraAWBGains[1]),
+		Denoise:               cnf.RPICameraDenoise,
+		Shutter:               uint32(cnf.RPICameraShutter),
+		Metering:              cnf.RPICameraMetering,
+		Gain:                  float32(cnf.RPICameraGain),
+		EV:                    float32(cnf.RPICameraEV),
+		ROI:                   cnf.RPICameraROI,
+		HDR:                   cnf.RPICameraHDR,
+		TuningFile:            cnf.RPICameraTuningFile,
+		Mode:                  cnf.RPICameraMode,
+		FPS:                   float32(cnf.RPICameraFPS),
+		AfMode:                cnf.RPICameraAfMode,
+		AfRange:               cnf.RPICameraAfRange,
+		AfSpeed:               cnf.RPICameraAfSpeed,
+		LensPosition:          float32(cnf.RPICameraLensPosition),
+		AfWindow:              cnf.RPICameraAfWindow,
+		FlickerPeriod:         uint32(cnf.RPICameraFlickerPeriod),
+		TextOverlayEnable:     cnf.RPICameraTextOverlayEnable,
+		TextOverlay:           cnf.RPICameraTextOverlay,
+		Codec:                 cnf.RPICameraCodec,
+		IDRPeriod:             uint32(cnf.RPICameraIDRPeriod),
+		Bitrate:               uint32(cnf.RPICameraBitrate),
+		HardwareH264Profile:   cnf.RPICameraHardwareH264Profile,
+		HardwareH264Level:     cnf.RPICameraHardwareH264Level,
+		SoftwareH264Profile:   cnf.RPICameraSoftwareH264Profile,
+		SoftwareH264Level:     cnf.RPICameraSoftwareH264Level,
+		SecondaryWidth:        uint32(cnf.RPICameraSecondaryWidth),
+		SecondaryHeight:       uint32(cnf.RPICameraSecondaryHeight),
+		SecondaryFPS:          float32(cnf.RPICameraSecondaryFPS),
+		SecondaryMJPEGQuality: uint32(cnf.RPICameraSecondaryMJPEGQuality),
 	}
 }
 
diff --git a/mediamtx.yml b/mediamtx.yml
index 648ee4ae..d0d2f0e3 100644
--- a/mediamtx.yml
+++ b/mediamtx.yml
@@ -622,6 +622,10 @@ pathDefaults:
   rpiCameraHardwareH264Profile: main
   # Hardware H264 level (4.0, 4.1 or 4.2) (when codec is hardwareH264).
   rpiCameraHardwareH264Level: '4.1'
+  # Software H264 profile (baseline, main or high) (when codec is softwareH264).
+  rpiCameraSoftwareH264Profile: baseline
+  # Software H264 level (4.0, 4.1 or 4.2) (when codec is softwareH264).
+  rpiCameraSoftwareH264Level: '4.1'
   # M-JPEG JPEG quality (when codec is mjpeg).
   rpiCameraMJPEGQuality: 60
 



Test patch:
diff --git a/internal/conf/conf_test.go b/internal/conf/conf_test.go
index 6864f649..d868617d 100644
--- a/internal/conf/conf_test.go
+++ b/internal/conf/conf_test.go
@@ -79,6 +79,8 @@ func TestConfFromFile(t *testing.T) {
 			RPICameraBitrate:             5000000,
 			RPICameraHardwareH264Profile: "main",
 			RPICameraHardwareH264Level:   "4.1",
+			RPICameraSoftwareH264Profile: "baseline",
+			RPICameraSoftwareH264Level:   "4.1",
 			RPICameraMJPEGQuality:        60,
 			RunOnDemandStartTimeout:      5 * Duration(time.Second),
 			RunOnDemandCloseAfter:        10 * Duration(time.Second),