@danything/arib 2.4.0 → 2.5.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 CHANGED
@@ -3,7 +3,7 @@
3
3
  日本のデジタル放送 (ISDB-T / ISDB-S / ISDB-C、ARIB 規格) の MPEG-TS と、4K の放送の MMT/TLV を TypeScript で読むライブラリです。
4
4
  録画サーバ [denpa](https://github.com/danything/denpa) の中で使っているものを切り出しました。
5
5
 
6
- - PSI (`psi`): パケットの切り出し、セクションの組み立て、PAT / PMT / SDT / NIT。衛星はネットワークの全部の TS の NIT と、ほかの TS の SDT もそろえて読む。ケーブルは SDT だけで局をそろえる
6
+ - PSI (`psi`): パケットの切り出し、セクションの組み立て、PAT / PMT / SDT / NIT。衛星は、ネットワークにある全部の TS を載せた NIT と、ほかの TS の SDT もそろえて読む。ケーブルは SDT だけで局をそろえる
7
7
  - 映像・音声の PES と ADTS を取り出す (`pes`)
8
8
  - 番組表の EIT[p/f] と EIT[schedule] を、ジャンル・音声・拡張形式イベント・シリーズ記述子まで読む。放送の時刻 (TDT / TOT) もここ (`eit`)
9
9
  - ARIB STD-B24 の 8 単位符号 (JIS X 0213 の 1 面・2 面まで) と ARIB 外字を文字列にする (`aribtext`、`aribtext-gaiji`)
@@ -13,7 +13,7 @@
13
13
  - 1 つの局だけを TS のまま抜き出す (`service-filter`)
14
14
  - 4K の放送 (MMT/TLV。ARIB STD-B32・B60) の土台として、TLV パケットの切り出し (`tlv`)、IP とヘッダー圧縮した IP から UDP の中身を取り出す処理 (`tlv-ip`)、MMTP のヘッダーとスクランブルの印、信号メッセージの分割と集約 (`mmtp`)
15
15
  - 4K の放送の表は、TLV-NIT・PLT・MPT・MH-SDT・MH-TOT (`mmt-si`)。局の一覧と、局ごとの CA_system_id (MPT のアクセス制御記述子) をそろえて読む `TlvServiceReader` もここ (TS の `psi` の `ServiceReader` に当たる)。番組表の MH-EIT は TS と同じ形式の番組にする (`mh-eit`)。1 つの局だけを TLV のまま抜き出す (`mmt-service-filter`)
16
- - 4K の放送の映像 (HEVC)・音声 (AAC)・字幕 (TTML) のアクセスユニットを、MPT の記述子から付けた PTS・DTS (90 kHz) と一緒に取り出す (`mmt-media`)。音声は LATM を外した AAC のフレームと AudioSpecificConfig、映像は NAL ユニットの並び。字幕の TTML は、TS の字幕と同じ字幕の文字の配置にする (`ttml`)。スクランブルの復号は無い
16
+ - 4K の放送の映像 (HEVC)・音声 (AAC)・字幕 (TTML) のアクセスユニットを、MPT の記述子から付けた PTS・DTS (90 kHz) と一緒に取り出す (`mmt-media`)。音声は LATM を外した AAC のフレームと AudioSpecificConfig、映像は NAL ユニットの並び。字幕の TTML は、TS の字幕と同じ形式 (字の置き場所まで決めた JSON) にする (`ttml`)。スクランブルの復号は無い
17
17
  - 試験用の TS (PSI・EIT・字幕・カルーセル・映像と音声) を組み立てる (`synth`、`synth-caption`、`synth-av`)
18
18
  - 試験用の TLV (TLV・IP・MMTP・MMT-SI と、MPU の中身・時刻の記述子・LATM) を組み立てる (`synth-mmt`)。本物の HEVC と AAC を時刻を進めて流し続ける (`synth-mmt-av`)
19
19
  - ほかのモジュールが使う道具として、バイト列 (`bytes`)、ビット単位の読み取り (`bits`)、言語の名前 (`language`)、文字の表 (`b24-tables`)、字幕の外字の画像をなめらかにする処理 (`drcs-smooth`)
package/dist/ait.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * AIT (Application Information Table) を読む。**Hybridcast の在り処。**
2
+ * AIT (Application Information Table) を読む。**Hybridcast の在りか。**
3
3
  *
4
4
  * データ放送 (BML) と違って、**Hybridcast のアプリは放送では送られていません**。
5
5
  * 送られているのはこの表だけで、中身は「どこにアプリがあるか」のアドレス (URL) です。
@@ -11,9 +11,9 @@
11
11
  * └ simple_application_location_descriptor (その先のパス) → アプリの URL
12
12
  * ```
13
13
  *
14
- * ここは読んで在ることを言うだけです。開くのはデータ放送が起動を求めたときで
14
+ * ここはアプリの URL を読むだけです。開くのはデータ放送が起動を求めたときで
15
15
  * (画面の `HybridcastFrame`)、受信機の API は用意できる範囲だけ。放送局のサーバが
16
- * 認証された受信機を期待するものは、denpa 側の努力では越えられません
16
+ * 認証された受信機を求めるアプリは、denpa では動きません
17
17
  * ([docs/stream.md](https://github.com/danything/denpa/blob/main/docs/stream.md#hybridcast))。
18
18
  */
19
19
  /**
package/dist/ait.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * AIT (Application Information Table) を読む。**Hybridcast の在り処。**
2
+ * AIT (Application Information Table) を読む。**Hybridcast の在りか。**
3
3
  *
4
4
  * データ放送 (BML) と違って、**Hybridcast のアプリは放送では送られていません**。
5
5
  * 送られているのはこの表だけで、中身は「どこにアプリがあるか」のアドレス (URL) です。
@@ -11,9 +11,9 @@
11
11
  * └ simple_application_location_descriptor (その先のパス) → アプリの URL
12
12
  * ```
13
13
  *
14
- * ここは読んで在ることを言うだけです。開くのはデータ放送が起動を求めたときで
14
+ * ここはアプリの URL を読むだけです。開くのはデータ放送が起動を求めたときで
15
15
  * (画面の `HybridcastFrame`)、受信機の API は用意できる範囲だけ。放送局のサーバが
16
- * 認証された受信機を期待するものは、denpa 側の努力では越えられません
16
+ * 認証された受信機を求めるアプリは、denpa では動きません
17
17
  * ([docs/stream.md](https://github.com/danything/denpa/blob/main/docs/stream.md#hybridcast))。
18
18
  */
19
19
  import { decodeAribText } from "./aribtext.js";
package/dist/mh-eit.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 4K の放送 (MMT/TLV) の番組表 MH-EIT (ARIB STD-B60) を、TS の番組表と同じ `EitEvent` に読む。
3
3
  *
4
- * 並びは TS の EIT と同じで、違うのは次の所 (実機のケーブルの BS4K で確かめた)。
4
+ * 並びは TS の EIT と同じで、違うのは次の所 (ケーブルで受けた実機の BS4K で確かめた)。
5
5
  *
6
6
  * - TS の transport_stream_id の位置に tlv_stream_id が入る。`EitEvent.transportStreamId` にはそれを入れる
7
7
  * - table_id は p/f が 0x8B、予定の基本が 0x8C から、詳細が 0x94 から。**TS の番号 (0x4E・0x50・0x58) に直して返す**
@@ -22,7 +22,7 @@ export declare function parseMhEit(section: Uint8Array): EitSection | null;
22
22
  * 流れてくる TLV のバイト列から番組表を溜める。**読み方のほかは TS の `EpgReader` と同じ**
23
23
  * (揃ったかの数え方、別の表で来るものの混ぜ方、p/f の「いま」)。
24
24
  *
25
- * MH-EIT は自分の TLV のストリームの局のぶんだけが来る (実機のケーブルの BS4K で確かめた)。
25
+ * MH-EIT は自分の TLV ストリームの局のぶんだけ来る (ケーブルで受けた実機の BS4K で確かめた)。
26
26
  * TS の `other` (ほかの TS の局の番組表) に当たるものは無い
27
27
  */
28
28
  export declare class MmtEpgReader extends EpgReader {
package/dist/mh-eit.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 4K の放送 (MMT/TLV) の番組表 MH-EIT (ARIB STD-B60) を、TS の番組表と同じ `EitEvent` に読む。
3
3
  *
4
- * 並びは TS の EIT と同じで、違うのは次の所 (実機のケーブルの BS4K で確かめた)。
4
+ * 並びは TS の EIT と同じで、違うのは次の所 (ケーブルで受けた実機の BS4K で確かめた)。
5
5
  *
6
6
  * - TS の transport_stream_id の位置に tlv_stream_id が入る。`EitEvent.transportStreamId` にはそれを入れる
7
7
  * - table_id は p/f が 0x8B、予定の基本が 0x8C から、詳細が 0x94 から。**TS の番号 (0x4E・0x50・0x58) に直して返す**
@@ -232,7 +232,7 @@ export function parseMhEit(section) {
232
232
  * 流れてくる TLV のバイト列から番組表を溜める。**読み方のほかは TS の `EpgReader` と同じ**
233
233
  * (揃ったかの数え方、別の表で来るものの混ぜ方、p/f の「いま」)。
234
234
  *
235
- * MH-EIT は自分の TLV のストリームの局のぶんだけが来る (実機のケーブルの BS4K で確かめた)。
235
+ * MH-EIT は自分の TLV ストリームの局のぶんだけ来る (ケーブルで受けた実機の BS4K で確かめた)。
236
236
  * TS の `other` (ほかの TS の局の番組表) に当たるものは無い
237
237
  */
238
238
  export class MmtEpgReader extends EpgReader {
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * **時刻はパケットには無く、MPT の記述子にある。** MPU タイムスタンプ記述子が MPU の最初の提示時刻を、
9
9
  * MPU 拡張タイムスタンプ記述子がアクセスユニットごとのずれを、これから来る MPU のぶんまで並べる
10
- * (実機のケーブルの BS4K で確かめた。映像は 1 MPU に 32 コマ、音声は 4 フレーム)。
10
+ * (ケーブルで受けた実機の BS4K で確かめた。映像は 1 MPU に 32 コマ、音声は 4 フレーム)。
11
11
  * 時刻は NTP の 64 ビットで来るので、ここで **90 kHz** に直す (TS の PTS と同じ物差し。一周はしない)。
12
12
  *
13
13
  * 中身の形式は次のとおり。実機の録りは映像・音声がスクランブルされていて中身を読めないので、どれも推定。
package/dist/mmt-media.js CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * **時刻はパケットには無く、MPT の記述子にある。** MPU タイムスタンプ記述子が MPU の最初の提示時刻を、
9
9
  * MPU 拡張タイムスタンプ記述子がアクセスユニットごとのずれを、これから来る MPU のぶんまで並べる
10
- * (実機のケーブルの BS4K で確かめた。映像は 1 MPU に 32 コマ、音声は 4 フレーム)。
10
+ * (ケーブルで受けた実機の BS4K で確かめた。映像は 1 MPU に 32 コマ、音声は 4 フレーム)。
11
11
  * 時刻は NTP の 64 ビットで来るので、ここで **90 kHz** に直す (TS の PTS と同じ物差し。一周はしない)。
12
12
  *
13
13
  * 中身の形式は次のとおり。実機の録りは映像・音声がスクランブルされていて中身を読めないので、どれも推定。
@@ -9,7 +9,7 @@
9
9
  * (PLT・MPT・番組表・ECM)、その局の MPT が挙げるアセット (映像・音声・字幕) の MPU
10
10
  * - 落とす … 詰め物、ほかの局の MPT とそのアセット、PLT がほかの局だけを挙げている並び
11
11
  *
12
- * 実機のケーブルの BS4K は、1 つの TLV のストリームに局が 1 つ (IP の並びも 1 つ) だったので、たいていは
12
+ * ケーブルで受けた実機の BS4K は、1 つの TLV ストリームに局が 1 つ (IP の並びも 1 つ) だったので、たいていは
13
13
  * 詰め物のほかは全部残る。AMT (局ごとの IP の宛先) は 0.8 秒の間に来なかったので使わない。
14
14
  * MPT は 0.1 秒おきに来ていた
15
15
  */
@@ -9,7 +9,7 @@
9
9
  * (PLT・MPT・番組表・ECM)、その局の MPT が挙げるアセット (映像・音声・字幕) の MPU
10
10
  * - 落とす … 詰め物、ほかの局の MPT とそのアセット、PLT がほかの局だけを挙げている並び
11
11
  *
12
- * 実機のケーブルの BS4K は、1 つの TLV のストリームに局が 1 つ (IP の並びも 1 つ) だったので、たいていは
12
+ * ケーブルで受けた実機の BS4K は、1 つの TLV ストリームに局が 1 つ (IP の並びも 1 つ) だったので、たいていは
13
13
  * 詰め物のほかは全部残る。AMT (局ごとの IP の宛先) は 0.8 秒の間に来なかったので使わない。
14
14
  * MPT は 0.1 秒おきに来ていた
15
15
  */
package/dist/mmt-si.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * 記述子のタグは 2 バイトで、**タグが 0xF000 未満なら長さは 1 バイト、0xF000 以上なら 2 バイト**
10
10
  *
11
11
  * 局名や番組名の文字は UTF-8 (TS の ARIB の 8 単位符号ではない)。
12
- * 実機のケーブルの BS4K では、PLT・MPT・MH-SDT[other]・MH-EIT・TLV-NIT を読めた。どれもスクランブルされていない。
12
+ * ケーブルで受けた実機の BS4K では、PLT・MPT・MH-SDT[other]・MH-EIT・TLV-NIT を読めた。どれもスクランブルされていない。
13
13
  */
14
14
  import { type FoundService, type Service } from "./psi.js";
15
15
  export declare const TABLE_MPT = 32;
@@ -93,7 +93,7 @@ export interface MhService extends Service {
93
93
  eitPresentFollowing: boolean;
94
94
  }
95
95
  export interface MhSdt {
96
- /** MH-SDT[other] (ほかの TLV のストリームのもの) なら true */
96
+ /** MH-SDT[other] (ほかの TLV ストリームのもの) なら true */
97
97
  other: boolean;
98
98
  tlvStreamId: number;
99
99
  originalNetworkId: number;
@@ -104,7 +104,7 @@ export interface MhSdt {
104
104
  }
105
105
  /** MH-SDT。局名と種類。TS の SDT と同じ並びで、記述子と文字が MMT のもの */
106
106
  export declare function parseMhSdt(section: Uint8Array): MhSdt | null;
107
- /** TLV-NIT の TLV のストリームの 1 件 */
107
+ /** TLV-NIT にある TLV ストリームの 1 件 */
108
108
  export interface TlvNetworkStream {
109
109
  tlvStreamId: number;
110
110
  originalNetworkId: number;
@@ -160,11 +160,16 @@ export declare class MmtTableStream {
160
160
  * TLV にはリモコン番号が無い (TLV-NIT に見当たらない) ので null。
161
161
  *
162
162
  * 局ごとの CA_system_id は、PLT から package (= service_id) の MPT を引き、そのアクセス制御記述子から読む (`caSystemIdsOf`)。
163
- * free_CA_mode は使わない。実機の BS4K は 0 なのに、映像と音声はスクランブルされていた
163
+ * free_CA_mode は使わない (`MhService.isFree`)。
164
+ *
165
+ * MH-SDT[actual] が来なくても、自分の TLV ストリームは TLV-NIT と PLT で分かる (`ownStream`)。
166
+ * ケーブルで受けた実機の BS4K (QC26) を 0.9 秒読んだときは、MH-SDT[other] は来たが MH-SDT[actual] は来なかった。何秒おきに来るかは確かめていない
164
167
  */
165
168
  export declare class TlvServiceReader {
166
169
  private readonly tables;
167
170
  private plt;
171
+ /** これまでに来た TLV-NIT[actual] のセクションにあった TLV ストリーム (tlv_stream_id → ストリーム) */
172
+ private readonly streams;
168
173
  /** package_id (= service_id) → MPT */
169
174
  private readonly mpts;
170
175
  network: TlvNetwork | null;
@@ -174,6 +179,12 @@ export declare class TlvServiceReader {
174
179
  get mptsKnown(): boolean;
175
180
  /** 任意の長さのバイト列を渡す。揃ったら true */
176
181
  feed(chunk: Uint8Array): boolean;
182
+ /**
183
+ * 自分の TLV ストリーム。TLV-NIT[actual] のストリームのうち、PLT にある package (= service_id) をサービスリスト記述子に持つ最初の 1 つ。
184
+ * 実機では 1 つに決まる。同じ service_id を持つストリームが 2 つあれば、TLV-NIT で先に来たほうを返す。
185
+ * TLV-NIT と PLT のどちらかが来ていないか、当てはまるストリームが無ければ null
186
+ */
187
+ get ownStream(): TlvNetworkStream | null;
177
188
  /** 録画の対象になる種類の局だけ (`psi.SERVICE_TYPES`)。揃うまでは空 */
178
189
  services(): FoundService[];
179
190
  /** その局の MPT のアクセス制御記述子 (package 全体とアセットのもの)。MPT が来なかった局は空 */
package/dist/mmt-si.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * 記述子のタグは 2 バイトで、**タグが 0xF000 未満なら長さは 1 バイト、0xF000 以上なら 2 バイト**
10
10
  *
11
11
  * 局名や番組名の文字は UTF-8 (TS の ARIB の 8 単位符号ではない)。
12
- * 実機のケーブルの BS4K では、PLT・MPT・MH-SDT[other]・MH-EIT・TLV-NIT を読めた。どれもスクランブルされていない。
12
+ * ケーブルで受けた実機の BS4K では、PLT・MPT・MH-SDT[other]・MH-EIT・TLV-NIT を読めた。どれもスクランブルされていない。
13
13
  */
14
14
  import { ascii, u16 } from "./bytes.js";
15
15
  import { parseMjdTime } from "./eit.js";
@@ -389,11 +389,16 @@ export class MmtTableStream {
389
389
  * TLV にはリモコン番号が無い (TLV-NIT に見当たらない) ので null。
390
390
  *
391
391
  * 局ごとの CA_system_id は、PLT から package (= service_id) の MPT を引き、そのアクセス制御記述子から読む (`caSystemIdsOf`)。
392
- * free_CA_mode は使わない。実機の BS4K は 0 なのに、映像と音声はスクランブルされていた
392
+ * free_CA_mode は使わない (`MhService.isFree`)。
393
+ *
394
+ * MH-SDT[actual] が来なくても、自分の TLV ストリームは TLV-NIT と PLT で分かる (`ownStream`)。
395
+ * ケーブルで受けた実機の BS4K (QC26) を 0.9 秒読んだときは、MH-SDT[other] は来たが MH-SDT[actual] は来なかった。何秒おきに来るかは確かめていない
393
396
  */
394
397
  export class TlvServiceReader {
395
398
  tables = new MmtTableStream();
396
399
  plt = null;
400
+ /** これまでに来た TLV-NIT[actual] のセクションにあった TLV ストリーム (tlv_stream_id → ストリーム) */
401
+ streams = new Map();
397
402
  /** package_id (= service_id) → MPT */
398
403
  mpts = new Map();
399
404
  network = null;
@@ -417,13 +422,33 @@ export class TlvServiceReader {
417
422
  if (mpt !== null)
418
423
  this.mpts.set(mpt.packageId, mpt);
419
424
  }
420
- else if (tableId === TABLE_TLV_NIT_ACTUAL && this.network === null)
421
- this.network = parseTlvNit(table);
425
+ else if (tableId === TABLE_TLV_NIT_ACTUAL) {
426
+ const network = parseTlvNit(table);
427
+ if (network === null)
428
+ continue;
429
+ this.network ??= network;
430
+ for (const stream of network.streams)
431
+ this.streams.set(stream.tlvStreamId, stream);
432
+ }
422
433
  else if (tableId === TABLE_MH_SDT_ACTUAL && this.transport === null)
423
434
  this.transport = parseMhSdt(table);
424
435
  }
425
436
  return this.complete;
426
437
  }
438
+ /**
439
+ * 自分の TLV ストリーム。TLV-NIT[actual] のストリームのうち、PLT にある package (= service_id) をサービスリスト記述子に持つ最初の 1 つ。
440
+ * 実機では 1 つに決まる。同じ service_id を持つストリームが 2 つあれば、TLV-NIT で先に来たほうを返す。
441
+ * TLV-NIT と PLT のどちらかが来ていないか、当てはまるストリームが無ければ null
442
+ */
443
+ get ownStream() {
444
+ const packages = new Set((this.plt?.packages ?? []).map((entry) => entry.packageId));
445
+ if (packages.size === 0)
446
+ return null;
447
+ for (const stream of this.streams.values())
448
+ if (stream.services.some((service) => packages.has(service.serviceId)))
449
+ return stream;
450
+ return null;
451
+ }
427
452
  /** 録画の対象になる種類の局だけ (`psi.SERVICE_TYPES`)。揃うまでは空 */
428
453
  services() {
429
454
  const transport = this.transport;
@@ -438,6 +463,8 @@ export class TlvServiceReader {
438
463
  networkId: transport.originalNetworkId,
439
464
  transportStreamId: transport.tlvStreamId,
440
465
  remoteControlKeyId: null,
466
+ // 4K の放送に部分受信 (ワンセグ) は無い
467
+ partialReception: false,
441
468
  }));
442
469
  }
443
470
  /** その局の MPT のアクセス制御記述子 (package 全体とアセットのもの)。MPT が来なかった局は空 */
package/dist/mmtp.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * 映像・音声・字幕を運ぶ MPU (payload_type 0) と、局の中身の一覧や番組表を運ぶ信号メッセージ (2)。
6
6
  * どちらかは packet_id ごとに決まっている。
7
7
  *
8
- * **4K の放送では映像と音声がスクランブルされ、信号はされていない** (実機のケーブルの BS4K で確かめた)。
8
+ * **4K の放送では映像と音声がスクランブルされ、信号はされていない** (ケーブルで受けた実機の BS4K で確かめた)。
9
9
  * スクランブルの印はヘッダー拡張にあり (ARIB STD-B61)、MPU のペイロードヘッダー (8 バイト) までは読める。
10
10
  * 復号はエージェントの仕事 (まだ扱わない)。
11
11
  */
@@ -58,7 +58,7 @@ export declare function parseMessage(data: Uint8Array): SignalingMessage | null;
58
58
  * 信号メッセージに入った表を返す。
59
59
  *
60
60
  * - PA メッセージは表の数と、表ごとの (table_id・バージョン・長さ) を先に並べ、そのあとに表を並べる。
61
- * 表の頭は table_id・バージョン・長さ (16 ビット) で、MPT と PLT はこの形式
61
+ * 表の先頭は table_id・バージョン・長さ (16 ビット) で、MPT と PLT はこの形式
62
62
  * - M2 セクションメッセージ・M2 短セクションメッセージ・CA メッセージは、表を 1 つだけ運ぶ。
63
63
  * M2 セクションは TS のセクションと同じ形式で、末尾に CRC32 が付く (確かめるのは `mmt-si.ts`)
64
64
  */
@@ -68,7 +68,7 @@ export declare function messageTables(message: SignalingMessage): Uint8Array[];
68
68
  *
69
69
  * - 長いメッセージ (番組表の MH-EIT や大きな MPT) は、いくつもの MMTP のパケットに分割して送られる。
70
70
  * 欠けは packet_sequence_number (packet_id ごとに 1 ずつ増える) で見る。続いていなければ、組み立て中のものを
71
- * 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** 実機のケーブルの BS4K は分割したものでも
71
+ * 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** ケーブルで受けた実機の BS4K は分割したものでも
72
72
  * いつも 0 を入れていた
73
73
  * - 短いメッセージは、1 つの MMTP のパケットに長さを前に付けて集約される (aggregation_flag)。
74
74
  * 長さは length_extension_flag が立っていれば 32 ビット、立っていなければ 16 ビット
package/dist/mmtp.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * 映像・音声・字幕を運ぶ MPU (payload_type 0) と、局の中身の一覧や番組表を運ぶ信号メッセージ (2)。
6
6
  * どちらかは packet_id ごとに決まっている。
7
7
  *
8
- * **4K の放送では映像と音声がスクランブルされ、信号はされていない** (実機のケーブルの BS4K で確かめた)。
8
+ * **4K の放送では映像と音声がスクランブルされ、信号はされていない** (ケーブルで受けた実機の BS4K で確かめた)。
9
9
  * スクランブルの印はヘッダー拡張にあり (ARIB STD-B61)、MPU のペイロードヘッダー (8 バイト) までは読める。
10
10
  * 復号はエージェントの仕事 (まだ扱わない)。
11
11
  */
@@ -116,7 +116,7 @@ export function parseMessage(data) {
116
116
  * 信号メッセージに入った表を返す。
117
117
  *
118
118
  * - PA メッセージは表の数と、表ごとの (table_id・バージョン・長さ) を先に並べ、そのあとに表を並べる。
119
- * 表の頭は table_id・バージョン・長さ (16 ビット) で、MPT と PLT はこの形式
119
+ * 表の先頭は table_id・バージョン・長さ (16 ビット) で、MPT と PLT はこの形式
120
120
  * - M2 セクションメッセージ・M2 短セクションメッセージ・CA メッセージは、表を 1 つだけ運ぶ。
121
121
  * M2 セクションは TS のセクションと同じ形式で、末尾に CRC32 が付く (確かめるのは `mmt-si.ts`)
122
122
  */
@@ -155,7 +155,7 @@ const PENDING_MOST = 256 * 1024;
155
155
  *
156
156
  * - 長いメッセージ (番組表の MH-EIT や大きな MPT) は、いくつもの MMTP のパケットに分割して送られる。
157
157
  * 欠けは packet_sequence_number (packet_id ごとに 1 ずつ増える) で見る。続いていなければ、組み立て中のものを
158
- * 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** 実機のケーブルの BS4K は分割したものでも
158
+ * 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** ケーブルで受けた実機の BS4K は分割したものでも
159
159
  * いつも 0 を入れていた
160
160
  * - 短いメッセージは、1 つの MMTP のパケットに長さを前に付けて集約される (aggregation_flag)。
161
161
  * 長さは length_extension_flag が立っていれば 32 ビット、立っていなければ 16 ビット
package/dist/psi.d.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * - SDT (PID 0x0011) … その TS に入っているサービスの ID と種別。衛星はほかの TS のぶん
9
9
  * (SDT[other]) も流れている (`SdtReader`)
10
10
  *
11
- * 局名も SDT から読む。ARIB の文字符号は独自だが、番組表を自分で集めるなら
12
- * どのみち要るので [aribtext.ts](aribtext.ts) に寄せてある。
11
+ * 局名も SDT から読む。ARIB の文字符号は独自だが、番組表を取得するにも要るので
12
+ * [aribtext.ts](aribtext.ts) に寄せてある。
13
13
  */
14
14
  export declare const PACKET = 188;
15
15
  export declare const SYNC = 71;
@@ -23,8 +23,8 @@ export declare const TABLE_PMT = 2;
23
23
  export declare const TABLE_SDT_ACTUAL = 66;
24
24
  /**
25
25
  * スキャンで残すサービス種別。Mirakurun のスキャンが通しているものと同じで、
26
- * ラジオ (0x02) とデータ (0xc0) も入る。映像の無いものを番組表に並べないのは
27
- * 取り込むほう (denpa の epg.ts の DIGITAL_TV)
26
+ * ラジオ (0x02) とデータ (0xc0) も入る。どの種別を番組表に並べるかは
27
+ * 取り込むほうが決める (denpa の schema.ts の `IMPORTED_SERVICE_TYPES`)
28
28
  */
29
29
  export declare const SERVICE_TYPES: Set<number>;
30
30
  /** MPEG-2 の CRC32。セクション末尾の 4 バイトを含めて回すと 0 になる */
@@ -50,7 +50,7 @@ export interface TransportInfo {
50
50
  }
51
51
  export interface NetworkInfo {
52
52
  networkId: number;
53
- /** 最初の TS のリモコン番号。地上波の NIT は自分の TS 1 つだけを載せる */
53
+ /** TS 情報記述子を最初に載せていた TS のリモコン番号。地上波の NIT は自分の TS 1 つだけを載せる */
54
54
  remoteControlKeyId: number | null;
55
55
  transportStreams: NetworkTransport[];
56
56
  }
@@ -78,7 +78,7 @@ export declare class SectionAssembler {
78
78
  * パケットを 1 つ渡す。組み上がったセクションを返す。
79
79
  *
80
80
  * 返りは**読むだけ**にすること。何も組み上がらないとき (ほとんどのパケット) は
81
- * 使い回しの空配列を返す (関係ないパケットのたびに配列を作っていた)
81
+ * 使い回しの空配列を返す (関係ないパケットのたびに配列を作らないため)
82
82
  */
83
83
  feed(packet: Uint8Array): readonly Uint8Array[];
84
84
  private append;
@@ -181,12 +181,39 @@ export interface TerrestrialDelivery {
181
181
  /** 周波数 (Hz)。中継局ごとに並ぶことがある。放送は 1/7 MHz 単位で書く */
182
182
  frequencies: number[];
183
183
  }
184
- export type Delivery = SatelliteDelivery | TerrestrialDelivery;
184
+ /**
185
+ * ケーブルの分配の記述子 (0x44)。どの周波数で、どう変調して流れているか。
186
+ *
187
+ * **並びは推定。** 有線の告示にある 0x44 の本文は読めていない。同じ系統の有線複数搬送波伝送分配システム記述子 (0xF3。総務省の告示の改正案) は
188
+ * 周波数 (32)・0xFF (8)・多重フレーム型式番号 (4)・外符号 (4)・変調 (8)・シンボルレート (28)・内符号 (4) の並びで、0x44 も同じと見ている。
189
+ * DVB (EN 300 468) では、多重フレーム型式番号の 4 ビットは reserved
190
+ */
191
+ export interface CableDelivery {
192
+ kind: "cable";
193
+ /** 周波数 (Hz)。放送は MHz の BCD 8 桁 (小数 4 桁) で書くので、100 Hz 単位 */
194
+ frequency: number;
195
+ /** 多重フレーム型式番号。0x1 は TSMF (53 スロット・最大 15 TS)、0xF は TS が 1 つの搬送波 */
196
+ frameType: number;
197
+ /** 外符号。0x2 は RS(204,188) */
198
+ fecOuter: number;
199
+ /** 変調。0x03 は 64QAM、0x05 は 256QAM */
200
+ modulation: number;
201
+ /** シンボルレート (シンボル/秒)。ISDB-C は 5274000 */
202
+ symbolRate: number;
203
+ /** 内符号。0xF は内符号なし */
204
+ fecInner: number;
205
+ }
206
+ export type Delivery = SatelliteDelivery | TerrestrialDelivery | CableDelivery;
185
207
  /** NIT の TS の 1 件。サービスの一覧に、どこで流れているかが付く */
186
208
  export interface NetworkTransport extends TransportInfo {
187
209
  delivery: Delivery | null;
188
210
  /** その TS のリモコン番号 (TS 情報記述子)。載せていなければ null */
189
211
  remoteControlKeyId: number | null;
212
+ /**
213
+ * 部分受信の局 (ワンセグ) の service_id (部分受信記述子 0xFB)。地上波の NIT[actual] が自分の TS に載せる。
214
+ * 載せていなければ空
215
+ */
216
+ partialReception: number[];
190
217
  }
191
218
  /** そろった NIT。セクションをつないだもの */
192
219
  export interface Network {
@@ -194,8 +221,12 @@ export interface Network {
194
221
  other: boolean;
195
222
  networkId: number;
196
223
  version: number;
224
+ /** ネットワーク名 (0x40)。載っていなければ null。`NitReader` を通さずに作った表では省いてよい */
225
+ name?: string | null;
197
226
  remoteControlKeyId: number | null;
198
227
  transportStreams: NetworkTransport[];
228
+ /** セクションのバイト列 (section_number の順)。読み方を確かめるために記録に出す (ケーブルのスキャン)。省いてよい */
229
+ sections?: Uint8Array[];
199
230
  }
200
231
  /**
201
232
  * NIT を**全部のセクション**そろえて読む。
@@ -257,6 +288,11 @@ export interface FoundService extends Service {
257
288
  networkId: number;
258
289
  transportStreamId: number;
259
290
  remoteControlKeyId: number | null;
291
+ /**
292
+ * 部分受信の局 (ワンセグ) か。NIT の自分の TS に部分受信記述子 (0xFB) があり、そこに載っていれば true。
293
+ * 記述子の無いネットワーク (BS・CS と、確かめていないケーブル) と NIT を読まなかったときは false
294
+ */
295
+ partialReception: boolean;
260
296
  }
261
297
  /**
262
298
  * 流れてくる TS を読んで、NIT と SDT が揃うまで待つ。
@@ -268,7 +304,7 @@ export interface FoundService extends Service {
268
304
  * **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
269
305
  * 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
270
306
  * (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
271
- * NIT が先に来ていれば、その TS のリモコン番号だけを使う。
307
+ * NIT が先に来ていれば、その TS のリモコン番号と部分受信の局だけを使う。
272
308
  * PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
273
309
  */
274
310
  export declare class ServiceReader {
@@ -287,6 +323,6 @@ export declare class ServiceReader {
287
323
  get complete(): boolean;
288
324
  /** 任意の長さのバイト列を渡す。揃ったら true */
289
325
  feed(chunk: Uint8Array): boolean;
290
- /** 録るに値するサービスだけ。リモコン番号は NIT のものを配る */
326
+ /** 録画の対象になる種類の局だけ (`SERVICE_TYPES`)。リモコン番号と部分受信の局は NIT から付ける */
291
327
  services(): FoundService[];
292
328
  }
package/dist/psi.js CHANGED
@@ -8,8 +8,8 @@
8
8
  * - SDT (PID 0x0011) … その TS に入っているサービスの ID と種別。衛星はほかの TS のぶん
9
9
  * (SDT[other]) も流れている (`SdtReader`)
10
10
  *
11
- * 局名も SDT から読む。ARIB の文字符号は独自だが、番組表を自分で集めるなら
12
- * どのみち要るので [aribtext.ts](aribtext.ts) に寄せてある。
11
+ * 局名も SDT から読む。ARIB の文字符号は独自だが、番組表を取得するにも要るので
12
+ * [aribtext.ts](aribtext.ts) に寄せてある。
13
13
  */
14
14
  import { decodeAribText } from "./aribtext.js";
15
15
  import { concatBytes, u16 } from "./bytes.js";
@@ -29,15 +29,18 @@ const TABLE_NIT_ACTUAL = 0x40;
29
29
  const TABLE_NIT_OTHER = 0x41;
30
30
  export const TABLE_SDT_ACTUAL = 0x42;
31
31
  const TABLE_SDT_OTHER = 0x46;
32
+ const DESC_NETWORK_NAME = 0x40;
32
33
  const DESC_SERVICE_LIST = 0x41;
33
34
  const DESC_SATELLITE_DELIVERY = 0x43;
35
+ const DESC_CABLE_DELIVERY = 0x44;
34
36
  const DESC_TERRESTRIAL_DELIVERY = 0xfa;
35
37
  const DESC_SERVICE = 0x48;
36
38
  const DESC_TS_INFORMATION = 0xcd;
39
+ const DESC_PARTIAL_RECEPTION = 0xfb;
37
40
  /**
38
41
  * スキャンで残すサービス種別。Mirakurun のスキャンが通しているものと同じで、
39
- * ラジオ (0x02) とデータ (0xc0) も入る。映像の無いものを番組表に並べないのは
40
- * 取り込むほう (denpa の epg.ts の DIGITAL_TV)
42
+ * ラジオ (0x02) とデータ (0xc0) も入る。どの種別を番組表に並べるかは
43
+ * 取り込むほうが決める (denpa の schema.ts の `IMPORTED_SERVICE_TYPES`)
41
44
  */
42
45
  export const SERVICE_TYPES = new Set([
43
46
  0x01, 0x02, 0xa1, 0xa4, 0xa5, 0xad, 0xc0,
@@ -109,7 +112,7 @@ export class SectionAssembler {
109
112
  * パケットを 1 つ渡す。組み上がったセクションを返す。
110
113
  *
111
114
  * 返りは**読むだけ**にすること。何も組み上がらないとき (ほとんどのパケット) は
112
- * 使い回しの空配列を返す (関係ないパケットのたびに配列を作っていた)
115
+ * 使い回しの空配列を返す (関係ないパケットのたびに配列を作らないため)
113
116
  */
114
117
  feed(packet) {
115
118
  // トランスポートエラーが立っているものは信用しない
@@ -458,6 +461,23 @@ function terrestrialDelivery(descriptor) {
458
461
  frequencies,
459
462
  };
460
463
  }
464
+ /**
465
+ * ケーブルの分配の記述子を読む。周波数は 8 桁の BCD で、上から 4 桁が MHz (0111.0000 MHz)。
466
+ * シンボルレートは 7 桁で、上から 3 桁が Msym/s (005.2740)
467
+ */
468
+ function cableDelivery(descriptor) {
469
+ if (descriptor.length < 11)
470
+ return null;
471
+ return {
472
+ kind: "cable",
473
+ frequency: bcdDigits(descriptor, 0, 8) * 100,
474
+ frameType: descriptor[5] >> 4,
475
+ fecOuter: descriptor[5] & 0x0f,
476
+ modulation: descriptor[6],
477
+ symbolRate: bcdDigits(descriptor, 7, 7) * 100,
478
+ fecInner: descriptor[10] & 0x0f,
479
+ };
480
+ }
461
481
  /** NIT のセクションを 1 つ読む。actual (0x40) と other (0x41) のどちらも */
462
482
  function parseNitSection(section) {
463
483
  const tableId = section[0];
@@ -469,6 +489,11 @@ function parseNitSection(section) {
469
489
  let at = 10 + networkLength;
470
490
  if (at + 2 > section.length)
471
491
  return null;
492
+ let name = null;
493
+ for (const [tag, descriptor] of descriptors(section.subarray(10, at))) {
494
+ if (tag === DESC_NETWORK_NAME)
495
+ name ??= decodeAribText(descriptor);
496
+ }
472
497
  at += 2; // transport_stream_loop_length
473
498
  let remoteControlKeyId = null;
474
499
  const transportStreams = [];
@@ -482,11 +507,16 @@ function parseNitSection(section) {
482
507
  const services = [];
483
508
  let delivery = null;
484
509
  let remote = null;
510
+ const partialReception = [];
485
511
  for (const [tag, descriptor] of descriptors(body)) {
486
512
  if (tag === DESC_TS_INFORMATION && descriptor.length > 0) {
487
513
  remote ??= descriptor[0];
488
514
  remoteControlKeyId ??= remote;
489
515
  }
516
+ else if (tag === DESC_PARTIAL_RECEPTION) {
517
+ for (let i = 0; i + 2 <= descriptor.length; i += 2)
518
+ partialReception.push(u16(descriptor, i));
519
+ }
490
520
  else if (tag === DESC_SERVICE_LIST) {
491
521
  for (let i = 0; i + 3 <= descriptor.length; i += 3) {
492
522
  services.push({
@@ -502,6 +532,9 @@ function parseNitSection(section) {
502
532
  else if (tag === DESC_TERRESTRIAL_DELIVERY) {
503
533
  delivery = terrestrialDelivery(descriptor) ?? delivery;
504
534
  }
535
+ else if (tag === DESC_CABLE_DELIVERY) {
536
+ delivery = cableDelivery(descriptor) ?? delivery;
537
+ }
505
538
  }
506
539
  transportStreams.push({
507
540
  transportStreamId,
@@ -509,6 +542,7 @@ function parseNitSection(section) {
509
542
  services,
510
543
  delivery,
511
544
  remoteControlKeyId: remote,
545
+ partialReception,
512
546
  });
513
547
  }
514
548
  return {
@@ -517,12 +551,14 @@ function parseNitSection(section) {
517
551
  version: (section[5] >> 1) & 0x1f,
518
552
  sectionNumber: section[6],
519
553
  lastSectionNumber: section[7],
554
+ name,
520
555
  remoteControlKeyId,
521
556
  transportStreams,
557
+ raw: section.slice(),
522
558
  };
523
559
  }
524
560
  /**
525
- * NIT からネットワーク ID とリモコン番号、他の TS の顔ぶれを読む。**セクション 1 つぶん。**
561
+ * NIT からネットワーク ID とリモコン番号、TS の一覧を読む。**セクション 1 つぶん。**
526
562
  * スキャンで「どのネットワークか」を知るにはこれで足りる。ネットワークの全部の TS が要るなら `NitReader`
527
563
  */
528
564
  function parseNit(section) {
@@ -622,9 +658,11 @@ export class NitReader {
622
658
  other: section.other,
623
659
  networkId: section.networkId,
624
660
  version: section.version,
661
+ name: parts.find((part) => part.name !== null)?.name ?? null,
625
662
  remoteControlKeyId: parts.find((part) => part.remoteControlKeyId !== null)
626
663
  ?.remoteControlKeyId ?? null,
627
664
  transportStreams: parts.flatMap((part) => part.transportStreams),
665
+ sections: parts.map((part) => part.raw),
628
666
  });
629
667
  return true;
630
668
  }
@@ -720,7 +758,7 @@ export class SdtReader {
720
758
  * **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
721
759
  * 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
722
760
  * (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
723
- * NIT が先に来ていれば、その TS のリモコン番号だけを使う。
761
+ * NIT が先に来ていれば、その TS のリモコン番号と部分受信の局だけを使う。
724
762
  * PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
725
763
  */
726
764
  export class ServiceReader {
@@ -763,12 +801,15 @@ export class ServiceReader {
763
801
  }
764
802
  return this.complete;
765
803
  }
766
- /** 録るに値するサービスだけ。リモコン番号は NIT のものを配る */
804
+ /** 録画の対象になる種類の局だけ (`SERVICE_TYPES`)。リモコン番号と部分受信の局は NIT から付ける */
767
805
  services() {
768
806
  const transport = this.transport;
769
807
  if (transport === null)
770
808
  return [];
771
809
  const { transportStreamId, originalNetworkId } = transport;
810
+ const own = this.network?.transportStreams.find((ts) => ts.transportStreamId === transportStreamId &&
811
+ ts.originalNetworkId === originalNetworkId);
812
+ const partial = new Set(own?.partialReception ?? []);
772
813
  const found = (networkId, remoteControlKeyId) => transport.services
773
814
  .filter((service) => SERVICE_TYPES.has(service.serviceType))
774
815
  .map((service) => ({
@@ -776,14 +817,13 @@ export class ServiceReader {
776
817
  networkId,
777
818
  transportStreamId,
778
819
  remoteControlKeyId,
820
+ partialReception: partial.has(service.serviceId),
779
821
  }));
780
822
  if (this.requireNit) {
781
823
  if (this.network === null)
782
824
  return [];
783
825
  return found(this.network.networkId, this.network.remoteControlKeyId);
784
826
  }
785
- const own = this.network?.transportStreams.find((ts) => ts.transportStreamId === transportStreamId &&
786
- ts.originalNetworkId === originalNetworkId);
787
827
  return found(originalNetworkId, own?.remoteControlKeyId ?? null);
788
828
  }
789
829
  }
@@ -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
+ * 絞るのに要るわけではない。**データ放送が受信機の API で読む**ので
70
+ * 持っておく。テレ朝の TVer リンクは `browser.getProgramID(4)` で
71
+ * これを取得し、TVer のサーバへそのまま送る。`null` のまま渡すと文字列の
72
+ * "null" が送られて TVer のサーバがエラーを返し、画面には `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;
@@ -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
+ * 絞るのに要るわけではない。**データ放送が受信機の API で読む**ので
141
+ * 持っておく。テレ朝の TVer リンクは `browser.getProgramID(4)` で
142
+ * これを取得し、TVer のサーバへそのまま送る。`null` のまま渡すと文字列の
143
+ * "null" が送られて TVer のサーバがエラーを返し、画面には `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() {
package/dist/synth-mmt.js CHANGED
@@ -351,7 +351,7 @@ export function mptTable(mpt) {
351
351
  ];
352
352
  return Uint8Array.from([0x20, mpt.version ?? 0, ...be(body.length), ...body]);
353
353
  }
354
- /** MMT の表のセクションの頭 (table_id・長さ・拡張・バージョン・セクション番号)。長さは `withCrc` が埋める */
354
+ /** MMT の表のセクションの先頭 (table_id・長さ・拡張・バージョン・セクション番号)。長さは `withCrc` が埋める */
355
355
  function head(tableId, extension, version = 0) {
356
356
  return [tableId, 0x00, 0x00, ...be(extension), 0xc1 | (version << 1), 0, 0];
357
357
  }
package/dist/synth.d.ts CHANGED
@@ -106,15 +106,25 @@ export declare function eitSection(options: EitOptions): Uint8Array;
106
106
  * 足す記述子は分配の記述子 (`satelliteDeliveryDescriptor`・`terrestrialDeliveryDescriptor`) など
107
107
  */
108
108
  export declare function nitSection(networkId: number, remoteControlKeyId: number | null, streams: [number, number, [number, number][], number[]?][],
109
- /** `other` なら NIT[other] (0x41。ほかのネットワークのもの) */
109
+ /** `other` なら NIT[other] (0x41。ほかのネットワークのもの)。`name` を渡すとネットワーク名の記述子 (0x40) を付ける */
110
110
  options?: SectionPart & {
111
111
  other?: boolean;
112
+ name?: string;
112
113
  }): Uint8Array;
113
114
  /**
114
115
  * 衛星の分配の記述子 (0x43)。周波数は kHz、シンボルレートはシンボル/秒で渡す
115
116
  * (BS-1 は 11727480 kHz・28860000)。東経 110 度・右旋・ISDB-S (変調 0x08) にしておく
116
117
  */
117
118
  export declare function satelliteDeliveryDescriptor(frequency: number, symbolRate?: number): number[];
119
+ /**
120
+ * ケーブルの分配の記述子 (0x44)。周波数は Hz で渡す (111000000)。
121
+ * 既定は TS が 1 つの搬送波 (多重フレーム型式番号 0xF)・RS(204,188)・64QAM・5.274 Msym/s・内符号なし
122
+ */
123
+ export declare function cableDeliveryDescriptor(frequency: number, options?: {
124
+ frameType?: number;
125
+ modulation?: number;
126
+ symbolRate?: number;
127
+ }): number[];
118
128
  /** 地上の分配の記述子 (0xFA)。周波数は Hz で渡す (1/7 MHz 単位に丸める) */
119
129
  export declare function terrestrialDeliveryDescriptor(areaCode: number, frequencies: number[]): number[];
120
130
  /** セクションを TS パケットに詰める。最初のパケットには pointer_field が付く */
@@ -210,7 +220,7 @@ export declare function bmlHead(): Uint8Array[];
210
220
  * 本物と同じく、先頭に Content-Type、境界で区切って各ファイルに Content-Location
211
221
  */
212
222
  export declare function multipartModule(files: [location: string, type: string, body: string][]): Uint8Array;
213
- /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在り処の印** */
223
+ /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在りかの印** */
214
224
  export declare function aitSignallingDescriptor(applicationType?: number, version?: number): number[];
215
225
  export interface SynthHybridcastApp {
216
226
  organisationId: number;
package/dist/synth.js CHANGED
@@ -250,11 +250,14 @@ export function eitSection(options) {
250
250
  * 足す記述子は分配の記述子 (`satelliteDeliveryDescriptor`・`terrestrialDeliveryDescriptor`) など
251
251
  */
252
252
  export function nitSection(networkId, remoteControlKeyId, streams,
253
- /** `other` なら NIT[other] (0x41。ほかのネットワークのもの) */
253
+ /** `other` なら NIT[other] (0x41。ほかのネットワークのもの)。`name` を渡すとネットワーク名の記述子 (0x40) を付ける */
254
254
  options = {}) {
255
+ const network = options.name === undefined ? [] : encodeSiText(options.name);
256
+ const networkDescriptors = network.length === 0 ? [] : [0x40, network.length, ...network];
255
257
  const body = [
256
258
  ...head(options.other === true ? 0x41 : 0x40, networkId, options),
257
- ...be(0xf000),
259
+ ...be(0xf000 | networkDescriptors.length),
260
+ ...networkDescriptors,
258
261
  ];
259
262
  const loop = [];
260
263
  for (const [transportStreamId, originalNetworkId, services, extra,] of streams) {
@@ -297,6 +300,25 @@ export function satelliteDeliveryDescriptor(frequency, symbolRate = 28_860_000)
297
300
  (symbol[3] & 0xf0) | 0x01,
298
301
  ];
299
302
  }
303
+ /**
304
+ * ケーブルの分配の記述子 (0x44)。周波数は Hz で渡す (111000000)。
305
+ * 既定は TS が 1 つの搬送波 (多重フレーム型式番号 0xF)・RS(204,188)・64QAM・5.274 Msym/s・内符号なし
306
+ */
307
+ export function cableDeliveryDescriptor(frequency, options = {}) {
308
+ const symbol = bcdBytes((options.symbolRate ?? 5_274_000) / 100, 7);
309
+ return [
310
+ 0x44,
311
+ 11,
312
+ ...bcdBytes(frequency / 100, 8),
313
+ 0xff,
314
+ ((options.frameType ?? 0x0f) << 4) | 0x02,
315
+ options.modulation ?? 0x03,
316
+ symbol[0],
317
+ symbol[1],
318
+ symbol[2],
319
+ (symbol[3] & 0xf0) | 0x0f,
320
+ ];
321
+ }
300
322
  /** 地上の分配の記述子 (0xFA)。周波数は Hz で渡す (1/7 MHz 単位に丸める) */
301
323
  export function terrestrialDeliveryDescriptor(areaCode, frequencies) {
302
324
  const body = [
@@ -558,7 +580,7 @@ export function multipartModule(files) {
558
580
  out += `--${boundary}--\r\n`;
559
581
  return new TextEncoder().encode(out);
560
582
  }
561
- /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在り処の印** */
583
+ /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在りかの印** */
562
584
  export function aitSignallingDescriptor(applicationType = 0x0010, version = 1) {
563
585
  return [0x6f, 0x03, ...be(applicationType), version & 0x1f];
564
586
  }
package/dist/tlv.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * TLV パケット (ARIB STD-B32 第 3 部) を切り出す。4K の放送 (MMT/TLV) の土台で、TS の `PacketStream` と同じ役。
3
3
  *
4
4
  * TLV パケットは `0x7F`・種類 (1 バイト)・長さ (2 バイト) のヘッダーと、長さぶんの中身でできている。
5
- * 長さが毎回違うので、TS のように決まった間隔で同期のバイトを探すことはできない。
5
+ * 長さが毎回違うので、TS のように決まった間隔で同期のバイトを探せない。
6
6
  * エージェントはケーブルの分割 TLV を組み立て直した TLV のバイト列を流す
7
7
  * ([agent.md](https://github.com/danything/denpa/blob/main/docs/agent.md))。
8
8
  */
package/dist/tlv.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * TLV パケット (ARIB STD-B32 第 3 部) を切り出す。4K の放送 (MMT/TLV) の土台で、TS の `PacketStream` と同じ役。
3
3
  *
4
4
  * TLV パケットは `0x7F`・種類 (1 バイト)・長さ (2 バイト) のヘッダーと、長さぶんの中身でできている。
5
- * 長さが毎回違うので、TS のように決まった間隔で同期のバイトを探すことはできない。
5
+ * 長さが毎回違うので、TS のように決まった間隔で同期のバイトを探せない。
6
6
  * エージェントはケーブルの分割 TLV を組み立て直した TLV のバイト列を流す
7
7
  * ([agent.md](https://github.com/danything/denpa/blob/main/docs/agent.md))。
8
8
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danything/arib",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "日本のデジタル放送 (ARIB) の TS と MMT/TLV を読む: PSI・PES・EIT・ARIB の文字・字幕・データ放送、TLV・MMTP・MMT-SI・MH-EIT",
5
5
  "license": "MIT",
6
6
  "repository": {