Compare commits

...

19 Commits
v5.0 ... v5.4

Author SHA1 Message Date
Maxim Devaev
3d4e9fbb1a Bump version: 5.3 → 5.4 2022-04-29 16:45:30 +03:00
Maxim Devaev
4a59b038ec refactoring 2022-04-29 16:44:13 +03:00
Maxim Devaev
6795744aea Merge pull request #153 from FallingSnow/cors
Better CORS support
2022-04-29 13:59:25 +03:00
Maxim Devaev
798fe04905 Merge pull request #152 from FallingSnow/patch-1
Add alpine linux instructions to readme
2022-04-29 13:56:27 +03:00
Ayrton Sparling
1460de95c1 Add "better cors" to /stream http endpoint
Signed-off-by: Ayrton Sparling <ayrton@sparling.us>
2022-04-28 08:40:15 -07:00
Ayrton Sparling
358950d0c2 Better CORS support
Current CORS support only adds an Access-Control-Allow-Origin header to some requests. It also does not support the OPTIONS preflight sent by modern browsers. This commit adds support for OPTIONS preflight as well as more CORS headers.

The OPTIONS preflight is sent without any credentials attached to it so it mist take place before Authorization header processesing. Firefox 99 requires other access control headers like Access-Control-Allow-Headers be returned for a successful CORS interaction.

Signed-off-by: Ayrton Sparling <ayrton@sparling.us>
2022-04-26 16:28:57 -07:00
Ayrton Sparling
caef82d96f Change WITH_PTHREAD_NP=false to WITH_PTHREAD_NP=0 2022-04-21 13:34:05 -07:00
Ayrton Sparling
dff7dd7087 Add alpine linux instructions to readme
Alpine's uses musl rather than libc and does not have support for `pthread_getname_np`. Therefore it must be build with `WITH_PTHREAD_NP=false`.
2022-04-21 10:34:25 -07:00
Maxim Devaev
79bb881a34 Merge pull request #150 from tiny-pilot/h264-guide
Add documentation for integrating with Janus to stream H.264 video
2022-04-16 19:34:19 +03:00
Michael Lynch
f0763e3865 Tweak language in H264 guide (#3)
* Adjust wording in uStreamer Janus guide

* Small tweaks

* Small changes

* Updating based on review notes
2022-04-13 09:50:15 -04:00
jotaen4tinypilot
a8dfa96db0 Document guide for H.264 (#2)
* Setup guide for H.264

* Phrasing, structure

* Remove MJPEG primer and rewrite intro

* Link to library files directly

* Adjust page title

* Remove todo

* Resolve todo (Janus setup)

* Comment all JavaScript

* Rephrase to use active voice, clarify details

* Phrasing and clarifications

* Write backend instructions

* Grammar

* Use generic URL with port number

* Use consistent terminology

* Use term “V4L2 device” to refer to the video device

* Link “building” section of uStreamer README

* Use active voice

* Change page title to “demo”

* Add comment about the `janus.js` library file

* Use window location to construct server URL

* Move `videoElement` variable closer to it’s usage

* Elaborate why we clone the media stream

* Drop obsolete `WITH_OMX` option

* Correct path of shared object file

* Fix shadowing variable of same name

* Send `start` request to avoid (harmless) `400` response

* Add `refcount.h` fix

Co-authored-by: Jan Heuermann <jan@jotaen.net>
2022-04-13 06:31:15 -04:00
Maxim Devaev
7ceb8a3da5 Bump version: 5.2 → 5.3 2022-04-01 18:35:34 +03:00
Maxim Devaev
9344059e0e systemd is required for arch now 2022-04-01 18:33:05 +03:00
Maxim Devaev
ee2b1afe5b note about kernel support 2022-04-01 18:30:21 +03:00
Maxim Devaev
31f47f2eac Bump version: 5.1 → 5.2 2022-03-21 15:01:23 +03:00
Maxim Devaev
69a9bafcd5 moved platform checks to tools.h 2022-03-21 15:00:06 +03:00
Maxim Devaev
dbecdf5d9b disabled paypal 2022-03-20 03:05:23 +03:00
Maxim Devaev
967574a78a Bump version: 5.0 → 5.1 2022-03-15 16:23:24 +03:00
Maxim Devaev
bb4f6f3993 h264 max kbps is 20000 2022-03-15 16:22:07 +03:00
16 changed files with 308 additions and 37 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 5.0
current_version = 5.4
parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize =
{major}.{minor}

2
.github/FUNDING.yml vendored
View File

@@ -1,4 +1,4 @@
# These are supported funding model platforms
patreon: pikvm
custom: https://www.paypal.me/mdevaev
#custom: https://www.paypal.me/mdevaev

View File

@@ -40,6 +40,7 @@ You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support,
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1`.
* Debian/Ubuntu: `sudo apt install build-essential libevent-dev libjpeg-dev libbsd-dev`.
* Alpine: `sudo apk add libevent-dev libbsd-dev libjpeg-turbo-dev musl-dev`. Build with `WITH_PTHREAD_NP=0`.
To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
@@ -56,6 +57,8 @@ FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
-----
# Usage
**For M2M hardware encoding on Raspberry Pi, you need at least 5.15.32 kernel. OpenMAX and MMAL support on older kernels is deprecated and removed.**
Without arguments, ```ustreamer``` will try to open ```/dev/video0``` with 640x480 resolution and start streaming on ```http://127.0.0.1:8080```. You can override this behavior using parameters ```--device```, ```--host``` and ```--port```. For example, to stream to the world, run:
```
# ./ustreamer --device=/dev/video1 --host=0.0.0.0 --port=80
@@ -97,6 +100,9 @@ $ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
-----
# Integrations
## Janus
µStreamer supports bandwidth-efficient streaming using [H.264 compression](https://en.wikipedia.org/wiki/Advanced_Video_Coding) and the Janus WebRTC server. See the [Janus integration guide](docs/h264.md) for full details.
## Nginx
When uStreamer is behind an Nginx proxy, it's buffering behavior introduces latency into the video stream. It's possible to disable Nginx's buffering to eliminate the additional latency:

View File

@@ -56,6 +56,8 @@ $ ./ustreamer --help
-----
# Использование
**Для аппаратного кодирования M2M на Raspberry Pi, вам нужно ядро минимальной версии 5.15.32. Поддержка OpenMAX и MMAL для более старых ядер объявлена устаревшей и была удалена.**
Будучи запущенным без аргументов, ```ustreamer``` попробует открыть устройство ```/dev/video0``` с разрешением 640x480 и начать трансляцию на ```http://127.0.0.1:8080```. Это поведение может быть изменено с помощью опций ```--device```, ```--host``` и ```--port```. Пример вещания на всю сеть по 80-м порту:
```
# ./ustreamer --device=/dev/video1 --host=0.0.0.0 --port=80

231
docs/h264.md Normal file
View File

@@ -0,0 +1,231 @@
# Streaming H.264 Video over WebRTC with Janus
µStreamer supports bandwidth-efficient streaming using [H.264 compression](https://en.wikipedia.org/wiki/Advanced_Video_Coding) and the Janus WebRTC server.
This guide explains how to configure µStreamer to provide an H.264 video stream and consume it within a web application using WebRTC.
## Components
In addition to µStreamer, H.264 streaming involves the following components:
- [**Janus**](https://janus.conf.meetecho.com/): A general-purpose WebRTC server.
- [**µStreamer Janus Plugin**](https://github.com/pikvm/ustreamer/tree/master/janus): A Janus plugin that allows Janus to consume a video stream from µStreamer via shared memory.
- [**Janus JavaScript Client**](https://janus.conf.meetecho.com/docs/JS.html): A frontend library that communicates with the Janus server and the µStreamer Janus plugin.
## Control Flow
To connect to a µStreamer video stream over WebRTC, an application must establish the following control flow:
1. The backend server starts the µStreamer service.
1. The backend server starts the Janus WebRTC server with the µStreamer Janus plugin.
1. The client-side JavaScript application establishes a connection to the Janus server.
1. The client-side JavaScript application instructs the Janus server to attach the µStreamer Janus plugin and start the video stream.
1. The client-side JavaScript application renders the video stream in the web browser.
## Server Setup
### Prerequisites
To integrate with Janus, µStreamer has the following prerequisites:
- The system packages that µStreamer depends on (see the [main build instructions](../README.md#building)).
- The Janus WebRTC server with WebSockets transport enabled (see the [Janus documentation](https://github.com/meetecho/janus-gateway)).
### Fixing Janus C Headers
To compile µStreamer with the Janus option, (see [“Installation”](#installation)), you need to make the Janus header files available within µStreamer's build context.
First, create a symbolic link from `/usr/include` to the Janus install directory. By default, Janus installs to `/opt/janus`.
```sh
ln -s /opt/janus/include/janus /usr/include/janus
```
Janus uses `#include` paths that make it difficult for third-party libraries to build against its headers. To fix this, modify the `#include` directive in `janus/plugins/plugin.h` to prepend a `../` to the included file name (`#include "refcount.h"``#include "../refcount.h"`).
```sh
sed \
--in-place \
--expression 's|^#include "refcount.h"$|#include "../refcount.h"|g' \
/usr/include/janus/plugins/plugin.h
```
### Installation
First, compile µStreamer with the Janus plugin option (`WITH_JANUS`).
```sh
git clone --depth=1 https://github.com/pikvm/ustreamer
cd ustreamer
make WITH_JANUS=1
```
The `WITH_JANUS` option compiles the µStreamer Janus plugin and outputs it to `janus/libjanus_ustreamer.so`. Move this file to the plugin directory of your Janus installation.
```sh
mv \
janus/libjanus_ustreamer.so \
/opt/janus/lib/janus/plugins/libjanus_ustreamer.so
```
Finally, specify a qualifier for the shared memory object so that the µStreamer Janus plugin can read µStreamer's video data.
```sh
cat > /opt/janus/lib/janus/configs/janus.plugin.ustreamer.jcfg <<EOF
memsink: {
object = "demo::ustreamer::h264"
}
EOF
```
### Start µStreamer and the Janus WebRTC Server
For µStreamer to share the video stream with the µStreamer Janus plugin, µStreamer must run with the following command-line flags:
- `--h264-sink` with the qualifier of the shared memory object you specified above (`demo::ustreamer::h264`)
- `--h264-sink-mode` with the permissions bitmask for the shared memory object (e.g., `660`)
- `--h264-sink-rm` to clean up the shared memory object when the µStreamer process exits
To load the µStreamer Janus plugin and configuration, the Janus WebRTC server must run with the following command-line flags:
- `--configs-folder` with the path to the Janus configuration directory (e.g., `/opt/janus/lib/janus/configs/`)
- `--plugins-folder` with the path to the Janus plugin directory (e.g., `/opt/janus/lib/janus/plugins/`)
## Client Setup
Once an application's backend server is running µStreamer and Janus, a browser-based JavaScript application can consume the server's video stream.
### Prerequisites
The client needs to load the following JavaScript libraries:
- [WebRTC Adapter library](https://webrtc.github.io/adapter/adapter-8.1.0.js) (`webrtc-adapter.js`, version `8.1.0`)
- [Janus Gateway JavaScript library](https://raw.githubusercontent.com/meetecho/janus-gateway/v1.0.0/html/janus.js) (`janus.js`, version `1.0.0`)
### Control Flow
The client-side JavaScript application uses the following control flow:
1. The client loads and initializes the Janus client library.
1. The client establishes a WebSockets connection to the Janus server.
1. The client instructs the Janus server to attach the µStreamer Janus plugin.
1. On success, the client obtains a plugin handle through which it can send requests directly to the µStreamer Janus plugin. The client processes responses via the `attach` callbacks:
- `onmessage` for general messages
- `onremotetrack` for the H.264 video stream
1. The client issues a `watch` request to the µStreamer Janus plugin, which initiates the H.264 stream in the plugin itself.
- It takes a few seconds for uStreamer's video stream to become available to Janus. The first `watch` request may fail, so the client must retry the `watch` request.
1. The client and server negotiate the underlying parameters of the WebRTC session. This procedure is called JavaScript Session Establishment Protocol (JSEP). The server makes a `jsepOffer` to the client, and the client responds with a `jsepAnswer`.
1. The client issues a `start` request to the µStreamer Janus plugin to indicate that the client wants to begin consuming the video stream.
1. The µStreamer Janus plugin delivers the H.264 video stream to the client via WebRTC.
1. The Janus client library invokes the `onremotetrack` callback. The client attaches the video stream to the `<video>` element, rendering the video stream in the browser window.
### Sample Code
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>µStreamer H.264 demo</title>
<script src="https://webrtc.github.io/adapter/adapter-8.1.0.js"></script>
<!-- janus.js is the JavaScript client library of Janus, as specified above in
the prerequisites section of the client setup. You might need to change
the `src` path, depending on where you serve this file from. -->
<script src="janus.js"></script>
<style>
video {
height: 100%;
width: 100%;
object-fit: contain;
}
</style>
</head>
<body>
<video id="webrtc-output" autoplay playsinline muted></video>
<script type="text/javascript">
// Initialize Janus library.
Janus.init({
// Turn on debug logs in the browser console.
debug: true,
// Configure Janus to use standard browser APIs internally.
dependencies: Janus.useDefaultDependencies(),
});
// Establish a WebSockets connection to the server.
const janus = new Janus({
// Specify the URL of the Janus servers WebSockets endpoint.
server: `ws://${window.location.hostname}:8188/`,
// Callback function if the client connects successfully.
success: attachUStreamerPlugin,
// Callback function if the client fails to connect.
error: console.error,
});
let uStreamerPluginHandle = null;
function attachUStreamerPlugin() {
// Instruct the server to attach the µStreamer Janus plugin.
janus.attach({
// Qualifier of the plugin.
plugin: "janus.plugin.ustreamer",
// Callback function, for when the server attached the plugin
// successfully.
success: function (pluginHandle) {
uStreamerPluginHandle = pluginHandle;
// Instruct the µStreamer Janus plugin to initiate the video stream.
uStreamerPluginHandle.send({ message: { request: "watch" } });
},
// Callback function if the server fails to attach the plugin.
error: console.error,
// Callback function for processing messages from the Janus server.
onmessage: function (msg, jsepOffer) {
// 503 indicates that the plugin is not ready to stream yet. Retry the
// watch request until the video stream is available.
if (msg.error_code === 503) {
uStreamerPluginHandle.send({ message: { request: "watch" } });
return;
}
// If there is a JSEP offer, respond to it. This starts the WebRTC
// connection.
if (jsepOffer) {
uStreamerPluginHandle.createAnswer({
jsep: jsepOffer,
// Prevent the client from sending audio and video, as this would
// trigger a permission dialog in the browser.
media: { audioSend: false, videoSend: false },
success: function (jsepAnswer) {
uStreamerPluginHandle.send({
message: { request: "start" },
jsep: jsepAnswer,
});
},
error: console.error,
});
}
},
// Callback function, for when the video stream arrives.
onremotetrack: function (mediaStreamTrack, mediaId, isAdded) {
if (isAdded) {
// Attach the received media track to the video element. Cloning the
// mediaStreamTrack creates a new object with a distinct, globally
// unique stream identifier.
const videoElement = document.getElementById("webrtc-output");
const stream = new MediaStream();
stream.addTrack(mediaStreamTrack.clone());
videoElement.srcObject = stream;
}
},
});
}
</script>
</body>
</html>
```

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 5.0" "January 2021"
.TH USTREAMER-DUMP 1 "version 5.4" "January 2021"
.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER 1 "version 5.0" "November 2020"
.TH USTREAMER 1 "version 5.4" "November 2020"
.SH NAME
ustreamer \- stream MJPEG video from any V4L2 device to the network

View File

@@ -3,19 +3,19 @@
pkgname=ustreamer
pkgver=5.0
pkgver=5.4
pkgrel=1
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"
license=(GPL)
arch=(i686 x86_64 armv6h armv7h aarch64)
depends=(libjpeg libevent libbsd libgpiod)
makedepends=(gcc make)
depends=(libjpeg libevent libbsd libgpiod systemd)
makedepends=(gcc make systemd)
source=(${pkgname}::"git+https://github.com/pikvm/ustreamer#commit=v${pkgver}")
md5sums=(SKIP)
_options="WITH_GPIO=1"
_options="WITH_GPIO=1 WITH_SYSTEMD=1"
if [ -e /usr/bin/python3 ]; then
_options="$_options WITH_PYTHON=1"
depends+=(python)
@@ -26,11 +26,6 @@ if [ -e /usr/include/janus/plugins/plugin.h ];then
makedepends+=(janus-gateway-pikvm)
_options="$_options WITH_JANUS=1"
fi
if [ -e /usr/include/systemd/sd-daemon.h ];then
depends+=(systemd)
makedepends+=(systemd)
_options="$_options WITH_SYSTEMD=1"
fi
# LD does not link mmal with this option

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=5.0
PKG_VERSION:=5.4
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

View File

@@ -8,7 +8,7 @@ from setuptools import setup
if __name__ == "__main__":
setup(
name="ustreamer",
version="5.0",
version="5.4",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",

View File

@@ -23,7 +23,7 @@
#pragma once
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
#define VERSION_MINOR 4
#define MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define MAKE_VERSION1(_major, _minor) MAKE_VERSION2(_major, _minor)

View File

@@ -28,6 +28,7 @@
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <locale.h>
#include <errno.h>
#include <math.h>
@@ -37,6 +38,15 @@
#include <sys/file.h>
#ifdef NDEBUG
# error WTF dude? Asserts are good things!
#endif
#if CHAR_BIT != 8
# error There are not 8 bits in a char!
#endif
#define RN "\r\n"
#define INLINE inline __attribute__((always_inline))

View File

@@ -76,7 +76,7 @@ server_s *server_init(stream_s *stream) {
assert(!evthread_use_pthreads());
assert((run->base = event_base_new()));
assert((run->http = evhttp_new(run->base)));
evhttp_set_allowed_methods(run->http, EVHTTP_REQ_GET|EVHTTP_REQ_HEAD);
evhttp_set_allowed_methods(run->http, EVHTTP_REQ_GET|EVHTTP_REQ_HEAD|EVHTTP_REQ_OPTIONS);
return server;
}
@@ -212,14 +212,36 @@ void server_loop_break(server_s *server) {
event_base_loopbreak(RUN(base));
}
#define GET_HEADER(_key) \
evhttp_find_header(evhttp_request_get_input_headers(request), _key)
#define ADD_HEADER(_key, _value) \
assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value))
static int _http_preprocess_request(struct evhttp_request *request, server_s *server) {
RUN(last_request_ts) = get_now_monotonic();
if (server->allow_origin[0] != '\0') {
const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
const char *cors_method = GET_HEADER("Access-Control-Request-Method");
ADD_HEADER("Access-Control-Allow-Origin", server->allow_origin);
ADD_HEADER("Access-Control-Allow-Credentials", "true");
if (cors_headers != NULL) {
ADD_HEADER("Access-Control-Allow-Headers", cors_headers);
}
if (cors_method != NULL) {
ADD_HEADER("Access-Control-Allow-Methods", cors_method);
}
}
if (evhttp_request_get_command(request) == EVHTTP_REQ_OPTIONS) {
evhttp_send_reply(request, HTTP_OK, "OK", NULL);
return -1;
}
if (RUN(auth_token)) {
const char *token = evhttp_find_header(evhttp_request_get_input_headers(request), "Authorization");
const char *token = GET_HEADER("Authorization");
if (token == NULL || strcmp(token, RUN(auth_token)) != 0) {
ADD_HEADER("WWW-Authenticate", "Basic realm=\"Restricted area\"");
@@ -228,8 +250,8 @@ static int _http_preprocess_request(struct evhttp_request *request, server_s *se
}
}
if (evhttp_request_get_command(request) == EVHTTP_REQ_HEAD) { \
evhttp_send_reply(request, HTTP_OK, "OK", NULL); \
if (evhttp_request_get_command(request) == EVHTTP_REQ_HEAD) {
evhttp_send_reply(request, HTTP_OK, "OK", NULL);
return -1;
}
@@ -464,9 +486,6 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
assert((buf = evbuffer_new()));
assert(!evbuffer_add(buf, (const void *)EX(frame->data), EX(frame->used)));
if (server->allow_origin[0] != '\0') {
ADD_HEADER("Access-Control-Allow-Origin", server->allow_origin);
}
ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0");
ADD_HEADER("Pragma", "no-cache");
ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT");
@@ -580,6 +599,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
# define BOUNDARY "boundarydonotcross"
stream_client_s *client = (stream_client_s *)v_client;
struct evhttp_request *request = client->request; // for GET_HEADER
server_s *server = client->server;
long double now = get_now_monotonic();
@@ -620,9 +640,24 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
if (client->need_initial) {
assert(evbuffer_add_printf(buf, "HTTP/1.0 200 OK" RN));
if (client->server->allow_origin[0] != '\0') {
assert(evbuffer_add_printf(buf, "Access-Control-Allow-Origin: %s" RN, client->server->allow_origin));
const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
const char *cors_method = GET_HEADER("Access-Control-Request-Method");
assert(evbuffer_add_printf(buf,
"Access-Control-Allow-Origin: %s" RN
"Access-Control-Allow-Credentials: true" RN,
client->server->allow_origin
));
if (cors_headers != NULL) {
assert(evbuffer_add_printf(buf, "Access-Control-Allow-Headers: %s" RN, cors_headers));
}
if (cors_method != NULL) {
assert(evbuffer_add_printf(buf, "Access-Control-Allow-Methods: %s" RN, cors_method));
}
}
assert(evbuffer_add_printf(buf,
"Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0" RN
"Pragma: no-cache" RN
@@ -895,7 +930,7 @@ static char *_http_get_client_hostport(struct evhttp_request *request) {
assert(addr = strdup(peer));
}
const char *xff = evhttp_find_header(evhttp_request_get_input_headers(request), "X-Forwarded-For");
const char *xff = GET_HEADER("X-Forwarded-For");
if (xff) {
if (addr) {
free(addr);
@@ -918,3 +953,5 @@ static char *_http_get_client_hostport(struct evhttp_request *request) {
free(addr);
return hostport;
}
#undef GET_HEADER

View File

@@ -70,7 +70,7 @@ m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigne
m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
const double b_min = 25;
const double b_max = 25000;
const double b_max = 20000;
const double step = 25;
double bitrate = log10(quality) * (b_max - b_min) / 2 + b_min;
bitrate = step * round(bitrate / step);

View File

@@ -20,16 +20,6 @@
*****************************************************************************/
#include <assert.h>
#ifdef NDEBUG
# error WTF dude? Asserts are good things!
#endif
#include <limits.h>
#if CHAR_BIT != 8
# error There are not 8 bits in a char!
#endif
#include <stdio.h>
#include <stdbool.h>
#include <signal.h>

View File

@@ -439,7 +439,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
ADD_SINK("", sink, SINK)
ADD_SINK("raw-", raw_sink, RAW_SINK)
ADD_SINK("h264-", h264_sink, H264_SINK)
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 25000, 0);
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
# undef ADD_SINK