vvidic / mjpeg-proxy Goto Github PK
View Code? Open in Web Editor NEWRepublish a MJPEG HTTP image stream using a server in Go
License: GNU General Public License v3.0
Republish a MJPEG HTTP image stream using a server in Go
License: GNU General Public License v3.0
Hi! Congrats for this small but very useful project.
It was exactly what I was looking for: pub/sub mjpeg proxy.
I made a similar solution some years ago in python for raspberry picamera
BigNerd95/picamera@f723b08#diff-9dc379f8ac02d2f947ab8b7e67be6877ae0ca646f31a475b0169867e105f91edR112
Anyway my question is:
If a client has a very slow connection, this method sends less frame than the original FPS (which is what I want), but due to tcp buffer (I think) the stream is not real time, because even if you use unbuffered channel, the thread sending frame to a client is not blocking during the tcp send, so it will keep reading frame from the publisher channel until the tcp buffer is full.
Only then it will start to drop frame.
I think a possible solution is to reduce the tcp buffer size to a size similar of a single frame, so it will start dropping frame as soon as possible if the viewer connection is too slow to handle the original FPS.
Do you have other ideas?
hey everybody. first of all thx for this piece - really helps me with a rather annoying ip camera.
so, my setup is pretty normale I guess, I'm starting mjpeg-proxy like this:
./mjpeg-proxy --bind [::1]:5500 --rate 16.0 --source "http://mycam/control/faststream.jpg?stream=full&fps=16.0" > /dev/null 2>&1 &
I'm serving it with Nginx like this:
location /stream {
proxy_pass http://[::1]:5500;
}
on my client (iPhone) I've added an img
tag with the correct src.
problem ist now, after some timeout, the stream stops as expected and the imp tag's content is empty - so I don't see anything again.
As a solution I've added some eventListeners and I remove the src or add it back in on visibilitychange
events.
now I can brute force mjpeg-proxy so to speak: when switching away from my client tab and switch back to it, I can see in the PubSub log, that each cycle adds a new Subscriber and doesn't close the not longer consumed one. I tried several things including remove the complete imp node from the DOM but I can't find a way to tell mjpeg-proxy that a particular consumer is gone.
"naturally" there is some kind of timeout happening if I stay away long enough from the tab/safari eventually all subscriber cancel the subscription - I can trigger the same with closing the tab or force-quitting Safari.
so, I'm not sure what else to do to further debug this so I thought you guys might be interested and might help me, please :)
let me know if I could provide additional information - disclaimer: I'm not a go developer.
If you have any other suggestions how to reliably (reconnecting) render a MJPEG stream please let me know.
Is this intended? If so, could you add a options to make it fixed?
The boundary of my camera is --myboundary. mjpeg-proxy uses a different boundary for every request.
eg:
multipart/x-mixed-replace; boundary=97fe87f7600ea95da17778fce99c9d72218ff1513e778ee3892c60081cef
multipart/x-mixed-replace; boundary=c690fd9c03c8d4cf408809d70a0ea8eec88b763f1f206fc173f132d9ff07
Also, I checked the response, and apparently the boundary used in the stream have "--" before, while the random boundary in the content-header does not. Maybe this is what causes #14 (comment) .
eg:
The content-header is:
multipart/x-mixed-replace; boundary=97fe87f7600ea95da17778fce99c9d72218ff1513e778ee3892c60081cef
but the stream actually uses:
--97fe87f7600ea95da17778fce99c9d72218ff1513e778ee3892c60081cef
Connecting directly to my camera the boundary in the content-type is --myboundary and --myboundary is actually used in the stream.
I came across a dlink camera who's content type is multipart/x-mixed-replace;boundary=video boundary--
This fails to parse as the --
is not valid unless its in quotes.
Thus we need a way to sanitize the header to get ``multipart/x-mixed-replace;boundary="video boundary--"` (quotes added)
The following might be sufficient
func SanitizeContentType(s string) string {
if strings.HasSuffix(s, "-") {
// Boundary is not escaped
segments:= strings.Split(s, "boundary=")
formatted := segments[0] + "boundary=\"" + segments[1] + "\""
return formatted
}
return s
}
Here you are using append, but you are not providing other arguments to append.
I also compared the address returned and it is the same of the original data.
So why did you use it?
Line 193 in 6920c7b
See title.
$ mjpeg-proxy -source http://example.com
chunker[/]: serving from http://example.com
server: starting on address :8080
pubsub[/]: added subscriber 127.0.0.1:37456 (total=1)
chunker[/]: connecting to http://example.com
pubsub[/]: failed to start chunker: expected multipart media type: text/html; charset=UTF-8
pubsub[/]: removed subscriber 127.0.0.1:37456 (total=0)
$ curl http://localhost:8080 -v
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 04 Jan 2021 00:05:28 GMT
< Content-Length: 68
< Content-Type: text/plain; charset=utf-8
<
--b17532825b4bfd3e0683422f4a0e6244ef58ac1be81d752b510b2d248538--
This is possibly a regression. The same thing is happening with timeouts (when using something like "http://this-host-does-not-exist.faketld/video.mjpg" as source).
Something like: http://127.0.0.1:8080/?fps=4
So each client can have what it needs.
I feel like I'm missing something, so forgive me if this is obvious.
I have 2 cameras that I want to proxy. I tried creating a JSON file with their sources. When I run ./mjpeg-proxy --sources sources.json
I see it starting, but nothing ever gets returned from the connections to localhost:8080/camera1
or localhost:8080/camera2
:
chunker[/camera1]: serving from http://192.168.2.139/mjpg/1/video.mjpg
chunker[/camera2]: serving from http://192.168.2.139/mjpg/2/video.mjpg
server: starting on address :8080
pubsub[/camera1]: added subscriber [::1]:58014 (total=1)
chunker[/camera1]: connecting to http://192.168.2.139/mjpg/1/video.mjpg
chunker[/camera1]: started
In the browser, it just blocks like this forever:
If I hit my cameras (which work) I get back a response like this:
curl -I http://192.168.2.139/mjpg/2/video.mjpg
HTTP/1.0 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Connection: close
Content-Type: multipart/x-mixed-replace; boundary=--myboundary
What am I missing?
This HTTP endpoint could return the amount of connected clients per stream in JSON format. We're using mjpeg-proxy for the camera in our hackerspace, and it's nice to know if somebody is watching, that way, we can wave to them.
For example, on /api//info
{
'connections': {
'stream1': 3,
'stream2': 5
}
}
I'm willing to implement this myself, but I'd first like to ask what you think of this, so I don't spend time coding a feature that you don't want in the codebase.
mjpeg-proxy currently panics when a source stream is not available due to a close of a closed channel.
server: starting on address unix:/run/mjpeg-proxy/socket
pubsub[/wc-lazy-lounge.mjpg]: added subscriber [REDACTED] (total=1)
chunker[/wc-lazy-lounge.mjpg]: connecting to http://[REDACTED]/mjpg/video.mjpg
server[/wc-lazy-lounge.mjpg]: stream failed
pubsub[/wc-lazy-lounge.mjpg]: failed to start chunker: Get "http://[REDACTED]/mjpg/video.mjpg": dial tcp [REDACTED]: i/o timeout
pubsub[/wc-lazy-lounge.mjpg]: added subscriber [REDACTED] (total=2)
chunker[/wc-lazy-lounge.mjpg]: connecting to http://[REDACTED]/mjpg/video.mjpg
server[/wc-lazy-lounge.mjpg]: stream failed
pubsub[/wc-lazy-lounge.mjpg]: failed to start chunker: Get "http://[REDACTED]/mjpg/video.mjpg": dial tcp [REDACTED]: i/o timeout
panic: close of closed channel
goroutine 13 [running]:
main.(*PubSub).stopSubscribers(0xc00006a700)
pubsub.go:133 +0x85
main.(*PubSub).doSubscribe(0xc00006a700, 0xc0001be000)
pubsub.go:126 +0x298
main.(*PubSub).loop(0xc00006a700)
pubsub.go:94 +0x245
created by main.(*PubSub).Start
pubsub.go:71 +0x3f
I currently work around this issue by restarting the process upon a crash, but I suppose this issue needs to be resolved so other streams don't get killed by this crash.
I can access my Dahua IPCam directly via http://guest:[email protected]/cgi-bin/mjpg/video.cgi?channel=1&subtype=1.
But ./mjpeg-proxy -source "http://192.168.1.108/cgi-bin/mjpg/video.cgi?channel=1&subtype=1" -username guest -password guest
fails with:
chunker[/]: connecting to http://192.168.1.108/cgi-bin/mjpg/video.cgi?channel=1&subtype=1
pubsub[/]: failed to start chunker: request failed: 401 Unauthorized
./mjpeg-proxy -source "http://guest:[email protected]/cgi-bin/mjpg/video.cgi?channel=1&subtype=1"
fails the same way.
PS: The autorization method of the camera is digest, not basic.
I've recently noticed an issue where mjpeg-proxy
would no longer realize that connections have ended.
I'm using it behind an NGINX reverse proxy.
sudo bandwhich -i eth0
reveals:
(Only small poll connections, nothing related to mjpeg-proxy, is hitting NGINX)
The log of my systemd unit for mjpeg-proxy reveals the following:
This stays true even when I stop NGINX entirely - mjpeg-proxy will still think those connections are open, even though nothing is connected to it anymore.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.