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