Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update MediaMTX #1224

Merged
merged 43 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7b44f7e
Update to MediaMTX 1.8.1
mrlt8 May 18, 2024
932b0b1
use mtx for recording
mrlt8 May 18, 2024
b152203
Add paths when not-on-demand
mrlt8 May 19, 2024
8fce858
STREAM_AUTH to add additional stream users
mrlt8 May 19, 2024
9af5763
Update MediaMTX version from v1.1.1 to v1.8.2 (#1223)
mrlt8 May 22, 2024
290e3f2
Merge branch 'main' into dev
mrlt8 May 25, 2024
cd6c426
Merge branch 'main' into dev
mrlt8 May 31, 2024
7d7c816
Add IP based access for stream auth
mrlt8 May 31, 2024
c1aad38
Merge branch 'main' into dev
mrlt8 Jun 3, 2024
834525d
Merge branch 'main' into dev
mrlt8 Jun 8, 2024
81bca4e
Disable no-auth if stream_auth is set
mrlt8 Jun 10, 2024
9842374
Merge branch 'main' into dev
mrlt8 Jun 11, 2024
defe8db
Merge branch 'main' into dev
mrlt8 Jun 20, 2024
fe34bf1
Merge branch 'main' into dev
mrlt8 Jun 21, 2024
035f634
typos
mrlt8 Jun 21, 2024
45648e4
Add backwards compat with old RECORD options
mrlt8 Jun 22, 2024
792edb2
mtx logging
mrlt8 Jun 22, 2024
365a08e
set user to any if not set
mrlt8 Jun 23, 2024
a0221dd
refactor stream auth in webui
mrlt8 Jun 23, 2024
1fdbc3a
fix restart WebRTC on error
mrlt8 Jun 23, 2024
1a42ba4
Fix webrtc play event for firefox
mrlt8 Jun 24, 2024
ded793f
Fix absolute path for recording
mrlt8 Jun 24, 2024
357857c
reload webrtc if times out
mrlt8 Jun 24, 2024
d7b9d4a
don't delete webrtc session if bridge offline
mrlt8 Jun 24, 2024
479b1e2
reset poster when bridge goes offline
mrlt8 Jun 24, 2024
65c7de7
case sensitive STREAM_AUTH
mrlt8 Jun 24, 2024
6315879
fix play on click event
mrlt8 Jun 25, 2024
71df0c2
Merge branch 'main' into dev
mrlt8 Jun 29, 2024
9af1acf
WB_API and WB_PASSWORD from on wyze email
mrlt8 Jul 3, 2024
1ac8e9b
Add lock icon to api thumb in webui
mrlt8 Jul 3, 2024
f2a8495
clear local WB auth data with FRESH_DATA
mrlt8 Jul 3, 2024
0ffe0d6
refactor WebUI credentials
mrlt8 Jul 4, 2024
8f72056
Remove deprecated MFA related code
mrlt8 Jul 4, 2024
e7c002b
remove deprecated recording commands
mrlt8 Jul 5, 2024
cf2c434
Merge branch 'main' into dev
mrlt8 Jul 5, 2024
83ac1b9
remove unused code
mrlt8 Jul 5, 2024
52bde1d
change default MTX config
mrlt8 Jul 5, 2024
0afa612
Merge branch 'main' into dev
mrlt8 Jul 18, 2024
a178f1c
revert changes
mrlt8 Jul 18, 2024
0259c44
Fix FromAsCasing
mrlt8 Jul 22, 2024
34748dc
Add STREAM_AUTH
mrlt8 Jul 22, 2024
7b53502
Fix HLS setup
mrlt8 Jul 22, 2024
9a38c86
Update changelog
mrlt8 Jul 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 31 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,150 +59,54 @@ You can then use the web interface at `http://localhost:5000` where localhost is

See [basic usage](#basic-usage) for additional information or visit the [wiki page](https://github.com/mrlt8/docker-wyze-bridge/wiki/Home-Assistant) for additional information on using the bridge as a Home Assistant Add-on.

## What's Changed in v2.9.11/12
## What's Changed in v2.10.0

- FIX: Fix regression introduced in v2.9.11 which caused connection issues for WYZEDB3, WVOD1, HL_WCO2, and WYZEC1 (#1294)
- FIX: Update stream state on startup to prevent multiple connections.
- FIX: No audio on HW and QSV builds. (#1281)
- Use k10056 if supported and not setting fps when updating resolution and bitrate (#1194)
- Temporary fix: Don't check bitrate on newer firmware which do not seem to report the actual bitrate. (#1194)
### WebUI Authentication

## What's Changed in v2.9.10
Simplify default credentials for the WebUI:

- FIX: `-20021` error when sending multiple ioctl commands to the camera.
- FIX: Regression introduced in v2.9.9 where the WebRTC/HLS icon in WebUI was missing.
- Reduced memory usage slightly.
- NEW: Option to use pre-hashed passwords (#1275):
- You must md5 hash your password three times and prefix it with `hashed:`
- Example: `WYZE_PASSWORD=hashed:<your-tripple-hashed-password>`
- NEW: REST/MQTT commands (#1274):
- `notifications` GET/SET wyze app push notifications on/off (CLOUD).
- `motion_detection` GET/SET motion detection on/off (LOCAL).
- This will not affect users who are setting their own `WB_PASSWORD` and `WB_API`.
- Default `WB_PASSWORD` will now be derived from the username part of the Wyze email address instead of using a randomly generated password.
- Example: For the email address `john123@doe.com`, the `WB_PASSWORD` will be `john123`.
- Default `WB_API` will be based on the wyze account for persistance.

### Stream Authentication

## What's Changed in v2.9.9
NEW: `STREAM_AUTH` option to specify multiple users and paths:

- FIX: Regression introduced in v2.9.8 where a pipe blocking issue would cause CPU to spike (#1268) (#1270)
- Tweak HLS latency and buffer.
- Username and password should be separated by a `:`
- An additional `:` can be used to specify the allowed IP address for the user.
- **This does NOT work with docker desktop**
- Specify multiple IPs using a comma
- Use the `@` to specify paths accessible to the user.
- Paths are optional for each user.
- Multiple paths can be specified by using a comma. If none are provided, the user will have access to all paths/streams
- Multiple users can be specified by using `|` as a separator

## What's Changed in v2.9.8
**EXAMPLE**:

KNOWN BUG: stream path may become unresponsive after stopping when ON_DEMAND is enabled until the onDemand timeout clears (60s).

- FIX: restart options in the WebUI
- FIX: Resume HLS/WebRTC on recover and play on first click in WebUI.
- NEW: Add 'reload cameras' option to refresh camera data without clearing all data (#1255)
- CHANGED: Use hls-js for HLS in WebUI.

## What's Changed in v2.9.7

- FIX: Pan and tilt cruise points 3 and 4 were broken. Thanks @Deach01! (#1228)
- FIX: Remove whitespaces from credentials (#1252)
- CHANGED: Removed `blank` option when setting `cruise_points` as it would be ignored anyways.


## What's Changed in v2.9.6

- FIX: Connection to camera would get stuck and not come back on it's own until the webui was opened. Thanks @vipergts450 and @g13092! (#1234) (#1240)
- FIX: Regression introduced in v2.9.5 where AAC audio sources would not work (#1241) Thanks @rspierenburg!
- Home Assistant FIX: Regression introduced in v2.9.5 where MQTT was not setting up automatically. (#1247)
- Home Assistant FIX: check if path exists when migrating HA config (#1242)
- Home Assistant NEW: Disable MQTT by setting MQTT to `false` (#1232)
- NEW: Ability to read credentials from Docker Secrets. Thanks @cliaz! (#1244)
- Supported variables: `WYZE_EMAIL`, `WYZE_PASSWORD`, `API_ID`,`API_KEY`, `WB_USERNAME`, `WB_PASSWORD`, and `WB_API`

## What's Changed in v2.9.5

- **POTENTIALLY BREAKING**: The bridge will now use **PCMU/8000** as the default audio codec when the camera does not provide an RTSP/WebRTC-compatible audio format. This change should enhance compatibility with various NVR systems like **Surveillance Station** which do not support opus. Thanks @Dot50Cal!
- To use a different audio codec, set the desired codec in the `AUDIO_CODEC` environment variable.
- Always re-encode `aac_eld` (Wyze Cam v4) even when WebRTC is not enabled (#1236) Thanks @Dot50Cal!
- HOME ASSISTANT: Disable MQTT from automatically setting up by setting `MQTT_DTOPIC` to something other than `homeassistant` (#1232)

## What's Changed in v2.9.4

- Adjust AV sync issue/delay when audio is enabled. (#1231) Thanks @delmlund!

## What's Changed in v2.9.3

- FIX: Clear the retain flag from MQTT Discovery which was causing commands to be resent to the bridge on startup for some users. (#1182)
- Ignore commands when connection is stopping.

## What's Changed in v2.9.2

- Improved video connection stability and audio sync. #1175 #1196 #1194 #1193 #1186 Thanks @vipergts450!
- FIX: Remove quotes from credentials #1158
- NEW: `FORCE_FPS` option for all cameras #1161
- Home Assistant: Add `FORCE_FPS` option #1161
- Home Assistant: Ignore whitespaces in api key/id #1188 Thanks @richh1!


## What's Changed in v2.9.1

- FIX: Setting bitrate higher than 255 would not report correctly (#1185) Thanks @Anc0dia!
- FIX: Wrong bitrate for HL_CFL2 (#1112) Thanks @dreondre!
- FIX: Could not set values with the REST API when `WB_AUTH` is enabled.(#1189) Thanks @kiwi-cam!
- NEW: `api` header authentication option for the RES API when `WB_AUTH` is enabled:
- `-H "api: MyWbApiKey"`

## What's Changed in v2.9.0

> [!IMPORTANT]
> WebUI and stream authentication will be enabled by default to prevent unintentional access.

**Default Authentication**

- To disable default authentication, set `WB_AUTH=False` explicitly.
- Note that all streams and the REST API will necessitate authentication when `WB_AUTH` is enabled.

**WebUI Authentication**

- If `WB_USERNAME` and `WB_PASSWORD` are not set, the system will try to use `WYZE_EMAIL` and `WYZE_PASSWORD`.
- In case neither sets of credentials are provided, the username will default to `wbadmin` with a randomly generated `WB_PASSWORD`, which will be logged and stored in a `wb_password` file within the tokens directory.
- Credentials are case sensitive.
```
STREAM_AUTH=user:pass@cam-1,other-cam|second-user:password@just-one-cam|user3:pass
```

**Stream and REST API Authentication**
- A unique API key will be accessible at the bottom of your WebUI and saved to a `wb_api` file in your tokens directory.
- For persistence, ensure to set the `WB_API` environment variable or volume mount the `/tokens` directory.
- REST API will require an `api` query parameter.
- Example: `http://localhost:5000/api/<camera-name>/state?api=<your-wb-api-key>`
- Streams will also require authentication.
- username: `wb`
- password: your unique wb api key
- `user:pass` has access to `cam-1` and `other-cam`
- `second-user:password` has access to `just-one-cam`
- `user3:pass` has access to **all** paths/cameras

**FIXES**
- Wrong file permission caused errors for non-root. (#1174) Thanks @GiZZoR!
- Fix `MOTION_API` when substreams were enabled. (#1125) Thanks @kiwi-cam!
- Changing FPS and `FORCE_FPS` were broken (#1161) Thanks @jarrah31!
- Dropped frame issue when camera is falling behind. (#1167) Thanks @34t614t1254y!
See [Wiki](https://github.com/mrlt8/docker-wyze-bridge/wiki/Authentication#custom-stream-auth) for more information and examples.

**NEW**
- Token based wyze authentication from WebUI. See [wiki](https://github.com/mrlt8/docker-wyze-bridge/wiki/Authentication#token-based-authentication).
- Remove 255 limit from `QUALITY`. Can now go as high as your network can handle. e.g. `- QUALITY=HD8000`
- Update snapshot with `MOTION_API` and push to mqtt (#709) (#970)
- Additional headers for `MOTION_WEBHOOKS`.
- `OFFLINE_WEBHOOKS` will send a POST request when the bridge cannot connect to a camera because it is offline. Replaces `ifttt_webhook`.
### Recording via MediaMTX

**POTENTIALLY BREAKING**
- CHANGES: `MOTION_WEBHOOKS` now makes a POST request instead of a GET request.
- CHANGES: `MOTION_WEBHOOKS` includes the event timestamp in the message body which may require you to adjust the timezone for your container with the `TZ` environment.
- REMOVED: `ifttt_webhook` as webhooks are no longer free with IFTTT.
- CHANGED: Renamed WebUI authentication related ENV options:
- `WEB_AUTH` -> `WB_AUTH`
- `WEB_USERNAME` -> `WB_USERNAME`
- `WEB_PASSWORD` -> `WB_PASSWORD`
Recoding streams has been updated to use MediaMTX with the option to delete older clips.

**HOME ASSISTANT**
- Login with API Key/ID or existing token via Ingress/WebUI.
- Config now uses yaml instead of json.
- Credentials are now optional to allow for WebUI based login, but it is still recommended to set them under advanced options.
Use `RECORD_ALL` or `RECORD_CAM_NAME` to enable recording.

- `RECORD_PATH` Available variables are `%path` or `{cam_name}`, `%Y` `%m` `%d` `%H` `%M` `%S` `%f` `%s` (time in strftime format).
- `RECORD_LENGTH` Length of each clip. Use `s` for seconds , `h` for hours. Defaults to `60s`
- `RECORD_KEEP` Delete older clips. Use `s` for seconds , `h` for hours. Set to 0s to disable automatic deletion. Defaults to `0s`

[View previous changes](https://github.com/mrlt8/docker-wyze-bridge/releases)

> [!TIP]
> Home Assistant: you may need to re-add the repo if you cannot see the latest updates.


## FAQ

* How does this work?
Expand Down
4 changes: 1 addition & 3 deletions app/.env
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
VERSION=2.9.12
MTX_TAG=1.1.1
MTX_TAG=1.8.4
IOS_VERSION=17.1.1
APP_VERSION=2.50.9.1
MTX_HLSVARIANT=fmp4
MTX_PROTOCOLS=tcp
MTX_READTIMEOUT=20s
MTX_LOGLEVEL=warn
MTX_WEBRTCICEUDPMUXADDRESS=:8189
MTX_WRITEQUEUESIZE=1024
v3=mt0hBFxNQ4CQaWPjTonDk3mHaDEK2hhI
SDK_KEY=AQAAAIZ44fijz5pURQiNw4xpEfV9ZysFH8LYBPDxiONQlbLKaDeb7n26TSOPSGHftbRVo25k3uz5of06iGNB4pSfmvsCvm/tTlmML6HKS0vVxZnzEuK95TPGEGt+aE15m6fjtRXQKnUav59VSRHwRj9Z1Kjm1ClfkSPUF5NfUvsb3IAbai0WlzZE1yYCtks7NFRMbTXUMq3bFtNhEERD/7oc504b
16 changes: 6 additions & 10 deletions app/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from werkzeug.exceptions import NotFound
from wyze_bridge import WyzeBridge
from wyzebridge import config, web_ui
from wyzebridge.auth import WbAuth
from wyzebridge.web_ui import url_for


Expand Down Expand Up @@ -45,7 +46,7 @@ def wyze_login():
if request.method == "GET":
return render_template(
"login.html",
api=config.WB_API,
api=WbAuth.api,
version=config.VERSION,
)

Expand Down Expand Up @@ -96,7 +97,7 @@ def index():
cam_data=web_ui.all_cams(wb.streams, wb.api.total_cams),
number_of_columns=number_of_columns,
refresh_period=refresh_period,
api=config.WB_API,
api=WbAuth.api,
version=config.VERSION,
webrtc=bool(config.BRIDGE_IP),
show_video=show_video,
Expand All @@ -122,11 +123,6 @@ def index():
@auth_required
def sse_status():
"""Server sent event for camera status."""
if wb.api.mfa_req:
return Response(
web_ui.mfa_generator(wb.api.get_mfa),
mimetype="text/event-stream",
)
return Response(
web_ui.sse_generator(wb.streams.get_sse_status),
mimetype="text/event-stream",
Expand Down Expand Up @@ -168,7 +164,7 @@ def api_cam_control(cam_name: str, cam_cmd: str, payload: str | dict = ""):
def webrtc_signaling(name):
if "kvs" in request.args:
return wb.api.get_kvs_signal(name)
return web_ui.get_webrtc_signal(name, config.WB_API)
return web_ui.get_webrtc_signal(name, WbAuth.api)

@app.route("/webrtc/<string:name>")
@auth_required
Expand Down Expand Up @@ -233,9 +229,9 @@ def restart_bridge(restart_cmd: str):
"""
if restart_cmd == "cameras":
wb.streams.stop_all()
wb.streams.monitor_streams(wb.rtsp.health_check)
wb.streams.monitor_streams(wb.mtx.health_check)
elif restart_cmd == "rtsp_server":
wb.rtsp.restart()
wb.mtx.restart()
elif restart_cmd == "cam_data":
wb.refresh_cams()
restart_cmd = "cameras"
Expand Down
2 changes: 1 addition & 1 deletion app/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ pydantic==2.8.*
python-dotenv==1.0.*
requests==2.32.*
PyYAML==6.0.*
xxtea==3.2.*
xxtea==3.2.*
58 changes: 15 additions & 43 deletions app/static/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,42 +395,6 @@ document.addEventListener("DOMContentLoaded", () => {
icon.classList.add("fa-plug-circle-exclamation");
})
});
sse.addEventListener("mfa", (e) => {
const alertDiv = document.getElementById("alert")
if (e.data == "clear") {
alertDiv.innerHTML = '<div class="columns is-centered"><div class="column is-half-desktop"><article class="message is-success container is-one-third-desktop"><div class="message-body"><span class="icon"><i class="fas fa-check-circle"></i></span><span>Verification code accepted!</div></article></div></div>'
setTimeout(function () { location.reload(1); }, 5000);
}
else if (alertDiv.classList.contains("is-hidden")) {
const mfaType = e.data === "PrimaryPhone" ? "SMS" : e.data === "Email" ? "Email" : "TOTP";
console.info(`${e.data} verification required`)
alertDiv.classList.toggle("is-hidden");
alertDiv.innerHTML = '<div class="columns is-centered"><div class="column is-half-desktop"><article class="message is-dark container is-one-third-desktop"><div class="message-header">Two Factor Authentication Required</div>\
<div class="message-body"><form id="mfa-form" action="#"><div class="field"><label class="label" for="mfa-code">'+ mfaType + ' verification code required</label><p class="control has-icons-left"><input id="mfa-code" class="input is-dark is-large is-fullwidth" type="tel" placeholder="e.g. 123456" inputmode="numeric" required pattern="\\d{3}\\s?\\d{3}" maxlength="7" autocomplete="one-time-code"><span class="icon is-left"><i class="fas fa-unlock" aria-hidden="true"></i></span></div><div class="field"><div class="control"><button id="mfa-submit" type="submit" class="button is-dark is-fullwidth">Submit</button></div></p></div></form></div>\
</article></div></div>'
const mfaForm = document.getElementById("mfa-form");
const button = document.getElementById("mfa-submit").classList;
const mfaCodeInput = document.getElementById("mfa-code");
const handleFormSubmit = async (e) => {
e.preventDefault();
button.add("is-loading");
try {
await fetch(`mfa/${mfaCodeInput.value.replace(/\s/g, "")}`);
mfaForm.reset();
} catch (error) {
console.error(error);
} finally {
button.remove("is-loading");
}
};
mfaForm.addEventListener("submit", handleFormSubmit);
mfaCodeInput.addEventListener("input", () => {
if (mfaCodeInput.value.replace(/\s/g, "").length == 6) {
mfaForm.dispatchEvent(new Event("submit"));
}
});
}
})
sse.addEventListener("message", (event) => {
const data = JSON.parse(event.data);

Expand Down Expand Up @@ -648,7 +612,9 @@ document.addEventListener("DOMContentLoaded", () => {
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
hls.loadSource(videoSrc);
videoElement.muted = true;
videoElement.play();
videoElement.play().catch((err) => {
console.info('play() error:', err);
});
});
hls.attachMedia(videoElement);
} else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
Expand All @@ -660,7 +626,7 @@ document.addEventListener("DOMContentLoaded", () => {
}

document.querySelectorAll('video.hls.placeholder').forEach((videoElement) => {
videoElement.parentElement.addEventListener("click", () => { loadHLS(videoElement), videoElement.play() }, { "once": true });
videoElement.parentElement.addEventListener("click", () => { videoElement.play() }, { "once": true });
videoElement.addEventListener('play', () => {
loadHLS(videoElement);
if (!videoElement.classList.contains("connected") && !videoElement.hasAttribute("connecting")) {
Expand All @@ -680,17 +646,19 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
// Load WS for WebRTC on demand
function loadWebRTC(video, force = false) {
if (!force && (!video.classList.contains("placeholder") || !video.classList.contains("connected"))) { return }
function loadWebRTC(video) {
if (!video.classList.contains("placeholder")) { return }
let videoFormat = getCookie("video");
video.classList.remove("placeholder");
video.controls = true;
fetch(`signaling/${video.dataset.cam}?${videoFormat}`).then((resp) => resp.json()).then((data) => new Receiver(data));
}
// Click to load WebRTC

document.querySelectorAll('[data-enabled=True] video.webrtc.placeholder').forEach((v) => {
v.parentElement.addEventListener("click", () => { loadWebRTC(v, true), v.play() }, { "once": true });
document.querySelectorAll('[data-enabled=True] video.webrtc.placeholder').forEach((videoElement) => {
videoElement.parentElement.addEventListener("click", () => { videoElement.play() }, { "once": true });
videoElement.addEventListener("play", () => { loadWebRTC(videoElement) }, { "once": true });
videoElement.addEventListener('pause', () => { videoElement.removeAttribute('autoplay'); });
});
// Auto-play video
function autoplay(action) {
Expand All @@ -701,6 +669,8 @@ document.addEventListener("DOMContentLoaded", () => {
video.pause();
video.controls = false;
video.classList.add("lost");
video.removeAttribute('src');
video.load();
});
return;
}
Expand All @@ -714,7 +684,9 @@ document.addEventListener("DOMContentLoaded", () => {
if (!resume && !autoPlay && !fullscreen && !video.autoplay) { return }
if (video.classList.contains("hls")) { loadHLS(video); }
if (video.classList.contains("webrtc")) { loadWebRTC(video); }
video.play();
video.play().catch((err) => {
console.info('play() error:', err);
});
});
}
// Change default video format for WebUI
Expand Down
Loading
Loading