Open-source project
gen2brain/cam2ip avatar
gen2brain/cam2ip

cam2ip, a webcam served as an MJPEG stream over HTTP

Turn any webcam into an IP camera

1,178 stars121 forksGoGPL-3.0

At a glance

What is it?
cam2ip takes a local camera and publishes it as an IP camera, with three handlers, a browser view that pushes frames over a websocket, and a per platform capture backend chosen at build time. It is a single Go binary with no cgo requirement, and authentication is off unless you point it at an htpasswd file.
Who is it for?
cam2ip fits a maker or lab that has a spare USB camera and needs it visible over the network, whether in a browser, in VLC or in a script that consumes MJPEG, and it fits a Raspberry Pi user because the Linux path is the native one. It does not fit a deployment where the camera must not be reachable without credentials, since authentication is disabled when no htpasswd file is given, and it does not fit a project that needs an RTSP server or a video recording pipeline.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 111 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three URLs, one camera

cam2ip turns any webcam into an IP camera, and the entire interface is a small set of HTTP endpoints on a port, 56000 by default. The browser view is at the /html path, the static image handler is at /jpeg, and the motion stream is at /mjpeg, and the last of those is the one that works everywhere, since motion JPEG is supported natively in the major browsers. A command line player can point at the same URL.

bash
ffplay -i http://localhost:56000/mjpeg

VLC is named as another consumer in the documentation. The value of the tool is that it is a small, honest adapter rather than a platform, and it does one thing that a USB camera on a laptop cannot do, which is to make the video reachable from another machine over the network. The audience is a maker, a robotics project, a home lab, or anyone who has a spare camera and needs it visible from a browser on another device.

Three capture backends, one binary

The capture layer is the part of the project that carries the engineering, and it is platform specific by design. On Linux and on the Raspberry Pi, a native Go Video4Linux implementation is used, backed by the korandiz/v4l package, so no cgo and no external tools are needed to talk to a camera. On Windows, Media Foundation is used by default, and Video for Windows is available instead through a build tag. On macOS, AVFoundation is used over purego, which means the framework is called without cgo and without a C toolchain. The build tags are documented separately and are the only manual step most people need. The vfw tag switches Windows from Media Foundation to Video for Windows, which is the answer for a camera that the newer framework does not enumerate. The libjpeg tag builds with libjpeg through the pixiv/go-libjpeg library instead of the standard library image/jpeg encoder, which is the escape hatch when you need a JPEG encoder with different behaviour or performance characteristics than the built-in one.

Every flag has an environment variable twin

The command line surface is a single set of flags, and the usage output shows that most of them are mirrored by an environment variable, which is what makes the tool usable from a container or a service manager. The camera is selected by index, with 0 as the default, and a device flag matches a camera name as a substring and overrides the index, which is the setting that saves you when two cameras are attached. The capture parameters are a frame delay in milliseconds defaulting to 10, a width defaulting to 640, a height defaulting to 480, and an image quality defaulting to 75. There is a rotate option for 90, 180 and 270 degrees, and a flip option for horizontal and vertical, so a camera mounted upside down can be corrected in software. The binding address defaults to all interfaces on port 56000, and the rest of the flags cover authentication, listing devices, printing the version, and opening the camera only while clients are connected.

bash
--device
   Camera name to use, matched as substring, overrides index [CAM2IP_DEVICE] (default "")
--width
   Frame width [CAM2IP_WIDTH] (default "640")
--quality
   Image quality [CAM2IP_QUALITY] (default "75")

The three handlers behave differently

The handlers are not three spellings of the same thing, and choosing between them is a real decision. The /html handler is a small viewer page that pushes frames to a canvas over a websocket, so the browser receives each frame as it is produced rather than polling for a new image, and there is a flag to turn off WebGL drawing of the image in that handler for clients that do not want it. The /jpeg handler serves a static image, which is what a script that grabs a single frame wants, and it is also the endpoint to check when verifying that capture works at all. The /mjpeg handler is the multipart stream that browsers and players understand directly, and it is the one to hand to another machine. The upstream dependencies reflect that split, with coder/websocket for the viewer path, gen2brain/jpegn for JPEG encoding, and abbot/go-http-auth for the optional authentication.

Authentication is opt-in, and the camera can sleep

Two flags carry the operational weight. The htpasswd-file flag points at an htpasswd file, and the documentation is explicit that if it is empty then authentication is disabled, which means the default behaviour of the tool is an open HTTP service on the bind address. There is a companion flag, add-user, that adds a user to that file and exits, so the intended workflow is to create the file before starting the service. The second flag is lazy, which opens the camera only while clients are connected. That is a privacy and resource decision as much as a performance one, since a camera that is powered and streaming to nobody is still a camera pointed at a room. A third detail worth knowing is the list-devices flag, which prints the available cameras and exits, and it is the first command to run when a device does not appear at the expected index.

A viewer binary and a scratch container image

Alongside the server there is a second command, camview, described as a small native window that shows the local camera live using the iv library for display, and it is installed the same way as the main binary. That split is a sensible one, since checking whether a camera works locally does not need a network round trip. The container story is equally lean. The Dockerfile builds in a golang:alpine stage with the module cache primed from go.mod and go.sum, compiles with CGO_ENABLED=0 and the trimpath and stripped linker flags, and then copies the single binary into a scratch image with nothing else in it.

dockerfile
FROM scratch

COPY --from=build /build/cam2ip /cam2ip

EXPOSE 56000

ENTRYPOINT ["/cam2ip"]

An image with a single static binary and no shell is a good shape for something that gets deployed to a small host, and the CGO_ENABLED=0 setting is consistent with the purego approach on macOS and the native Go V4L path on Linux, so the same binary works in the container as on the desktop. The installation route outside Docker is a go install of the command, which puts cam2ip into GOBIN, and the documentation suggests pointing GOBIN at a directory such as /usr/local/bin or ~/.local/bin.

Licence, release cadence and the module requirements

The project is licensed under GPL-3.0, with the text in the COPYING file, which matters for anyone who intends to modify it and distribute the result, since a derived binary inherits the obligation. The release history has a shape that is worth reading. The newest release is v1.7.0 from 2026-06-30, and before it come 1.6 from 2019-10-06 and 1.5 from 2018-10-29, so there is a seven year gap between the 1.6 tag and the 1.7 one, which suggests a long period of development on a branch before a release rather than a stream of patches. The repository is not archived and the last push was on 2026-07-04. The go.mod asks for go 1.26, which sets the toolchain floor for anyone building from source, and the dependency list is short and deliberate, with the camera libraries, the websocket library, the HTTP authentication library, the purego bridge, the JPEG encoder and a small font library. A.goreleaser.yml in the tree handles the cross platform binary builds that appear on the releases page.

Editorial conclusion

cam2ip fits a maker or lab that has a spare USB camera and needs it visible over the network, whether in a browser, in VLC or in a script that consumes MJPEG, and it fits a Raspberry Pi user because the Linux path is the native one. It does not fit a deployment where the camera must not be reachable without credentials, since authentication is disabled when no htpasswd file is given, and it does not fit a project that needs an RTSP server or a video recording pipeline. Verify three things first: that the camera you attach is the one the tool picks, since selection is by index or by a name substring, that you set a bind address and an htpasswd file before exposing the port beyond your own network, and that the output format your consumer expects is the one you enable, since the browser view and the MJPEG stream are different paths.

Frequently asked questions

What is cam2ip?

It is a Go program that turns any webcam into an IP camera, serving the video over HTTP at port 56000 through an HTML viewer, a static JPEG handler and an MJPEG stream. The project description is simply turn any webcam into an IP camera.

How do I install and run cam2ip?

Install it with go install github.com/gen2brain/cam2ip/cmd/cam2ip@latest, which places cam2ip in GOBIN, or download a release binary. Run it and open http://localhost:56000/html for the browser view or http://localhost:56000/mjpeg for the stream.

Which camera backend does cam2ip use on each platform?

On Linux and the Raspberry Pi it uses a native Go Video4Linux implementation, on Windows it uses Media Foundation, or Video for Windows with the vfw build tag, and on macOS it uses AVFoundation over purego with no cgo required. The libjpeg build tag swaps the JPEG encoder for the pixiv/go-libjpeg one.

Does cam2ip require a password?

Not by default. The htpasswd-file flag points at an htpasswd file, and if it is empty then authentication is disabled. The add-user flag adds a user to that file and exits, so the file is normally created before the service starts.

Official sources

  1. gen2brain/cam2ip on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gen2brain-cam2ip.svg)](https://hysenlabs.com/projects/gen2brain-cam2ip)

Was this useful?