Files
Adam Shiervani cb7746fb78 feat(video): add H.265 codec support with auto-negotiation (#1371)
* feat(video): add H.265 codec support with auto-negotiation

Add H.265 (HEVC) encoding support to the RV1106 hardware encoder alongside
existing H.264. The codec is negotiated per-WebRTC session based on browser
capabilities.

- Add codec preference setting (Auto/H.265/H.264) to config, RPC, and UI
- Auto mode inspects the browser's SDP offer and prefers H.265 when supported,
  with graceful fallback to H.264 for browsers without H.265 (e.g. Firefox)
- Move WebRTC video track creation from newSession() to ExchangeOffer() so
  the codec can be resolved after seeing the browser's offer
- Set encoder codec type in onFirstSessionConnected() before VideoStart()
- Show active codec in the status bar when troubleshooting mode is enabled
- Remove quality factor >1.0 ceiling from ctrl.c to allow bitrate testing
- Fix Go wrapper to check return value from C quality factor setter
- Add e2e tests: video quality bitrate measurement, codec negotiation,
  codec preference persistence, and a quality factor sweep benchmark
- Add visual noise helpers (remote host terminal) to e2e test infrastructure

* chore(e2e): remove video quality benchmark tests and helpers

Remove video-quality-sweep and video-quality spec files — these are
benchmarking tools, not regression tests. Also removes the visual noise
helpers and hardcoded developer SSH address from helpers.ts.

* feat(video): bump bitrate cap to 4000 kbps and tighten VBR ceiling

- Increase base_bitrate_high from 2000 to 4000 kbps, giving users
  better image quality at every quality factor setting.
- Tighten VBR max_bitrate from 2x to 1.5x target, reducing encoder
  overshoot while still allowing headroom for dynamic content.
- Add frames dropped, decode time, freeze count to WebRTC test hooks
  for pipeline health monitoring.
- Move bitrate sweep benchmark to ui/benchmarks/ with its own
  playwright config, separate from the e2e test suite.

Sweep results (visual noise, H.264, 1080p):
  factor=0.1: 3082 kbps, 60fps, 0 dropped, 2.9ms decode
  factor=0.5: 6357 kbps, 60fps, 0 dropped, 3.6ms decode
  factor=1.0: 9445 kbps, 59fps, 0 dropped, 4.3ms decode
2026-03-29 21:34:38 +02:00

120 lines
3.0 KiB
Go

package native
type EmptyNativeInterface struct {
}
func (e *EmptyNativeInterface) Start() error { return nil }
func (e *EmptyNativeInterface) VideoSetSleepMode(enabled bool) error { return nil }
func (e *EmptyNativeInterface) VideoGetSleepMode() (bool, error) { return false, nil }
func (e *EmptyNativeInterface) VideoSleepModeSupported() bool {
return false
}
func (e *EmptyNativeInterface) VideoSetQualityFactor(factor float64) error {
return nil
}
func (e *EmptyNativeInterface) VideoGetQualityFactor() (float64, error) {
return 0, nil
}
func (e *EmptyNativeInterface) VideoSetCodecType(codecType int) error {
return nil
}
func (e *EmptyNativeInterface) VideoGetCodecType() (int, error) {
return 0, nil
}
func (e *EmptyNativeInterface) VideoSetEDID(edid string) error {
return nil
}
func (e *EmptyNativeInterface) VideoGetEDID() (string, error) {
return "", nil
}
func (e *EmptyNativeInterface) VideoLogStatus() (string, error) {
return "", nil
}
func (e *EmptyNativeInterface) VideoStop() error {
return nil
}
func (e *EmptyNativeInterface) VideoStart() error {
return nil
}
func (e *EmptyNativeInterface) GetLVGLVersion() (string, error) {
return "", nil
}
func (e *EmptyNativeInterface) UIObjHide(objName string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjShow(objName string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UISetVar(name string, value string) {
}
func (e *EmptyNativeInterface) UIGetVar(name string) string {
return ""
}
func (e *EmptyNativeInterface) UIObjAddState(objName string, state string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjClearState(objName string, state string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjAddFlag(objName string, flag string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjClearFlag(objName string, flag string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjSetOpacity(objName string, opacity int) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjFadeIn(objName string, duration uint32) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjFadeOut(objName string, duration uint32) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjSetLabelText(objName string, text string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UIObjSetImageSrc(objName string, image string) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) DisplaySetRotation(rotation uint16) (bool, error) {
return false, nil
}
func (e *EmptyNativeInterface) UpdateLabelIfChanged(objName string, newText string) {}
func (e *EmptyNativeInterface) UpdateLabelAndChangeVisibility(objName string, newText string) {}
func (e *EmptyNativeInterface) SwitchToScreenIf(screenName string, shouldSwitch []string) {}
func (e *EmptyNativeInterface) SwitchToScreenIfDifferent(screenName string) {}
func (e *EmptyNativeInterface) DoNotUseThisIsForCrashTestingOnly() {}