Modifying Video Codec Parameters
Tip
PJSUA-LIB readers — symbol equivalents are listed at the bottom of this page.
Video codec parameters are exposed by pj::VidCodecParam,
which carries separate settings for the encoding and decoding
directions. Read with pj::Endpoint::getVideoCodecParam(),
modify, and write back with
pj::Endpoint::setVideoCodecParam().
VidCodecParam param = Endpoint::instance().getVideoCodecParam("H264");
// Modify param here
...
Endpoint::instance().setVideoCodecParam("H264", param);
The relevant VidCodecParam fields used below are
pj::VidCodecParam::encFmt (a pj::MediaFormatVideo,
with width / height / fpsNum / fpsDenum / avgBps /
maxBps), the same fields on decFmt, and the fmtp lists
encFmtp / decFmtp (each a vector of {name, val} strings).
Size or resolution
Specify the video picture dimension.
For the encoding direction, configure
encFmt:// Sending 1280 x 720 param.encFmt.width = 1280; param.encFmt.height = 720;
Note
Both width and height must be even numbers.
The value may be adjusted to follow remote capability — for example, if the peer signals a maximum of 640 × 480 but you set 1280 × 720 locally, the negotiated size will be 640 × 480.
The library finds the closest size/ratio that the capture device supports. Choose a size ratio the device supports; otherwise the video may get stretched. For example, if the device supports 640 × 480 and 1280 × 720 and you set 500 × 500, the camera opens at 640 × 480 and is later stretched to 500 × 500.
For the decoding direction:
Set
decFmt.width/decFmt.heightto the highest values expected for incoming video.If the resolution exceeds the supported maximum compiled into the codec backend, you need to bump the per-codec macro (
MAX_RX_WIDTH/MAX_RX_HEIGHTinopenh264.cpp,vid_toolbox.m, orand_vid_mediacodec.cpp;MAX_RX_RESinvpx.corffmpeg_vid_codecs.c). Defaults at the time of writing:Codec source
Macro
Default
openh264.cppMAX_RX_WIDTH/MAX_RX_HEIGHT1200 × 800
vid_toolbox.mMAX_RX_WIDTH/MAX_RX_HEIGHT1280 × 800
and_vid_mediacodec.cppMAX_RX_WIDTH/MAX_RX_HEIGHT1280 × 800
vpx.cMAX_RX_RES1200 (max dim)
ffmpeg_vid_codecs.cMAX_RX_RES1200 (max dim)
Verify in the source if you need to push beyond these — the values may have been updated since.
Signal to the remote side via codec-specific SDP fmtp parameters on
decFmtp:H.263-1998:
// 1st preference: 352 × 288 (CIF) param.decFmtp.push_back({"CIF", "1"}); // 2nd preference: 176 × 144 (QCIF) param.decFmtp.push_back({"QCIF", "1"});
The fmtp value is the framerate divisor — see Framerate below.
H.264: size is implicitly specified in the H.264 level (see the standard or the H.264/MPEG-4 AVC levels table), signalled via the H.264 SDP fmtp profile-level-id:
// Can receive up to 1280 × 720 @ 30 fps // Set the profile level to "1f", which means level 3.1 param.decFmtp.push_back({"profile-level-id", "xxxx1f"});
Framerate
Specify the number of frames processed per second.
For the encoding direction, configure
encFmt:// Sending @ 30 fps param.encFmt.fpsNum = 30; param.encFmt.fpsDenum = 1;
Note
The value may be adjusted to follow remote capability — for example, if the peer signals a maximum of 10 fps but you set 30 fps locally, 10 fps will be used.
Limitation: if preview is enabled before the call is established, the capture device opens at the device’s default framerate, and subsequent calls reusing that device run at that framerate regardless of the encoding framerate set above. The current workaround is to disable preview before media is established and re-enable it once video media is active.
For the decoding direction:
Set
decFmt.fpsNum/decFmt.fpsDenumto the highest values expected for incoming video.Signal to the remote side via codec-specific SDP fmtp on
decFmtp:H.263-1998: maximum framerate is specified per size/resolution. See RFC 4629 §8.1.1.
// 3000 / (1.001 × 2) fps for CIF param.decFmtp.push_back({"CIF", "2"}); // 3000 / (1.001 × 1) fps for QCIF param.decFmtp.push_back({"QCIF", "1"});
H.264: like resolution, framerate is implicitly specified in the H.264 level and signalled via
profile-level-id:// Can receive up to 1280 × 720 @ 30 fps param.decFmtp.push_back({"profile-level-id", "xxxx1f"});
Bitrate
Specify the bandwidth requirement for the video payload stream.
This is configurable via avgBps and maxBps on encFmt:
// Bitrate range preferred: 512 – 1024 kbps
param.encFmt.avgBps = 512000;
param.encFmt.maxBps = 1024000;
Note
This setting applies to encoding and decoding directions — there is currently no way to set asymmetric bitrate. On the decoding side it is just queried when generating the bandwidth info for the local SDP (next point).
The bitrate setting of all codecs is enumerated and the highest value is signalled in the bandwidth info of the local SDP (see ticket #1244).
The negotiated encoding bitrate may be adjusted to follow the remote setting (read from the SDP
b=TIASline in the remote SDP). For example, if the peer signals a max bitrate of 128 kbps but you set 512 kbps locally, 128 kbps will be used.For codec-specific bitrate signalling via SDP fmtp (e.g. MaxBR for H.263), set the fmtp manually:
// H.263 specific maximum bitrate 512 kbps param.decFmtp.push_back({"MaxBR", "5120"}); // = max_bps / 100
The codec’s avgBps / maxBps only configure the encoder’s
target; they do not by themselves shape the actual outgoing packet
stream. Per-stream send rate control is configured separately on the
account, via two fields on AccountVideoConfig:
rateControlMethod— selects how transmission is paced. Values come frompjmedia_vid_stream_rc_method:PJMEDIA_VID_STREAM_RC_NONE: no shaping; RTP packets are sent immediately after encoding.PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING(PJSUA2 default): the thread invokingput_frame()(typically the capture thread) blocks when transmission is ahead of schedule.PJMEDIA_VID_STREAM_RC_SEND_THREAD(PJMEDIA / PJSUA-LIB default): a dedicated sending thread queues and paces RTP packets, so the capture thread never blocks. Generally yields better video latency than the blocking method.
Note the PJSUA2
AccountVideoConfigconstructor initialisesrateControlMethodtoSIMPLE_BLOCKING, which differs from the PJSUA-LIB / PJMEDIA default ofSEND_THREAD. Set it explicitly if you wantSEND_THREADfrom PJSUA2.rateControlBandwidth— explicit upstream bandwidth in bps. When0(default), the rate controller follows the codec’smaxBps. Set this if you need stricter shaping than the encoder target.
AccountConfig acc_cfg;
// ... other configuration ...
acc_cfg.videoConfig.rateControlMethod = PJMEDIA_VID_STREAM_RC_SEND_THREAD;
acc_cfg.videoConfig.rateControlBandwidth = 0;
MyAccount *acc = new MyAccount;
acc->create(acc_cfg);
Choosing a bitrate
There is no single authoritative table for video bitrates; appropriate values depend on the usage (realtime call vs file/VOD playback), the codec (H.264 vs VP8/VP9 vs H.265), the codec profile, the target quality, and the motion characteristics of the content.
The two usages have very different budgets:
Realtime SIP/RTC video — the call must stay responsive, so encoding is single-pass with near-CBR rate control to keep the link stable, the GOP is kept small, and occasional loss is tolerated via PLI/FIR keyframe requests. Content is usually low-motion (head-and-shoulders), which compresses well, so the target bitrates are deliberately conservative. End-to-end one-way latency in practice typically lands in the few-hundred-millisecond range rather than the sub-200 ms ideal often quoted; the figures below are tuned for this case.
File / VOD streaming — no realtime constraint, so the encoder can use multi-pass, large GOPs, and high VBR peaks; targets for the same resolution and quality are typically 2–4× higher than realtime calls. PJSIP itself doesn’t drive a VOD pipeline (its AVI device just plays a file into a call), but bitrate values copied from streaming- service tables (YouTube, Twitch, broadcast) will not behave well in a realtime call.
As an order-of-magnitude starting point for H.264 (Baseline/Main) realtime calls:
Resolution |
Framerate |
Typical max bitrate |
|---|---|---|
QCIF (176 × 144) |
15 fps |
64 – 128 kbps |
CIF (352 × 288) |
15 fps |
128 – 384 kbps |
VGA (640 × 480) |
15 – 30 |
384 – 1024 kbps |
720p (1280 × 720) |
30 fps |
1500 – 4000 kbps |
1080p (1920 × 1080) |
30 fps |
3000 – 8000 kbps |
A common rule of thumb for realtime H.264 is bitrate ≈ K × W × H × FPS, with K roughly between 0.05 (low motion, acceptable quality) and 0.15 (high motion, good quality). VP8/VP9 typically need 20–30% less for similar perceived quality, and H.265 even less.
For codec-level upper bounds (which the negotiated H.264 level imposes), see the H.264/MPEG-4 AVC levels table. For H.263 framerate-per-resolution limits, see RFC 4629 §8.1.1.
PJSUA-LIB equivalents
PJSUA2 |
PJSUA-LIB |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|