@danything/arib 1.3.1 → 2.1.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/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`)
@@ -21,14 +21,14 @@ export function packetPid(packet) {
21
21
  }
22
22
  /** PAT。どの PID に PMT があるかを持つ。**入口はいつもここ** */
23
23
  export const PID_PAT = 0x0000;
24
- export const PID_NIT = 0x0010;
24
+ const PID_NIT = 0x0010;
25
25
  export const PID_SDT = 0x0011;
26
26
  export const TABLE_PAT = 0x00;
27
27
  export const TABLE_PMT = 0x02;
28
- export const TABLE_NIT_ACTUAL = 0x40;
29
- export const TABLE_NIT_OTHER = 0x41;
28
+ const TABLE_NIT_ACTUAL = 0x40;
29
+ const TABLE_NIT_OTHER = 0x41;
30
30
  export const TABLE_SDT_ACTUAL = 0x42;
31
- export const TABLE_SDT_OTHER = 0x46;
31
+ const TABLE_SDT_OTHER = 0x46;
32
32
  const DESC_SERVICE_LIST = 0x41;
33
33
  const DESC_SATELLITE_DELIVERY = 0x43;
34
34
  const DESC_TERRESTRIAL_DELIVERY = 0xfa;
@@ -80,14 +80,14 @@ export function withCrc(body) {
80
80
  new DataView(out.buffer).setUint32(section.length, crc32(section));
81
81
  return out;
82
82
  }
83
- /** 何も組み上がらなかったときの返り。使い回すので凍らせてある */
83
+ /** 何も組み上がらなかったときの返り。使い回すので `Object.freeze` してある */
84
84
  const NONE = Object.freeze([]);
85
85
  /**
86
86
  * PSI セクションを組み立てる。
87
87
  *
88
88
  * セクションは TS パケットをまたいで運ばれ、パケットの切れ目とは無関係な
89
89
  * 位置で終わる。payload_unit_start_indicator と pointer_field を見て
90
- * 頭を合わせ、section_length ぶん溜まったら 1 本として返す。
90
+ * 先頭を合わせ、section_length ぶん溜まったら 1 本として返す。
91
91
  */
92
92
  export class SectionAssembler {
93
93
  pid;
@@ -133,7 +133,7 @@ export class SectionAssembler {
133
133
  // pointer_field の手前は前のセクションの続き
134
134
  this.append(payload.subarray(1, 1 + pointer));
135
135
  const done = this.flush();
136
- // 写す。パケットは届いた塊の上の窓で、塊は使い回される (PacketStream)
136
+ // コピーする。パケットは届いたチャンクの一部を指し、チャンクのバッファは使い回される (PacketStream)
137
137
  this.buffer = payload.slice(1 + pointer);
138
138
  const next = this.flush();
139
139
  if (done.length === 0)
@@ -173,7 +173,7 @@ export class SectionAssembler {
173
173
  }
174
174
  /**
175
175
  * 同期が取れているとみなすのに必要な連続パケット数。
176
- * 1 つだけでは中身のたまたまの 0x47 を頭と誤認する
176
+ * 1 つだけでは中身にたまたまある 0x47 をパケットの先頭と誤認する
177
177
  */
178
178
  const CONFIRM = 3;
179
179
  /** `from` 以降 `until` の手前までで、188 間隔に 0x47 が続くところを探す。無ければ -1 (denpa の `seek.ts` も使う) */
@@ -190,21 +190,21 @@ export function findSync(data, from = 0, until = data.length) {
190
190
  /**
191
191
  * 任意の長さで届くバイト列を 188 バイトのパケットに切り分ける。
192
192
  *
193
- * 頭が必ずパケットの先頭とは限らず、電波が弱いと途中で数バイト落ちる。
194
- * ずれたままだと**以降ずっと 1 パケットも読めなくなる**ので、頭が 0x47 でなければ
193
+ * 渡されたバイト列の先頭がパケットの先頭とは限らず、電波が弱いと途中で数バイト落ちる。
194
+ * ずれたままだと**以降ずっと 1 パケットも読めなくなる**ので、先頭が 0x47 でなければ
195
195
  * 取り直す。普段は先頭が 0x47 なので、探しに行くのはずれたときだけ。
196
196
  *
197
- * **出すパケットは渡された塊の上の窓** (写しではない。写すと録画の処理の 1/3 を食う)。
198
- * 塊の入れ物を使い回す呼び出し側がある
199
- * (denpa の `server/recorded-bml.ts`) ので、**次の `feed` より後まで持つなら写すこと**。
200
- * いまのパーサ (`SectionAssembler`・`PesDemuxer`) は溜めるときに写している
197
+ * **出すパケットは渡されたチャンクの一部を指す** (コピーではない。コピーすると録画の処理時間の 1/3 を使う)。
198
+ * チャンクのバッファを使い回す呼び出し側がある
199
+ * (denpa の `server/recorded-bml.ts`) ので、**次の `feed` より後まで持つならコピーすること**。
200
+ * いまのパーサ (`SectionAssembler`・`PesDemuxer`) は溜めるときにコピーしている
201
201
  */
202
202
  export class PacketStream {
203
203
  rest = new Uint8Array(0);
204
204
  *feed(chunk) {
205
205
  /*
206
- * 前の残り (`rest`) と塊を繋いだ並びの上を歩く。位置 `at` はその並びでの位置。
207
- * 繋ぐのは**残りに頭があるパケットを読むのに要るぶんだけ** (頭を探すのに
206
+ * 前の残り (`rest`) とチャンクを繋いだ並びを順に読む。位置 `at` はその並びでの位置。
207
+ * 繋ぐのは**残りに先頭があるパケットを読むのに要るぶんだけ** (先頭を探すのに
208
208
  * 3 パケットぶん先まで見る)
209
209
  */
210
210
  const split = this.rest.length;
@@ -218,7 +218,7 @@ export class PacketStream {
218
218
  while (at < split && at + PACKET <= total) {
219
219
  if (head[at] !== SYNC) {
220
220
  const found = findSync(head, at, split);
221
- // 残りの中には無い。塊のほうを探す
221
+ // 残りの中には無い。チャンクのほうを探す
222
222
  if (found < 0) {
223
223
  lost = true;
224
224
  break;
@@ -252,13 +252,13 @@ export class PacketStream {
252
252
  }
253
253
  at = split + offset;
254
254
  }
255
- // 同期が取れないまま溜め込まないよう、頭を探せるぶんだけ残す
255
+ // 同期が取れないまま溜め込まないよう、先頭を探せるぶんだけ残す
256
256
  const from = Math.max(at, total - PACKET * CONFIRM);
257
257
  if (from >= split) {
258
258
  this.rest = chunk.slice(from - split);
259
259
  }
260
260
  else {
261
- // 塊が短くて、前の残りにまだ食い込んでいる
261
+ // チャンクが短くて、前の残りの途中から残す
262
262
  const rest = new Uint8Array(total - from);
263
263
  rest.set(this.rest.subarray(from));
264
264
  rest.set(chunk, split - from);
@@ -333,10 +333,10 @@ export function pmtProgramInfo(section) {
333
333
  /**
334
334
  * PMT の ES の並びを `[stream_type, PID, 記述子]` で返す。
335
335
  *
336
- * **`stream_type` では物を見分けられません。** データ放送も AIT もロゴも同じ
336
+ * **`stream_type` では中身を見分けられない。** データ放送も AIT もロゴも同じ
337
337
  * DSM-CC セクション (0x0D) を名乗るので、見分けるのは記述子のほう
338
- * (component_tag や application_signalling) です。だからここは**素通しで並べる
339
- * だけ**にして、選り分けは呼ぶ側に置いてあります。
338
+ * (component_tag や application_signalling)。だからここは**素通しで並べる
339
+ * だけ**にして、選り分けは呼ぶ側に置いてある。
340
340
  */
341
341
  export function* pmtStreams(section) {
342
342
  if (section[0] !== TABLE_PMT || section.length < 16)
@@ -368,7 +368,7 @@ export function parsePat(section) {
368
368
  /**
369
369
  * SDT のサービスの並びを (service_id, 記述子の並び) に切り分ける。
370
370
  *
371
- * **読む側が 2 つある。** 局名 (`parseSdt`) とロゴの対応 (denpa の `ts/logo.ts`) で、
371
+ * **読む側が 2 つある。** 局名 (`sdtTransport`) とロゴの対応 (denpa の `ts/logo.ts`) で、
372
372
  * 欲しい記述子が違うだけで歩き方は同じ。並びの読み方をここ 1 箇所に置く。
373
373
  */
374
374
  export function* sdtServices(section) {
@@ -411,18 +411,9 @@ function sdtTransport(section) {
411
411
  };
412
412
  }
413
413
  /** SDT からサービスの一覧を読む。自分の TS のものだけ */
414
- export function parseSdt(section) {
414
+ function parseSdt(section) {
415
415
  return section[0] === TABLE_SDT_ACTUAL ? sdtTransport(section) : null;
416
416
  }
417
- /**
418
- * SDT[other] からサービスの一覧を読む。**ほかの TS のもの。**
419
- *
420
- * 衛星は、どの TS にもネットワークのほかの TS の局名を流している (番組表の EIT[other] と同じ考え)。
421
- * 開いていない TS の局名をこれで知る。TS ごとに別のセクションで来るので、集めるのは `SdtReader`
422
- */
423
- export function parseSdtOther(section) {
424
- return section[0] === TABLE_SDT_OTHER ? sdtTransport(section) : null;
425
- }
426
417
  /** BCD の桁を左から `digits` 桁ぶん読んで、10 進の整数にする */
427
418
  function bcdDigits(data, at, digits) {
428
419
  let value = 0;
@@ -468,7 +459,7 @@ function terrestrialDelivery(descriptor) {
468
459
  };
469
460
  }
470
461
  /** NIT のセクションを 1 つ読む。actual (0x40) と other (0x41) のどちらも */
471
- export function parseNitSection(section) {
462
+ function parseNitSection(section) {
472
463
  const tableId = section[0];
473
464
  if (tableId !== TABLE_NIT_ACTUAL && tableId !== TABLE_NIT_OTHER)
474
465
  return null;
@@ -490,11 +481,11 @@ export function parseNitSection(section) {
490
481
  at += 6 + loop;
491
482
  const services = [];
492
483
  let delivery = null;
484
+ let remote = null;
493
485
  for (const [tag, descriptor] of descriptors(body)) {
494
- if (tag === DESC_TS_INFORMATION &&
495
- descriptor.length > 0 &&
496
- remoteControlKeyId === null) {
497
- remoteControlKeyId = descriptor[0];
486
+ if (tag === DESC_TS_INFORMATION && descriptor.length > 0) {
487
+ remote ??= descriptor[0];
488
+ remoteControlKeyId ??= remote;
498
489
  }
499
490
  else if (tag === DESC_SERVICE_LIST) {
500
491
  for (let i = 0; i + 3 <= descriptor.length; i += 3) {
@@ -517,13 +508,13 @@ export function parseNitSection(section) {
517
508
  originalNetworkId,
518
509
  services,
519
510
  delivery,
511
+ remoteControlKeyId: remote,
520
512
  });
521
513
  }
522
514
  return {
523
515
  other: tableId === TABLE_NIT_OTHER,
524
516
  networkId: u16(section, 3),
525
517
  version: (section[5] >> 1) & 0x1f,
526
- current: (section[5] & 0x01) !== 0,
527
518
  sectionNumber: section[6],
528
519
  lastSectionNumber: section[7],
529
520
  remoteControlKeyId,
@@ -534,7 +525,7 @@ export function parseNitSection(section) {
534
525
  * NIT からネットワーク ID とリモコン番号、他の TS の顔ぶれを読む。**セクション 1 つぶん。**
535
526
  * スキャンで「どのネットワークか」を知るにはこれで足りる。ネットワークの全部の TS が要るなら `NitReader`
536
527
  */
537
- export function parseNit(section) {
528
+ function parseNit(section) {
538
529
  if (section[0] !== TABLE_NIT_ACTUAL)
539
530
  return null;
540
531
  const parsed = parseNitSection(section);
@@ -725,15 +716,30 @@ export class SdtReader {
725
716
  * Mirakurun のスキャンと同じで、**両方**揃って初めてそのチャンネルを
726
717
  * 「受信できた」とみなす。SDT だけ取れても、どのネットワークのものか
727
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`)
728
725
  */
729
726
  export class ServiceReader {
730
727
  nit = new SectionAssembler(PID_NIT);
731
728
  sdt = new SectionAssembler(PID_SDT);
729
+ pat = new SectionAssembler(PID_PAT);
732
730
  packets = new PacketStream();
731
+ requireNit;
733
732
  network = null;
734
733
  transport = null;
734
+ /** PAT に局が 1 つも無かった (`requireNit: false` のときだけ読む) */
735
+ empty = false;
736
+ constructor({ requireNit = true } = {}) {
737
+ this.requireNit = requireNit;
738
+ }
735
739
  get complete() {
736
- return this.network !== null && this.transport !== null;
740
+ return this.requireNit
741
+ ? this.network !== null && this.transport !== null
742
+ : this.transport !== null || this.empty;
737
743
  }
738
744
  /** 任意の長さのバイト列を渡す。揃ったら true */
739
745
  feed(chunk) {
@@ -748,16 +754,22 @@ export class ServiceReader {
748
754
  this.transport = parseSdt(section) ?? this.transport;
749
755
  }
750
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
+ }
751
763
  }
752
764
  return this.complete;
753
765
  }
754
766
  /** 録るに値するサービスだけ。リモコン番号は NIT のものを配る */
755
767
  services() {
756
- if (this.network === null || this.transport === null)
768
+ const transport = this.transport;
769
+ if (transport === null)
757
770
  return [];
758
- const { networkId, remoteControlKeyId } = this.network;
759
- const { transportStreamId } = this.transport;
760
- return this.transport.services
771
+ const { transportStreamId, originalNetworkId } = transport;
772
+ const found = (networkId, remoteControlKeyId) => transport.services
761
773
  .filter((service) => SERVICE_TYPES.has(service.serviceType))
762
774
  .map((service) => ({
763
775
  ...service,
@@ -765,5 +777,13 @@ export class ServiceReader {
765
777
  transportStreamId,
766
778
  remoteControlKeyId,
767
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);
768
788
  }
769
789
  }
@@ -5,8 +5,8 @@
5
5
  * ([agent.md](https://github.com/danything/denpa/blob/main/docs/agent.md))、選り分けるのは denpa の仕事。
6
6
  *
7
7
  * **これをやらないと 1 つの録画に複数の番組が入る。** 実機の TOKYO MX は
8
- * 1 本の TS に MX1 と MX2 を載せていて、丸ごと保存すると ffprobe が 2 つの
9
- * プログラムを返す (これで CM 検出の fps を読み違えたことがある)。
8
+ * 1 本の TS に MX1 と MX2 が入っていて、丸ごと保存すると ffprobe が 2 つの
9
+ * プログラムを返す。
10
10
  *
11
11
  * 残すものは次のとおり。
12
12
  *
@@ -41,7 +41,7 @@ export declare class ServiceFilter {
41
41
  private tsid;
42
42
  private copyNeverNow;
43
43
  private streamsNow;
44
- /** PMT を読む前に届いた、局の分からないパケット (写したもの)。届いた順 */
44
+ /** PMT を読む前に届いた、局の分からないパケット (コピーしたもの)。届いた順 */
45
45
  private held;
46
46
  constructor(serviceId: number);
47
47
  /** その局の PMT を読めたか。読めるまで映像は 1 バイトも出ない (届いたものは取っておく) */
@@ -66,10 +66,10 @@ export declare class ServiceFilter {
66
66
  /**
67
67
  * **中継の番号 (transport_stream_id)。** PAT を読むまでは null。
68
68
  *
69
- * 絞るのに要るわけではありません。**データ放送が受信機に訊いてくる**ので
70
- * 覚えておきます。テレ朝の TVer リンクは `browser.getProgramID(4)` で
71
- * これを取り、TVer のサーバへそのまま送る。`null` のまま渡すと文字の
72
- * "null" が飛んで、あちらが断り、画面には `DT-RE` とだけ出ます
69
+ * 絞るのに要るわけではない。**データ放送が受信機に訊いてくる**ので
70
+ * 覚えておく。テレ朝の TVer リンクは `browser.getProgramID(4)` で
71
+ * これを取り、TVer のサーバへそのまま送る。`null` のまま渡すと文字列の
72
+ * "null" が送られて、あちらが断り、画面には `DT-RE` とだけ出る
73
73
  * ([server/live.ts](https://github.com/danything/denpa/blob/main/src/lib/server/live.ts) の `programInfo`)
74
74
  */
75
75
  get transportStreamId(): number | null;
@@ -84,7 +84,7 @@ export declare class ServiceFilter {
84
84
  * その局のぶんだけを PMT の後ろに足す (並びは届いた順のまま。読む側は PAT・PMT を先に知る)
85
85
  */
86
86
  filter(chunk: Uint8Array): Uint8Array;
87
- /** PMT を読む前のパケットを取っておく。**写す** (パケットは届いた塊の上の窓。`PacketStream`) */
87
+ /** PMT を読む前のパケットを取っておく。**コピーする** (パケットは届いたチャンクの一部を指す。`PacketStream`) */
88
88
  private hold;
89
89
  /** 取っておいたもののうち、その局のぶんを出す */
90
90
  private release;
@@ -101,6 +101,6 @@ export declare class ServiceFilter {
101
101
  */
102
102
  export declare class StreamsWatch {
103
103
  private seen;
104
- /** `filter` に塊を通したあとに呼ぶ */
104
+ /** `filter` にチャンクを通したあとに呼ぶ */
105
105
  changed(filter: ServiceFilter): boolean;
106
106
  }
@@ -5,8 +5,8 @@
5
5
  * ([agent.md](https://github.com/danything/denpa/blob/main/docs/agent.md))、選り分けるのは denpa の仕事。
6
6
  *
7
7
  * **これをやらないと 1 つの録画に複数の番組が入る。** 実機の TOKYO MX は
8
- * 1 本の TS に MX1 と MX2 を載せていて、丸ごと保存すると ffprobe が 2 つの
9
- * プログラムを返す (これで CM 検出の fps を読み違えたことがある)。
8
+ * 1 本の TS に MX1 と MX2 が入っていて、丸ごと保存すると ffprobe が 2 つの
9
+ * プログラムを返す。
10
10
  *
11
11
  * 残すものは次のとおり。
12
12
  *
@@ -37,7 +37,7 @@ const KEEP = new Set([
37
37
  ]);
38
38
  const DESC_CA = 0x09;
39
39
  /**
40
- * PMT を読むまで取っておくパケットの数の上限。**選局したての頭を捨てない** (`ServiceFilter.filter`)。
40
+ * PMT を読むまで取っておくパケットの数の上限。**選局したての先頭を捨てない** (`ServiceFilter.filter`)。
41
41
  *
42
42
  * PAT と PMT はそれぞれ 100ms ごとに来るので、待つのはふつう 0.2 秒まで。地上波 (17Mbit/s) なら 2,300 個、
43
43
  * BS の中継丸ごと (52Mbit/s) でも 7,000 個ほど。来ない (局を読み違えた) ときに抱え込み続けないよう、超えたら古いほうから捨てる
@@ -104,7 +104,7 @@ export class ServiceFilter {
104
104
  tsid = null;
105
105
  copyNeverNow = false;
106
106
  streamsNow = null;
107
- /** PMT を読む前に届いた、局の分からないパケット (写したもの)。届いた順 */
107
+ /** PMT を読む前に届いた、局の分からないパケット (コピーしたもの)。届いた順 */
108
108
  held = [];
109
109
  constructor(serviceId) {
110
110
  this.serviceId = serviceId;
@@ -137,10 +137,10 @@ export class ServiceFilter {
137
137
  /**
138
138
  * **中継の番号 (transport_stream_id)。** PAT を読むまでは null。
139
139
  *
140
- * 絞るのに要るわけではありません。**データ放送が受信機に訊いてくる**ので
141
- * 覚えておきます。テレ朝の TVer リンクは `browser.getProgramID(4)` で
142
- * これを取り、TVer のサーバへそのまま送る。`null` のまま渡すと文字の
143
- * "null" が飛んで、あちらが断り、画面には `DT-RE` とだけ出ます
140
+ * 絞るのに要るわけではない。**データ放送が受信機に訊いてくる**ので
141
+ * 覚えておく。テレ朝の TVer リンクは `browser.getProgramID(4)` で
142
+ * これを取り、TVer のサーバへそのまま送る。`null` のまま渡すと文字列の
143
+ * "null" が送られて、あちらが断り、画面には `DT-RE` とだけ出る
144
144
  * ([server/live.ts](https://github.com/danything/denpa/blob/main/src/lib/server/live.ts) の `programInfo`)
145
145
  */
146
146
  get transportStreamId() {
@@ -182,7 +182,7 @@ export class ServiceFilter {
182
182
  }
183
183
  return concatBytes(out);
184
184
  }
185
- /** PMT を読む前のパケットを取っておく。**写す** (パケットは届いた塊の上の窓。`PacketStream`) */
185
+ /** PMT を読む前のパケットを取っておく。**コピーする** (パケットは届いたチャンクの一部を指す。`PacketStream`) */
186
186
  hold(ts) {
187
187
  this.held.push(ts.slice());
188
188
  // 溢れたら古い半分を捨てる (1 つずつ捨てると、来ないあいだずっと詰め直すことになる)
@@ -254,7 +254,7 @@ export class ServiceFilter {
254
254
  */
255
255
  export class StreamsWatch {
256
256
  seen = null;
257
- /** `filter` に塊を通したあとに呼ぶ */
257
+ /** `filter` にチャンクを通したあとに呼ぶ */
258
258
  changed(filter) {
259
259
  const now = filter.streams;
260
260
  if (now === null)
@@ -12,14 +12,14 @@
12
12
  * - 音声 … 48kHz ステレオの AAC-LC の**無音**を ADTS で。1 コマ 7 バイト
13
13
  *
14
14
  * 規格は ISO/IEC 13818-2 (映像) と 13818-7 / 14496-3 (AAC)。表の出どころは
15
- * 各関数の頭に書いてある。
15
+ * 各関数の冒頭に書いてある。
16
16
  */
17
17
  /** 29.97 コマ/秒の 1 コマ (90kHz)。放送と同じ */
18
18
  export declare const FRAME_TICKS = 3003;
19
19
  /** AAC の 1 コマ (1024 標本 @48kHz を 90kHz で) */
20
20
  export declare const AUDIO_TICKS = 1920;
21
21
  /**
22
- * I フレームを 1 枚。**毎枚、系列ヘッダから書く。** どこから受け取っても頭からデコードできる
22
+ * I フレームを 1 枚。**毎枚、系列ヘッダから書く。** どこから受け取っても先頭からデコードできる
23
23
  * (放送は GOP ごとに書く。試験ではその手間を省く)。
24
24
  *
25
25
  * @param n 何枚目か。色と time_code に使う
@@ -27,21 +27,21 @@ export declare const AUDIO_TICKS = 1920;
27
27
  */
28
28
  export declare function mpeg2Frame(n: number, color?: (col: number, row: number, n: number, field: 0 | 1) => [number, number, number]): Uint8Array;
29
29
  /**
30
- * AAC-LC の無音を 1 コマ、ADTS の頭付きで。48kHz・ステレオ (channel_configuration 2)。
30
+ * AAC-LC の無音を 1 コマ、ADTS のヘッダ付きで。48kHz・ステレオ (channel_configuration 2)。
31
31
  *
32
32
  * 中身は CPE が 1 つ: 両耳とも `max_sfb = 0` (係数を 1 つも持たない) で、あとは END。
33
33
  * 55 ビットなので 7 バイトに収まる (14496-3 の raw_data_block)
34
34
  */
35
35
  export declare function silentAac(): Uint8Array;
36
36
  /**
37
- * PES にする。映像は長さ 0 (「次の頭まで」。放送もそう書く)、音声は長さを書く
37
+ * PES にする。映像は長さ 0 (「次の PES の先頭まで」。放送もそう書く)、音声は長さを書く
38
38
  *
39
39
  * @param streamId 0xE0 (映像) / 0xC0 (音声) / 0xBD (字幕。`synth-caption.ts`)
40
40
  */
41
41
  export declare function pes(streamId: number, pts: number, data: Uint8Array): Uint8Array;
42
42
  /**
43
43
  * PES を TS パケットに割る。**最後のパケットは適応フィールドで詰める** (中身を 0xFF で
44
- * 埋めると PES の続きに見える)。頭のパケットに PCR を載せることもできる
44
+ * 埋めると PES の続きに見える)。最初のパケットに PCR を載せることもできる
45
45
  *
46
46
  * @param counter 連続性カウンタの続き。返り値の `counter` を次に渡す
47
47
  */
package/dist/synth-av.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * - 音声 … 48kHz ステレオの AAC-LC の**無音**を ADTS で。1 コマ 7 バイト
13
13
  *
14
14
  * 規格は ISO/IEC 13818-2 (映像) と 13818-7 / 14496-3 (AAC)。表の出どころは
15
- * 各関数の頭に書いてある。
15
+ * 各関数の冒頭に書いてある。
16
16
  */
17
17
  import { concatBytes } from "./bytes.js";
18
18
  import { PACKET, SYNC } from "./psi.js";
@@ -96,7 +96,7 @@ function colorAt(col, row, n, field) {
96
96
  return [y, 96 + ((row * 16) % 64), 160 - ((col * 4) % 64)];
97
97
  }
98
98
  /**
99
- * I フレームを 1 枚。**毎枚、系列ヘッダから書く。** どこから受け取っても頭からデコードできる
99
+ * I フレームを 1 枚。**毎枚、系列ヘッダから書く。** どこから受け取っても先頭からデコードできる
100
100
  * (放送は GOP ごとに書く。試験ではその手間を省く)。
101
101
  *
102
102
  * @param n 何枚目か。色と time_code に使う
@@ -162,7 +162,7 @@ export function mpeg2Frame(n, color = colorAt) {
162
162
  bits.put(0, 1); // progressive_frame = 0
163
163
  bits.put(0, 1); // composite_display_flag
164
164
  for (let row = 0; row < mbHeight; row++) {
165
- // 1 行を 1 スライスに。予測は行の頭で 128 に戻る (7.2.1)
165
+ // 1 行を 1 スライスに。予測は行の先頭で 128 に戻る (7.2.1)
166
166
  bits.startCode(row + 1);
167
167
  bits.put(8, 5); // quantiser_scale_code (DC だけなので効かない)
168
168
  bits.put(0, 1); // extra_bit_slice
@@ -187,7 +187,7 @@ export function mpeg2Frame(n, color = colorAt) {
187
187
  return bits.bytes();
188
188
  }
189
189
  /**
190
- * AAC-LC の無音を 1 コマ、ADTS の頭付きで。48kHz・ステレオ (channel_configuration 2)。
190
+ * AAC-LC の無音を 1 コマ、ADTS のヘッダ付きで。48kHz・ステレオ (channel_configuration 2)。
191
191
  *
192
192
  * 中身は CPE が 1 つ: 両耳とも `max_sfb = 0` (係数を 1 つも持たない) で、あとは END。
193
193
  * 55 ビットなので 7 バイトに収まる (14496-3 の raw_data_block)
@@ -224,7 +224,7 @@ export function silentAac() {
224
224
  head.put(0, 2); // number_of_raw_data_blocks - 1
225
225
  return Uint8Array.from([...head.bytes(), ...payload]);
226
226
  }
227
- /** PTS/DTS の 5 バイト (2.4.3.7)。頭の 4 ビットは '0010' (PTS だけ) */
227
+ /** PTS/DTS の 5 バイト (2.4.3.7)。先頭の 4 ビットは '0010' (PTS だけ) */
228
228
  function stamp(pts) {
229
229
  const value = pts % 2 ** 33;
230
230
  const high = Math.floor(value / 2 ** 30) & 0x07;
@@ -239,7 +239,7 @@ function stamp(pts) {
239
239
  ];
240
240
  }
241
241
  /**
242
- * PES にする。映像は長さ 0 (「次の頭まで」。放送もそう書く)、音声は長さを書く
242
+ * PES にする。映像は長さ 0 (「次の PES の先頭まで」。放送もそう書く)、音声は長さを書く
243
243
  *
244
244
  * @param streamId 0xE0 (映像) / 0xC0 (音声) / 0xBD (字幕。`synth-caption.ts`)
245
245
  */
@@ -259,7 +259,7 @@ export function pes(streamId, pts, data) {
259
259
  }
260
260
  /**
261
261
  * PES を TS パケットに割る。**最後のパケットは適応フィールドで詰める** (中身を 0xFF で
262
- * 埋めると PES の続きに見える)。頭のパケットに PCR を載せることもできる
262
+ * 埋めると PES の続きに見える)。最初のパケットに PCR を載せることもできる
263
263
  *
264
264
  * @param counter 連続性カウンタの続き。返り値の `counter` を次に渡す
265
265
  */
@@ -271,7 +271,7 @@ export function packetizePes(pid, data, counter, pcr = null) {
271
271
  const withPcr = at === 0 && pcr !== null;
272
272
  const rest = data.length - at;
273
273
  /*
274
- * 適応フィールドの長さ。PCR を載せるなら旗 1 バイト + 6 バイト。**中身が
274
+ * 適応フィールドの長さ。PCR を載せるならフラグ 1 バイト + 6 バイト。**中身が
275
275
  * 足りないときもここを伸ばして詰める** (中身を 0xFF で埋めると PES の続きに見える)
276
276
  */
277
277
  const least = withPcr ? 7 : 0;
@@ -30,7 +30,7 @@ export declare function text(body: number[], language?: number): Uint8Array;
30
30
  */
31
31
  export declare function drcsUnit(code: number, rows: string[]): number[];
32
32
  /**
33
- * ffmpeg が `-c:s copy -f matroska` で書くのと同じ骨組みの Matroska (トラック 1 本・コマごとに塊 1 つ)。
33
+ * ffmpeg が `-c:s copy -f matroska` で書くのと同じ骨組みの Matroska (トラック 1 本・コマごとに SimpleBlock 1 つ)。
34
34
  * 中身は字幕の PES の中身 (`text(...)` など) をそのまま入れる
35
35
  *
36
36
  * @param frames [時刻 (ミリ秒。65535 まで), 中身] の並び
@@ -100,7 +100,7 @@ function element(id, body) {
100
100
  return [...id, 0x01, 0, 0, 0, (size >>> 24) & 0xff, ...u24(size), ...body];
101
101
  }
102
102
  /**
103
- * ffmpeg が `-c:s copy -f matroska` で書くのと同じ骨組みの Matroska (トラック 1 本・コマごとに塊 1 つ)。
103
+ * ffmpeg が `-c:s copy -f matroska` で書くのと同じ骨組みの Matroska (トラック 1 本・コマごとに SimpleBlock 1 つ)。
104
104
  * 中身は字幕の PES の中身 (`text(...)` など) をそのまま入れる
105
105
  *
106
106
  * @param frames [時刻 (ミリ秒。65535 まで), 中身] の並び
package/dist/synth.d.ts CHANGED
@@ -2,17 +2,17 @@
2
2
  * TS のセクションを組み立てる。
3
3
  *
4
4
  * 実チューナーが無いので、解析のテストにも偽エージェントにも「それらしい TS」が要る。
5
- * 読む側 (psi.ts・eit.ts・dsmcc.ts・ait.ts、denpa の logo.ts など) と対になる書く側で、ここだけが仕様の写し。
5
+ * 読む側 (psi.ts・eit.ts・dsmcc.ts・ait.ts、denpa の logo.ts など) と対になる書く側で、仕様を書き写しているのはここだけ。
6
6
  */
7
7
  import { withCrc } from "./psi.js";
8
8
  export { withCrc };
9
- /** 表のバージョンと、何本に分けたうちのどれか。指定しなければバージョン 0・節 0/0 */
9
+ /** 表のバージョンと、何本に分けたうちのどれか。指定しなければバージョン 0・セクション 0/0 */
10
10
  export interface SectionPart {
11
11
  version?: number;
12
12
  sectionNumber?: number;
13
13
  lastSectionNumber?: number;
14
14
  }
15
- /** PCR の基準部 (90kHz) を適応フィールドに書く (旗は呼ぶ側)。拡張部 (27MHz の端数) は読む側が見ていないので 0 */
15
+ /** PCR の基準部 (90kHz) を適応フィールドに書く (フラグは呼ぶ側が立てる)。拡張部 (27MHz の端数) は読む側が見ていないので 0 */
16
16
  export declare function putPcr(packet: Uint8Array, pcr: number): void;
17
17
  /**
18
18
  * 文字列を ARIB STD-B24 の 8 単位符号にする。
@@ -113,9 +113,9 @@ options?: SectionPart & {
113
113
  export declare function satelliteDeliveryDescriptor(frequency: number, symbolRate?: number): number[];
114
114
  /** 地上の分配の記述子 (0xFA)。周波数は Hz で渡す (1/7 MHz 単位に丸める) */
115
115
  export declare function terrestrialDeliveryDescriptor(areaCode: number, frequencies: number[]): number[];
116
- /** セクションを TS パケットに詰める。頭のパケットには pointer_field が付く */
116
+ /** セクションを TS パケットに詰める。最初のパケットには pointer_field が付く */
117
117
  export declare function packetize(pid: number, section: Uint8Array, counter?: number): Uint8Array;
118
- /** 部品を 1 本のバイト列に繋ぐ。組んだパケットを流し込むときの糊 */
118
+ /** 部品を 1 本のバイト列に繋ぐ。組んだパケットを流し込むときに使う */
119
119
  export declare function stream(...parts: Uint8Array[]): Uint8Array;
120
120
  /** PCR を 1 つ運ぶだけのパケット */
121
121
  export declare function pcrPacket(pid: number, seconds: number, counter?: number): Uint8Array;
@@ -124,7 +124,7 @@ export declare function tdtPacket(unixMs: number): Uint8Array;
124
124
  /** PAT。サービス ID → PMT の PID */
125
125
  export declare function patSection(programs: [number, number][], transportStreamId?: number): Uint8Array;
126
126
  /**
127
- * PMT。ロゴのカルーセルが乗る ES を 1 つだけ持たせる。
127
+ * PMT。ロゴのカルーセルを運ぶ ES を 1 つだけ持たせる。
128
128
  * 目印は stream_identifier_descriptor (0x52) の component_tag
129
129
  */
130
130
  export declare function pmtSection(serviceId: number, esPid: number, componentTag: number): Uint8Array;
@@ -198,11 +198,11 @@ export declare const BML_TS: {
198
198
  };
199
199
  /** BML の ES が 1 本だけ載った PMT (`BML_TS` の割り振り) */
200
200
  export declare function bmlPmt(): Uint8Array;
201
- /** PAT と PMT。データ放送のストリームの頭 */
201
+ /** PAT と PMT。データ放送のストリームの先頭 */
202
202
  export declare function bmlHead(): Uint8Array[];
203
203
  /**
204
204
  * モジュール 1 つぶんの中身。**放送は multipart/mixed で複数のファイルを詰める。**
205
- * 本物と同じく、頭に Content-Type、境界で区切って各ファイルに Content-Location
205
+ * 本物と同じく、先頭に Content-Type、境界で区切って各ファイルに Content-Location
206
206
  */
207
207
  export declare function multipartModule(files: [location: string, type: string, body: string][]): Uint8Array;
208
208
  /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在り処の印** */
@@ -213,7 +213,7 @@ export interface SynthHybridcastApp {
213
213
  /** `CONTROL` の値。既定は autostart */
214
214
  controlCode?: number;
215
215
  name?: string;
216
- /** URL の頭。**通信路で運ぶもの** (protocol_id = 3) として入れる */
216
+ /** URL の先頭部分。**通信路で運ぶもの** (protocol_id = 3) として入れる */
217
217
  base?: string;
218
218
  /** その先のパス */
219
219
  path?: string;
@@ -221,6 +221,6 @@ export interface SynthHybridcastApp {
221
221
  /**
222
222
  * AIT (table_id 0x74)。**Hybridcast のアプリのアドレス (URL) が載っている表。**
223
223
  *
224
- * 中身そのものは電波に乗っていないので、ここに入るのは URL と名前だけ
224
+ * アプリそのものは放送では送られていないので、ここに入るのは URL と名前だけ
225
225
  */
226
226
  export declare function aitSection(applications: SynthHybridcastApp[], applicationType?: number): Uint8Array;