@danything/arib 2.0.0 → 2.2.0
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/README.md +40 -3
- package/dist/b24caption.js +4 -1
- package/dist/bits.d.ts +25 -0
- package/dist/bits.js +66 -0
- package/dist/eit.d.ts +14 -2
- package/dist/eit.js +56 -33
- package/dist/mh-eit.d.ts +43 -0
- package/dist/mh-eit.js +275 -0
- package/dist/mmt-media.d.ts +168 -0
- package/dist/mmt-media.js +536 -0
- package/dist/mmt-service-filter.d.ts +44 -0
- package/dist/mmt-service-filter.js +165 -0
- package/dist/mmt-si.d.ts +183 -0
- package/dist/mmt-si.js +456 -0
- package/dist/mmtp.d.ts +84 -0
- package/dist/mmtp.js +232 -0
- package/dist/psi.d.ts +20 -2
- package/dist/psi.js +41 -12
- package/dist/synth-mmt-av.d.ts +83 -0
- package/dist/synth-mmt-av.js +336 -0
- package/dist/synth-mmt.d.ts +176 -0
- package/dist/synth-mmt.js +476 -0
- package/dist/synth.d.ts +4 -0
- package/dist/synth.js +2 -2
- package/dist/tlv-ip.d.ts +44 -0
- package/dist/tlv-ip.js +118 -0
- package/dist/tlv.d.ts +43 -0
- package/dist/tlv.js +119 -0
- package/dist/ttml.d.ts +25 -0
- package/dist/ttml.js +455 -0
- package/package.json +5 -3
package/dist/mh-eit.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 4K の放送 (MMT/TLV) の番組表 MH-EIT (ARIB STD-B60) を、TS の番組表と同じ `EitEvent` に読む。
|
|
3
|
+
*
|
|
4
|
+
* 並びは TS の EIT と同じで、違うのは次の所 (実機のケーブルの BS4K で確かめた)。
|
|
5
|
+
*
|
|
6
|
+
* - TS の transport_stream_id の位置に tlv_stream_id が入る。`EitEvent.transportStreamId` にはそれを入れる
|
|
7
|
+
* - table_id は p/f が 0x8B、予定の基本が 0x8C から、詳細が 0x94 から。**TS の番号 (0x4E・0x50・0x58) に直して返す**
|
|
8
|
+
* ので、揃ったかの数え方 (`ScheduleProgress`) と混ぜ方 (`EpgReader`) をそのまま使える
|
|
9
|
+
* - 記述子のタグが 2 バイトで (`mmt-si.ts` の `mmtDescriptors`)、文字は UTF-8。番組名の「🈑」なども Unicode の文字で来る
|
|
10
|
+
*
|
|
11
|
+
* 番組の記述子で読むのは、MH-短形式イベント (0xF001)・MH-拡張形式イベント (0xF002)・MH-コンテント (0x8012)・
|
|
12
|
+
* 映像コンポーネント (0x8010)・MH-音声コンポーネント (0x8014)・コンテンツコピー制御 (0x8038)。
|
|
13
|
+
* シリーズの記述子は来ていないので読まない (`series` を付けない。「この表は言っていない」)
|
|
14
|
+
*/
|
|
15
|
+
import { ascii, joinBytes, u16 } from "./bytes.js";
|
|
16
|
+
import { EpgReader, parseBcdDuration, parseMjdTime, PresentFollowing, } from "./eit.js";
|
|
17
|
+
import { mmtDescriptors, MmtTableStream, mmtText } from "./mmt-si.js";
|
|
18
|
+
import { isCopyNever } from "./psi.js";
|
|
19
|
+
const MH_PF_ACTUAL = 0x8b;
|
|
20
|
+
const MH_SCHEDULE_MIN = 0x8c;
|
|
21
|
+
const MH_SCHEDULE_MAX = 0x9b;
|
|
22
|
+
/** TS の EIT の番号 (p/f 0x4E、予定 0x50〜0x5F) */
|
|
23
|
+
const TS_PF_ACTUAL = 0x4e;
|
|
24
|
+
const TS_SCHEDULE_MIN = 0x50;
|
|
25
|
+
/** TS の詳細の表の始まり。これより後ろの表は基本の記述子 (コピー制御など) を載せない */
|
|
26
|
+
const TS_SCHEDULE_EXTENDED = 0x58;
|
|
27
|
+
const DESC_SHORT_EVENT = 0xf001;
|
|
28
|
+
const DESC_EXTENDED_EVENT = 0xf002;
|
|
29
|
+
const DESC_CONTENT = 0x8012;
|
|
30
|
+
const DESC_VIDEO_COMPONENT = 0x8010;
|
|
31
|
+
const DESC_AUDIO_COMPONENT = 0x8014;
|
|
32
|
+
const DESC_CONTENT_COPY_CONTROL = 0x8038;
|
|
33
|
+
/** MH-EIT の table_id を TS の EIT の番号に。番組表でなければ null */
|
|
34
|
+
function tsTableId(tableId) {
|
|
35
|
+
if (tableId === MH_PF_ACTUAL)
|
|
36
|
+
return TS_PF_ACTUAL;
|
|
37
|
+
if (tableId >= MH_SCHEDULE_MIN && tableId <= MH_SCHEDULE_MAX)
|
|
38
|
+
return TS_SCHEDULE_MIN + (tableId - MH_SCHEDULE_MIN);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/** video_resolution (4 ビット) の走査線の数。0 は未定義 */
|
|
42
|
+
const LINES = {
|
|
43
|
+
1: 180,
|
|
44
|
+
2: 240,
|
|
45
|
+
3: 480,
|
|
46
|
+
4: 720,
|
|
47
|
+
5: 1080,
|
|
48
|
+
6: 2160,
|
|
49
|
+
7: 4320,
|
|
50
|
+
};
|
|
51
|
+
/** sampling_rate (3 ビット)。TS の音声コンポーネント記述子と同じ符号 */
|
|
52
|
+
const SAMPLING_RATE = {
|
|
53
|
+
1: 16000,
|
|
54
|
+
2: 22050,
|
|
55
|
+
3: 24000,
|
|
56
|
+
5: 32000,
|
|
57
|
+
6: 44100,
|
|
58
|
+
7: 48000,
|
|
59
|
+
};
|
|
60
|
+
const lang = (data, at) => ascii(data, at, 3).toLowerCase();
|
|
61
|
+
/**
|
|
62
|
+
* 番組の記述子を読んで埋める。**拡張形式イベントは項目ごとに、バイトのまま繋いでから文字にする。**
|
|
63
|
+
* UTF-8 の 1 文字が記述子の切れ目をまたぐことがあり、部分ごとに文字にすると、またいだ字が U+FFFD になる。
|
|
64
|
+
* 見出しが空の項目は直前の項目の続き (TS の `eit.ts` と同じ決まり)
|
|
65
|
+
*/
|
|
66
|
+
function readDescriptors(event, body) {
|
|
67
|
+
const pending = new Map();
|
|
68
|
+
let lastHeading = "";
|
|
69
|
+
for (const [tag, data] of mmtDescriptors(body)) {
|
|
70
|
+
switch (tag) {
|
|
71
|
+
case DESC_SHORT_EVENT: {
|
|
72
|
+
// 言語 (3)・番組名の長さ (1)・番組名・説明の長さ (2)・説明
|
|
73
|
+
if (data.length < 4)
|
|
74
|
+
break;
|
|
75
|
+
const textAt = 4 + data[3];
|
|
76
|
+
event.name = mmtText(data.subarray(4, textAt));
|
|
77
|
+
if (textAt + 2 > data.length)
|
|
78
|
+
break;
|
|
79
|
+
event.description = mmtText(data.subarray(textAt + 2, textAt + 2 + u16(data, textAt)));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case DESC_EXTENDED_EVENT: {
|
|
83
|
+
// 番号 (1)・言語 (3)・項目の長さ (2)。項目は見出し (長さ 1 バイト) と本文 (長さ 2 バイト) の組
|
|
84
|
+
if (data.length < 6)
|
|
85
|
+
break;
|
|
86
|
+
const itemsEnd = Math.min(data.length, 6 + u16(data, 4));
|
|
87
|
+
let at = 6;
|
|
88
|
+
while (at + 1 <= itemsEnd) {
|
|
89
|
+
const headingEnd = at + 1 + data[at];
|
|
90
|
+
if (headingEnd + 2 > itemsEnd)
|
|
91
|
+
break;
|
|
92
|
+
const heading = mmtText(data.subarray(at + 1, headingEnd));
|
|
93
|
+
const textEnd = headingEnd + 2 + u16(data, headingEnd);
|
|
94
|
+
const text = data.subarray(headingEnd + 2, textEnd);
|
|
95
|
+
at = textEnd;
|
|
96
|
+
if (heading === "") {
|
|
97
|
+
if (lastHeading === "")
|
|
98
|
+
continue;
|
|
99
|
+
pending.get(lastHeading).at(-1).push(text);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const items = pending.get(heading);
|
|
103
|
+
if (items === undefined)
|
|
104
|
+
pending.set(heading, [[text]]);
|
|
105
|
+
else
|
|
106
|
+
items.push([text]);
|
|
107
|
+
lastHeading = heading;
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case DESC_CONTENT: {
|
|
112
|
+
for (let at = 0; at + 2 <= data.length; at += 2)
|
|
113
|
+
event.genres.push({ lv1: data[at] >> 4, lv2: data[at] & 0x0f });
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case DESC_VIDEO_COMPONENT: {
|
|
117
|
+
// video_resolution (4)・アスペクト比 (4)、走査 (1: プログレッシブ)・予約 (2)・コマの速さ (5)
|
|
118
|
+
if (data.length < 2)
|
|
119
|
+
break;
|
|
120
|
+
const lines = LINES[data[0] >> 4];
|
|
121
|
+
const progressive = (data[1] & 0x80) !== 0;
|
|
122
|
+
// 符号化の方式はこの記述子に無い (MPT のアセットの型で分かる)
|
|
123
|
+
event.video = {
|
|
124
|
+
type: null,
|
|
125
|
+
resolution: lines === undefined ? null : `${lines}${progressive ? "p" : "i"}`,
|
|
126
|
+
};
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
/*
|
|
130
|
+
* MH-音声コンポーネント記述子。TS の音声コンポーネント記述子と比べて、component_tag が 2 バイトになっている。
|
|
131
|
+
*
|
|
132
|
+
* [0] reserved(4) stream_content(4)
|
|
133
|
+
* [1] component_type
|
|
134
|
+
* [2..3] component_tag
|
|
135
|
+
* [4] stream_type
|
|
136
|
+
* [5] simulcast_group_tag
|
|
137
|
+
* [6] 多言語(1) 主音声(1) 品質(2) 標本化周波数(3) reserved(1)
|
|
138
|
+
* [7..9] ISO 639-2
|
|
139
|
+
* [10..12] ISO 639-2 (多言語のときだけ)
|
|
140
|
+
* [残り] text_char (UTF-8)
|
|
141
|
+
*/
|
|
142
|
+
case DESC_AUDIO_COMPONENT: {
|
|
143
|
+
if (data.length < 10)
|
|
144
|
+
break;
|
|
145
|
+
const flags = data[6];
|
|
146
|
+
const multiLingual = (flags & 0x80) !== 0;
|
|
147
|
+
const langs = [lang(data, 7)];
|
|
148
|
+
if (multiLingual)
|
|
149
|
+
langs.push(lang(data, 10));
|
|
150
|
+
const text = mmtText(data.subarray(multiLingual ? 13 : 10)).trim();
|
|
151
|
+
const samplingRate = SAMPLING_RATE[(flags >> 1) & 0x07];
|
|
152
|
+
event.audios.push({
|
|
153
|
+
componentType: data[1],
|
|
154
|
+
langs,
|
|
155
|
+
...(samplingRate === undefined ? {} : { samplingRate }),
|
|
156
|
+
...(text === "" ? {} : { text }),
|
|
157
|
+
main: (flags & 0x40) !== 0,
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case DESC_CONTENT_COPY_CONTROL: {
|
|
162
|
+
// 先頭 2 ビットが digital_recording_control_data で、TS のデジタルコピー制御記述子と同じ符号
|
|
163
|
+
event.copyNever = isCopyNever(data);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
default:
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const [heading, items] of pending)
|
|
171
|
+
event.extended[heading] = items
|
|
172
|
+
.map((parts) => mmtText(joinBytes(parts)))
|
|
173
|
+
.join("");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* MH-EIT のセクションを 1 本読む。table_id は TS の番号に直す。番組表でなければ null。
|
|
177
|
+
* CRC は読む前に確かめてある (`MmtTableStream`) ので、長さの合わない番組の手前まで返す
|
|
178
|
+
*/
|
|
179
|
+
export function parseMhEit(section) {
|
|
180
|
+
if (section.length < 18)
|
|
181
|
+
return null;
|
|
182
|
+
const tableId = tsTableId(section[0]);
|
|
183
|
+
if (tableId === null)
|
|
184
|
+
return null;
|
|
185
|
+
const extended = tableId >= TS_SCHEDULE_EXTENDED;
|
|
186
|
+
const lastTableId = tsTableId(section[13]) ?? tableId;
|
|
187
|
+
const result = {
|
|
188
|
+
tableId,
|
|
189
|
+
serviceId: u16(section, 3),
|
|
190
|
+
version: (section[5] >> 1) & 0x1f,
|
|
191
|
+
sectionNumber: section[6],
|
|
192
|
+
lastSectionNumber: section[7],
|
|
193
|
+
transportStreamId: u16(section, 8),
|
|
194
|
+
originalNetworkId: u16(section, 10),
|
|
195
|
+
segmentLastSectionNumber: section[12],
|
|
196
|
+
lastTableId,
|
|
197
|
+
events: [],
|
|
198
|
+
};
|
|
199
|
+
let at = 14;
|
|
200
|
+
const end = section.length - 4;
|
|
201
|
+
while (at + 12 <= end) {
|
|
202
|
+
const descriptorsLength = u16(section, at + 10) & 0x0fff;
|
|
203
|
+
const body = section.subarray(at + 12, at + 12 + descriptorsLength);
|
|
204
|
+
const event = {
|
|
205
|
+
serviceId: result.serviceId,
|
|
206
|
+
transportStreamId: result.transportStreamId,
|
|
207
|
+
originalNetworkId: result.originalNetworkId,
|
|
208
|
+
eventId: u16(section, at),
|
|
209
|
+
startAt: parseMjdTime(section, at + 2),
|
|
210
|
+
duration: parseBcdDuration(section, at + 7),
|
|
211
|
+
runningStatus: (section[at + 10] >> 5) & 0x07,
|
|
212
|
+
isFree: (section[at + 10] & 0x10) === 0,
|
|
213
|
+
name: "",
|
|
214
|
+
description: "",
|
|
215
|
+
extended: {},
|
|
216
|
+
genres: [],
|
|
217
|
+
audios: [],
|
|
218
|
+
video: null,
|
|
219
|
+
copyNever: extended ? null : false,
|
|
220
|
+
// B-CAS の契約確認は使わない
|
|
221
|
+
...(extended ? {} : { contractVerification: null }),
|
|
222
|
+
};
|
|
223
|
+
at += 12 + descriptorsLength;
|
|
224
|
+
if (body.length < descriptorsLength)
|
|
225
|
+
break;
|
|
226
|
+
readDescriptors(event, body);
|
|
227
|
+
result.events.push(event);
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 流れてくる TLV のバイト列から番組表を溜める。**読み方のほかは TS の `EpgReader` と同じ**
|
|
233
|
+
* (揃ったかの数え方、別の表で来るものの混ぜ方、p/f の「いま」)。
|
|
234
|
+
*
|
|
235
|
+
* MH-EIT は自分の TLV のストリームの局のぶんだけが来る (実機のケーブルの BS4K で確かめた)。
|
|
236
|
+
* TS の `other` (ほかの TS の局の番組表) に当たるものは無い
|
|
237
|
+
*/
|
|
238
|
+
export class MmtEpgReader extends EpgReader {
|
|
239
|
+
tables = new MmtTableStream();
|
|
240
|
+
/** @param now いまの時刻。過ぎた時間帯の予定を待たないのに使う (`ScheduleProgress`) */
|
|
241
|
+
constructor(now = Date.now) {
|
|
242
|
+
super(now);
|
|
243
|
+
}
|
|
244
|
+
/** 任意の長さの TLV のバイト列を渡す。番組が 1 つでも増えたら true */
|
|
245
|
+
feed(chunk) {
|
|
246
|
+
let added = false;
|
|
247
|
+
for (const { table } of this.tables.feed(chunk)) {
|
|
248
|
+
const section = parseMhEit(table);
|
|
249
|
+
if (section !== null && this.add(section))
|
|
250
|
+
added = true;
|
|
251
|
+
}
|
|
252
|
+
return added;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* **1 局の「いま」と「次」(MH-EIT[p/f]) を追う。** TS の `PresentFollowing` を 4K の放送 (MMT/TLV) で使えるようにしたもので、
|
|
257
|
+
* 違うのは読み方だけ (覚え方と、変わったかの比べ方は同じ)。ライブで番組の切り替わりを知るのに使う
|
|
258
|
+
*/
|
|
259
|
+
export class MmtPresentFollowing extends PresentFollowing {
|
|
260
|
+
tables = new MmtTableStream();
|
|
261
|
+
/** 任意の長さの TLV のバイト列を渡す。**いまか次のどちらかが変わったら true** (最初に読めたときも true) */
|
|
262
|
+
feed(chunk) {
|
|
263
|
+
let changed = false;
|
|
264
|
+
for (const { table } of this.tables.feed(chunk)) {
|
|
265
|
+
// 予定 (0x8C〜) は読まずに捨てる。同じ並びに流れてくる
|
|
266
|
+
if (table[0] !== MH_PF_ACTUAL || table.length < 18)
|
|
267
|
+
continue;
|
|
268
|
+
if (u16(table, 3) !== this.serviceId)
|
|
269
|
+
continue;
|
|
270
|
+
if (this.hold(parseMhEit(table)))
|
|
271
|
+
changed = true;
|
|
272
|
+
}
|
|
273
|
+
return changed;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 4K の放送 (MMT/TLV) の映像・音声・字幕を取り出す (ISO/IEC 23008-1、ARIB STD-B60)。TS の `pes.ts` に当たる。
|
|
3
|
+
*
|
|
4
|
+
* MMT では、映像・音声・字幕は MPU (頭出しできる所から次の頭出しできる所まで) に分けて送られる。
|
|
5
|
+
* MPU の中身はアクセスユニット (映像の 1 コマ、音声の 1 フレーム) の並びで、データユニットを
|
|
6
|
+
* MMTP のパケットに分割したり、小さいものを 1 つに集約したりして運ぶ (`mmtp.ts` の `parseMpu`)。
|
|
7
|
+
*
|
|
8
|
+
* **時刻はパケットには無く、MPT の記述子にある。** MPU タイムスタンプ記述子が MPU の最初の提示時刻を、
|
|
9
|
+
* MPU 拡張タイムスタンプ記述子がアクセスユニットごとのずれを、これから来る MPU のぶんまで並べる
|
|
10
|
+
* (実機のケーブルの BS4K で確かめた。映像は 1 MPU に 32 コマ、音声は 4 フレーム)。
|
|
11
|
+
* 時刻は NTP の 64 ビットで来るので、ここで **90 kHz** に直す (TS の PTS と同じ物差し。一周はしない)。
|
|
12
|
+
*
|
|
13
|
+
* 中身の形式は次のとおり。実機の録りは映像・音声がスクランブルされていて中身を読めないので、どれも推定。
|
|
14
|
+
*
|
|
15
|
+
* - 映像 (`hev1`): データユニット 1 つが、長さ (4 バイト) を前に付けた NAL ユニット。MPU は ISO BMFF の形式なので、
|
|
16
|
+
* サンプルの中身も MP4 と同じ形と見ている。同じ sample_number のデータユニットが 1 つのコマ
|
|
17
|
+
* - 音声 (`mp4a`): データユニット 1 つが LATM の AudioMuxElement (LOAS の同期のヘッダーは付かない)。
|
|
18
|
+
* MPT の音声コンポーネント記述子の stream_type は 0x11 (LATM/LOAS) だった (確かめた)
|
|
19
|
+
* - 字幕 (`stpp`): MPU 1 つが TTML の文書 1 つ (`ttml.ts` で読む)。文書の中の画像 (PNG) は扱わない
|
|
20
|
+
*
|
|
21
|
+
* スクランブルされたパケットは数えて捨てる (`scrambled`)。復号はエージェントの仕事
|
|
22
|
+
*/
|
|
23
|
+
/** 放送の時刻の物差し (TS の PTS と同じ) */
|
|
24
|
+
export declare const CLOCK = 90000;
|
|
25
|
+
/** NTP の 64 ビットの時刻 (上位 32 ビットが秒、下位 32 ビットが端数) を 90 kHz に */
|
|
26
|
+
export declare function ntpToClock(data: Uint8Array, at: number): number;
|
|
27
|
+
/** MPU タイムスタンプ記述子を読む。`[mpu_sequence_number, 最初の提示時刻 (90 kHz)]` の並び */
|
|
28
|
+
export declare function parseMpuTimestamps(body: Uint8Array): [number, number][];
|
|
29
|
+
/** MPU 拡張タイムスタンプ記述子の、MPU 1 つぶん。時間は `timescale` の単位 */
|
|
30
|
+
export interface MpuExtendedTimestamp {
|
|
31
|
+
mpuSequenceNumber: number;
|
|
32
|
+
timescale: number;
|
|
33
|
+
/** mpu_decoding_time_offset。最初のアクセスユニットの DTS が、最初の提示時刻より前にずれる長さ */
|
|
34
|
+
decodingOffset: number;
|
|
35
|
+
/** アクセスユニットごとの、DTS から PTS までのずれ (デコードの順) */
|
|
36
|
+
dtsPtsOffsets: number[];
|
|
37
|
+
/** アクセスユニットごとの、次のアクセスユニットの DTS までの長さ (pts_offset) */
|
|
38
|
+
durations: number[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* MPU 拡張タイムスタンプ記述子を読む。次のものは DTS を進められないので、空を返す (その MPU は時刻が分からないものとして捨てる)
|
|
42
|
+
*
|
|
43
|
+
* - timescale を持たないもの (MPU のメタデータの mdhd を見る、の意味)。放送ではメタデータを送らない
|
|
44
|
+
* - pts_offset を持たないもの (pts_offset_type 0)。コマの長さが分からない
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseMpuExtendedTimestamps(body: Uint8Array): MpuExtendedTimestamp[];
|
|
47
|
+
/** アクセスユニットの時刻 (90 kHz) */
|
|
48
|
+
export interface UnitTime {
|
|
49
|
+
pts: number;
|
|
50
|
+
dts: number;
|
|
51
|
+
/** 次のアクセスユニットの DTS まで */
|
|
52
|
+
duration: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* MPU の `index` 番目 (デコードの順、0 から) のアクセスユニットの時刻。
|
|
56
|
+
* DTS は最初の提示時刻から decodingOffset だけ戻った所に始まり、durations を足して進む。
|
|
57
|
+
* PTS は DTS に dtsPtsOffsets を足したもの。記述子に無い番目なら null
|
|
58
|
+
*/
|
|
59
|
+
export declare function unitTime(presentation: number, extended: MpuExtendedTimestamp, index: number): UnitTime | null;
|
|
60
|
+
/** AAC の設定 (LATM の StreamMuxConfig にある AudioSpecificConfig) */
|
|
61
|
+
export interface AacConfig {
|
|
62
|
+
/** AudioSpecificConfig のバイト列 (MP4 の esds にそのまま入れる) */
|
|
63
|
+
audioSpecificConfig: Uint8Array;
|
|
64
|
+
/** audioObjectType (2 は AAC LC) */
|
|
65
|
+
objectType: number;
|
|
66
|
+
sampleRate: number;
|
|
67
|
+
/** チャンネルの数 (channelConfiguration 7 は 8) */
|
|
68
|
+
channels: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* LATM の AudioMuxElement (muxConfigPresent = 1) から、AAC のフレームを取り出す。
|
|
72
|
+
* **設定 (StreamMuxConfig) は来たときだけ書いてあり、あいだは「前と同じ」になる** ので、前の設定を覚えておく。
|
|
73
|
+
*
|
|
74
|
+
* 読むのは放送で使う形 (番組 1・層 1・サブフレーム 1・frameLengthType 0) だけ。ほかの形は null を返す
|
|
75
|
+
*/
|
|
76
|
+
export declare class LatmReader {
|
|
77
|
+
private config;
|
|
78
|
+
read(element: Uint8Array): {
|
|
79
|
+
config: AacConfig;
|
|
80
|
+
frame: Uint8Array;
|
|
81
|
+
} | null;
|
|
82
|
+
private readStreamMuxConfig;
|
|
83
|
+
}
|
|
84
|
+
export type MediaKind = "video" | "audio" | "caption";
|
|
85
|
+
/** 取り出すアセット (MPT のアセットのうち、映像・音声・字幕のそれぞれ最初のもの) */
|
|
86
|
+
export interface MediaAsset {
|
|
87
|
+
kind: MediaKind;
|
|
88
|
+
/** asset_type (`hev1`・`mp4a`・`stpp`) */
|
|
89
|
+
type: string;
|
|
90
|
+
packetId: number;
|
|
91
|
+
/** MH-ストリーム識別記述子の component_tag。無ければ null */
|
|
92
|
+
componentTag: number | null;
|
|
93
|
+
}
|
|
94
|
+
interface UnitBase extends UnitTime {
|
|
95
|
+
asset: MediaAsset;
|
|
96
|
+
mpuSequenceNumber: number;
|
|
97
|
+
}
|
|
98
|
+
/** 映像の 1 コマ */
|
|
99
|
+
export interface VideoUnit extends UnitBase {
|
|
100
|
+
kind: "video";
|
|
101
|
+
/** NAL ユニットの並び (長さの前置きを外したもの) */
|
|
102
|
+
nals: Uint8Array[];
|
|
103
|
+
/** IRAP (IDR・CRA・BLA) のピクチャを含む */
|
|
104
|
+
keyframe: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** 音声の 1 フレーム */
|
|
107
|
+
export interface AudioUnit extends UnitBase {
|
|
108
|
+
kind: "audio";
|
|
109
|
+
/** AAC のフレーム (LATM を外したもの。ADTS のヘッダーも無い) */
|
|
110
|
+
frame: Uint8Array;
|
|
111
|
+
config: AacConfig;
|
|
112
|
+
}
|
|
113
|
+
/** 字幕の文書 1 つ。時刻は MPU の最初の提示時刻 */
|
|
114
|
+
export interface CaptionUnit extends UnitBase {
|
|
115
|
+
kind: "caption";
|
|
116
|
+
/** TTML の文書 (UTF-8 を読んだもの) */
|
|
117
|
+
document: string;
|
|
118
|
+
}
|
|
119
|
+
export type MediaUnit = VideoUnit | AudioUnit | CaptionUnit;
|
|
120
|
+
/**
|
|
121
|
+
* `MmtMediaReader.feed` が返すもの。選んだアセットが変わったら (番組が替わったときなど)、
|
|
122
|
+
* 先に `assets` が来る。受け側は組み立て中のもの (fMP4 の初期化セグメント) を作り直す
|
|
123
|
+
*/
|
|
124
|
+
export type MediaEvent = {
|
|
125
|
+
kind: "assets";
|
|
126
|
+
assets: MediaAsset[];
|
|
127
|
+
} | MediaUnit;
|
|
128
|
+
/**
|
|
129
|
+
* 1 局に絞った TLV のバイト列 (`mmt-service-filter.ts`) から、映像・音声・字幕のアクセスユニットを時刻つきで取り出す。
|
|
130
|
+
*
|
|
131
|
+
* - 局 (`serviceId` = MMT_package_id) の MPT を読んで、映像・音声・字幕のそれぞれ最初のアセットを選ぶ。
|
|
132
|
+
* アセットは MPT と同じ IP の並びのもの (在りかの種類 0) だけを読む
|
|
133
|
+
* - 時刻は MPT の記述子から引く。時刻がまだ分からない MPU (最初の MPT より先に来たもの) は数えて捨てる (`untimed`)
|
|
134
|
+
* - パケットが欠けた (packet_sequence_number が続かない) アクセスユニットは捨てる (`dropped`)
|
|
135
|
+
* - アクセスユニットは、次のアクセスユニット (か次の MPU) の始まりが来たときに返す。最後のものは `end` で返す
|
|
136
|
+
*/
|
|
137
|
+
export declare class MmtMediaReader {
|
|
138
|
+
private readonly serviceId;
|
|
139
|
+
private readonly tlv;
|
|
140
|
+
private readonly ip;
|
|
141
|
+
private readonly signaling;
|
|
142
|
+
/** `並び/packet_id` → 組み立て */
|
|
143
|
+
private tracks;
|
|
144
|
+
/** 選んだアセットの並び (変わったかを見る) */
|
|
145
|
+
private selection;
|
|
146
|
+
/** スクランブルされていて読めなかった MMTP のパケットの数 */
|
|
147
|
+
scrambled: number;
|
|
148
|
+
/** 時刻が分からずに捨てたアクセスユニットの数 */
|
|
149
|
+
untimed: number;
|
|
150
|
+
/** パケットが欠けたか、中身を読めずに捨てたアクセスユニットの数 */
|
|
151
|
+
dropped: number;
|
|
152
|
+
constructor(serviceId: number);
|
|
153
|
+
/** 選んでいるアセット */
|
|
154
|
+
get assets(): MediaAsset[];
|
|
155
|
+
feed(chunk: Uint8Array): Generator<MediaEvent>;
|
|
156
|
+
/** 終わりまで読んだとき、組み立て中のアクセスユニットを閉じて返す (録画のファイルの最後など) */
|
|
157
|
+
end(): Generator<MediaUnit>;
|
|
158
|
+
/** MPT から、アセットを選び直して時刻を覚える */
|
|
159
|
+
private readMpt;
|
|
160
|
+
/** MPU のパケットの中身を 1 つ読む */
|
|
161
|
+
private readMpu;
|
|
162
|
+
/** データユニット (分割したものはその断片) を 1 つ受ける。ヘッダーは断片ごとに付いている */
|
|
163
|
+
private dataUnit;
|
|
164
|
+
private keep;
|
|
165
|
+
/** 組み立て中のアクセスユニットを閉じて返す */
|
|
166
|
+
private finish;
|
|
167
|
+
}
|
|
168
|
+
export {};
|