@torrent-tv/proxy 2.87.16 → 2.87.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/services/encode/segment-formats/fmp4.js +6 -1
- package/services/encode/segment-formats/index.js +3 -0
- package/services/server/ViewerRequests.js +3 -1
- package/services/storage/segment-store/SegmentStore.js +11 -2
- package/test/fmp4-presentation-clock.test.js +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 2.87.17
|
|
2
|
+
|
|
3
|
+
- **Fix**: Measure re-encoded HLS fragment intervals against their separate init and served timestamp position, without caching an absent header as empty media.
|
|
4
|
+
|
|
1
5
|
## 2.87.16
|
|
2
6
|
|
|
3
7
|
- **Fix**: Hold spilled source bytes across memory reservation so concurrent eviction cannot terminate ffmpeg input with a missing disk piece.
|
package/package.json
CHANGED
|
@@ -279,7 +279,12 @@ export const fmp4Format = {
|
|
|
279
279
|
* transcode itself; the box walk never descends into `mdat`.
|
|
280
280
|
*/
|
|
281
281
|
needsSegmentRewrite: true,
|
|
282
|
-
readMediaRanges
|
|
282
|
+
readMediaRanges(raw, { initBytes = null, startSeconds = 0 } = {}) {
|
|
283
|
+
if (this.extractInit(raw)) return readPresentationRanges(raw);
|
|
284
|
+
if (!initBytes?.length) return undefined;
|
|
285
|
+
const served = this.prepareSegmentBytes(raw, { initBytes, startSeconds, rawBytes: raw });
|
|
286
|
+
return readPresentationRanges(Buffer.concat([initBytes, served]));
|
|
287
|
+
},
|
|
283
288
|
|
|
284
289
|
/**
|
|
285
290
|
* Whether a segment carries every track the init promises.
|
|
@@ -49,6 +49,9 @@ import { mpegtsFormat } from "./mpegts.js";
|
|
|
49
49
|
* @property {(bytes: Buffer, context: PrepareSegmentContext) => Buffer}
|
|
50
50
|
* prepareSegmentBytes - Correct a segment before serving. Identity for
|
|
51
51
|
* formats that need nothing.
|
|
52
|
+
* @property {(bytes: Buffer, context?: Partial<PrepareSegmentContext>) =>
|
|
53
|
+
* { start: number, end: number }[] | undefined} [readMediaRanges] - Actual
|
|
54
|
+
* served presentation intervals, or undefined until their header is available.
|
|
52
55
|
*/
|
|
53
56
|
|
|
54
57
|
/** @type {Readonly<Record<string, SegmentFormat>>} */
|
|
@@ -569,7 +569,9 @@ export class ViewerRequests {
|
|
|
569
569
|
index,
|
|
570
570
|
startSeconds: timeline.publishedStartOf(index),
|
|
571
571
|
endSeconds: timeline.publishedStartOf(index + 1),
|
|
572
|
-
mediaRanges: this.#host.segmentStore.mediaRangesOf(output.outputKey, index
|
|
572
|
+
mediaRanges: this.#host.segmentStore.mediaRangesOf(output.outputKey, index, {
|
|
573
|
+
startSeconds: timeline.publishedStartOf(index)
|
|
574
|
+
})
|
|
573
575
|
}));
|
|
574
576
|
const sourceIndexes = [output.spec.video?.fileIndex, output.spec.audio?.fileIndex]
|
|
575
577
|
.filter((fileIndex) => Number.isInteger(fileIndex) && fileIndex >= 0);
|
|
@@ -471,7 +471,7 @@ export class SegmentStore {
|
|
|
471
471
|
return served;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
mediaRangesOf(key, index) {
|
|
474
|
+
mediaRangesOf(key, index, { startSeconds = 0 } = {}) {
|
|
475
475
|
const known = this.#mediaRanges.get(key)?.get(index);
|
|
476
476
|
if (known) return known;
|
|
477
477
|
const format = this.#formats.get(key);
|
|
@@ -479,7 +479,16 @@ export class SegmentStore {
|
|
|
479
479
|
const filePath = this.pathOf(key, index);
|
|
480
480
|
if (!filePath) return undefined;
|
|
481
481
|
try {
|
|
482
|
-
|
|
482
|
+
let initBytes = this.initOf(key);
|
|
483
|
+
if (!initBytes && format.initFileName) {
|
|
484
|
+
try {
|
|
485
|
+
initBytes = readFileSync(path.join(this.pathFor(key), format.initFileName));
|
|
486
|
+
} catch {
|
|
487
|
+
// A header not yet produced is not an empty media interval.
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const ranges = format.readMediaRanges(readFileSync(filePath), { initBytes, startSeconds });
|
|
491
|
+
if (!ranges) return undefined;
|
|
483
492
|
const byIndex = this.#mediaRanges.get(key) ?? new Map();
|
|
484
493
|
byIndex.set(index, ranges);
|
|
485
494
|
this.#mediaRanges.set(key, byIndex);
|
|
@@ -2,6 +2,10 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { fmp4Format } from "../services/encode/segment-formats/fmp4.js";
|
|
4
4
|
import { readPresentationRanges, walkBoxes } from "../services/encode/segment-formats/mp4-boxes.js";
|
|
5
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { SegmentStore } from "../services/storage/segment-store/SegmentStore.js";
|
|
5
9
|
|
|
6
10
|
function box(type, body) {
|
|
7
11
|
const header = Buffer.alloc(8);
|
|
@@ -70,3 +74,24 @@ test("a shared init first read after a seek also supports returning to the begin
|
|
|
70
74
|
});
|
|
71
75
|
assert.deepEqual(readPresentationRanges(Buffer.concat([shared, prepared])), readPresentationRanges(raw));
|
|
72
76
|
});
|
|
77
|
+
|
|
78
|
+
test("HLS fragments use the separate init and the same timeline translation as serving", (t) => {
|
|
79
|
+
const root = mkdtempSync(path.join(os.tmpdir(), "presentation-ranges-"));
|
|
80
|
+
const store = new SegmentStore({ root });
|
|
81
|
+
t.after(() => {
|
|
82
|
+
store.dropAll("the check is over");
|
|
83
|
+
rmSync(root, { recursive: true, force: true });
|
|
84
|
+
});
|
|
85
|
+
const key = "encoded-video";
|
|
86
|
+
store.useFormat(key, fmp4Format);
|
|
87
|
+
const dir = store.directoryFor(key);
|
|
88
|
+
const media = fragment(0, 2000);
|
|
89
|
+
writeFileSync(path.join(dir, fmp4Format.segmentFileName(1)), media);
|
|
90
|
+
assert.equal(store.mediaRangesOf(key, 1, { startSeconds: 4 }), undefined);
|
|
91
|
+
const header = init(0, 0);
|
|
92
|
+
writeFileSync(path.join(dir, fmp4Format.initFileName), header);
|
|
93
|
+
const served = fmp4Format.prepareSegmentBytes(media, { initBytes: header, startSeconds: 4 });
|
|
94
|
+
assert.deepEqual(store.mediaRangesOf(key, 1, { startSeconds: 4 }),
|
|
95
|
+
readPresentationRanges(Buffer.concat([header, served])));
|
|
96
|
+
assert.ok(store.mediaRangesOf(key, 1)[0].start > 4);
|
|
97
|
+
});
|