Skip to content

Commit

Permalink
Merge pull request #681 from tsightler/dev
Browse files Browse the repository at this point in the history
Release v5.5.1
  • Loading branch information
tsightler authored Jul 25, 2023
2 parents 85635a7 + 643a15d commit c437d89
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 271 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ENV LANG="C.UTF-8" \
COPY . /app/ring-mqtt
RUN S6_VERSION="v3.1.5.0" && \
BASHIO_VERSION="v0.15.0" && \
GO2RTC_VERSION="v1.6.0" && \
GO2RTC_VERSION="v1.6.2" && \
APK_ARCH="$(apk --print-arch)" && \
apk add --no-cache tar xz git libcrypto3 libssl3 musl-utils musl bash curl jq tzdata nodejs npm mosquitto-clients && \
curl -L -s "https://github.com/just-containers/s6-overlay/releases/download/${S6_VERSION}/s6-overlay-noarch.tar.xz" | tar -Jxpf - -C / && \
Expand Down
17 changes: 2 additions & 15 deletions devices/camera-livestream.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { parentPort, workerData } from 'worker_threads'
import { WebrtcConnection } from '../lib/streaming/webrtc-connection.js'
import { RingEdgeConnection } from '../lib/streaming/ring-edge-connection.js'
import { StreamingSession } from '../lib/streaming/streaming-session.js'

const deviceName = workerData.deviceName
Expand Down Expand Up @@ -31,9 +30,7 @@ async function startLiveStream(streamData) {
id: doorbotId
}

const streamConnection = (streamData.sessionId)
? new WebrtcConnection(streamData.sessionId, cameraData)
: new RingEdgeConnection(streamData.authToken, cameraData)
const streamConnection = new WebrtcConnection(streamData.ticket, cameraData)
liveStream = new StreamingSession(cameraData, streamConnection)

liveStream.connection.pc.onConnectionState.subscribe(async (data) => {
Expand Down Expand Up @@ -65,17 +62,7 @@ async function startLiveStream(streamData) {
'-c:a:1', 'copy',
],
video: [
...streamData.hevcEnabled
? [
'-c:v', 'libx264',
'-g', '20',
'-keyint_min', '10',
'-crf', '23',
'-preset', 'ultrafast'
]
: [
'-c:v', 'copy'
]
'-c:v', 'copy'
],
output: [
'-flags', '+global_header',
Expand Down
83 changes: 43 additions & 40 deletions devices/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,32 +719,42 @@ export default class Camera extends RingPolledDevice {

async refreshSnapshot(type, image_uuid) {
let newSnapshot = false
let loop = 3

if (this.device.snapshotsAreBlocked) {
this.debug('Snapshots are unavailable, check if motion capture is disabled manually or via modes settings')
return
}

try {
switch (type) {
case 'interval':
this.debug('Requesting an updated interval snapshot')
newSnapshot = await this.device.getSnapshot()
break;
case 'motion':
if (image_uuid) {
this.debug(`Requesting motion snapshot using notification image UUID: ${image_uuid}`)
newSnapshot = await this.device.getNextSnapshot({ uuid: image_uuid })
} else if (!this.device.operatingOnBattery) {
this.debug('Requesting an updated motion snapshot')
newSnapshot = await this.device.getNextSnapshot()
} else {
this.debug('Motion snapshot needed but notification did not contain image UUID and battery cameras are unable to snapshot while recording')
}
while (!newSnapshot && loop > 0) {
try {
switch (type) {
case 'interval':
this.debug('Requesting an updated interval snapshot')
newSnapshot = await this.device.getNextSnapshot({ force: true })
break;
case 'motion':
if (image_uuid) {
this.debug(`Requesting motion snapshot using notification image UUID: ${image_uuid}`)
newSnapshot = await this.device.getNextSnapshot({ uuid: image_uuid })
} else if (!this.device.operatingOnBattery) {
this.debug('Requesting an updated motion snapshot')
newSnapshot = await this.device.getNextSnapshot({ force: true })
} else {
this.debug('Motion snapshot needed but notification did not contain image UUID and battery cameras are unable to snapshot while recording')
loop = 0 // Don't retry in this case
}
}
} catch (err) {
this.debug(err)
if (loop > 1) {
this.debug('Failed to retrieve updated snapshot, retrying in one second...')
await utils.sleep(1)
} else {
this.debug('Failed to retrieve updated snapshot after three attempts, aborting')
}
}
} catch (error) {
this.debug(error)
this.debug('Failed to retrieve updated snapshot')
loop--
}

if (newSnapshot) {
Expand All @@ -757,28 +767,19 @@ export default class Camera extends RingPolledDevice {

async startLiveStream(rtspPublishUrl) {
this.data.stream.live.session = true

const streamData = {
rtspPublishUrl,
sessionId: false,
authToken: false,
hevcEnabled: this.hevcEnabled
ticket: null
}

try {
if (this.device.isRingEdgeEnabled) {
this.debug('Initializing a live stream session for Ring Edge')
const auth = await this.device.restClient.getCurrentAuth()
streamData.authToken = auth.access_token
} else {
this.debug('Initializing a live stream session for Ring cloud')
const liveCall = await this.device.restClient.request({
method: 'POST',
url: this.device.doorbotUrl('live_call')
})
if (liveCall.data?.session_id) {
streamData.sessionId = liveCall.data.session_id
}
}
this.debug('Acquiring a live stream WebRTC signaling session ticket')
const response = await this.device.restClient.request({
method: 'POST',
url: 'https://app.ring.com/api/v1/clap/ticket/request/signalsocket'
})
streamData.ticket = response.ticket
} catch(error) {
if (error?.response?.statusCode === 403) {
this.debug(`Camera returned 403 when starting a live stream. This usually indicates that live streaming is blocked by Modes settings. Check your Ring app and verify that you are able to stream from this camera with the current Modes settings.`)
Expand All @@ -787,11 +788,11 @@ export default class Camera extends RingPolledDevice {
}
}

if (streamData.sessionId || streamData.authToken) {
this.debug('Live stream session successfully initialized, starting worker')
if (streamData.ticket) {
this.debug('Live stream WebRTC signaling session ticket acquired, starting live stream worker')
this.data.stream.live.worker.postMessage({ command: 'start', streamData })
} else {
this.debug('Live stream activation failed to initialize session data')
this.debug('Live stream failed to initialize WebRTC signaling session')
this.data.stream.live.status = 'failed'
this.data.stream.live.session = false
this.publishStreamState()
Expand All @@ -815,7 +816,9 @@ export default class Camera extends RingPolledDevice {

try {
if (this.data.event_select.transcoded || this.hevcEnabled) {
// Ring videos transcoded for download are poorly optimized for RTSP streaming so they must be re-encoded on-the-fly
// If camera is in HEVC mode, recordings are also in HEVC so transcode the video back to H.264/AVC on the fly
// Ring videos transcoded for download are not optimized for RTSP streaming (limited keyframes) so they must
// also be re-transcoded on-the-fly to allow streamers to join early
this.data.stream.event.session = spawn(pathToFfmpeg, [
'-re',
'-i', this.data.event_select.recordingUrl,
Expand Down
3 changes: 1 addition & 2 deletions devices/security-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ export default class SecurityPanel extends RingSocketDevice {
this.data.attributes.exitSecondsLeft = 0
}

// Sometimes a countdown event comes in just before the mode switch event
// so suppress publish of attribute state in this case
// Suppress attribute publish if countdown event comes before mode switch
if (this.data.publishedState !== this.data.attributes.targetState) {
this.publishAlarmAttributes()
}
Expand Down
12 changes: 12 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## v5.5.1
**New Features**
- Improved support for HEVC mode cameras\
While the initial support for HEVC required local transcoding, this update uses a different streaming API that is able to negotiates down to H.264/AVC for these cameras on-the-fly which means HEVC enabled cameras should now work fine even on lower-performance hardware like RPi3/4 devices. Hopefully this new API does not break streaming for other cases.

**Other Changes**
- Use a non-cached snapshot for all cases
- Implement multiple retries if initial request for snapshot update fails

**Dependency Updates**
- go2rtc v1.6.2

## v5.5.0
**New Features**
- Initial support for HEVC mode cameras\
Expand Down
21 changes: 3 additions & 18 deletions lib/streaming/peer-connection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This code is largely copied from ring-client-api, but converted from Typescript
// to straight Javascript and some code not required for ring-mqtt removed.
// to native Javascript with custom logging for ring-mqtt and some unused code removed.
// Much thanks to @dgreif for the original code which is the basis for this work.

import { RTCPeerConnection, RTCRtpCodecParameters } from 'werift'
Expand Down Expand Up @@ -51,27 +51,12 @@ export class WeriftPeerConnection extends Subscribed {
{ type: 'nack', parameter: 'pli' },
{ type: 'goog-remb' },
],
parameters: 'packetization-mode=1;profile-level-id=640029;level-asymmetry-allowed=1',
}),
new RTCRtpCodecParameters({
mimeType: 'video/H265',
clockRate: 90000,
rtcpFeedback: [
{ type: 'transport-cc' },
{ type: 'ccm', parameter: 'fir' },
{ type: 'nack' },
{ type: 'nack', parameter: 'pli' },
{ type: 'goog-remb' },
],
parameters: 'packetization-mode=1;profile-level-id=42001f;level-asymmetry-allowed=1',
}),
new RTCRtpCodecParameters({
mimeType: "video/rtx",
clockRate: 90000,
}),
new RTCRtpCodecParameters({
mimeType: "video/red",
clockRate: 90000,
}),
})
],
},
iceServers: ringIceServers.map((server) => ({ urls: server })),
Expand Down
154 changes: 0 additions & 154 deletions lib/streaming/ring-edge-connection.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/streaming/streaming-connection-base.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This code is largely copied from ring-client-api, but converted from Typescript
// to straight Javascript and some code not required for ring-mqtt removed.
// to native Javascript with custom logging for ring-mqtt and some unused code removed.
// Much thanks to @dgreif for the original code which is the basis for this work.

import { WeriftPeerConnection } from './peer-connection.js'
Expand Down
Loading

0 comments on commit c437d89

Please sign in to comment.