@danything/arib 2.3.1 → 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 +2 -2
- package/dist/ait.d.ts +3 -3
- package/dist/ait.js +3 -3
- package/dist/bml.d.ts +8 -1
- package/dist/bml.js +3 -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 +15 -4
- package/dist/mmt-si.js +31 -4
- package/dist/mmtp.d.ts +3 -3
- package/dist/mmtp.js +3 -3
- package/dist/psi.d.ts +45 -9
- package/dist/psi.js +50 -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 +15 -4
- package/dist/synth.js +29 -7
- 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/bml.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `.bml.jsonl` がこの形式で残っているので、変えると前に録ったものが読めなくなる。
|
|
18
18
|
* モジュールの中身 (multipart) は [multipart.ts](multipart.ts) がデコードする。
|
|
19
19
|
*/
|
|
20
|
-
import type { AdditionalAribBXMLInfo, ModuleFile, ResponseMessage } from "./bml-protocol.js";
|
|
20
|
+
import type { AdditionalAribBXMLInfo, ComponentPMT, ModuleFile, ResponseMessage } from "./bml-protocol.js";
|
|
21
21
|
/**
|
|
22
22
|
* データ符号化方式記述子の後半を読む。
|
|
23
23
|
*
|
|
@@ -26,6 +26,13 @@ import type { AdditionalAribBXMLInfo, ModuleFile, ResponseMessage } from "./bml-
|
|
|
26
26
|
* **`data_component_id` は 16 ビット** (aribts は 8 ビットで読んでいる)。
|
|
27
27
|
*/
|
|
28
28
|
export declare function parseBxmlInfo(data: Uint8Array): AdditionalAribBXMLInfo;
|
|
29
|
+
/**
|
|
30
|
+
* PMT から ES を並べる。**部品タグ (component_tag) を持たない ES は数に入れない。**
|
|
31
|
+
* データ放送は「何番の部品か」でしか物を指せないので、番号の無いものは指しようがない。
|
|
32
|
+
*
|
|
33
|
+
* `BmlDecoder` の外からも使う。denpa のライブは、カルーセルをデコードする前に入口の部品の自動起動の指定をこれで読む
|
|
34
|
+
*/
|
|
35
|
+
export declare function parsePmt(section: Uint8Array): ComponentPMT[];
|
|
29
36
|
/**
|
|
30
37
|
* 1 局に絞った TS を渡すと、データ放送の通知が出てくる。
|
|
31
38
|
*
|
package/dist/bml.js
CHANGED
|
@@ -109,8 +109,10 @@ export function parseBxmlInfo(data) {
|
|
|
109
109
|
/**
|
|
110
110
|
* PMT から ES を並べる。**部品タグ (component_tag) を持たない ES は数に入れない。**
|
|
111
111
|
* データ放送は「何番の部品か」でしか物を指せないので、番号の無いものは指しようがない。
|
|
112
|
+
*
|
|
113
|
+
* `BmlDecoder` の外からも使う。denpa のライブは、カルーセルをデコードする前に入口の部品の自動起動の指定をこれで読む
|
|
112
114
|
*/
|
|
113
|
-
function parsePmt(section) {
|
|
115
|
+
export function parsePmt(section) {
|
|
114
116
|
const components = [];
|
|
115
117
|
for (const [streamType, pid, info] of pmtStreams(section)) {
|
|
116
118
|
let componentId;
|
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,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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
421
|
-
|
|
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 の放送では映像と音声がスクランブルされ、信号はされていない** (
|
|
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/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;
|
|
@@ -181,12 +181,39 @@ export interface TerrestrialDelivery {
|
|
|
181
181
|
/** 周波数 (Hz)。中継局ごとに並ぶことがある。放送は 1/7 MHz 単位で書く */
|
|
182
182
|
frequencies: number[];
|
|
183
183
|
}
|
|
184
|
-
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
// トランスポートエラーが立っているものは信用しない
|
|
@@ -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
|
|
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
|
-
/**
|
|
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
|
}
|
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 が付く */
|
|
@@ -146,12 +156,13 @@ extra?: number[]): Uint8Array;
|
|
|
146
156
|
/**
|
|
147
157
|
* データ符号化方式記述子 (0xFD)。**BML が載っている ES の目印。**
|
|
148
158
|
*
|
|
149
|
-
*
|
|
150
|
-
* 既定は地上波 (0x0C)
|
|
159
|
+
* 中身は「入口かどうか」と、自動起動の指定 (`autoStart`。入口のときだけ)、カルーセルの回 (dataEventId)。
|
|
160
|
+
* 既定は地上波 (0x0C) の入口つき・自動起動つき・960x540
|
|
151
161
|
*/
|
|
152
162
|
export declare function bxmlDescriptor(options?: {
|
|
153
163
|
dataComponentId?: number;
|
|
154
164
|
entryPoint?: boolean;
|
|
165
|
+
autoStart?: boolean;
|
|
155
166
|
}): number[];
|
|
156
167
|
/**
|
|
157
168
|
* 字幕の ES (stream_type 0x06) に付ける記述子。**字幕の目印。**
|
|
@@ -209,7 +220,7 @@ export declare function bmlHead(): Uint8Array[];
|
|
|
209
220
|
* 本物と同じく、先頭に Content-Type、境界で区切って各ファイルに Content-Location
|
|
210
221
|
*/
|
|
211
222
|
export declare function multipartModule(files: [location: string, type: string, body: string][]): Uint8Array;
|
|
212
|
-
/** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT
|
|
223
|
+
/** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在りかの印** */
|
|
213
224
|
export declare function aitSignallingDescriptor(applicationType?: number, version?: number): number[];
|
|
214
225
|
export interface SynthHybridcastApp {
|
|
215
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 = [
|
|
@@ -402,15 +424,15 @@ extra = []) {
|
|
|
402
424
|
/**
|
|
403
425
|
* データ符号化方式記述子 (0xFD)。**BML が載っている ES の目印。**
|
|
404
426
|
*
|
|
405
|
-
*
|
|
406
|
-
* 既定は地上波 (0x0C)
|
|
427
|
+
* 中身は「入口かどうか」と、自動起動の指定 (`autoStart`。入口のときだけ)、カルーセルの回 (dataEventId)。
|
|
428
|
+
* 既定は地上波 (0x0C) の入口つき・自動起動つき・960x540
|
|
407
429
|
*/
|
|
408
430
|
export function bxmlDescriptor(options = {}) {
|
|
409
|
-
const { dataComponentId = 0x0c, entryPoint = true } = options;
|
|
431
|
+
const { dataComponentId = 0x0c, entryPoint = true, autoStart = true, } = options;
|
|
410
432
|
const body = [
|
|
411
433
|
...be(dataComponentId),
|
|
412
434
|
// 伝送方式 00 / 入口 / 自動起動 / 解像度 0011 (960x540)
|
|
413
|
-
...(entryPoint ? [0x33, 0x70] : [0x00]),
|
|
435
|
+
...(entryPoint ? [autoStart ? 0x33 : 0x23, 0x70] : [0x00]),
|
|
414
436
|
0xf8, // dataEventId 0xF / event_section_flag
|
|
415
437
|
0xa0, // 即時取得あり / 蓄積不可 / 起動優先
|
|
416
438
|
];
|
|
@@ -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
|
*/
|