Skip to content

Commit fc7881d

Browse files
committed
fix(metadata)!: MetadataPipeline stops working after a while
- Remove the `ONVIFDepay` handler. Instead this tube now only depays the XML messages (as it did before). To handle the messages either add a separate pipe to your pipeline after the Onvif one, or end your pipeline with a sink where you can handle the messages. - The `MetadataPipeline` now adds a handler sink at the end of the pipeline that calls the metadata handler. This fixes a bug where the pipeline would stall after `ONVIFDepay` would push messages to a non-existing next pipe or sink. - Add a new browser example on how to use the `MetadataPipeline`.
1 parent 7f39fbc commit fc7881d

File tree

7 files changed

+136
-18
lines changed

7 files changed

+136
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const { pipelines } = window.mediaStreamLibrary
2+
3+
// force auth
4+
const authorize = async host => {
5+
// Force a login by fetching usergroup
6+
const fetchOptions = {
7+
credentials: 'include',
8+
headers: {
9+
'Axis-Orig-Sw': true,
10+
'X-Requested-With': 'XMLHttpRequest',
11+
},
12+
mode: 'no-cors',
13+
}
14+
try {
15+
await window.fetch(`http://${host}/axis-cgi/usergroup.cgi`, fetchOptions)
16+
} catch (err) {
17+
console.error(err)
18+
}
19+
}
20+
21+
const play = host => {
22+
const initialTime = window.performance.now()
23+
// Setup a new pipeline
24+
const pipeline = new pipelines.MetadataPipeline({
25+
ws: {
26+
uri: `ws://${host}/rtsp-over-websocket`,
27+
tokenUri: `http://${host}/axis-cgi/rtspwssession.cgi`,
28+
protocol: 'binary',
29+
timeout: 10000,
30+
},
31+
rtsp: {
32+
uri: `rtsp://${host}/axis-media/media.amp?event=on&video=0&audio=0`,
33+
},
34+
metadataHandler: msg => {
35+
const title = document.createElement('div')
36+
title.textContent = `+${window.performance.now() - initialTime}`
37+
title.classList.add('metadata-title')
38+
39+
const content = document.createElement('div')
40+
content.textContent = new TextDecoder().decode(msg.data)
41+
content.classList.add('metadata-content')
42+
43+
document.querySelector('#placeholder').prepend(title, content)
44+
},
45+
})
46+
pipeline.ready.then(() => {
47+
pipeline.rtsp.play()
48+
})
49+
50+
return pipeline
51+
}
52+
53+
let pipeline
54+
55+
// Each time a device ip is entered, authorize and then play
56+
const playButton = document.querySelector('#play')
57+
playButton.addEventListener('click', async e => {
58+
pipeline && pipeline.close()
59+
60+
const device = document.querySelector('#device')
61+
const host = device.value || device.placeholder
62+
63+
console.log(host)
64+
65+
await authorize(host)
66+
67+
pipeline = play(host)
68+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Example metadata streaming from Axis camera</title>
5+
</head>
6+
<style>
7+
* {
8+
box-sizing: border-box;
9+
}
10+
11+
.metadata-title {
12+
margin-bottom: 5px;
13+
font-weight: bold;
14+
}
15+
16+
.metadata-content {
17+
margin-bottom: 20px;
18+
font-family: monospace;
19+
font-size: 80%;
20+
}
21+
22+
.metadata-container {
23+
height: 400px;
24+
width: 100%;
25+
border: 1px solid black;
26+
padding: 10px;
27+
margin: 10px 0;
28+
overflow: hidden auto;
29+
}
30+
</style>
31+
<body>
32+
<div>
33+
<div>
34+
<label for="device">Device IP:</label>
35+
<input id="device" type="text" placeholder="192.168.0.90" />
36+
</div>
37+
<button id="play">Play</button>
38+
</div>
39+
<div class="metadata-container" id="placeholder" />
40+
<script src="../media-stream-library.min.js"></script>
41+
<script src="simple-metadata-player.js"></script>
42+
</body>
43+
</html>

lib/components/component.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ export class Source extends AbstractComponent {
157157

158158
export class Tube extends Source {
159159
public static fromHandlers(
160-
fnIncoming: MessageHandler,
161-
fnOutgoing: MessageHandler,
160+
fnIncoming: MessageHandler | undefined,
161+
fnOutgoing: MessageHandler | undefined,
162162
) {
163163
const incomingStream = fnIncoming
164164
? StreamFactory.peeker(fnIncoming)

lib/components/onvifdepay/index.test.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import { runComponentTests } from '../../utils/validate-component'
33

44
describe('ONVIF depay component', () => {
55
describe('is a valid component', () => {
6-
const c = new ONVIFDepay(() => {
7-
/** noop */
8-
})
6+
const c = new ONVIFDepay()
97
runComponentTests(c, 'ONVIF depay component')
108
})
119
})

lib/components/onvifdepay/index.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '../../utils/protocols/rtp'
1010

1111
export class ONVIFDepay extends Tube {
12-
constructor(handler?: (msg: XmlMessage) => void) {
12+
constructor() {
1313
let XMLPayloadType: number
1414
let packets: Buffer[] = []
1515

@@ -49,15 +49,9 @@ export class ONVIFDepay extends Tube {
4949
data: Buffer.concat(packets),
5050
type: MessageType.XML,
5151
}
52-
// If there is a handler, the XML message will leave
53-
// through the handler, otherwise send it on to the
54-
// next component
55-
if (handler) {
56-
handler(xmlMsg)
57-
} else {
58-
this.push(xmlMsg)
59-
}
52+
callback(undefined, xmlMsg)
6053
packets = []
54+
return
6155
}
6256
callback()
6357
} else {

lib/pipelines/html5-video-metadata-pipeline.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Html5VideoPipeline, Html5VideoConfig } from './html5-video-pipeline'
22
import { ONVIFDepay } from '../components/onvifdepay'
3-
import { XmlMessage } from '../components/message'
3+
import { XmlMessage, MessageType } from '../components/message'
4+
import { Tube } from '../components/component'
45

56
export interface Html5VideoMetadataConfig extends Html5VideoConfig {
67
metadataHandler: (msg: XmlMessage) => void
@@ -18,7 +19,14 @@ export class Html5VideoMetadataPipeline extends Html5VideoPipeline {
1819

1920
super(config)
2021

21-
const onvifDepay = new ONVIFDepay(metadataHandler)
22+
const onvifDepay = new ONVIFDepay()
2223
this.insertAfter(this.rtsp, onvifDepay)
24+
25+
const onvifHandlerPipe = Tube.fromHandlers(msg => {
26+
if (msg.type === MessageType.XML) {
27+
metadataHandler(msg as XmlMessage)
28+
}
29+
}, undefined)
30+
this.insertAfter(onvifDepay, onvifHandlerPipe)
2331
}
2432
}

lib/pipelines/metadata-pipeline.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { ONVIFDepay } from '../components/onvifdepay'
33
import { WSSource } from '../components/ws-source'
44
import { WSConfig } from '../components/ws-source/openwebsocket'
55
import { RtspConfig } from '../components/rtsp-session'
6-
import { XmlMessage } from '../components/message'
6+
import { XmlMessage, MessageType } from '../components/message'
7+
import { Sink } from '../components/component'
78

89
// Default configuration for XML event stream
910
const DEFAULT_RTSP_PARAMETERS = {
@@ -31,8 +32,14 @@ export class MetadataPipeline extends RtspPipeline {
3132

3233
super(Object.assign({}, DEFAULT_RTSP_PARAMETERS, rtspConfig))
3334

34-
const onvifDepay = new ONVIFDepay(metadataHandler)
35+
const onvifDepay = new ONVIFDepay()
3536
this.append(onvifDepay)
37+
const handlerSink = Sink.fromHandler(msg => {
38+
if (msg.type === MessageType.XML) {
39+
metadataHandler(msg as XmlMessage)
40+
}
41+
})
42+
this.append(handlerSink)
3643

3744
const waitForWs = WSSource.open(wsConfig)
3845
this.ready = waitForWs.then(wsSource => {

0 commit comments

Comments
 (0)