No video at peer (persistent black)

Some black at the very start of a video stream is normal — the codec is initialising, the first keyframe (IDR) hasn’t arrived yet, and the renderer surface may not be wired up. The library pre-fills the renderer’s frame buffer with black via pjmedia_video_format_fill_black(), so the user does not see uninitialised colours during this window. The cleanest way to hide the startup window from the user is to leave the renderer hidden until PJMEDIA_EVENT_FMT_CHANGED arrives — see Video window UX.

If the remote side keeps showing black past that initial window, work through these in order:

  1. Verify outgoing transmission is enabled. Outgoing video is not started by default. Either set pjsua_acc_config::vid_out_auto_transmit to PJ_TRUE on the account, or start it explicitly per call with pjsua_call_set_vid_strm() and PJSUA_CALL_VID_STRM_START_TRANSMIT / PJSUA_CALL_VID_STRM_ADD. See Modifying video during a call in Working with video media.

  2. Verify the SDP carries video sendrecv. Inspect the media[i].dir field of pjsua_call_info and look for video marked sendonly / recvonly / inactive. If the call setting’s pjsua_call_setting::media_dir was used, it persists across re-INVITEs.

  3. Verify a video codec is actually negotiated by both ends. At least one video codec must be enabled in the build and supported by the peer. Use pjsua_vid_enum_codecs() to see what we offer; check Video Components and Backends for which backends provide which codecs per platform.

  4. Confirm RTP packets are actually being received. See Check if RTP packets are received — the same diagnostic applies to video. If no RTP arrives, the problem is at the transport / NAT level, not the codec.

  5. Force a keyframe if the peer is decoding but stuck without one (e.g. lost the original IDR): call pjsua_call_set_vid_strm() with PJSUA_CALL_VID_STRM_SEND_KEYFRAME. Conversely, the peer requests a keyframe from us via SIP INFO or RTCP PLI; allowed transports are governed by pjsua_call_setting::req_keyframe_method. The default already enables both. See Video Keyframe Transmission.

  6. Hook the call media-state callback. Implement pjsua_callback::on_call_media_state and read the per-stream status from pjsua_call_info to confirm the video stream actually transitioned to active.

Related: Green frames, Mobile: video stops after backgrounding, Network / IP change leaves video black.