@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/dist/mmtp.js ADDED
@@ -0,0 +1,232 @@
1
+ /**
2
+ * MMTP のパケット (ISO/IEC 23008-1、ARIB STD-B60) を読み、信号メッセージを組み立てる。
3
+ *
4
+ * MMTP のパケットは UDP 1 つに 1 つ入っている (`tlv-ip.ts`)。中身は 2 種類で、
5
+ * 映像・音声・字幕を運ぶ MPU (payload_type 0) と、局の中身の一覧や番組表を運ぶ信号メッセージ (2)。
6
+ * どちらかは packet_id ごとに決まっている。
7
+ *
8
+ * **4K の放送では映像と音声がスクランブルされ、信号はされていない** (実機のケーブルの BS4K で確かめた)。
9
+ * スクランブルの印はヘッダー拡張にあり (ARIB STD-B61)、MPU のペイロードヘッダー (8 バイト) までは読める。
10
+ * 復号はエージェントの仕事 (まだ扱わない)。
11
+ */
12
+ import { concatBytes, u16, u32 } from "./bytes.js";
13
+ export const PAYLOAD_MPU = 0x00;
14
+ export const PAYLOAD_SIGNALING = 0x02;
15
+ /** 種類がいくつも入るヘッダー拡張 (B61)。中に種類と長さの組が並ぶ */
16
+ const EXTENSION_MULTI = 0x0000;
17
+ /** ヘッダー拡張の中の、スクランブルの印の種類 */
18
+ const EXTENSION_SCRAMBLE = 0x0001;
19
+ const SCRAMBLING = {
20
+ 0: "none",
21
+ 2: "even",
22
+ 3: "odd",
23
+ };
24
+ /** MMTP のパケットを 1 つ読む。短すぎれば null */
25
+ export function parseMmtp(data) {
26
+ if (data.length < 12)
27
+ return null;
28
+ const flags = data[0];
29
+ let at = 12;
30
+ // packet_counter_flag
31
+ if (flags & 0x20)
32
+ at += 4;
33
+ let scrambling = null;
34
+ // extension_flag
35
+ if (flags & 0x02) {
36
+ if (at + 4 > data.length)
37
+ return null;
38
+ const type = u16(data, at) & 0x7fff;
39
+ const length = u16(data, at + 2);
40
+ const body = data.subarray(at + 4, at + 4 + length);
41
+ if (body.length < length)
42
+ return null;
43
+ if (type === EXTENSION_MULTI)
44
+ scrambling = scramblingOf(body);
45
+ at += 4 + length;
46
+ }
47
+ return {
48
+ rap: (flags & 0x01) !== 0,
49
+ payloadType: data[1] & 0x3f,
50
+ packetId: u16(data, 2),
51
+ deliveryTimestamp: u32(data, 4),
52
+ sequenceNumber: u32(data, 8),
53
+ scrambling,
54
+ payload: data.subarray(at),
55
+ };
56
+ }
57
+ /** 種類がいくつも入るヘッダー拡張から、スクランブルの印を探す。種類の上位 1 ビットは「これで終わり」 */
58
+ function scramblingOf(body) {
59
+ let at = 0;
60
+ while (at + 4 <= body.length) {
61
+ const type = u16(body, at);
62
+ const length = u16(body, at + 2);
63
+ if ((type & 0x7fff) === EXTENSION_SCRAMBLE && length >= 1)
64
+ return SCRAMBLING[body[at + 4] >> 6] ?? null;
65
+ at += 4 + length;
66
+ if (type & 0x8000)
67
+ break;
68
+ }
69
+ return null;
70
+ }
71
+ /** 分割の印 (fragmentation_indicator)。0 は分割していない、1 は先頭、2 は途中、3 は最後 */
72
+ const WHOLE = 0;
73
+ const FIRST = 1;
74
+ const LAST = 3;
75
+ export function parseMpu(payload) {
76
+ if (payload.length < 8)
77
+ return null;
78
+ const flags = payload[2];
79
+ return {
80
+ fragmentType: flags >> 4,
81
+ timed: (flags & 0x08) !== 0,
82
+ fragmentation: (flags >> 1) & 0x03,
83
+ aggregated: (flags & 0x01) !== 0,
84
+ fragmentCounter: payload[3],
85
+ mpuSequenceNumber: u32(payload, 4),
86
+ data: payload.subarray(8, Math.min(payload.length, 2 + u16(payload, 0))),
87
+ };
88
+ }
89
+ export const MESSAGE_PA = 0x0000;
90
+ export const MESSAGE_M2_SECTION = 0x8000;
91
+ export const MESSAGE_CA = 0x8001;
92
+ export const MESSAGE_M2_SHORT_SECTION = 0x8002;
93
+ /** PA メッセージは長さが 32 ビット、ほかの信号メッセージは 16 ビット */
94
+ function lengthBytes(messageId) {
95
+ return messageId === MESSAGE_PA ? 4 : 2;
96
+ }
97
+ /** 信号メッセージを 1 つ読む。長さが合わなければ null */
98
+ export function parseMessage(data) {
99
+ if (data.length < 3)
100
+ return null;
101
+ const messageId = u16(data, 0);
102
+ const width = lengthBytes(messageId);
103
+ const at = 3 + width;
104
+ if (data.length < at)
105
+ return null;
106
+ const length = width === 4 ? u32(data, 3) : u16(data, 3);
107
+ if (data.length < at + length)
108
+ return null;
109
+ return {
110
+ messageId,
111
+ version: data[2],
112
+ body: data.subarray(at, at + length),
113
+ };
114
+ }
115
+ /**
116
+ * 信号メッセージに入った表を返す。
117
+ *
118
+ * - PA メッセージは表の数と、表ごとの (table_id・バージョン・長さ) を先に並べ、そのあとに表を並べる。
119
+ * 表の頭は table_id・バージョン・長さ (16 ビット) で、MPT と PLT はこの形式
120
+ * - M2 セクションメッセージ・M2 短セクションメッセージ・CA メッセージは、表を 1 つだけ運ぶ。
121
+ * M2 セクションは TS のセクションと同じ形式で、末尾に CRC32 が付く (確かめるのは `mmt-si.ts`)
122
+ */
123
+ export function messageTables(message) {
124
+ const body = message.body;
125
+ switch (message.messageId) {
126
+ case MESSAGE_PA: {
127
+ if (body.length < 1)
128
+ return [];
129
+ const tables = [];
130
+ let at = 1 + body[0] * 4;
131
+ while (at + 4 <= body.length) {
132
+ const end = at + 4 + u16(body, at + 2);
133
+ if (end > body.length)
134
+ break;
135
+ tables.push(body.subarray(at, end));
136
+ at = end;
137
+ }
138
+ return tables;
139
+ }
140
+ case MESSAGE_M2_SECTION:
141
+ case MESSAGE_M2_SHORT_SECTION:
142
+ case MESSAGE_CA:
143
+ return body.length > 0 ? [body] : [];
144
+ default:
145
+ return [];
146
+ }
147
+ }
148
+ /**
149
+ * 組み立て中のメッセージの大きさの上限。PA メッセージの表は長さが 16 ビット (64 KiB まで) で、M2 セクションは 4 KiB まで。
150
+ * 最後の断片が来ないまま途中の断片が続いても、溜め続けない
151
+ */
152
+ const PENDING_MOST = 256 * 1024;
153
+ /**
154
+ * MMTP の信号メッセージを組み立てる。**分割と集約の両方を戻す。**
155
+ *
156
+ * - 長いメッセージ (番組表の MH-EIT や大きな MPT) は、いくつもの MMTP のパケットに分割して送られる。
157
+ * 欠けは packet_sequence_number (packet_id ごとに 1 ずつ増える) で見る。続いていなければ、組み立て中のものを
158
+ * 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** 実機のケーブルの BS4K は分割したものでも
159
+ * いつも 0 を入れていた
160
+ * - 短いメッセージは、1 つの MMTP のパケットに長さを前に付けて集約される (aggregation_flag)。
161
+ * 長さは length_extension_flag が立っていれば 32 ビット、立っていなければ 16 ビット
162
+ *
163
+ * 組み立て中のものは、並び (`flow`) と packet_id の組ごとに持つ
164
+ */
165
+ export class SignalingAssembler {
166
+ pending = new Map();
167
+ /** 欠けたか大きすぎて捨てた、組み立て中のメッセージの数 */
168
+ dropped = 0;
169
+ /** 信号メッセージを運ぶ MMTP のパケットを 1 つ渡す。そろったメッセージを返す */
170
+ feed(flow, packet) {
171
+ const payload = packet.payload;
172
+ if (packet.payloadType !== PAYLOAD_SIGNALING || payload.length < 2)
173
+ return [];
174
+ const fragmentation = payload[0] >> 6;
175
+ const longLength = (payload[0] & 0x02) !== 0;
176
+ const aggregated = (payload[0] & 0x01) !== 0;
177
+ const body = payload.subarray(2);
178
+ const key = `${flow}/${packet.packetId}`;
179
+ let whole = null;
180
+ if (fragmentation === WHOLE) {
181
+ if (this.pending.delete(key))
182
+ this.dropped++;
183
+ whole = body;
184
+ }
185
+ else if (fragmentation === FIRST) {
186
+ if (this.pending.delete(key))
187
+ this.dropped++;
188
+ // 断片を持ち越すのでコピーする (中身は届いたチャンクの一部を指す。`TlvStream`)
189
+ this.pending.set(key, {
190
+ parts: [body.slice()],
191
+ size: body.length,
192
+ sequence: packet.sequenceNumber,
193
+ });
194
+ }
195
+ else {
196
+ const held = this.pending.get(key);
197
+ if (held === undefined)
198
+ return [];
199
+ held.size += body.length;
200
+ if (packet.sequenceNumber !== (held.sequence + 1) >>> 0 ||
201
+ held.size > PENDING_MOST) {
202
+ this.pending.delete(key);
203
+ this.dropped++;
204
+ return [];
205
+ }
206
+ held.parts.push(body.slice());
207
+ held.sequence = packet.sequenceNumber;
208
+ if (fragmentation === LAST) {
209
+ this.pending.delete(key);
210
+ whole = concatBytes(held.parts);
211
+ }
212
+ }
213
+ if (whole === null)
214
+ return [];
215
+ return aggregated ? splitAggregated(whole, longLength) : [whole];
216
+ }
217
+ }
218
+ /** 集約されたメッセージを、前に付いた長さで切り分ける */
219
+ function splitAggregated(data, longLength) {
220
+ const width = longLength ? 4 : 2;
221
+ const out = [];
222
+ let at = 0;
223
+ while (at + width <= data.length) {
224
+ const length = longLength ? u32(data, at) : u16(data, at);
225
+ const end = at + width + length;
226
+ if (end > data.length)
227
+ break;
228
+ out.push(data.subarray(at + width, end));
229
+ at = end;
230
+ }
231
+ return out;
232
+ }
package/dist/psi.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * TS の土台 (パケットの切り出し・セクションの組み立て・PAT/PMT) と、局の一覧。
3
3
  * 局の一覧はチャンネルスキャンで「この物理チャンネルに何が入っているか」を知るために使い、
4
- * 読むのは NIT と SDT の 2 つ。
4
+ * 読むのは NIT と SDT の 2 つ。ケーブルでは、局の無い TS を見分けるのに PAT も読む (`ServiceReader`)。
5
5
  *
6
6
  * - NIT (PID 0x0010) … ネットワーク ID と、地上波ならリモコン番号。衛星はネットワークの全部の TS
7
7
  * (TSID・周波数・サービスの一覧) が載っている (`NitReader`)
@@ -27,6 +27,8 @@ export declare const TABLE_SDT_ACTUAL = 66;
27
27
  * 取り込むほう (denpa の epg.ts の DIGITAL_TV)
28
28
  */
29
29
  export declare const SERVICE_TYPES: Set<number>;
30
+ /** MPEG-2 の CRC32。セクション末尾の 4 バイトを含めて回すと 0 になる */
31
+ export declare function crc32(data: Uint8Array): number;
30
32
  /**
31
33
  * セクション本体に CRC32 を付けて仕上げる。`section_length` も実長に直す。
32
34
  *
@@ -48,8 +50,9 @@ export interface TransportInfo {
48
50
  }
49
51
  export interface NetworkInfo {
50
52
  networkId: number;
53
+ /** 最初の TS のリモコン番号。地上波の NIT は自分の TS 1 つだけを載せる */
51
54
  remoteControlKeyId: number | null;
52
- transportStreams: TransportInfo[];
55
+ transportStreams: NetworkTransport[];
53
56
  }
54
57
  /**
55
58
  * PSI セクションを組み立てる。
@@ -182,6 +185,8 @@ export type Delivery = SatelliteDelivery | TerrestrialDelivery;
182
185
  /** NIT の TS の 1 件。サービスの一覧に、どこで流れているかが付く */
183
186
  export interface NetworkTransport extends TransportInfo {
184
187
  delivery: Delivery | null;
188
+ /** その TS のリモコン番号 (TS 情報記述子)。載せていなければ null */
189
+ remoteControlKeyId: number | null;
185
190
  }
186
191
  /** そろった NIT。セクションをつないだもの */
187
192
  export interface Network {
@@ -259,13 +264,26 @@ export interface FoundService extends Service {
259
264
  * Mirakurun のスキャンと同じで、**両方**揃って初めてそのチャンネルを
260
265
  * 「受信できた」とみなす。SDT だけ取れても、どのネットワークのものか
261
266
  * 分からないと設定に書けない。
267
+ *
268
+ * **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
269
+ * 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
270
+ * (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
271
+ * NIT が先に来ていれば、その TS のリモコン番号だけを使う。
272
+ * PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
262
273
  */
263
274
  export declare class ServiceReader {
264
275
  private readonly nit;
265
276
  private readonly sdt;
277
+ private readonly pat;
266
278
  private readonly packets;
279
+ private readonly requireNit;
267
280
  network: NetworkInfo | null;
268
281
  transport: TransportInfo | null;
282
+ /** PAT に局が 1 つも無かった (`requireNit: false` のときだけ読む) */
283
+ empty: boolean;
284
+ constructor({ requireNit }?: {
285
+ requireNit?: boolean;
286
+ });
269
287
  get complete(): boolean;
270
288
  /** 任意の長さのバイト列を渡す。揃ったら true */
271
289
  feed(chunk: Uint8Array): boolean;
package/dist/psi.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * TS の土台 (パケットの切り出し・セクションの組み立て・PAT/PMT) と、局の一覧。
3
3
  * 局の一覧はチャンネルスキャンで「この物理チャンネルに何が入っているか」を知るために使い、
4
- * 読むのは NIT と SDT の 2 つ。
4
+ * 読むのは NIT と SDT の 2 つ。ケーブルでは、局の無い TS を見分けるのに PAT も読む (`ServiceReader`)。
5
5
  *
6
6
  * - NIT (PID 0x0010) … ネットワーク ID と、地上波ならリモコン番号。衛星はネットワークの全部の TS
7
7
  * (TSID・周波数・サービスの一覧) が載っている (`NitReader`)
@@ -55,7 +55,7 @@ const CRC32_TABLE = (() => {
55
55
  return table;
56
56
  })();
57
57
  /** MPEG-2 の CRC32。セクション末尾の 4 バイトを含めて回すと 0 になる */
58
- function crc32(data) {
58
+ export function crc32(data) {
59
59
  let crc = 0xffffffff;
60
60
  for (const byte of data) {
61
61
  crc =
@@ -481,11 +481,11 @@ function parseNitSection(section) {
481
481
  at += 6 + loop;
482
482
  const services = [];
483
483
  let delivery = null;
484
+ let remote = null;
484
485
  for (const [tag, descriptor] of descriptors(body)) {
485
- if (tag === DESC_TS_INFORMATION &&
486
- descriptor.length > 0 &&
487
- remoteControlKeyId === null) {
488
- remoteControlKeyId = descriptor[0];
486
+ if (tag === DESC_TS_INFORMATION && descriptor.length > 0) {
487
+ remote ??= descriptor[0];
488
+ remoteControlKeyId ??= remote;
489
489
  }
490
490
  else if (tag === DESC_SERVICE_LIST) {
491
491
  for (let i = 0; i + 3 <= descriptor.length; i += 3) {
@@ -508,13 +508,13 @@ function parseNitSection(section) {
508
508
  originalNetworkId,
509
509
  services,
510
510
  delivery,
511
+ remoteControlKeyId: remote,
511
512
  });
512
513
  }
513
514
  return {
514
515
  other: tableId === TABLE_NIT_OTHER,
515
516
  networkId: u16(section, 3),
516
517
  version: (section[5] >> 1) & 0x1f,
517
- current: (section[5] & 0x01) !== 0,
518
518
  sectionNumber: section[6],
519
519
  lastSectionNumber: section[7],
520
520
  remoteControlKeyId,
@@ -716,15 +716,30 @@ export class SdtReader {
716
716
  * Mirakurun のスキャンと同じで、**両方**揃って初めてそのチャンネルを
717
717
  * 「受信できた」とみなす。SDT だけ取れても、どのネットワークのものか
718
718
  * 分からないと設定に書けない。
719
+ *
720
+ * **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
721
+ * 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
722
+ * (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
723
+ * NIT が先に来ていれば、その TS のリモコン番号だけを使う。
724
+ * PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
719
725
  */
720
726
  export class ServiceReader {
721
727
  nit = new SectionAssembler(PID_NIT);
722
728
  sdt = new SectionAssembler(PID_SDT);
729
+ pat = new SectionAssembler(PID_PAT);
723
730
  packets = new PacketStream();
731
+ requireNit;
724
732
  network = null;
725
733
  transport = null;
734
+ /** PAT に局が 1 つも無かった (`requireNit: false` のときだけ読む) */
735
+ empty = false;
736
+ constructor({ requireNit = true } = {}) {
737
+ this.requireNit = requireNit;
738
+ }
726
739
  get complete() {
727
- return this.network !== null && this.transport !== null;
740
+ return this.requireNit
741
+ ? this.network !== null && this.transport !== null
742
+ : this.transport !== null || this.empty;
728
743
  }
729
744
  /** 任意の長さのバイト列を渡す。揃ったら true */
730
745
  feed(chunk) {
@@ -739,16 +754,22 @@ export class ServiceReader {
739
754
  this.transport = parseSdt(section) ?? this.transport;
740
755
  }
741
756
  }
757
+ if (!this.requireNit && !this.empty && this.transport === null) {
758
+ for (const section of this.pat.feed(packet)) {
759
+ if (section[0] === TABLE_PAT && parsePat(section).size === 0)
760
+ this.empty = true;
761
+ }
762
+ }
742
763
  }
743
764
  return this.complete;
744
765
  }
745
766
  /** 録るに値するサービスだけ。リモコン番号は NIT のものを配る */
746
767
  services() {
747
- if (this.network === null || this.transport === null)
768
+ const transport = this.transport;
769
+ if (transport === null)
748
770
  return [];
749
- const { networkId, remoteControlKeyId } = this.network;
750
- const { transportStreamId } = this.transport;
751
- return this.transport.services
771
+ const { transportStreamId, originalNetworkId } = transport;
772
+ const found = (networkId, remoteControlKeyId) => transport.services
752
773
  .filter((service) => SERVICE_TYPES.has(service.serviceType))
753
774
  .map((service) => ({
754
775
  ...service,
@@ -756,5 +777,13 @@ export class ServiceReader {
756
777
  transportStreamId,
757
778
  remoteControlKeyId,
758
779
  }));
780
+ if (this.requireNit) {
781
+ if (this.network === null)
782
+ return [];
783
+ return found(this.network.networkId, this.network.remoteControlKeyId);
784
+ }
785
+ const own = this.network?.transportStreams.find((ts) => ts.transportStreamId === transportStreamId &&
786
+ ts.originalNetworkId === originalNetworkId);
787
+ return found(originalNetworkId, own?.remoteControlKeyId ?? null);
759
788
  }
760
789
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * **本物の HEVC と AAC を MMT/TLV に包んで、4K の局の放送を作る** (試験用。`synth-av.ts` の 4K の放送に当たる)。
3
+ *
4
+ * 素材は ffmpeg で作り、ここに base64 で持つ (E2E のイメージには本物の ffmpeg が無い)。
5
+ *
6
+ * - 映像: `testsrc2` の 64x64・59.94 コマ・16 コマを libx265 で HEVC Main10 にしたもの
7
+ * (keyint 8・B ピクチャ 3・閉じた GOP・AUD と VPS/SPS/PPS をキーフレームごとに)。`-f hevc` の Annex B
8
+ * - 音声: 440 Hz の正弦波を AAC LC (48 kHz・2 チャンネル) にした 13 フレームを `-f latm` (LOAS) で書いたもの
9
+ *
10
+ * どちらも 1 つの mp4 から書き出した。そのときの映像の PTS と DTS (180000 の単位、デコードの順) が `VIDEO_TIMES`。
11
+ * `SynthMmtAv` は映像の GOP (8 コマ) を 1 つの MPU にして繰り返し、時刻と番号を進めながら流し続ける
12
+ * (偽エージェントの 4K の局。`tests/fake/broadcast.ts`)
13
+ */
14
+ /** 映像の素材 (HEVC Main10 の Annex B。base64) */
15
+ export declare const HEVC: string;
16
+ /** 音声の素材 (AAC LC の LOAS。base64) */
17
+ export declare const LOAS: string;
18
+ /** ffprobe が読んだ映像の [PTS, DTS] (180000 の単位、デコードの順) */
19
+ export declare const VIDEO_TIMES: [number, number][];
20
+ /** 映像の 1 GOP のコマ数 (keyint) */
21
+ export declare const GOP = 8;
22
+ /** 音声の素材のフレーム数 */
23
+ export declare const AUDIO_FRAMES = 13;
24
+ /** MPT の時刻の単位 (実機の BS4K と同じ) */
25
+ export declare const TIMESCALE = 180000;
26
+ /** base64 をバイト列に */
27
+ export declare function base64Bytes(base64: string): Uint8Array;
28
+ /** Annex B を NAL ユニットに切り、AUD (35) ごとにコマにまとめる (デコードの順) */
29
+ export declare function hevcPictures(annexB: Uint8Array): Uint8Array[][];
30
+ /** LOAS の同期のヘッダー (11 ビットの 0x2B7 と 13 ビットの長さ) を外し、AudioMuxElement を並べる */
31
+ export declare function latmElements(loas: Uint8Array): Uint8Array[];
32
+ /** 4K の局 1 つぶんの組み立ての決まり (`SynthMmtAv`) */
33
+ export interface SynthMmtAvOptions {
34
+ /** 局の service_id (MPT の MMT_package_id) */
35
+ serviceId: number;
36
+ /** MPT の packet_id */
37
+ mptPacketId: number;
38
+ /** 映像の packet_id。省くと実機の BS4K と同じ 0xF100 */
39
+ videoPacketId?: number;
40
+ /** 音声の packet_id。省くと 0xF110 */
41
+ audioPacketId?: number;
42
+ /** 字幕の packet_id。省くと 0xF130 */
43
+ captionPacketId?: number;
44
+ /** 映像の最初の MPU の提示時刻 (UNIX 時刻のミリ秒)。放送の時計 (NTP) にして MPT に書く */
45
+ startMs: number;
46
+ /** 字幕の文字。省くと字幕を流さない。映像の MPU `CAPTION_EVERY` 個ごとに TTML の文書を 1 つ流す */
47
+ caption?: string;
48
+ /** ヘッダー圧縮した IP の CID。省くと 1 */
49
+ cid?: number;
50
+ }
51
+ /** 映像の 1 MPU の長さ (ミリ秒)。実時間で流すときの間隔 */
52
+ export declare const MPU_MS: number;
53
+ /**
54
+ * **素材の映像と音声 (と字幕) を、MMT/TLV で流し続ける。** `next` を呼ぶたびに映像の 1 MPU (1 GOP・8 コマ・約 0.13 秒) と、
55
+ * そこまでに要る音声の MPU を、時刻と番号を進めて返す。どの MPU の前にも、これから来る MPU の時刻を並べた MPT を付ける
56
+ * (途中から読み始めても、次の MPU から読める)
57
+ */
58
+ export declare class SynthMmtAv {
59
+ private readonly options;
60
+ private readonly video;
61
+ private readonly audio;
62
+ private readonly ids;
63
+ private readonly cid;
64
+ /** 映像の最初の MPU の提示時刻 (180000 の単位、NTP の始まりから) */
65
+ private readonly base;
66
+ /** 次に流す映像の MPU (0 から。mpu_sequence_number) */
67
+ private k;
68
+ /** 次に流す音声の MPU (0 から) */
69
+ private j;
70
+ /** packet_id ごとの、次の packet_sequence_number */
71
+ private readonly sequence;
72
+ constructor(options: SynthMmtAvOptions);
73
+ /** 180000 の単位の時刻を NTP に */
74
+ private ntp;
75
+ /** packet_id ごとに続き番号を振りながら MFU を作る */
76
+ private mfu;
77
+ /** 映像の MPU `k` のコマの [PTS, DTS] (180000 の単位、NTP の始まりから。デコードの順) */
78
+ private videoTimes;
79
+ /** これから流す映像・音声・字幕の MPU の時刻を並べた MPT */
80
+ private mpt;
81
+ /** 映像の 1 MPU と、そこまでの音声 (と字幕) を TLV のバイト列で */
82
+ next(): Uint8Array;
83
+ }