@danything/arib 2.4.0 → 2.5.1
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 +2 -2
- package/dist/ait.d.ts +3 -3
- package/dist/ait.js +3 -3
- package/dist/b24-tables.js +1 -0
- package/dist/bml-protocol.d.ts +7 -0
- package/dist/bml.d.ts +5 -0
- package/dist/bml.js +21 -3
- package/dist/eit.d.ts +2 -2
- package/dist/eit.js +1 -1
- package/dist/mh-eit.d.ts +2 -2
- package/dist/mh-eit.js +2 -2
- package/dist/mmt-media.d.ts +1 -1
- package/dist/mmt-media.js +1 -1
- package/dist/mmt-service-filter.d.ts +1 -1
- package/dist/mmt-service-filter.js +1 -1
- package/dist/mmt-si.d.ts +25 -5
- package/dist/mmt-si.js +46 -6
- package/dist/mmtp.d.ts +3 -3
- package/dist/mmtp.js +3 -3
- package/dist/pes.js +2 -3
- package/dist/psi.d.ts +50 -9
- package/dist/psi.js +55 -10
- package/dist/service-filter.d.ts +4 -4
- package/dist/service-filter.js +4 -4
- package/dist/synth-mmt.js +1 -1
- package/dist/synth.d.ts +12 -2
- package/dist/synth.js +25 -3
- package/dist/tlv.d.ts +1 -1
- package/dist/tlv.js +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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/b24-tables.js
CHANGED
|
@@ -501,4 +501,5 @@ export const DRCS_REPLACE = new Map([
|
|
|
501
501
|
['c42450812c184aee2ae01cc5e39ae957', 0x1f4fb], // denpa: ラジオ 📻 (72x72・16 階調)。テレ東 2026-11-01 のアニメの字幕で 1 本に 28 回
|
|
502
502
|
['06cb56043b9c4006bcfbe07cc831feaf', 0x1f50a], // denpa: 音の出ているスピーカー 🔊 (72x72・16 階調)。上と同じ番組で 4 回 (テレビ朝日・TBS のものと画像が違う)
|
|
503
503
|
['063c95566807d5e7b51ab706426bedf9', 0x1f4f1], // denpa: 携帯電話 📱 (72x72・16 階調)。上と同じ番組で 4 回 (NHK総合のものと画像が違う)
|
|
504
|
+
['44cbc7dc0af84f6ab857f00a9bc2560a', 0x1f4fa], // denpa: テレビ 📺 (72x72・16 階調)。日テレ 2026-11-14 のらんま1/2 の字幕で 1 本に 2 回 (テレビ朝日のテレビと画像が違う)
|
|
504
505
|
]);
|
package/dist/bml-protocol.d.ts
CHANGED
|
@@ -69,6 +69,13 @@ export interface ModuleFile {
|
|
|
69
69
|
export interface PMTMessage {
|
|
70
70
|
type: "pmt";
|
|
71
71
|
components: ComponentPMT[];
|
|
72
|
+
/**
|
|
73
|
+
* PMT にある音声 (stream_type 0x0f の AAC) の ES の部品タグを、PMT の順に並べたもの。**部品タグの無い ES は null で並ぶ**
|
|
74
|
+
* (bml.ts の `pmtAudios`)。`components` は部品タグの無い ES を落とすので、何本目の音声かはこちらで数える。
|
|
75
|
+
* 画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` も、部品タグに関係なく PMT の順に数える。
|
|
76
|
+
* これを足す前に録った `.bml.jsonl` の PMT には無い
|
|
77
|
+
*/
|
|
78
|
+
audios?: (number | null)[];
|
|
72
79
|
}
|
|
73
80
|
export interface ModuleDownloadedMessage {
|
|
74
81
|
type: "moduleDownloaded";
|
package/dist/bml.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export declare function parseBxmlInfo(data: Uint8Array): AdditionalAribBXMLInfo;
|
|
|
33
33
|
* `BmlDecoder` の外からも使う。denpa のライブは、カルーセルをデコードする前に入口の部品の自動起動の指定をこれで読む
|
|
34
34
|
*/
|
|
35
35
|
export declare function parsePmt(section: Uint8Array): ComponentPMT[];
|
|
36
|
+
/**
|
|
37
|
+
* PMT にある音声 (AAC) の ES の部品タグを、PMT の順に並べる (`PMTMessage.audios`)。**部品タグの無い ES は null で数に入れる。**
|
|
38
|
+
* `parsePmt` と違って落とさないので、何本目の音声かが画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` にそろう
|
|
39
|
+
*/
|
|
40
|
+
export declare function pmtAudios(section: Uint8Array): (number | null)[];
|
|
36
41
|
/**
|
|
37
42
|
* 1 局に絞った TS を渡すと、データ放送の通知が出てくる。
|
|
38
43
|
*
|
package/dist/bml.js
CHANGED
|
@@ -23,7 +23,7 @@ import { u16, u32 } from "./bytes.js";
|
|
|
23
23
|
import { dataEventIdOf, MAX_MODULE_SIZE, ModuleBuilder, parseDdb, parseDii, TABLE_DII, TABLE_STREAM_DESCRIPTOR, } from "./dsmcc.js";
|
|
24
24
|
import { parseTimeTable, PID_TIME } from "./eit.js";
|
|
25
25
|
import { parseMediaType, readEntity } from "./multipart.js";
|
|
26
|
-
import { descriptors, packetPid, PacketStream, pmtStreams, PmtTracker, SectionAssembler, } from "./psi.js";
|
|
26
|
+
import { descriptors, packetPid, PacketStream, pmtStreams, PmtTracker, SectionAssembler, STREAM_AAC, } from "./psi.js";
|
|
27
27
|
const DESC_STREAM_IDENTIFIER = 0x52;
|
|
28
28
|
/** データ符号化方式記述子 (STD-B10 第 2 部 6.2.20) */
|
|
29
29
|
const DESC_DATA_COMPONENT = 0xfd;
|
|
@@ -139,6 +139,23 @@ export function parsePmt(section) {
|
|
|
139
139
|
}
|
|
140
140
|
return components;
|
|
141
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* PMT にある音声 (AAC) の ES の部品タグを、PMT の順に並べる (`PMTMessage.audios`)。**部品タグの無い ES は null で数に入れる。**
|
|
144
|
+
* `parsePmt` と違って落とさないので、何本目の音声かが画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` にそろう
|
|
145
|
+
*/
|
|
146
|
+
export function pmtAudios(section) {
|
|
147
|
+
const audios = [];
|
|
148
|
+
for (const [streamType, , info] of pmtStreams(section)) {
|
|
149
|
+
if (streamType !== STREAM_AAC)
|
|
150
|
+
continue;
|
|
151
|
+
let componentId = null;
|
|
152
|
+
for (const [tag, descriptor] of descriptors(info))
|
|
153
|
+
if (tag === DESC_STREAM_IDENTIFIER && descriptor.length >= 1)
|
|
154
|
+
componentId = descriptor[0];
|
|
155
|
+
audios.push(componentId);
|
|
156
|
+
}
|
|
157
|
+
return audios;
|
|
158
|
+
}
|
|
142
159
|
/** `at` の下位 1 ビットと続く 4 バイトで 33 ビット (STC・NPT) */
|
|
143
160
|
const u33 = (data, at) => (data[at] & 1) * 0x100000000 + u32(data, at + 1);
|
|
144
161
|
/** ストリーム記述子 (table_id 0x3D)。NPT と イベントメッセージが載っている */
|
|
@@ -252,14 +269,15 @@ export class BmlDecoder {
|
|
|
252
269
|
const components = parsePmt(section);
|
|
253
270
|
if (components.length === 0)
|
|
254
271
|
return;
|
|
272
|
+
const audios = pmtAudios(section);
|
|
255
273
|
/*
|
|
256
274
|
* 放送は同じ表を何度も送る。**変わったときだけ配る。** 受け側は
|
|
257
275
|
* PMT が来るたびに画面を組み直すので、そのまま通すと出っ放しになる
|
|
258
276
|
*/
|
|
259
|
-
const shape = JSON.stringify(components);
|
|
277
|
+
const shape = JSON.stringify([components, audios]);
|
|
260
278
|
if (this.sent !== shape) {
|
|
261
279
|
this.sent = shape;
|
|
262
|
-
this.send({ type: "pmt", components });
|
|
280
|
+
this.send({ type: "pmt", components, audios });
|
|
263
281
|
}
|
|
264
282
|
for (const component of components) {
|
|
265
283
|
// カルーセルが流れているのは、BML の符号化方式が付いた ES だけ
|
package/dist/eit.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export interface EitEvent {
|
|
|
70
70
|
/** 尺 (ms)。未定なら null。番組表に入れられるのは決まっているものだけ */
|
|
71
71
|
duration: number | null;
|
|
72
72
|
isFree: boolean;
|
|
73
|
-
/** 1
|
|
73
|
+
/** 0=未定義 1=非実行中 2=数秒以内に開始 3=停止中 4=放送中 (STD-B10)。p/f の「いま」を見分けるのに使う */
|
|
74
74
|
runningStatus: number;
|
|
75
75
|
name: string;
|
|
76
76
|
description: string;
|
|
@@ -292,7 +292,7 @@ export declare class EpgReader {
|
|
|
292
292
|
*
|
|
293
293
|
* そこで 0 のときは**セクションの番号で決める**。p/f は仕様で
|
|
294
294
|
* **section 0 が現在・section 1 が次**と決まっていて、こちらは局の都合で
|
|
295
|
-
* 変わらない。1 (
|
|
295
|
+
* 変わらない。1〜3 (非実行中・数秒以内に開始・停止中) は今までどおり入れない。
|
|
296
296
|
*/
|
|
297
297
|
private onAir;
|
|
298
298
|
/**
|
package/dist/eit.js
CHANGED
|
@@ -652,7 +652,7 @@ export class EpgReader {
|
|
|
652
652
|
*
|
|
653
653
|
* そこで 0 のときは**セクションの番号で決める**。p/f は仕様で
|
|
654
654
|
* **section 0 が現在・section 1 が次**と決まっていて、こちらは局の都合で
|
|
655
|
-
* 変わらない。1 (
|
|
655
|
+
* 変わらない。1〜3 (非実行中・数秒以内に開始・停止中) は今までどおり入れない。
|
|
656
656
|
*/
|
|
657
657
|
onAir(section, event) {
|
|
658
658
|
if (event.runningStatus === 4)
|
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 と同じで、違うのは次の所 (
|
|
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
|
|
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 と同じで、違うのは次の所 (
|
|
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
|
|
235
|
+
* MH-EIT は自分の TLV ストリームの局のぶんだけ来る (ケーブルで受けた実機の BS4K で確かめた)。
|
|
236
236
|
* TS の `other` (ほかの TS の局の番組表) に当たるものは無い
|
|
237
237
|
*/
|
|
238
238
|
export class MmtEpgReader extends EpgReader {
|
package/dist/mmt-media.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* **時刻はパケットには無く、MPT の記述子にある。** MPU タイムスタンプ記述子が MPU の最初の提示時刻を、
|
|
9
9
|
* MPU 拡張タイムスタンプ記述子がアクセスユニットごとのずれを、これから来る MPU のぶんまで並べる
|
|
10
|
-
* (
|
|
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
|
-
* (
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
|
107
|
+
/** TLV-NIT にある TLV ストリームの 1 件 */
|
|
108
108
|
export interface TlvNetworkStream {
|
|
109
109
|
tlvStreamId: number;
|
|
110
110
|
originalNetworkId: number;
|
|
@@ -160,20 +160,40 @@ 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
|
|
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;
|
|
175
|
+
/**
|
|
176
|
+
* TLV-NIT[actual]。番号・名前・セクションの番号は最初に来たセクションのもので、`streams` はこれまでに来た全部のセクションの
|
|
177
|
+
* TLV ストリーム (tlv_stream_id ごとに 1 つ。同じ tlv_stream_id は後に来たもので置き換える)
|
|
178
|
+
*/
|
|
170
179
|
network: TlvNetwork | null;
|
|
171
180
|
transport: MhSdt | null;
|
|
172
181
|
get complete(): boolean;
|
|
173
182
|
/** PLT が来て、そこにある package の MPT が全部 1 回は来たか。スキャンで、局ごとの CA_system_id が分かるまで少し長く読むのに使う */
|
|
174
183
|
get mptsKnown(): boolean;
|
|
175
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* 任意の長さのバイト列を渡す。揃ったら true。
|
|
186
|
+
*
|
|
187
|
+
* 読んだ表は渡されたチャンクの一部を指すことがあり、呼ぶ側はチャンクのバッファを使い回す。**記述子の並び (`descriptors`) と
|
|
188
|
+
* asset_id を持ち続ける MPT と TLV-NIT は、写してから読む**
|
|
189
|
+
*/
|
|
176
190
|
feed(chunk: Uint8Array): boolean;
|
|
191
|
+
/**
|
|
192
|
+
* 自分の TLV ストリーム。TLV-NIT[actual] のストリームのうち、PLT にある package (= service_id) をサービスリスト記述子に持つ最初の 1 つ。
|
|
193
|
+
* 実機では 1 つに決まる。同じ service_id を持つストリームが 2 つあれば、TLV-NIT で先に来たほうを返す。
|
|
194
|
+
* TLV-NIT と PLT のどちらかが来ていないか、当てはまるストリームが無ければ null
|
|
195
|
+
*/
|
|
196
|
+
get ownStream(): TlvNetworkStream | null;
|
|
177
197
|
/** 録画の対象になる種類の局だけ (`psi.SERVICE_TYPES`)。揃うまでは空 */
|
|
178
198
|
services(): FoundService[];
|
|
179
199
|
/** その局の 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
|
-
*
|
|
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,13 +389,22 @@ 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
|
|
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();
|
|
404
|
+
/**
|
|
405
|
+
* TLV-NIT[actual]。番号・名前・セクションの番号は最初に来たセクションのもので、`streams` はこれまでに来た全部のセクションの
|
|
406
|
+
* TLV ストリーム (tlv_stream_id ごとに 1 つ。同じ tlv_stream_id は後に来たもので置き換える)
|
|
407
|
+
*/
|
|
399
408
|
network = null;
|
|
400
409
|
transport = null;
|
|
401
410
|
get complete() {
|
|
@@ -406,24 +415,53 @@ export class TlvServiceReader {
|
|
|
406
415
|
return (this.plt !== null &&
|
|
407
416
|
this.plt.packages.every((entry) => this.mpts.has(entry.packageId)));
|
|
408
417
|
}
|
|
409
|
-
/**
|
|
418
|
+
/**
|
|
419
|
+
* 任意の長さのバイト列を渡す。揃ったら true。
|
|
420
|
+
*
|
|
421
|
+
* 読んだ表は渡されたチャンクの一部を指すことがあり、呼ぶ側はチャンクのバッファを使い回す。**記述子の並び (`descriptors`) と
|
|
422
|
+
* asset_id を持ち続ける MPT と TLV-NIT は、写してから読む**
|
|
423
|
+
*/
|
|
410
424
|
feed(chunk) {
|
|
411
425
|
for (const { table } of this.tables.feed(chunk)) {
|
|
412
426
|
const tableId = table[0];
|
|
413
427
|
if (tableId === TABLE_PLT)
|
|
414
428
|
this.plt = parsePlt(table) ?? this.plt;
|
|
415
429
|
else if (tableId === TABLE_MPT) {
|
|
416
|
-
const mpt = parseMpt(table);
|
|
430
|
+
const mpt = parseMpt(table.slice());
|
|
417
431
|
if (mpt !== null)
|
|
418
432
|
this.mpts.set(mpt.packageId, mpt);
|
|
419
433
|
}
|
|
420
|
-
else if (tableId === TABLE_TLV_NIT_ACTUAL
|
|
421
|
-
|
|
434
|
+
else if (tableId === TABLE_TLV_NIT_ACTUAL) {
|
|
435
|
+
const network = parseTlvNit(table.slice());
|
|
436
|
+
if (network === null)
|
|
437
|
+
continue;
|
|
438
|
+
for (const stream of network.streams)
|
|
439
|
+
this.streams.set(stream.tlvStreamId, stream);
|
|
440
|
+
// ネットワークの番号と名前は最初に来たセクションのもの。ストリームは来たセクションの全部
|
|
441
|
+
this.network = {
|
|
442
|
+
...(this.network ?? network),
|
|
443
|
+
streams: [...this.streams.values()],
|
|
444
|
+
};
|
|
445
|
+
}
|
|
422
446
|
else if (tableId === TABLE_MH_SDT_ACTUAL && this.transport === null)
|
|
423
447
|
this.transport = parseMhSdt(table);
|
|
424
448
|
}
|
|
425
449
|
return this.complete;
|
|
426
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* 自分の TLV ストリーム。TLV-NIT[actual] のストリームのうち、PLT にある package (= service_id) をサービスリスト記述子に持つ最初の 1 つ。
|
|
453
|
+
* 実機では 1 つに決まる。同じ service_id を持つストリームが 2 つあれば、TLV-NIT で先に来たほうを返す。
|
|
454
|
+
* TLV-NIT と PLT のどちらかが来ていないか、当てはまるストリームが無ければ null
|
|
455
|
+
*/
|
|
456
|
+
get ownStream() {
|
|
457
|
+
const packages = new Set((this.plt?.packages ?? []).map((entry) => entry.packageId));
|
|
458
|
+
if (packages.size === 0)
|
|
459
|
+
return null;
|
|
460
|
+
for (const stream of this.streams.values())
|
|
461
|
+
if (stream.services.some((service) => packages.has(service.serviceId)))
|
|
462
|
+
return stream;
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
427
465
|
/** 録画の対象になる種類の局だけ (`psi.SERVICE_TYPES`)。揃うまでは空 */
|
|
428
466
|
services() {
|
|
429
467
|
const transport = this.transport;
|
|
@@ -438,6 +476,8 @@ export class TlvServiceReader {
|
|
|
438
476
|
networkId: transport.originalNetworkId,
|
|
439
477
|
transportStreamId: transport.tlvStreamId,
|
|
440
478
|
remoteControlKeyId: null,
|
|
479
|
+
// 4K の放送に部分受信 (ワンセグ) は無い
|
|
480
|
+
partialReception: false,
|
|
441
481
|
}));
|
|
442
482
|
}
|
|
443
483
|
/** その局の 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 の放送では映像と音声がスクランブルされ、信号はされていない** (
|
|
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
|
-
*
|
|
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 は当てにしない。**
|
|
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 の放送では映像と音声がスクランブルされ、信号はされていない** (
|
|
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
|
-
*
|
|
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 は当てにしない。**
|
|
158
|
+
* 捨てて次の先頭を待つ。**fragment_counter は当てにしない。** ケーブルで受けた実機の BS4K は分割したものでも
|
|
159
159
|
* いつも 0 を入れていた
|
|
160
160
|
* - 短いメッセージは、1 つの MMTP のパケットに長さを前に付けて集約される (aggregation_flag)。
|
|
161
161
|
* 長さは length_extension_flag が立っていれば 32 ビット、立っていなければ 16 ビット
|
package/dist/pes.js
CHANGED
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
* DOM も WASM も触らないので、ここだけ単体で確かめられる (`pes.test.ts`)。
|
|
10
10
|
*/
|
|
11
11
|
import { concatBytes, u16 } from "./bytes.js";
|
|
12
|
-
import { PACKET, packetPid, PacketStream, PID_PAT, pmtStreams, PmtTracker, } from "./psi.js";
|
|
12
|
+
import { PACKET, packetPid, PacketStream, PID_PAT, pmtStreams, PmtTracker, STREAM_AAC, } from "./psi.js";
|
|
13
13
|
/** PTS・PCR の刻み (90kHz)。denpa の取り決めの時刻もこの刻み (docs/stream.md#websocket-プロトコル) */
|
|
14
14
|
export const CLOCK = 90_000;
|
|
15
15
|
/** PTS の一周 (33 ビット)。26.5 時間 */
|
|
16
16
|
export const WRAP = 2 ** 33;
|
|
17
|
-
/** PMT の stream_type。**放送の映像は MPEG-2、音声は ADTS の AAC** (ARIB STD-B32) */
|
|
17
|
+
/** PMT の stream_type。**放送の映像は MPEG-2、音声は ADTS の AAC** (ARIB STD-B32。AAC は psi.ts の `STREAM_AAC`) */
|
|
18
18
|
const STREAM_MPEG2 = 0x02;
|
|
19
|
-
const STREAM_AAC = 0x0f;
|
|
20
19
|
/** 5 バイトの PTS / DTS を読む (ISO/IEC 13818-1 2.4.3.7) */
|
|
21
20
|
export function readStamp(data, at) {
|
|
22
21
|
return (((data[at] >> 1) & 0x07) * 2 ** 30 +
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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;
|
|
@@ -120,6 +120,11 @@ export declare class PmtTracker {
|
|
|
120
120
|
export declare function descriptors(data: Uint8Array): Generator<[number, Uint8Array]>;
|
|
121
121
|
/** デジタルコピー制御記述子。EIT の番組ごとと PMT に入る */
|
|
122
122
|
export declare const DESC_DIGITAL_COPY_CONTROL = 193;
|
|
123
|
+
/**
|
|
124
|
+
* 音声の ES の stream_type (AAC。日本の放送の音声)。主音声の何本目かは、どこでもこの stream_type だけを PMT の順に数える
|
|
125
|
+
* (pes.ts の `readPmt`・bml.ts の `pmtAudios`・データ放送の主音声)。ffmpeg の `0:a:<n>` ともそろう
|
|
126
|
+
*/
|
|
127
|
+
export declare const STREAM_AAC = 15;
|
|
123
128
|
/**
|
|
124
129
|
* デジタルコピー制御記述子の中身 (タグと長さを除いたもの) から、**コピー禁止か**を読む。
|
|
125
130
|
*
|
|
@@ -181,12 +186,39 @@ export interface TerrestrialDelivery {
|
|
|
181
186
|
/** 周波数 (Hz)。中継局ごとに並ぶことがある。放送は 1/7 MHz 単位で書く */
|
|
182
187
|
frequencies: number[];
|
|
183
188
|
}
|
|
184
|
-
|
|
189
|
+
/**
|
|
190
|
+
* ケーブルの分配の記述子 (0x44)。どの周波数で、どう変調して流れているか。
|
|
191
|
+
*
|
|
192
|
+
* **並びは推定。** 有線の告示にある 0x44 の本文は読めていない。同じ系統の有線複数搬送波伝送分配システム記述子 (0xF3。総務省の告示の改正案) は
|
|
193
|
+
* 周波数 (32)・0xFF (8)・多重フレーム型式番号 (4)・外符号 (4)・変調 (8)・シンボルレート (28)・内符号 (4) の並びで、0x44 も同じと見ている。
|
|
194
|
+
* DVB (EN 300 468) では、多重フレーム型式番号の 4 ビットは reserved
|
|
195
|
+
*/
|
|
196
|
+
export interface CableDelivery {
|
|
197
|
+
kind: "cable";
|
|
198
|
+
/** 周波数 (Hz)。放送は MHz の BCD 8 桁 (小数 4 桁) で書くので、100 Hz 単位 */
|
|
199
|
+
frequency: number;
|
|
200
|
+
/** 多重フレーム型式番号。0x1 は TSMF (53 スロット・最大 15 TS)、0xF は TS が 1 つの搬送波 */
|
|
201
|
+
frameType: number;
|
|
202
|
+
/** 外符号。0x2 は RS(204,188) */
|
|
203
|
+
fecOuter: number;
|
|
204
|
+
/** 変調。0x03 は 64QAM、0x05 は 256QAM */
|
|
205
|
+
modulation: number;
|
|
206
|
+
/** シンボルレート (シンボル/秒)。ISDB-C は 5274000 */
|
|
207
|
+
symbolRate: number;
|
|
208
|
+
/** 内符号。0xF は内符号なし */
|
|
209
|
+
fecInner: number;
|
|
210
|
+
}
|
|
211
|
+
export type Delivery = SatelliteDelivery | TerrestrialDelivery | CableDelivery;
|
|
185
212
|
/** NIT の TS の 1 件。サービスの一覧に、どこで流れているかが付く */
|
|
186
213
|
export interface NetworkTransport extends TransportInfo {
|
|
187
214
|
delivery: Delivery | null;
|
|
188
215
|
/** その TS のリモコン番号 (TS 情報記述子)。載せていなければ null */
|
|
189
216
|
remoteControlKeyId: number | null;
|
|
217
|
+
/**
|
|
218
|
+
* 部分受信の局 (ワンセグ) の service_id (部分受信記述子 0xFB)。地上波の NIT[actual] が自分の TS に載せる。
|
|
219
|
+
* 載せていなければ空
|
|
220
|
+
*/
|
|
221
|
+
partialReception: number[];
|
|
190
222
|
}
|
|
191
223
|
/** そろった NIT。セクションをつないだもの */
|
|
192
224
|
export interface Network {
|
|
@@ -194,8 +226,12 @@ export interface Network {
|
|
|
194
226
|
other: boolean;
|
|
195
227
|
networkId: number;
|
|
196
228
|
version: number;
|
|
229
|
+
/** ネットワーク名 (0x40)。載っていなければ null。`NitReader` を通さずに作った表では省いてよい */
|
|
230
|
+
name?: string | null;
|
|
197
231
|
remoteControlKeyId: number | null;
|
|
198
232
|
transportStreams: NetworkTransport[];
|
|
233
|
+
/** セクションのバイト列 (section_number の順)。読み方を確かめるために記録に出す (ケーブルのスキャン)。省いてよい */
|
|
234
|
+
sections?: Uint8Array[];
|
|
199
235
|
}
|
|
200
236
|
/**
|
|
201
237
|
* NIT を**全部のセクション**そろえて読む。
|
|
@@ -257,6 +293,11 @@ export interface FoundService extends Service {
|
|
|
257
293
|
networkId: number;
|
|
258
294
|
transportStreamId: number;
|
|
259
295
|
remoteControlKeyId: number | null;
|
|
296
|
+
/**
|
|
297
|
+
* 部分受信の局 (ワンセグ) か。NIT の自分の TS に部分受信記述子 (0xFB) があり、そこに載っていれば true。
|
|
298
|
+
* 記述子の無いネットワーク (BS・CS と、確かめていないケーブル) と NIT を読まなかったときは false
|
|
299
|
+
*/
|
|
300
|
+
partialReception: boolean;
|
|
260
301
|
}
|
|
261
302
|
/**
|
|
262
303
|
* 流れてくる TS を読んで、NIT と SDT が揃うまで待つ。
|
|
@@ -268,7 +309,7 @@ export interface FoundService extends Service {
|
|
|
268
309
|
* **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
|
|
269
310
|
* 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
|
|
270
311
|
* (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
|
|
271
|
-
* NIT が先に来ていれば、その TS
|
|
312
|
+
* NIT が先に来ていれば、その TS のリモコン番号と部分受信の局だけを使う。
|
|
272
313
|
* PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
|
|
273
314
|
*/
|
|
274
315
|
export declare class ServiceReader {
|
|
@@ -287,6 +328,6 @@ export declare class ServiceReader {
|
|
|
287
328
|
get complete(): boolean;
|
|
288
329
|
/** 任意の長さのバイト列を渡す。揃ったら true */
|
|
289
330
|
feed(chunk: Uint8Array): boolean;
|
|
290
|
-
/**
|
|
331
|
+
/** 録画の対象になる種類の局だけ (`SERVICE_TYPES`)。リモコン番号と部分受信の局は NIT から付ける */
|
|
291
332
|
services(): FoundService[];
|
|
292
333
|
}
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
// トランスポートエラーが立っているものは信用しない
|
|
@@ -311,6 +314,11 @@ export function* descriptors(data) {
|
|
|
311
314
|
}
|
|
312
315
|
/** デジタルコピー制御記述子。EIT の番組ごとと PMT に入る */
|
|
313
316
|
export const DESC_DIGITAL_COPY_CONTROL = 0xc1;
|
|
317
|
+
/**
|
|
318
|
+
* 音声の ES の stream_type (AAC。日本の放送の音声)。主音声の何本目かは、どこでもこの stream_type だけを PMT の順に数える
|
|
319
|
+
* (pes.ts の `readPmt`・bml.ts の `pmtAudios`・データ放送の主音声)。ffmpeg の `0:a:<n>` ともそろう
|
|
320
|
+
*/
|
|
321
|
+
export const STREAM_AAC = 0x0f;
|
|
314
322
|
/**
|
|
315
323
|
* デジタルコピー制御記述子の中身 (タグと長さを除いたもの) から、**コピー禁止か**を読む。
|
|
316
324
|
*
|
|
@@ -458,6 +466,23 @@ function terrestrialDelivery(descriptor) {
|
|
|
458
466
|
frequencies,
|
|
459
467
|
};
|
|
460
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* ケーブルの分配の記述子を読む。周波数は 8 桁の BCD で、上から 4 桁が MHz (0111.0000 MHz)。
|
|
471
|
+
* シンボルレートは 7 桁で、上から 3 桁が Msym/s (005.2740)
|
|
472
|
+
*/
|
|
473
|
+
function cableDelivery(descriptor) {
|
|
474
|
+
if (descriptor.length < 11)
|
|
475
|
+
return null;
|
|
476
|
+
return {
|
|
477
|
+
kind: "cable",
|
|
478
|
+
frequency: bcdDigits(descriptor, 0, 8) * 100,
|
|
479
|
+
frameType: descriptor[5] >> 4,
|
|
480
|
+
fecOuter: descriptor[5] & 0x0f,
|
|
481
|
+
modulation: descriptor[6],
|
|
482
|
+
symbolRate: bcdDigits(descriptor, 7, 7) * 100,
|
|
483
|
+
fecInner: descriptor[10] & 0x0f,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
461
486
|
/** NIT のセクションを 1 つ読む。actual (0x40) と other (0x41) のどちらも */
|
|
462
487
|
function parseNitSection(section) {
|
|
463
488
|
const tableId = section[0];
|
|
@@ -469,6 +494,11 @@ function parseNitSection(section) {
|
|
|
469
494
|
let at = 10 + networkLength;
|
|
470
495
|
if (at + 2 > section.length)
|
|
471
496
|
return null;
|
|
497
|
+
let name = null;
|
|
498
|
+
for (const [tag, descriptor] of descriptors(section.subarray(10, at))) {
|
|
499
|
+
if (tag === DESC_NETWORK_NAME)
|
|
500
|
+
name ??= decodeAribText(descriptor);
|
|
501
|
+
}
|
|
472
502
|
at += 2; // transport_stream_loop_length
|
|
473
503
|
let remoteControlKeyId = null;
|
|
474
504
|
const transportStreams = [];
|
|
@@ -482,11 +512,16 @@ function parseNitSection(section) {
|
|
|
482
512
|
const services = [];
|
|
483
513
|
let delivery = null;
|
|
484
514
|
let remote = null;
|
|
515
|
+
const partialReception = [];
|
|
485
516
|
for (const [tag, descriptor] of descriptors(body)) {
|
|
486
517
|
if (tag === DESC_TS_INFORMATION && descriptor.length > 0) {
|
|
487
518
|
remote ??= descriptor[0];
|
|
488
519
|
remoteControlKeyId ??= remote;
|
|
489
520
|
}
|
|
521
|
+
else if (tag === DESC_PARTIAL_RECEPTION) {
|
|
522
|
+
for (let i = 0; i + 2 <= descriptor.length; i += 2)
|
|
523
|
+
partialReception.push(u16(descriptor, i));
|
|
524
|
+
}
|
|
490
525
|
else if (tag === DESC_SERVICE_LIST) {
|
|
491
526
|
for (let i = 0; i + 3 <= descriptor.length; i += 3) {
|
|
492
527
|
services.push({
|
|
@@ -502,6 +537,9 @@ function parseNitSection(section) {
|
|
|
502
537
|
else if (tag === DESC_TERRESTRIAL_DELIVERY) {
|
|
503
538
|
delivery = terrestrialDelivery(descriptor) ?? delivery;
|
|
504
539
|
}
|
|
540
|
+
else if (tag === DESC_CABLE_DELIVERY) {
|
|
541
|
+
delivery = cableDelivery(descriptor) ?? delivery;
|
|
542
|
+
}
|
|
505
543
|
}
|
|
506
544
|
transportStreams.push({
|
|
507
545
|
transportStreamId,
|
|
@@ -509,6 +547,7 @@ function parseNitSection(section) {
|
|
|
509
547
|
services,
|
|
510
548
|
delivery,
|
|
511
549
|
remoteControlKeyId: remote,
|
|
550
|
+
partialReception,
|
|
512
551
|
});
|
|
513
552
|
}
|
|
514
553
|
return {
|
|
@@ -517,12 +556,14 @@ function parseNitSection(section) {
|
|
|
517
556
|
version: (section[5] >> 1) & 0x1f,
|
|
518
557
|
sectionNumber: section[6],
|
|
519
558
|
lastSectionNumber: section[7],
|
|
559
|
+
name,
|
|
520
560
|
remoteControlKeyId,
|
|
521
561
|
transportStreams,
|
|
562
|
+
raw: section.slice(),
|
|
522
563
|
};
|
|
523
564
|
}
|
|
524
565
|
/**
|
|
525
|
-
* NIT からネットワーク ID
|
|
566
|
+
* NIT からネットワーク ID とリモコン番号、TS の一覧を読む。**セクション 1 つぶん。**
|
|
526
567
|
* スキャンで「どのネットワークか」を知るにはこれで足りる。ネットワークの全部の TS が要るなら `NitReader`
|
|
527
568
|
*/
|
|
528
569
|
function parseNit(section) {
|
|
@@ -622,9 +663,11 @@ export class NitReader {
|
|
|
622
663
|
other: section.other,
|
|
623
664
|
networkId: section.networkId,
|
|
624
665
|
version: section.version,
|
|
666
|
+
name: parts.find((part) => part.name !== null)?.name ?? null,
|
|
625
667
|
remoteControlKeyId: parts.find((part) => part.remoteControlKeyId !== null)
|
|
626
668
|
?.remoteControlKeyId ?? null,
|
|
627
669
|
transportStreams: parts.flatMap((part) => part.transportStreams),
|
|
670
|
+
sections: parts.map((part) => part.raw),
|
|
628
671
|
});
|
|
629
672
|
return true;
|
|
630
673
|
}
|
|
@@ -720,7 +763,7 @@ export class SdtReader {
|
|
|
720
763
|
* **ケーブル (`requireNit: false`) は SDT[actual] だけで揃ったとみなす。** 1 つのネットワークが
|
|
721
764
|
* 地上波・BS の再送信と自主放送の TS をまとめて流していて、NIT は CATV のネットワークのもの
|
|
722
765
|
* (局の元の放送とは別)。どの放送の局かは SDT の original_network_id で分かる。
|
|
723
|
-
* NIT が先に来ていれば、その TS
|
|
766
|
+
* NIT が先に来ていれば、その TS のリモコン番号と部分受信の局だけを使う。
|
|
724
767
|
* PAT に局が 1 つも無い TS (データだけの TS) も揃ったとみなし、局は返さない (`empty`)
|
|
725
768
|
*/
|
|
726
769
|
export class ServiceReader {
|
|
@@ -763,12 +806,15 @@ export class ServiceReader {
|
|
|
763
806
|
}
|
|
764
807
|
return this.complete;
|
|
765
808
|
}
|
|
766
|
-
/**
|
|
809
|
+
/** 録画の対象になる種類の局だけ (`SERVICE_TYPES`)。リモコン番号と部分受信の局は NIT から付ける */
|
|
767
810
|
services() {
|
|
768
811
|
const transport = this.transport;
|
|
769
812
|
if (transport === null)
|
|
770
813
|
return [];
|
|
771
814
|
const { transportStreamId, originalNetworkId } = transport;
|
|
815
|
+
const own = this.network?.transportStreams.find((ts) => ts.transportStreamId === transportStreamId &&
|
|
816
|
+
ts.originalNetworkId === originalNetworkId);
|
|
817
|
+
const partial = new Set(own?.partialReception ?? []);
|
|
772
818
|
const found = (networkId, remoteControlKeyId) => transport.services
|
|
773
819
|
.filter((service) => SERVICE_TYPES.has(service.serviceType))
|
|
774
820
|
.map((service) => ({
|
|
@@ -776,14 +822,13 @@ export class ServiceReader {
|
|
|
776
822
|
networkId,
|
|
777
823
|
transportStreamId,
|
|
778
824
|
remoteControlKeyId,
|
|
825
|
+
partialReception: partial.has(service.serviceId),
|
|
779
826
|
}));
|
|
780
827
|
if (this.requireNit) {
|
|
781
828
|
if (this.network === null)
|
|
782
829
|
return [];
|
|
783
830
|
return found(this.network.networkId, this.network.remoteControlKeyId);
|
|
784
831
|
}
|
|
785
|
-
const own = this.network?.transportStreams.find((ts) => ts.transportStreamId === transportStreamId &&
|
|
786
|
-
ts.originalNetworkId === originalNetworkId);
|
|
787
832
|
return found(originalNetworkId, own?.remoteControlKeyId ?? null);
|
|
788
833
|
}
|
|
789
834
|
}
|
package/dist/service-filter.d.ts
CHANGED
|
@@ -66,10 +66,10 @@ export declare class ServiceFilter {
|
|
|
66
66
|
/**
|
|
67
67
|
* **中継の番号 (transport_stream_id)。** PAT を読むまでは null。
|
|
68
68
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* "null"
|
|
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;
|
package/dist/service-filter.js
CHANGED
|
@@ -137,10 +137,10 @@ export class ServiceFilter {
|
|
|
137
137
|
/**
|
|
138
138
|
* **中継の番号 (transport_stream_id)。** PAT を読むまでは null。
|
|
139
139
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* "null"
|
|
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
|
|
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
|
*/
|