Skip to content

esp_capture_sink_set_bitrate() returns NOT_SUPPORTED and confuses audio/video (AUD-6879) #29

@josefeliuf

Description

@josefeliuf

Checklist

  • Checked the issue tracker for similar issues to ensure this is not a duplicate
  • Read the documentation to confirm the issue is not addressed there and your configuration is set correctly
  • Tested with the latest version to ensure the issue hasn't been fixed

How often does this bug occurs?

always

Expected behavior

esp_capture_sink_set_bitrate() always returned ESP_CAPTURE_ERR_NOT_SUPPORTED and mixed up audio/video routing. This prevents bitrate changes from reaching the active video encoder.

Calling esp_capture_sink_set_bitrate(h, ESP_CAPTURE_STREAM_TYPE_VIDEO, bps) should:

  • Route to the video path manager’s .set with ESP_CAPTURE_PATH_SET_TYPE_VIDEO_BITRATE

  • Update video_path_res_t.bitrate

  • If venc_el is present, call esp_gmf_video_enc_set_bitrate(venc_el, bitrate) immediately; otherwise apply on prepare/start

Actual behavior (suspected bug)

  • Function returned ESP_CAPTURE_ERR_NOT_SUPPORTED

  • Audio/video handling was inverted, so video bitrate never propagated

Error logs or terminal output

W (53288) webrtc: Failed to set video bitrate: -3

Steps to reproduce the behavior

  1. Context

App is built on espressif/esp-webrtc-solution, which provides the WebRTC layer and plugs into esp_capture for media (capture/encode). The repo’s README states that esp_capture is used to capture media (now provided as a Component Registry package), and the solution bundles several demo apps (Doorbell, Video Call, WHIP Publisher, etc.). However, the WebRTC library itself does not expose a public API to adjust encoder bitrate at runtime, so I added a small wrapper that calls esp_capture_sink_set_bitrate(...) under the hood to test runtime bitrate changes

  1. Environment

ESP-IDF: latest

Repo: espressif/esp-webrtc-solution (cloned at main)

Board: ESP32-P4

Video: H.264

Notes: ESP-GMF includes the esp_capture package and the GMF video encoder element used by esp-webrtc-solution.
GitHub

  1. Add a public bitrate setter to esp_webrtc (temporary test hook)

Because esp-webrtc-solution doesn’t expose bitrate control, add a thin wrapper in your app that ultimately invokes esp_capture_sink_set_bitrate on the capture path:

int esp_webrtc_set_video_bitrate(esp_webrtc_handle_t handle, uint32_t bps) {
    if (handle == NULL) return ESP_PEER_ERR_INVALID_ARG;
    webrtc_t *rtc = (webrtc_t *)handle;

    if (rtc->capture_path) {
        // Call into esp_capture sink bitrate setter for VIDEO
        int ret = esp_capture_sink_set_bitrate(rtc->capture_path, ESP_CAPTURE_STREAM_TYPE_VIDEO, bps);
        return ret;  // IMPORTANT: propagate the underlying return code
    }

    // If not started yet, cache and apply on start
    rtc->pending_video_bitrate_bps = bps;
    rtc->video_bitrate_pending = true;
    return 0;
}

  1. Build & flash a demo

Use any of the bundled demos (e.g., Doorbell, Video Call, or WHIP Publisher). They all set up a WebRTC call and send video via GMF:

  1. Start a WebRTC session

Bring the device online and establish a call with the browser/peer using one of the solution demos from the repo’s Solutions section (Doorbell/Video Call/WHIP).
GitHub

  1. While streaming, attempt to lower bitrate

From an app task (or via your control UI/CLI), invoke the new method:

// e.g., drop to ~300 kbps while running
int ret = esp_webrtc_set_video_bitrate(rtc, 300000);
ESP_LOGI("TEST", "set bitrate ret=%d", ret);

Project release version

latest

System architecture

ARM 64-bit (Apple M1/M2, Raspberry Pi 4/5)

Operating system

MacOS

Operating system version

Sequoia 15.6

Shell

ZSH

Additional context

Proposed fix (function)

esp_capture_err_t esp_capture_sink_set_bitrate(esp_capture_sink_handle_t h,
                                               esp_capture_stream_type_t stream_type,
                                               uint32_t bitrate)
{
    capture_path_t *path = (capture_path_t *)h;
    if (path == NULL || path->parent == NULL) {
        return ESP_CAPTURE_ERR_INVALID_ARG;
    }

    capture_t *capture = path->parent;
    capture_mutex_lock(capture->api_lock, CAPTURE_MAX_LOCK_TIME);

    esp_capture_err_t ret = ESP_CAPTURE_ERR_NOT_SUPPORTED;
    if (stream_type == ESP_CAPTURE_STREAM_TYPE_VIDEO) {
        esp_capture_path_mngr_if_t *video_path =
            (capture->cfg.video_path ? &capture->cfg.video_path->base : NULL);
        if (video_path && video_path->set) {
            ret = video_path->set(video_path,
                                  path->path_type,
                                  ESP_CAPTURE_PATH_SET_TYPE_VIDEO_BITRATE,
                                  &bitrate,
                                  sizeof(uint32_t));
        }
    } else if (stream_type == ESP_CAPTURE_STREAM_TYPE_AUDIO) {
        esp_capture_path_mngr_if_t *audio_path =
            (capture->cfg.audio_path ? &capture->cfg.audio_path->base : NULL);
        if (audio_path && audio_path->set) {
            ret = audio_path->set(audio_path,
                                  path->path_type,
                                  ESP_CAPTURE_PATH_SET_TYPE_AUDIO_BITRATE,
                                  &bitrate,
                                  sizeof(uint32_t));
        }
    }

    capture_mutex_unlock(capture->api_lock);
    return ret;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions