werift 0.18.13 → 0.18.14
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/lib/common/src/index.d.ts +0 -1
- package/lib/common/src/index.js +0 -1
- package/lib/common/src/index.js.map +1 -1
- package/lib/rtp/src/container/index.d.ts +2 -1
- package/lib/rtp/src/container/index.js +2 -1
- package/lib/rtp/src/container/index.js.map +1 -1
- package/lib/rtp/src/container/mp4/container.d.ts +66 -0
- package/lib/rtp/src/container/mp4/container.js +262 -0
- package/lib/rtp/src/container/mp4/container.js.map +1 -0
- package/lib/rtp/src/container/mp4/exp-golomb.d.ts +20 -0
- package/lib/rtp/src/container/mp4/exp-golomb.js +144 -0
- package/lib/rtp/src/container/mp4/exp-golomb.js.map +1 -0
- package/lib/rtp/src/container/mp4/h264.d.ts +42 -0
- package/lib/rtp/src/container/mp4/h264.js +240 -0
- package/lib/rtp/src/container/mp4/h264.js.map +1 -0
- package/lib/rtp/src/container/mp4/index.d.ts +2 -0
- package/lib/rtp/src/container/mp4/index.js +19 -0
- package/lib/rtp/src/container/mp4/index.js.map +1 -0
- package/lib/rtp/src/container/mp4/mp4box.d.ts +5 -0
- package/lib/rtp/src/container/mp4/mp4box.js +32 -0
- package/lib/rtp/src/container/mp4/mp4box.js.map +1 -0
- package/lib/rtp/src/container/mp4/sps-parser.d.ts +40 -0
- package/lib/rtp/src/container/mp4/sps-parser.js +291 -0
- package/lib/rtp/src/container/mp4/sps-parser.js.map +1 -0
- package/lib/rtp/src/container/ogg/index.d.ts +1 -0
- package/lib/rtp/src/container/ogg/index.js +18 -0
- package/lib/rtp/src/container/ogg/index.js.map +1 -0
- package/lib/rtp/src/container/ogg/parser.d.ts +12 -0
- package/lib/rtp/src/container/ogg/parser.js +100 -0
- package/lib/rtp/src/container/ogg/parser.js.map +1 -0
- package/lib/rtp/src/container/{webm.d.ts → webm/container.d.ts} +1 -1
- package/lib/rtp/src/container/{webm.js → webm/container.js} +6 -6
- package/lib/rtp/src/container/webm/container.js.map +1 -0
- package/lib/rtp/src/container/webm/ebml/ebml.js.map +1 -0
- package/lib/rtp/src/container/webm/ebml/id.js.map +1 -0
- package/lib/rtp/src/container/webm/ebml/index.js.map +1 -0
- package/lib/rtp/src/container/webm/ebml/typedArrayUtils.js.map +1 -0
- package/lib/rtp/src/container/webm/index.d.ts +2 -0
- package/lib/rtp/src/container/webm/index.js +19 -0
- package/lib/rtp/src/container/webm/index.js.map +1 -0
- package/lib/rtp/src/processor/index.d.ts +2 -0
- package/lib/rtp/src/processor/index.js +2 -0
- package/lib/rtp/src/processor/index.js.map +1 -1
- package/lib/rtp/src/processor/mp4.d.ts +50 -0
- package/lib/rtp/src/processor/mp4.js +162 -0
- package/lib/rtp/src/processor/mp4.js.map +1 -0
- package/lib/rtp/src/processor/mp4Callback.d.ts +12 -0
- package/lib/rtp/src/processor/mp4Callback.js +83 -0
- package/lib/rtp/src/processor/mp4Callback.js.map +1 -0
- package/lib/rtp/src/processor/mute.js +6 -3
- package/lib/rtp/src/processor/mute.js.map +1 -1
- package/lib/rtp/src/processor/nack.js +2 -3
- package/lib/rtp/src/processor/nack.js.map +1 -1
- package/lib/rtp/src/processor/webm.d.ts +1 -1
- package/lib/rtp/src/processor/webm.js +1 -1
- package/lib/rtp/src/processor/webm.js.map +1 -1
- package/lib/rtp/src/processor/webmCallback.d.ts +1 -1
- package/lib/rtp/src/processor/webmCallback.js.map +1 -1
- package/lib/rtp/src/processor/webmStream.d.ts +1 -1
- package/lib/rtp/src/processor/webmStream.js.map +1 -1
- package/lib/rtp/src/rtcp/rr.js +2 -6
- package/lib/rtp/src/rtcp/rr.js.map +1 -1
- package/lib/rtp/src/rtcp/rtpfb/nack.js +5 -12
- package/lib/rtp/src/rtcp/rtpfb/nack.js.map +1 -1
- package/lib/rtp/src/rtcp/rtpfb/twcc.js +11 -7
- package/lib/rtp/src/rtcp/rtpfb/twcc.js.map +1 -1
- package/lib/rtp/src/rtcp/sr.js +1 -5
- package/lib/rtp/src/rtcp/sr.js.map +1 -1
- package/lib/rtp/src/rtp/headerExtension.d.ts +5 -0
- package/lib/rtp/src/rtp/headerExtension.js +35 -9
- package/lib/rtp/src/rtp/headerExtension.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +1 -0
- package/lib/webrtc/src/utils.js +9 -1
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/nonstandard/recorder/writer/webm.ts +1 -1
- package/src/utils.ts +9 -0
- package/lib/common/src/array.d.ts +0 -1
- package/lib/common/src/array.js +0 -15
- package/lib/common/src/array.js.map +0 -1
- package/lib/rtp/src/container/ebml/ebml.js.map +0 -1
- package/lib/rtp/src/container/ebml/id.js.map +0 -1
- package/lib/rtp/src/container/ebml/index.js.map +0 -1
- package/lib/rtp/src/container/ebml/typedArrayUtils.js.map +0 -1
- package/lib/rtp/src/container/webm.js.map +0 -1
- /package/lib/rtp/src/container/{ebml → webm/ebml}/ebml.d.ts +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/ebml.js +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/id.d.ts +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/id.js +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/index.d.ts +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/index.js +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/typedArrayUtils.d.ts +0 -0
- /package/lib/rtp/src/container/{ebml → webm/ebml}/typedArrayUtils.js +0 -0
package/lib/common/src/index.js
CHANGED
|
@@ -18,7 +18,6 @@ __exportStar(require("./binary"), exports);
|
|
|
18
18
|
__exportStar(require("./number"), exports);
|
|
19
19
|
__exportStar(require("./promise"), exports);
|
|
20
20
|
__exportStar(require("./network"), exports);
|
|
21
|
-
__exportStar(require("./array"), exports);
|
|
22
21
|
__exportStar(require("./type"), exports);
|
|
23
22
|
__exportStar(require("./log"), exports);
|
|
24
23
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../common/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,2CAAyB;AACzB,4CAA0B;AAC1B,4CAA0B;AAC1B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../common/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,2CAAyB;AACzB,4CAA0B;AAC1B,4CAA0B;AAC1B,yCAAuB;AACvB,wCAAsB","sourcesContent":["export * from \"./binary\";\nexport * from \"./number\";\nexport * from \"./promise\";\nexport * from \"./network\";\nexport * from \"./type\";\nexport * from \"./log\";\n"]}
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./mp4"), exports);
|
|
18
|
+
__exportStar(require("./ogg"), exports);
|
|
18
19
|
__exportStar(require("./webm"), exports);
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../rtp/src/container/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../rtp/src/container/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wCAAsB;AACtB,wCAAsB;AACtB,yCAAuB","sourcesContent":["export * from \"./mp4\";\nexport * from \"./ogg\";\nexport * from \"./webm\";\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Event from "rx.mini";
|
|
2
|
+
type DecoderConfig = AudioDecoderConfig | VideoDecoderConfig;
|
|
3
|
+
type EncodedChunk = EncodedAudioChunk | EncodedVideoChunk;
|
|
4
|
+
export type DataType = "init" | "delta" | "key";
|
|
5
|
+
export interface Mp4Data {
|
|
6
|
+
type: DataType;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
duration: number;
|
|
9
|
+
data: Uint8Array;
|
|
10
|
+
kind: "audio" | "video";
|
|
11
|
+
}
|
|
12
|
+
export declare class Mp4Container {
|
|
13
|
+
#private;
|
|
14
|
+
private props;
|
|
15
|
+
audioTrack?: number;
|
|
16
|
+
videoTrack?: number;
|
|
17
|
+
onData: Event<[Mp4Data]>;
|
|
18
|
+
constructor(props: {
|
|
19
|
+
track: {
|
|
20
|
+
audio: boolean;
|
|
21
|
+
video: boolean;
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
get tracksReady(): boolean;
|
|
25
|
+
write(frame: (DecoderConfig | EncodedChunk) & {
|
|
26
|
+
track: "video" | "audio";
|
|
27
|
+
}): void;
|
|
28
|
+
frameBuffer: (EncodedChunk & {
|
|
29
|
+
track: "video" | "audio";
|
|
30
|
+
})[];
|
|
31
|
+
private _enqueue;
|
|
32
|
+
}
|
|
33
|
+
export interface AudioDecoderConfig {
|
|
34
|
+
codec: string;
|
|
35
|
+
description?: ArrayBuffer | undefined;
|
|
36
|
+
numberOfChannels: number;
|
|
37
|
+
sampleRate: number;
|
|
38
|
+
}
|
|
39
|
+
export interface VideoDecoderConfig {
|
|
40
|
+
codec: string;
|
|
41
|
+
codedHeight?: number | undefined;
|
|
42
|
+
codedWidth?: number | undefined;
|
|
43
|
+
description?: ArrayBuffer | undefined;
|
|
44
|
+
displayAspectHeight?: number | undefined;
|
|
45
|
+
displayAspectWidth?: number | undefined;
|
|
46
|
+
optimizeForLatency?: boolean | undefined;
|
|
47
|
+
}
|
|
48
|
+
interface EncodedAudioChunk {
|
|
49
|
+
readonly byteLength: number;
|
|
50
|
+
readonly duration: number | null;
|
|
51
|
+
readonly timestamp: number;
|
|
52
|
+
readonly type: EncodedAudioChunkType;
|
|
53
|
+
copyTo(destination: ArrayBuffer): void;
|
|
54
|
+
}
|
|
55
|
+
type EncodedAudioChunkType = "delta" | "key";
|
|
56
|
+
interface EncodedVideoChunk {
|
|
57
|
+
readonly byteLength: number;
|
|
58
|
+
readonly duration: number | null;
|
|
59
|
+
readonly timestamp: number;
|
|
60
|
+
readonly type: EncodedVideoChunkType;
|
|
61
|
+
copyTo(destination: ArrayBuffer): void;
|
|
62
|
+
}
|
|
63
|
+
type EncodedVideoChunkType = "delta" | "key";
|
|
64
|
+
export declare const mp4SupportedCodecs: readonly ["avc1", "opus"];
|
|
65
|
+
export type Mp4SupportedCodec = typeof mp4SupportedCodecs[number];
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
26
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
27
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
28
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
29
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
30
|
+
};
|
|
31
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
32
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
33
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
34
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
35
|
+
};
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
var _Mp4Container_instances, _Mp4Container_mp4, _Mp4Container_audioFrame, _Mp4Container_videoFrame, _Mp4Container_audioSegment, _Mp4Container_videoSegment, _Mp4Container_init, _Mp4Container_enqueue;
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.mp4SupportedCodecs = exports.Mp4Container = void 0;
|
|
42
|
+
const rx_mini_1 = __importDefault(require("rx.mini"));
|
|
43
|
+
const MP4 = __importStar(require("./mp4box"));
|
|
44
|
+
class Mp4Container {
|
|
45
|
+
constructor(props) {
|
|
46
|
+
_Mp4Container_instances.add(this);
|
|
47
|
+
Object.defineProperty(this, "props", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
writable: true,
|
|
51
|
+
value: props
|
|
52
|
+
});
|
|
53
|
+
_Mp4Container_mp4.set(this, void 0);
|
|
54
|
+
_Mp4Container_audioFrame.set(this, void 0);
|
|
55
|
+
_Mp4Container_videoFrame.set(this, void 0); // 1 frame buffer
|
|
56
|
+
Object.defineProperty(this, "audioTrack", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "videoTrack", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
68
|
+
_Mp4Container_audioSegment.set(this, 0);
|
|
69
|
+
_Mp4Container_videoSegment.set(this, 0);
|
|
70
|
+
Object.defineProperty(this, "onData", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: new rx_mini_1.default()
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "frameBuffer", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: []
|
|
81
|
+
});
|
|
82
|
+
__classPrivateFieldSet(this, _Mp4Container_mp4, new MP4.ISOFile(), "f");
|
|
83
|
+
__classPrivateFieldGet(this, _Mp4Container_mp4, "f").init();
|
|
84
|
+
}
|
|
85
|
+
get tracksReady() {
|
|
86
|
+
let ready = true;
|
|
87
|
+
if (this.props.track.audio && !this.audioTrack) {
|
|
88
|
+
ready = false;
|
|
89
|
+
}
|
|
90
|
+
if (this.props.track.video && !this.videoTrack) {
|
|
91
|
+
ready = false;
|
|
92
|
+
}
|
|
93
|
+
return ready;
|
|
94
|
+
}
|
|
95
|
+
write(frame) {
|
|
96
|
+
if (isDecoderConfig(frame)) {
|
|
97
|
+
return __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_init).call(this, frame);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_enqueue).call(this, frame);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
_enqueue(frame) {
|
|
104
|
+
const track = frame.track === "audio" ? this.audioTrack : this.videoTrack;
|
|
105
|
+
if (!track) {
|
|
106
|
+
throw new Error("track missing");
|
|
107
|
+
}
|
|
108
|
+
// Check if we should create a new segment
|
|
109
|
+
if (frame.track === "video") {
|
|
110
|
+
if (frame.type == "key") {
|
|
111
|
+
__classPrivateFieldSet(this, _Mp4Container_videoSegment, __classPrivateFieldGet(this, _Mp4Container_videoSegment, "f") + 1, "f");
|
|
112
|
+
}
|
|
113
|
+
else if (__classPrivateFieldGet(this, _Mp4Container_videoSegment, "f") == 0) {
|
|
114
|
+
throw new Error("must start with keyframe");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
__classPrivateFieldSet(this, _Mp4Container_audioSegment, __classPrivateFieldGet(this, _Mp4Container_audioSegment, "f") + 1, "f");
|
|
119
|
+
}
|
|
120
|
+
// We need a one frame buffer to compute the duration
|
|
121
|
+
if (frame.track === "video") {
|
|
122
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_videoFrame, "f")) {
|
|
123
|
+
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_audioFrame, "f")) {
|
|
129
|
+
__classPrivateFieldSet(this, _Mp4Container_audioFrame, frame, "f");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const bufferFrame = frame.track === "video" ? __classPrivateFieldGet(this, _Mp4Container_videoFrame, "f") : __classPrivateFieldGet(this, _Mp4Container_audioFrame, "f");
|
|
134
|
+
if (!bufferFrame) {
|
|
135
|
+
throw new Error("bufferFrame missing");
|
|
136
|
+
}
|
|
137
|
+
const duration = frame.timestamp - bufferFrame.timestamp;
|
|
138
|
+
// TODO avoid this extra copy by writing to the mdat directly
|
|
139
|
+
// ...which means changing mp4box.js to take an offset instead of ArrayBuffer
|
|
140
|
+
const buffer = new Uint8Array(bufferFrame.byteLength);
|
|
141
|
+
bufferFrame.copyTo(buffer);
|
|
142
|
+
// Add the sample to the container
|
|
143
|
+
__classPrivateFieldGet(this, _Mp4Container_mp4, "f").addSample(track, buffer, {
|
|
144
|
+
duration,
|
|
145
|
+
dts: bufferFrame.timestamp,
|
|
146
|
+
cts: bufferFrame.timestamp,
|
|
147
|
+
is_sync: bufferFrame.type == "key",
|
|
148
|
+
});
|
|
149
|
+
const stream = new MP4.Stream(undefined, 0, MP4.Stream.BIG_ENDIAN);
|
|
150
|
+
// Moof and mdat atoms are written in pairs.
|
|
151
|
+
// TODO remove the moof/mdat from the Box to reclaim memory once everything works
|
|
152
|
+
for (;;) {
|
|
153
|
+
const moof = __classPrivateFieldGet(this, _Mp4Container_mp4, "f").moofs.shift();
|
|
154
|
+
const mdat = __classPrivateFieldGet(this, _Mp4Container_mp4, "f").mdats.shift();
|
|
155
|
+
if (!moof && !mdat)
|
|
156
|
+
break;
|
|
157
|
+
if (!moof)
|
|
158
|
+
throw new Error("moof missing");
|
|
159
|
+
if (!mdat)
|
|
160
|
+
throw new Error("mdat missing");
|
|
161
|
+
moof.write(stream);
|
|
162
|
+
mdat.write(stream);
|
|
163
|
+
}
|
|
164
|
+
// TODO avoid this extra copy by writing to the buffer provided in copyTo
|
|
165
|
+
const data = new Uint8Array(stream.buffer);
|
|
166
|
+
if (frame.track === "video") {
|
|
167
|
+
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
__classPrivateFieldSet(this, _Mp4Container_audioFrame, frame, "f");
|
|
171
|
+
}
|
|
172
|
+
const res = {
|
|
173
|
+
type: bufferFrame.type,
|
|
174
|
+
timestamp: bufferFrame.timestamp,
|
|
175
|
+
kind: frame.track,
|
|
176
|
+
duration,
|
|
177
|
+
data,
|
|
178
|
+
};
|
|
179
|
+
this.onData.execute(res);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.Mp4Container = Mp4Container;
|
|
183
|
+
_Mp4Container_mp4 = new WeakMap(), _Mp4Container_audioFrame = new WeakMap(), _Mp4Container_videoFrame = new WeakMap(), _Mp4Container_audioSegment = new WeakMap(), _Mp4Container_videoSegment = new WeakMap(), _Mp4Container_instances = new WeakSet(), _Mp4Container_init = function _Mp4Container_init(frame) {
|
|
184
|
+
let codec = frame.codec.substring(0, 4);
|
|
185
|
+
if (codec == "opus") {
|
|
186
|
+
codec = "Opus";
|
|
187
|
+
}
|
|
188
|
+
const options = {
|
|
189
|
+
type: codec,
|
|
190
|
+
timescale: 1000000,
|
|
191
|
+
};
|
|
192
|
+
if (isVideoConfig(frame)) {
|
|
193
|
+
options.width = frame.codedWidth;
|
|
194
|
+
options.height = frame.codedHeight;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
options.channel_count = frame.numberOfChannels;
|
|
198
|
+
options.samplerate = frame.sampleRate;
|
|
199
|
+
options.hdlr = "soun";
|
|
200
|
+
}
|
|
201
|
+
if (!frame.description)
|
|
202
|
+
throw new Error("missing frame description");
|
|
203
|
+
const desc = frame.description;
|
|
204
|
+
if (codec === "avc1") {
|
|
205
|
+
options.avcDecoderConfigRecord = desc;
|
|
206
|
+
}
|
|
207
|
+
else if (codec === "hev1") {
|
|
208
|
+
options.hevcDecoderConfigRecord = desc;
|
|
209
|
+
}
|
|
210
|
+
else if (codec === "Opus") {
|
|
211
|
+
// description is an identification header: https://datatracker.ietf.org/doc/html/rfc7845#section-5.1
|
|
212
|
+
// The first 8 bytes are the magic string "OpusHead", followed by what we actually want.
|
|
213
|
+
const dops = new MP4.BoxParser.dOpsBox();
|
|
214
|
+
// Annoyingly, the header is little endian while MP4 is big endian, so we have to parse.
|
|
215
|
+
dops.parse(new MP4.Stream(desc, 8, MP4.Stream.LITTLE_ENDIAN));
|
|
216
|
+
options.description = dops;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
throw new Error(`unsupported codec: ${codec}`);
|
|
220
|
+
}
|
|
221
|
+
const track = __classPrivateFieldGet(this, _Mp4Container_mp4, "f").addTrack(options);
|
|
222
|
+
if (track == undefined) {
|
|
223
|
+
throw new Error("failed to initialize MP4 track");
|
|
224
|
+
}
|
|
225
|
+
if (frame.track === "audio") {
|
|
226
|
+
this.audioTrack = track;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
this.videoTrack = track;
|
|
230
|
+
}
|
|
231
|
+
if (!this.tracksReady) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const buffer = MP4.ISOFile.writeInitializationSegment(__classPrivateFieldGet(this, _Mp4Container_mp4, "f").ftyp, __classPrivateFieldGet(this, _Mp4Container_mp4, "f").moov, 0, 0);
|
|
235
|
+
const data = new Uint8Array(buffer);
|
|
236
|
+
const res = {
|
|
237
|
+
type: "init",
|
|
238
|
+
timestamp: 0,
|
|
239
|
+
duration: 0,
|
|
240
|
+
data,
|
|
241
|
+
kind: frame.track,
|
|
242
|
+
};
|
|
243
|
+
this.onData.execute(res);
|
|
244
|
+
}, _Mp4Container_enqueue = function _Mp4Container_enqueue(frame) {
|
|
245
|
+
this.frameBuffer.push(frame);
|
|
246
|
+
if (!this.tracksReady) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
for (const frame of this.frameBuffer) {
|
|
250
|
+
this._enqueue(frame);
|
|
251
|
+
}
|
|
252
|
+
this.frameBuffer = [];
|
|
253
|
+
};
|
|
254
|
+
function isDecoderConfig(frame) {
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
256
|
+
return frame.codec !== undefined;
|
|
257
|
+
}
|
|
258
|
+
function isVideoConfig(frame) {
|
|
259
|
+
return frame.codedWidth !== undefined;
|
|
260
|
+
}
|
|
261
|
+
exports.mp4SupportedCodecs = ["avc1", "opus"];
|
|
262
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../../../../../rtp/src/container/mp4/container.ts"],"names":[],"mappings":"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","sourcesContent":["import Event from \"rx.mini\";\n\nimport * as MP4 from \"./mp4box\";\n\ntype DecoderConfig = AudioDecoderConfig | VideoDecoderConfig;\ntype EncodedChunk = EncodedAudioChunk | EncodedVideoChunk;\n\nexport type DataType = \"init\" | \"delta\" | \"key\";\n\nexport interface Mp4Data {\n type: DataType;\n timestamp: number;\n duration: number;\n data: Uint8Array;\n kind: \"audio\" | \"video\";\n}\n\nexport class Mp4Container {\n #mp4: MP4.ISOFile;\n #audioFrame?: EncodedAudioChunk | EncodedVideoChunk;\n #videoFrame?: EncodedAudioChunk | EncodedVideoChunk; // 1 frame buffer\n audioTrack?: number;\n videoTrack?: number;\n #audioSegment = 0;\n #videoSegment = 0;\n onData = new Event<[Mp4Data]>();\n\n constructor(\n private props: {\n track: { audio: boolean; video: boolean };\n }\n ) {\n this.#mp4 = new MP4.ISOFile();\n this.#mp4.init();\n }\n\n get tracksReady() {\n let ready = true;\n if (this.props.track.audio && !this.audioTrack) {\n ready = false;\n }\n if (this.props.track.video && !this.videoTrack) {\n ready = false;\n }\n return ready;\n }\n\n write(\n frame: (DecoderConfig | EncodedChunk) & {\n track: \"video\" | \"audio\";\n }\n ) {\n if (isDecoderConfig(frame)) {\n return this.#init(frame);\n } else {\n return this.#enqueue(frame);\n }\n }\n\n #init(\n frame: DecoderConfig & {\n track: \"video\" | \"audio\";\n }\n ) {\n let codec = frame.codec.substring(0, 4);\n if (codec == \"opus\") {\n codec = \"Opus\";\n }\n\n const options: MP4.TrackOptions = {\n type: codec,\n timescale: 1_000_000,\n };\n\n if (isVideoConfig(frame)) {\n options.width = frame.codedWidth;\n options.height = frame.codedHeight;\n } else {\n options.channel_count = frame.numberOfChannels;\n options.samplerate = frame.sampleRate;\n options.hdlr = \"soun\";\n }\n\n if (!frame.description) throw new Error(\"missing frame description\");\n const desc = frame.description as ArrayBufferLike;\n\n if (codec === \"avc1\") {\n options.avcDecoderConfigRecord = desc;\n } else if (codec === \"hev1\") {\n options.hevcDecoderConfigRecord = desc;\n } else if (codec === \"Opus\") {\n // description is an identification header: https://datatracker.ietf.org/doc/html/rfc7845#section-5.1\n // The first 8 bytes are the magic string \"OpusHead\", followed by what we actually want.\n const dops = new MP4.BoxParser.dOpsBox();\n\n // Annoyingly, the header is little endian while MP4 is big endian, so we have to parse.\n dops.parse(new MP4.Stream(desc, 8, MP4.Stream.LITTLE_ENDIAN));\n\n options.description = dops;\n } else {\n throw new Error(`unsupported codec: ${codec}`);\n }\n\n const track = this.#mp4.addTrack(options);\n if (track == undefined) {\n throw new Error(\"failed to initialize MP4 track\");\n }\n if (frame.track === \"audio\") {\n this.audioTrack = track;\n } else {\n this.videoTrack = track;\n }\n\n if (!this.tracksReady) {\n return;\n }\n\n const buffer = MP4.ISOFile.writeInitializationSegment(\n this.#mp4.ftyp!,\n this.#mp4.moov!,\n 0,\n 0\n );\n const data = new Uint8Array(buffer);\n const res = {\n type: \"init\",\n timestamp: 0,\n duration: 0,\n data,\n kind: frame.track,\n } as const;\n this.onData.execute(res);\n }\n\n frameBuffer: (EncodedChunk & {\n track: \"video\" | \"audio\";\n })[] = [];\n\n #enqueue(\n frame: EncodedChunk & {\n track: \"video\" | \"audio\";\n }\n ) {\n this.frameBuffer.push(frame);\n if (!this.tracksReady) {\n return;\n }\n for (const frame of this.frameBuffer) {\n this._enqueue(frame);\n }\n this.frameBuffer = [];\n }\n\n private _enqueue(\n frame: EncodedChunk & {\n track: \"video\" | \"audio\";\n }\n ) {\n const track = frame.track === \"audio\" ? this.audioTrack : this.videoTrack;\n if (!track) {\n throw new Error(\"track missing\");\n }\n\n // Check if we should create a new segment\n if (frame.track === \"video\") {\n if (frame.type == \"key\") {\n this.#videoSegment += 1;\n } else if (this.#videoSegment == 0) {\n throw new Error(\"must start with keyframe\");\n }\n } else {\n this.#audioSegment += 1;\n }\n\n // We need a one frame buffer to compute the duration\n if (frame.track === \"video\") {\n if (!this.#videoFrame) {\n this.#videoFrame = frame;\n return;\n }\n } else {\n if (!this.#audioFrame) {\n this.#audioFrame = frame;\n return;\n }\n }\n\n const bufferFrame =\n frame.track === \"video\" ? this.#videoFrame : this.#audioFrame;\n\n if (!bufferFrame) {\n throw new Error(\"bufferFrame missing\");\n }\n\n const duration = frame.timestamp - bufferFrame.timestamp;\n\n // TODO avoid this extra copy by writing to the mdat directly\n // ...which means changing mp4box.js to take an offset instead of ArrayBuffer\n const buffer = new Uint8Array(bufferFrame.byteLength);\n bufferFrame.copyTo(buffer);\n\n // Add the sample to the container\n this.#mp4.addSample(track, buffer, {\n duration,\n dts: bufferFrame.timestamp,\n cts: bufferFrame.timestamp,\n is_sync: bufferFrame.type == \"key\",\n });\n\n const stream = new MP4.Stream(undefined, 0, MP4.Stream.BIG_ENDIAN);\n\n // Moof and mdat atoms are written in pairs.\n // TODO remove the moof/mdat from the Box to reclaim memory once everything works\n for (;;) {\n const moof = this.#mp4.moofs.shift();\n const mdat = this.#mp4.mdats.shift();\n\n if (!moof && !mdat) break;\n if (!moof) throw new Error(\"moof missing\");\n if (!mdat) throw new Error(\"mdat missing\");\n\n moof.write(stream);\n mdat.write(stream);\n }\n\n // TODO avoid this extra copy by writing to the buffer provided in copyTo\n const data = new Uint8Array(stream.buffer);\n\n if (frame.track === \"video\") {\n this.#videoFrame = frame;\n } else {\n this.#audioFrame = frame;\n }\n\n const res = {\n type: bufferFrame.type,\n timestamp: bufferFrame.timestamp,\n kind: frame.track,\n duration,\n data,\n };\n this.onData.execute(res);\n }\n\n /* TODO flush the last frame\n\t#flush(controller: TransformStreamDefaultController<Chunk>) {\n\t\tif (this.#frame) {\n\t\t\t// TODO guess the duration\n\t\t\tthis.#enqueue(this.#frame, 0, controller)\n\t\t}\n\t}\n\t*/\n}\n\nfunction isDecoderConfig(\n frame: DecoderConfig | EncodedChunk\n): frame is DecoderConfig {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (frame as DecoderConfig).codec !== undefined;\n}\n\nfunction isVideoConfig(frame: DecoderConfig): frame is VideoDecoderConfig {\n return (frame as VideoDecoderConfig).codedWidth !== undefined;\n}\n\nexport interface AudioDecoderConfig {\n codec: string;\n description?: ArrayBuffer | undefined;\n numberOfChannels: number;\n sampleRate: number;\n}\n\nexport interface VideoDecoderConfig {\n codec: string;\n codedHeight?: number | undefined;\n codedWidth?: number | undefined;\n description?: ArrayBuffer | undefined;\n displayAspectHeight?: number | undefined;\n displayAspectWidth?: number | undefined;\n optimizeForLatency?: boolean | undefined;\n}\n\ninterface EncodedAudioChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedAudioChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedAudioChunkType = \"delta\" | \"key\";\n\ninterface EncodedVideoChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedVideoChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedVideoChunkType = \"delta\" | \"key\";\n\nexport const mp4SupportedCodecs = [\"avc1\", \"opus\"] as const;\nexport type Mp4SupportedCodec = typeof mp4SupportedCodecs[number];\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare class ExpGolomb {
|
|
2
|
+
private uint8array;
|
|
3
|
+
TAG: string;
|
|
4
|
+
_buffer: Uint8Array;
|
|
5
|
+
_buffer_index: number;
|
|
6
|
+
_total_bytes: number;
|
|
7
|
+
_total_bits: number;
|
|
8
|
+
_current_word: number;
|
|
9
|
+
_current_word_bits_left: number;
|
|
10
|
+
constructor(uint8array: Uint8Array);
|
|
11
|
+
destroy(): void;
|
|
12
|
+
_fillCurrentWord(): void;
|
|
13
|
+
readBits(bits: any): number;
|
|
14
|
+
readBool(): boolean;
|
|
15
|
+
readByte(): number;
|
|
16
|
+
_skipLeadingZero(): any;
|
|
17
|
+
readUEG(): number;
|
|
18
|
+
readSEG(): number;
|
|
19
|
+
}
|
|
20
|
+
export default ExpGolomb;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2016 Bilibili. All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* @author zheng qian <xqq@xqq.im>
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
// Exponential-Golomb buffer decoder
|
|
21
|
+
class ExpGolomb {
|
|
22
|
+
constructor(uint8array) {
|
|
23
|
+
Object.defineProperty(this, "uint8array", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: uint8array
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "TAG", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: "ExpGolomb"
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "_buffer", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
Object.defineProperty(this, "_buffer_index", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: 0
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(this, "_total_bytes", {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
configurable: true,
|
|
50
|
+
writable: true,
|
|
51
|
+
value: void 0
|
|
52
|
+
});
|
|
53
|
+
Object.defineProperty(this, "_total_bits", {
|
|
54
|
+
enumerable: true,
|
|
55
|
+
configurable: true,
|
|
56
|
+
writable: true,
|
|
57
|
+
value: void 0
|
|
58
|
+
});
|
|
59
|
+
Object.defineProperty(this, "_current_word", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: 0
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(this, "_current_word_bits_left", {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
value: 0
|
|
70
|
+
});
|
|
71
|
+
this._buffer = uint8array;
|
|
72
|
+
this._total_bytes = uint8array.byteLength;
|
|
73
|
+
this._total_bits = uint8array.byteLength * 8;
|
|
74
|
+
}
|
|
75
|
+
destroy() {
|
|
76
|
+
this._buffer = null;
|
|
77
|
+
}
|
|
78
|
+
_fillCurrentWord() {
|
|
79
|
+
const buffer_bytes_left = this._total_bytes - this._buffer_index;
|
|
80
|
+
if (buffer_bytes_left <= 0)
|
|
81
|
+
throw new Error("ExpGolomb: _fillCurrentWord() but no bytes available");
|
|
82
|
+
const bytes_read = Math.min(4, buffer_bytes_left);
|
|
83
|
+
const word = new Uint8Array(4);
|
|
84
|
+
word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read));
|
|
85
|
+
this._current_word = new DataView(word.buffer).getUint32(0, false);
|
|
86
|
+
this._buffer_index += bytes_read;
|
|
87
|
+
this._current_word_bits_left = bytes_read * 8;
|
|
88
|
+
}
|
|
89
|
+
readBits(bits) {
|
|
90
|
+
if (bits > 32)
|
|
91
|
+
throw new Error("ExpGolomb: readBits() bits exceeded max 32bits!");
|
|
92
|
+
if (bits <= this._current_word_bits_left) {
|
|
93
|
+
const result = this._current_word >>> (32 - bits);
|
|
94
|
+
this._current_word <<= bits;
|
|
95
|
+
this._current_word_bits_left -= bits;
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
let result = this._current_word_bits_left ? this._current_word : 0;
|
|
99
|
+
result = result >>> (32 - this._current_word_bits_left);
|
|
100
|
+
const bits_need_left = bits - this._current_word_bits_left;
|
|
101
|
+
this._fillCurrentWord();
|
|
102
|
+
const bits_read_next = Math.min(bits_need_left, this._current_word_bits_left);
|
|
103
|
+
const result2 = this._current_word >>> (32 - bits_read_next);
|
|
104
|
+
this._current_word <<= bits_read_next;
|
|
105
|
+
this._current_word_bits_left -= bits_read_next;
|
|
106
|
+
result = (result << bits_read_next) | result2;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
readBool() {
|
|
110
|
+
return this.readBits(1) === 1;
|
|
111
|
+
}
|
|
112
|
+
readByte() {
|
|
113
|
+
return this.readBits(8);
|
|
114
|
+
}
|
|
115
|
+
_skipLeadingZero() {
|
|
116
|
+
let zero_count;
|
|
117
|
+
for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) {
|
|
118
|
+
if (0 !== (this._current_word & (0x80000000 >>> zero_count))) {
|
|
119
|
+
this._current_word <<= zero_count;
|
|
120
|
+
this._current_word_bits_left -= zero_count;
|
|
121
|
+
return zero_count;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this._fillCurrentWord();
|
|
125
|
+
return zero_count + this._skipLeadingZero();
|
|
126
|
+
}
|
|
127
|
+
readUEG() {
|
|
128
|
+
// unsigned exponential golomb
|
|
129
|
+
const leading_zeros = this._skipLeadingZero();
|
|
130
|
+
return this.readBits(leading_zeros + 1) - 1;
|
|
131
|
+
}
|
|
132
|
+
readSEG() {
|
|
133
|
+
// signed exponential golomb
|
|
134
|
+
const value = this.readUEG();
|
|
135
|
+
if (value & 0x01) {
|
|
136
|
+
return (value + 1) >>> 1;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
return -1 * (value >>> 1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.default = ExpGolomb;
|
|
144
|
+
//# sourceMappingURL=exp-golomb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exp-golomb.js","sourceRoot":"","sources":["../../../../../../rtp/src/container/mp4/exp-golomb.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;AAEH,oCAAoC;AACpC,MAAM,SAAS;IASb,YAAoB,UAAsB;;;;;mBAAtB;;QARpB;;;;mBAAM,WAAW;WAAC;QAClB;;;;;WAAoB;QACpB;;;;mBAAgB,CAAC;WAAC;QAClB;;;;;WAAqB;QACrB;;;;;WAAoB;QACpB;;;;mBAAgB,CAAC;WAAC;QAClB;;;;mBAA0B,CAAC;WAAC;QAG1B,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC;QAC1B,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,IAAI,CAAC,OAAO,GAAG,IAAW,CAAC;IAC7B,CAAC;IAED,gBAAgB;QACd,MAAM,iBAAiB,GAAG,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC;QACjE,IAAI,iBAAiB,IAAI,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAE1E,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG,CACN,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,CAC3E,CAAC;QACF,IAAI,CAAC,aAAa,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAEnE,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC;QACjC,IAAI,CAAC,uBAAuB,GAAG,UAAU,GAAG,CAAC,CAAC;IAChD,CAAC;IAED,QAAQ,CAAC,IAAI;QACX,IAAI,IAAI,GAAG,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QAErE,IAAI,IAAI,IAAI,IAAI,CAAC,uBAAuB,EAAE;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC;YAC5B,IAAI,CAAC,uBAAuB,IAAI,IAAI,CAAC;YACrC,OAAO,MAAM,CAAC;SACf;QAED,IAAI,MAAM,GAAG,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACxD,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC,uBAAuB,CAAC;QAE3D,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAC7B,cAAc,EACd,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,KAAK,CAAC,EAAE,GAAG,cAAc,CAAC,CAAC;QAC7D,IAAI,CAAC,aAAa,KAAK,cAAc,CAAC;QACtC,IAAI,CAAC,uBAAuB,IAAI,cAAc,CAAC;QAE/C,MAAM,GAAG,CAAC,MAAM,IAAI,cAAc,CAAC,GAAG,OAAO,CAAC;QAC9C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,gBAAgB;QACd,IAAI,UAAU,CAAC;QACf,KACE,UAAU,GAAG,CAAC,EACd,UAAU,GAAG,IAAI,CAAC,uBAAuB,EACzC,UAAU,EAAE,EACZ;YACA,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,EAAE;gBAC5D,IAAI,CAAC,aAAa,KAAK,UAAU,CAAC;gBAClC,IAAI,CAAC,uBAAuB,IAAI,UAAU,CAAC;gBAC3C,OAAO,UAAU,CAAC;aACnB;SACF;QACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,OAAO,UAAU,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,8BAA8B;QAC9B,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,4BAA4B;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,GAAG,IAAI,EAAE;YAChB,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;SAC1B;aAAM;YACL,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;SAC3B;IACH,CAAC;CACF;AAED,kBAAe,SAAS,CAAC","sourcesContent":["/*\n * Copyright (C) 2016 Bilibili. All Rights Reserved.\n *\n * @author zheng qian <xqq@xqq.im>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Exponential-Golomb buffer decoder\nclass ExpGolomb {\n TAG = \"ExpGolomb\";\n _buffer: Uint8Array;\n _buffer_index = 0;\n _total_bytes: number;\n _total_bits: number;\n _current_word = 0;\n _current_word_bits_left = 0;\n\n constructor(private uint8array: Uint8Array) {\n this._buffer = uint8array;\n this._total_bytes = uint8array.byteLength;\n this._total_bits = uint8array.byteLength * 8;\n }\n\n destroy() {\n this._buffer = null as any;\n }\n\n _fillCurrentWord() {\n const buffer_bytes_left = this._total_bytes - this._buffer_index;\n if (buffer_bytes_left <= 0)\n throw new Error(\"ExpGolomb: _fillCurrentWord() but no bytes available\");\n\n const bytes_read = Math.min(4, buffer_bytes_left);\n const word = new Uint8Array(4);\n word.set(\n this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read)\n );\n this._current_word = new DataView(word.buffer).getUint32(0, false);\n\n this._buffer_index += bytes_read;\n this._current_word_bits_left = bytes_read * 8;\n }\n\n readBits(bits) {\n if (bits > 32)\n throw new Error(\"ExpGolomb: readBits() bits exceeded max 32bits!\");\n\n if (bits <= this._current_word_bits_left) {\n const result = this._current_word >>> (32 - bits);\n this._current_word <<= bits;\n this._current_word_bits_left -= bits;\n return result;\n }\n\n let result = this._current_word_bits_left ? this._current_word : 0;\n result = result >>> (32 - this._current_word_bits_left);\n const bits_need_left = bits - this._current_word_bits_left;\n\n this._fillCurrentWord();\n const bits_read_next = Math.min(\n bits_need_left,\n this._current_word_bits_left\n );\n\n const result2 = this._current_word >>> (32 - bits_read_next);\n this._current_word <<= bits_read_next;\n this._current_word_bits_left -= bits_read_next;\n\n result = (result << bits_read_next) | result2;\n return result;\n }\n\n readBool() {\n return this.readBits(1) === 1;\n }\n\n readByte() {\n return this.readBits(8);\n }\n\n _skipLeadingZero() {\n let zero_count;\n for (\n zero_count = 0;\n zero_count < this._current_word_bits_left;\n zero_count++\n ) {\n if (0 !== (this._current_word & (0x80000000 >>> zero_count))) {\n this._current_word <<= zero_count;\n this._current_word_bits_left -= zero_count;\n return zero_count;\n }\n }\n this._fillCurrentWord();\n return zero_count + this._skipLeadingZero();\n }\n\n readUEG() {\n // unsigned exponential golomb\n const leading_zeros = this._skipLeadingZero();\n return this.readBits(leading_zeros + 1) - 1;\n }\n\n readSEG() {\n // signed exponential golomb\n const value = this.readUEG();\n if (value & 0x01) {\n return (value + 1) >>> 1;\n } else {\n return -1 * (value >>> 1);\n }\n }\n}\n\nexport default ExpGolomb;\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export declare enum H264NaluType {
|
|
3
|
+
kUnspecified = 0,
|
|
4
|
+
kSliceNonIDR = 1,
|
|
5
|
+
kSliceDPA = 2,
|
|
6
|
+
kSliceDPB = 3,
|
|
7
|
+
kSliceDPC = 4,
|
|
8
|
+
kSliceIDR = 5,
|
|
9
|
+
kSliceSEI = 6,
|
|
10
|
+
kSliceSPS = 7,
|
|
11
|
+
kSlicePPS = 8,
|
|
12
|
+
kSliceAUD = 9,
|
|
13
|
+
kEndOfSequence = 10,
|
|
14
|
+
kEndOfStream = 11,
|
|
15
|
+
kFiller = 12,
|
|
16
|
+
kSPSExt = 13,
|
|
17
|
+
kReserved0 = 14
|
|
18
|
+
}
|
|
19
|
+
export declare class H264NaluPayload {
|
|
20
|
+
type: H264NaluType;
|
|
21
|
+
data: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
export declare class H264NaluAVC1 {
|
|
24
|
+
type: H264NaluType;
|
|
25
|
+
data: Uint8Array;
|
|
26
|
+
constructor(nalu: H264NaluPayload);
|
|
27
|
+
}
|
|
28
|
+
export declare class H264AnnexBParser {
|
|
29
|
+
private readonly TAG;
|
|
30
|
+
private data_;
|
|
31
|
+
private current_startcode_offset_;
|
|
32
|
+
private eof_flag_;
|
|
33
|
+
constructor(data: Uint8Array);
|
|
34
|
+
private findNextStartCodeOffset;
|
|
35
|
+
readNextNaluPayload(): H264NaluPayload | null;
|
|
36
|
+
}
|
|
37
|
+
export declare class AVCDecoderConfigurationRecord {
|
|
38
|
+
private data;
|
|
39
|
+
constructor(sps: Uint8Array, pps: Uint8Array, sps_details: any);
|
|
40
|
+
getData(): Uint8Array;
|
|
41
|
+
}
|
|
42
|
+
export declare function annexb2avcc(data: Buffer): Uint8Array;
|