Skip to content

CaptureMJPEG fails when Content-Type and/or per-part Content-Length are missing (MJPEG over HTTP) #3604

@LikoIlya

Description

@LikoIlya

Summary

CaptureMJPEG assumes MJPEG streams always include:

  • a top-level Content-Type: multipart/x-mixed-replace; boundary=...
  • and per-part Content-Length headers

Many valid MJPEG sources (e.g., FFmpeg’s -f mpjpeg, some IP cams, microcontroller streams) omit one or both. As a result, Mission Planner throws and the video pipeline stops.

Environment

  • OS: Windows 10/11 (x64)
  • Camera: Logitech BRIO (MJPEG 1280×720 @ 60 fps)
  • FFmpeg: 8.0 (essentials build by gyan.dev)
  • Mission Planner: current (master/latest)
  • .NET Framework: (project targets 4.6–4.7)

Steps to Reproduce

  1. Start a simple HTTP MJPEG stream (FFmpeg acts as a tiny server):

    ffmpeg -f dshow -thread_queue_size 512 -rtbufsize 512M -framerate 60 -video_size 1280x720 -input_format mjpeg -i video="<WEBCAM_DEVICE>" -an -map 0:v:0 -c:v copy -f mpjpeg -boundary_tag frame  -fflags nobuffer -muxdelay 0 -listen 1  http://<IP>:<PORT>/stream.mjpg

    (This produces multipart MJPEG without per-part Content-Length.)

  2. In Mission Planner, set video source to:
    http://<IP>:<PORT>/stream.mjpg

  3. Observe: MP throws an exception and shows no video.

Expected behavior

Mission Planner should display the MJPEG stream even when:

  • Content-Type is absent or lacks boundary=...
  • individual parts omit Content-Length (client should read until the next boundary)

Actual behavior

MP crashes or stops the pipeline with:

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at MissionPlanner.Utilities.CaptureMJPEG.getUrl() in ...\CaptureMJPEG.cs:line 138

In other cases, MP logs a connection error if a browser has already connected (FFmpeg -listen 1 serves only one client):

System.Net.WebException: Unable to connect to the remote server
   ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:1889

Additional context

Browsers handle these streams fine. Many MJPEG servers legally omit per-part lengths; clients are expected to read until the next boundary delimiter (RFC 2046 multipart).

Some endpoints also omit the top-level Content-Type header; clients should detect the boundary from the first boundary line in the body (e.g., --frame).

Proposed fix

Make CaptureMJPEG tolerant to missing headers:

  1. Boundary detection

    • Parse from Content-Type if present.
    • If missing, sniff boundary from body (read lines until --<token>).
  2. Part reading

    • If Content-Length exists → read exact bytes (current behavior).
    • If missing → stream until the next boundary sequence \r\n--<boundary>, without over-reading (network stream is non-seekable).
  3. Header parsing

    • Case-insensitive dictionary; skip malformed lines gracefully.
  4. HTTP request hardening

    • req.KeepAlive = true, req.Accept = "multipart/x-mixed-replace", optional read timeout.

Workarounds

  • Use a MJPEG server that always sets per-part Content-Length (not always possible).

  • Patch CaptureMJPEG per the proposed fix (see related PR).

Related PR

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