Skip to content

Commit 36957b5

Browse files
committed
improve: refactor error handling and optimize key
1 parent cefb230 commit 36957b5

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

src/playback/hls/HLSHandler.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export default class HLSHandler extends PassThrough {
245245
if (this.stop) return null
246246
const isRecoverable = err.message === 'aborted' || err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT'
247247
if (isRecoverable && attempt <= 3) {
248-
const delay = Math.pow(2, attempt) * 500
248+
const delay = 2 ** attempt * 500
249249
logger('warn', 'HLSHandler', `Segment fetch failed (attempt ${attempt}/3): ${err.message}. Retrying in ${delay}ms...`)
250250
await new Promise(r => setTimeout(r, delay))
251251
return this._fetchWithRetry(segment, attempt + 1)
@@ -255,6 +255,23 @@ export default class HLSHandler extends PassThrough {
255255
}
256256
}
257257

258+
async _waitForDrain() {
259+
if (this.destroyed || this.stop) return
260+
return new Promise((resolve) => {
261+
const cleanup = () => {
262+
this.removeListener('drain', onDrain)
263+
this.removeListener('close', onFinish)
264+
this.removeListener('error', onFinish)
265+
resolve()
266+
}
267+
const onDrain = cleanup
268+
const onFinish = cleanup
269+
this.once('drain', onDrain)
270+
this.once('close', onFinish)
271+
this.once('error', onFinish)
272+
})
273+
}
274+
258275
async _fetchSegments() {
259276
if (this.isFetching || this.stop) return
260277
this.isFetching = true
@@ -296,20 +313,20 @@ export default class HLSHandler extends PassThrough {
296313
if (segment.map && segment.map.uri !== this.lastMapUri) {
297314
const mapData = await this.fetcher.fetchMap(segment.map, segment.key)
298315
if (mapData && !this.stop) {
299-
if (!this.write(mapData)) await new Promise(r => this.once('drain', r))
316+
if (!this.write(mapData)) await this._waitForDrain()
300317
this.lastMapUri = segment.map.uri
301318
}
302319
}
303320

304321
if (this.strategy === 'segmented') {
305322
if (!this.stop && data) {
306-
if (!this.write(data)) await new Promise(r => this.once('drain', r))
323+
if (!this.write(data)) await this._waitForDrain()
307324
}
308325
} else if (stream) {
309326
this.activeSegmentStreams.set(key, stream)
310327
for await (const chunk of stream) {
311328
if (this.stop) break
312-
if (!this.write(chunk)) await new Promise(r => this.once('drain', r))
329+
if (!this.write(chunk)) await this._waitForDrain()
313330
}
314331
this.activeSegmentStreams.delete(key)
315332
}

src/playback/hls/SegmentFetcher.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class DecryptTransform extends Transform {
88
this.decipher = crypto.createDecipheriv(algorithm, key, iv)
99
this.decipher.setAutoPadding(false)
1010
}
11-
_transform(chunk, encoding, callback) {
11+
_transform(chunk, _encoding, callback) {
1212
try { this.push(this.decipher.update(chunk)); callback() }
1313
catch (err) { callback(err) }
1414
}
@@ -44,6 +44,10 @@ export default class SegmentFetcher {
4444
logger('error', 'SegmentFetcher', `Key fetch failed for ${keyInfo.uri}: Status ${statusCode}, Error: ${error?.message || 'Empty Body'}`)
4545
throw new Error(`Key fetch failed: ${statusCode}`)
4646
}
47+
if (this.keyMap.size >= 20) {
48+
const firstKey = this.keyMap.keys().next().value
49+
this.keyMap.delete(firstKey)
50+
}
4751
this.keyMap.set(keyInfo.uri, body)
4852
return body
4953
}
@@ -54,7 +58,7 @@ export default class SegmentFetcher {
5458
headers: this.headers, responseType: 'buffer', localAddress: this.localAddress
5559
})
5660
if (error || statusCode !== 200) throw new Error(`Map fetch failed: ${statusCode}`)
57-
if (keyInfo && keyInfo.iv && body.length % 16 === 0) {
61+
if (keyInfo?.iv && body.length % 16 === 0) {
5862
const keyData = await this.fetchKey(keyInfo)
5963
const algorithm = keyInfo.method === 'AES-128' ? 'aes-128-cbc' : 'aes-256-cbc'
6064
const decipher = crypto.createDecipheriv(algorithm, keyData, keyInfo.iv)

0 commit comments

Comments
 (0)