@danything/arib 0.1.0 → 1.0.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/LICENSE +39 -0
- package/README.md +12 -8
- package/dist/ait.d.ts +1 -1
- package/dist/ait.js +4 -1
- package/dist/aribtext.d.ts +8 -5
- package/dist/aribtext.js +60 -26
- package/dist/b24-tables.d.ts +22 -4
- package/dist/b24-tables.js +97 -6
- package/dist/b24caption.d.ts +5 -1
- package/dist/b24caption.js +42 -13
- package/dist/bml-protocol.d.ts +3 -2
- package/dist/bml-protocol.js +2 -1
- package/dist/bml.d.ts +4 -4
- package/dist/bml.js +30 -9
- package/dist/caption-text.d.ts +8 -3
- package/dist/caption-text.js +2 -2
- package/dist/drcs-smooth.d.ts +1 -1
- package/dist/drcs-smooth.js +2 -1
- package/dist/dsmcc.d.ts +23 -5
- package/dist/dsmcc.js +50 -10
- package/dist/eit.d.ts +42 -9
- package/dist/eit.js +44 -14
- package/dist/multipart.d.ts +1 -1
- package/dist/multipart.js +1 -1
- package/dist/pes.d.ts +3 -3
- package/dist/pes.js +24 -11
- package/dist/psi.d.ts +4 -4
- package/dist/psi.js +5 -5
- package/dist/service-filter.d.ts +16 -5
- package/dist/service-filter.js +38 -6
- package/dist/synth-av.d.ts +3 -3
- package/dist/synth-av.js +3 -3
- package/dist/synth-caption.d.ts +1 -1
- package/dist/synth-caption.js +1 -1
- package/dist/synth.d.ts +21 -3
- package/dist/synth.js +39 -6
- package/package.json +1 -1
package/dist/b24caption.js
CHANGED
|
@@ -14,12 +14,16 @@
|
|
|
14
14
|
* 取っていて常に 0 になり、最初の1回しか読まない。ここは群を正しく見る
|
|
15
15
|
* (番組が変わって群が替わったときに書式を読み直す)
|
|
16
16
|
* - **点滅 (FLC) を読み取る。** あちらは読み捨てている
|
|
17
|
+
* - **JIS X 0213 を最後まで読む。** JIS互換漢字2面 (0x3A) を2面の表で、1面 (0x39) の 85〜94区を
|
|
18
|
+
* 第3水準の漢字で引く。か゚ のような2つのコードポイントの字も出す (あちらは空き)
|
|
19
|
+
* - **表で空いている点 (U+FFFD) はゲタ (〓) にする。** 「�」は Denpa Font に無く、端末の字の
|
|
20
|
+
* 「?」の印で出る。定義されていない外字と同じ扱い
|
|
17
21
|
*
|
|
18
22
|
* 入力は字幕の PES の中身 (データ識別 0x80 から)。ffmpeg が `-c:s copy` で Matroska
|
|
19
23
|
* (`S_ARIBSUB`) に入れたコマの中身がそのままこれになる。
|
|
20
24
|
*/
|
|
21
25
|
import { createHash } from "node:crypto";
|
|
22
|
-
import { ADDITIONAL, ALNUM_FULL, ALNUM_HALF, CLUT, DRCS_REPLACE, HIRAGANA, JISX0201, JISX0201_HALF, KANA_SYMBOLS_HALF, KANJI_DIFF, KANJI_SYMBOLS_HALF, KATAKANA, } from "./b24-tables.js";
|
|
26
|
+
import { ADDITIONAL, ALNUM_FULL, ALNUM_HALF, CLUT, DRCS_REPLACE, HIRAGANA, JISX0201, JISX0201_HALF, KANA_SYMBOLS_HALF, KANJI_DIFF, KANJI_SEQUENCES, KANJI_SYMBOLS_HALF, KATAKANA, PLANE1_HIGH, PLANE2, } from "./b24-tables.js";
|
|
23
27
|
import { ascii, u16, u24 } from "./bytes.js";
|
|
24
28
|
import { CAPTION_TEXT_VERSION, captionLanguage, } from "./caption-text.js";
|
|
25
29
|
import { smoothDrcs } from "./drcs-smooth.js";
|
|
@@ -29,7 +33,13 @@ const HIRA = { set: "hiragana", bytes: 1 };
|
|
|
29
33
|
const KATA = { set: "katakana", bytes: 1 };
|
|
30
34
|
const MACRO = { set: "macro", bytes: 1 };
|
|
31
35
|
const MOSAIC = { set: "mosaic", bytes: 1 };
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* 終端バイト → 集合 (G の指示)。プロポーショナルは同じ表で引く (libaribcaption と同じ)。
|
|
38
|
+
*
|
|
39
|
+
* JIS X 0213 の1面 (0x39) は 1〜84区を漢字集合 (0x42) と同じ表で引き (漢字集合でも JIS X 0213 にだけある字を出す)、
|
|
40
|
+
* 85〜94区は JIS X 0213 の漢字 (`PLANE1_HIGH`)。漢字集合と追加記号 (0x3B) の 85区から先は追加記号。
|
|
41
|
+
* 2面 (0x3A) は別の表 (`PLANE2`)。libaribcaption は 1面・2面とも漢字集合の表で引いていた
|
|
42
|
+
*/
|
|
33
43
|
const G_SETS = new Map([
|
|
34
44
|
[0x42, KANJI],
|
|
35
45
|
[0x4a, ALNUM],
|
|
@@ -43,8 +53,8 @@ const G_SETS = new Map([
|
|
|
43
53
|
[0x37, HIRA],
|
|
44
54
|
[0x38, KATA],
|
|
45
55
|
[0x49, { set: "jisx0201", bytes: 1 }],
|
|
46
|
-
[0x39,
|
|
47
|
-
[0x3a,
|
|
56
|
+
[0x39, { set: "plane1", bytes: 2 }],
|
|
57
|
+
[0x3a, { set: "plane2", bytes: 2 }],
|
|
48
58
|
[0x3b, { set: "additional", bytes: 2 }],
|
|
49
59
|
]);
|
|
50
60
|
/** 終端バイト → DRCS の面 (0x40 は2バイトの面 0)。0x70 はマクロ */
|
|
@@ -81,7 +91,7 @@ const MACROS = [
|
|
|
81
91
|
return Uint8Array.from([...bytes, ...MACRO_TAIL]);
|
|
82
92
|
});
|
|
83
93
|
const eucjp = new TextDecoder("euc-jp");
|
|
84
|
-
/** 漢字の表 (区×94+点 → 字)。EUC-JP
|
|
94
|
+
/** 漢字の表 (区×94+点 → 字)。EUC-JP で読み、違うところ (`KANJI_DIFF`) だけ差し替える */
|
|
85
95
|
const kanjiOverride = new Map();
|
|
86
96
|
for (const [start, chars] of KANJI_DIFF) {
|
|
87
97
|
let i = start;
|
|
@@ -110,7 +120,12 @@ const T = {
|
|
|
110
120
|
alnumHalf: points(ALNUM_HALF),
|
|
111
121
|
kanjiHalf: points(KANJI_SYMBOLS_HALF),
|
|
112
122
|
additional: points(ADDITIONAL),
|
|
123
|
+
plane1High: points(PLANE1_HIGH),
|
|
124
|
+
/** 2面の区 (0 から) → 94 字 */
|
|
125
|
+
plane2: new Map(PLANE2.map(([ku, row]) => [ku - 1, points(row)])),
|
|
113
126
|
};
|
|
127
|
+
/** 1区画に2つのコードポイントを描く字 (区×94+点 → 字)。JIS X 0213 の か゚ など */
|
|
128
|
+
const sequences = new Map(KANJI_SEQUENCES);
|
|
114
129
|
/** 色。CLUT の組 (0〜7) と番号 (0〜15) */
|
|
115
130
|
function clut(palette, index) {
|
|
116
131
|
return `#${CLUT[palette * 16 + index] ?? CLUT[index]}`;
|
|
@@ -151,7 +166,7 @@ function sendable(drcs) {
|
|
|
151
166
|
* ストリームごとに1つ作って、届いた順に食わせる。
|
|
152
167
|
*
|
|
153
168
|
* **読むのは1つの言語だけ** (`language`。1〜8)。言語ごとに書式も位置も別に続くので、2つの言語を出し分けるなら
|
|
154
|
-
* 言語ごとに1つ作って、同じコマを両方に食わせる (`server/captions.ts` の `CaptionText`)
|
|
169
|
+
* 言語ごとに1つ作って、同じコマを両方に食わせる (denpa の `server/captions.ts` の `CaptionText`)
|
|
155
170
|
*/
|
|
156
171
|
export class B24CaptionDecoder {
|
|
157
172
|
language;
|
|
@@ -764,13 +779,15 @@ export class B24CaptionDecoder {
|
|
|
764
779
|
this.push((this.msz() ? T.jisx0201Half : T.jisx0201)[index]);
|
|
765
780
|
this.moveRelative(1, 0);
|
|
766
781
|
}
|
|
767
|
-
else if (set === "kanji" || set === "additional") {
|
|
782
|
+
else if (set === "kanji" || set === "plane1" || set === "additional") {
|
|
768
783
|
const ku = ch - 0x21;
|
|
769
784
|
const ten = ch2 - 0x21;
|
|
770
785
|
let code;
|
|
786
|
+
let joined = null;
|
|
771
787
|
if (ku < 84) {
|
|
772
788
|
const i = ku * 94 + ten;
|
|
773
|
-
|
|
789
|
+
joined = sequences.get(i) ?? null;
|
|
790
|
+
code = joined?.codePointAt(0) ?? kanji(i);
|
|
774
791
|
if (this.msz()) {
|
|
775
792
|
if (ku < 2)
|
|
776
793
|
code = T.kanjiHalf[i];
|
|
@@ -778,10 +795,17 @@ export class B24CaptionDecoder {
|
|
|
778
795
|
code = (code & 0xff) + 0x20;
|
|
779
796
|
}
|
|
780
797
|
}
|
|
798
|
+
else if (set === "plane1") {
|
|
799
|
+
code = T.plane1High[(ku - 84) * 94 + ten] ?? 0xfffd;
|
|
800
|
+
}
|
|
781
801
|
else {
|
|
782
802
|
code = T.additional[(ku - 84) * 94 + ten] ?? 0xfffd;
|
|
783
803
|
}
|
|
784
|
-
this.push(code);
|
|
804
|
+
this.push(code, null, joined);
|
|
805
|
+
this.moveRelative(1, 0);
|
|
806
|
+
}
|
|
807
|
+
else if (set === "plane2") {
|
|
808
|
+
this.push(T.plane2.get(ch - 0x21)?.[ch2 - 0x21] ?? 0xfffd);
|
|
785
809
|
this.moveRelative(1, 0);
|
|
786
810
|
}
|
|
787
811
|
else if (set === "alnum") {
|
|
@@ -812,9 +836,11 @@ export class B24CaptionDecoder {
|
|
|
812
836
|
// モザイクは描かない (libaribcaption と同じ)
|
|
813
837
|
return entry.bytes;
|
|
814
838
|
}
|
|
815
|
-
push(code, drcs = null) {
|
|
839
|
+
push(code, drcs = null, joined = null) {
|
|
816
840
|
this.caption.chars.push({
|
|
817
|
-
|
|
841
|
+
// 表で空いている点 (規格の空き・JIS X 0213 にも無い区点) もゲタで埋める
|
|
842
|
+
code: code === 0xfffd ? 0x3013 : code,
|
|
843
|
+
joined,
|
|
818
844
|
drcs,
|
|
819
845
|
x: this.posX,
|
|
820
846
|
y: this.posY - this.sectionHeight(),
|
|
@@ -920,10 +946,12 @@ function toPage(caption, plane) {
|
|
|
920
946
|
fy: (char.vSpacing * char.vScale) / 2,
|
|
921
947
|
size,
|
|
922
948
|
scaleX,
|
|
923
|
-
text: String.fromCodePoint(char.code),
|
|
949
|
+
text: char.joined ?? String.fromCodePoint(char.code),
|
|
924
950
|
fg: char.fg,
|
|
925
951
|
bg: char.bg,
|
|
926
952
|
};
|
|
953
|
+
if (char.joined !== null)
|
|
954
|
+
run.joined = true;
|
|
927
955
|
if (char.drcs !== null) {
|
|
928
956
|
const key = String(char.drcs);
|
|
929
957
|
run.drcs = key;
|
|
@@ -946,11 +974,12 @@ function toPage(caption, plane) {
|
|
|
946
974
|
run.flash = true;
|
|
947
975
|
if (char.ruby)
|
|
948
976
|
run.ruby = true;
|
|
949
|
-
//
|
|
977
|
+
// 見た目が同じで、すぐ右に続いていれば束ねる。外字の画像と、2つのコードポイントの字は1字ずつ
|
|
950
978
|
const { x: _x, y: _y, text: _t, ...looks } = run;
|
|
951
979
|
const key = JSON.stringify(looks);
|
|
952
980
|
if (last !== null &&
|
|
953
981
|
run.drcs === undefined &&
|
|
982
|
+
run.joined === undefined &&
|
|
954
983
|
last.key === key &&
|
|
955
984
|
last.run.y === run.y &&
|
|
956
985
|
last.run.x + last.count * last.run.w === run.x) {
|
package/dist/bml-protocol.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* データ放送の通知の形式。**デコードする側 ([bml.ts](bml.ts)) と描く側の取り決め。**
|
|
3
3
|
*
|
|
4
|
-
* もとは web-bml の `
|
|
4
|
+
* もとは web-bml の `server/ws_api.ts` の型を借りていた。形式はそのまま写してある —
|
|
5
5
|
* 録画の隣に置いた `.bml.jsonl` はこの形式の JSON を1行ずつ持っているので、
|
|
6
6
|
* **形式を変えると前に録ったものが読めなくなる**。足すのはよいが、名前や意味は変えない。
|
|
7
|
+
* web-bml は MIT で、著作権表示と許諾の文は [LICENSE](../LICENSE) に置いてある。
|
|
7
8
|
*
|
|
8
9
|
* 載せるのは denpa が実際に作るものだけ (PMT・モジュール一覧・揃ったモジュール・
|
|
9
10
|
* ストリーム記述子のイベント・番組の名乗り・放送の時刻)。web-bml が自前の
|
|
@@ -122,7 +123,7 @@ export interface ESEventUpdatedMessage {
|
|
|
122
123
|
events: ESEvent[];
|
|
123
124
|
dataEventId: number;
|
|
124
125
|
}
|
|
125
|
-
/** 番組の名乗り。TS からではなく DB から組む (`server/live.ts`・`server/recorded-bml.ts`) */
|
|
126
|
+
/** 番組の名乗り。TS からではなく DB から組む (denpa の `server/live.ts`・`server/recorded-bml.ts`) */
|
|
126
127
|
export interface ProgramInfoMessage {
|
|
127
128
|
type: "programInfo";
|
|
128
129
|
originalNetworkId: number | null;
|
package/dist/bml-protocol.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* データ放送の通知の形式。**デコードする側 ([bml.ts](bml.ts)) と描く側の取り決め。**
|
|
3
3
|
*
|
|
4
|
-
* もとは web-bml の `
|
|
4
|
+
* もとは web-bml の `server/ws_api.ts` の型を借りていた。形式はそのまま写してある —
|
|
5
5
|
* 録画の隣に置いた `.bml.jsonl` はこの形式の JSON を1行ずつ持っているので、
|
|
6
6
|
* **形式を変えると前に録ったものが読めなくなる**。足すのはよいが、名前や意味は変えない。
|
|
7
|
+
* web-bml は MIT で、著作権表示と許諾の文は [LICENSE](../LICENSE) に置いてある。
|
|
7
8
|
*
|
|
8
9
|
* 載せるのは denpa が実際に作るものだけ (PMT・モジュール一覧・揃ったモジュール・
|
|
9
10
|
* ストリーム記述子のイベント・番組の名乗り・放送の時刻)。web-bml が自前の
|
package/dist/bml.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* ([dsmcc.ts](dsmcc.ts) / [logo-dsmcc.ts](https://github.com/danything/denpa/blob/main/src/lib/ts/logo-dsmcc.ts))。
|
|
12
12
|
* [web-bml](https://github.com/otya128/web-bml) の `decode_ts.ts` は使わない — 要るのは
|
|
13
13
|
* その一部だけで、残り (EIT/SDT/NIT・字幕の PES) は自前で読んでおり
|
|
14
|
-
* ([eit.ts](eit.ts) / [psi.ts](psi.ts) /
|
|
14
|
+
* ([eit.ts](eit.ts) / [psi.ts](psi.ts) / [b24caption.ts](b24caption.ts))、`@chinachu/aribts` も抱えずに済む。
|
|
15
15
|
*
|
|
16
16
|
* **出す形式は web-bml の頃のまま** ([bml-protocol.ts](bml-protocol.ts))。録画の隣の
|
|
17
17
|
* `.bml.jsonl` がこの形式で残っているので、変えると前に録ったものが読めなくなる。
|
|
@@ -47,7 +47,7 @@ export declare class BmlDecoder {
|
|
|
47
47
|
feed(chunk: Uint8Array): void;
|
|
48
48
|
/**
|
|
49
49
|
* 切り分け済みのパケットを1つ。ライブは同じ TS を時計や Hybridcast とも
|
|
50
|
-
* 分け合うので、切り分けは1回で済ませて配る (`server/live.ts` の `tap`)。
|
|
50
|
+
* 分け合うので、切り分けは1回で済ませて配る (denpa の `server/live.ts` の `tap`)。
|
|
51
51
|
*
|
|
52
52
|
* **PID で行き先を引く。** 組み立て役を全部に回していた頃は、データ放送と
|
|
53
53
|
* 関係ない映像のパケットのたびにカルーセルの数だけ呼んでいた。
|
|
@@ -66,8 +66,8 @@ export declare class BmlDecoder {
|
|
|
66
66
|
/**
|
|
67
67
|
* **来ないモジュールを諦める** (`GIVE_UP_ROUNDS`)。待っている受け側 (`Resources` の `wait`) は、
|
|
68
68
|
* DII に無いモジュールを「もう来ない」と決める (`gone`) ので、諦めたものを除いた一覧を配り直して
|
|
69
|
-
* 待ちを終わらせる。前の DII
|
|
70
|
-
* 組み立てるのをやめるだけ。次に DII
|
|
69
|
+
* 待ちを終わらせる。前の DII で配り終えたバージョンのままのもの (受け側が持っている) は一覧から外さず、
|
|
70
|
+
* 組み立てるのをやめるだけ。次に DII のバージョンが変われば、また待つ
|
|
71
71
|
*/
|
|
72
72
|
private giveUp;
|
|
73
73
|
private onDdb;
|
package/dist/bml.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* ([dsmcc.ts](dsmcc.ts) / [logo-dsmcc.ts](https://github.com/danything/denpa/blob/main/src/lib/ts/logo-dsmcc.ts))。
|
|
12
12
|
* [web-bml](https://github.com/otya128/web-bml) の `decode_ts.ts` は使わない — 要るのは
|
|
13
13
|
* その一部だけで、残り (EIT/SDT/NIT・字幕の PES) は自前で読んでおり
|
|
14
|
-
* ([eit.ts](eit.ts) / [psi.ts](psi.ts) /
|
|
14
|
+
* ([eit.ts](eit.ts) / [psi.ts](psi.ts) / [b24caption.ts](b24caption.ts))、`@chinachu/aribts` も抱えずに済む。
|
|
15
15
|
*
|
|
16
16
|
* **出す形式は web-bml の頃のまま** ([bml-protocol.ts](bml-protocol.ts))。録画の隣の
|
|
17
17
|
* `.bml.jsonl` がこの形式で残っているので、変えると前に録ったものが読めなくなる。
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { Buffer } from "node:buffer";
|
|
21
21
|
import { inflateSync } from "node:zlib";
|
|
22
22
|
import { u16, u32 } from "./bytes.js";
|
|
23
|
-
import { dataEventIdOf, ModuleBuilder, parseDdb, parseDii, TABLE_DII, TABLE_STREAM_DESCRIPTOR, } from "./dsmcc.js";
|
|
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
26
|
import { descriptors, packetPid, PacketStream, pmtStreams, PmtTracker, SectionAssembler, } from "./psi.js";
|
|
@@ -187,7 +187,13 @@ function parseEsEvents(section) {
|
|
|
187
187
|
* 短く見積もる) ため
|
|
188
188
|
*/
|
|
189
189
|
const GIVE_UP_ROUNDS = 4;
|
|
190
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* 1つの `BmlDecoder` で同時に組み立てるモジュールの大きさの合計の上限 (64MiB)。
|
|
192
|
+
* 1つずつは `MAX_MODULE_SIZE` に収まっても、たくさん並べた DII や ES でメモリを使い切らせられる。
|
|
193
|
+
* 実際のカルーセルは全部で数 MB に収まる
|
|
194
|
+
*/
|
|
195
|
+
const MAX_CAROUSEL_SIZE = 64 * 1024 * 1024;
|
|
196
|
+
/** `Component.done` に置く印。モジュールのバージョンとカルーセルの回 (dataEventId) の組 */
|
|
191
197
|
function doneMark(component, version) {
|
|
192
198
|
return `${version}:${component.dataEventId}`;
|
|
193
199
|
}
|
|
@@ -218,7 +224,7 @@ export class BmlDecoder {
|
|
|
218
224
|
}
|
|
219
225
|
/**
|
|
220
226
|
* 切り分け済みのパケットを1つ。ライブは同じ TS を時計や Hybridcast とも
|
|
221
|
-
* 分け合うので、切り分けは1回で済ませて配る (`server/live.ts` の `tap`)。
|
|
227
|
+
* 分け合うので、切り分けは1回で済ませて配る (denpa の `server/live.ts` の `tap`)。
|
|
222
228
|
*
|
|
223
229
|
* **PID で行き先を引く。** 組み立て役を全部に回していた頃は、データ放送と
|
|
224
230
|
* 関係ない映像のパケットのたびにカルーセルの数だけ呼んでいた。
|
|
@@ -320,7 +326,21 @@ export class BmlDecoder {
|
|
|
320
326
|
component.abandoned.clear();
|
|
321
327
|
component.dii = dii;
|
|
322
328
|
component.size = 0;
|
|
329
|
+
// ほかの ES で組み立て中のものと合わせて `MAX_CAROUSEL_SIZE` に収める
|
|
330
|
+
let budget = MAX_CAROUSEL_SIZE;
|
|
331
|
+
for (const other of this.components.values())
|
|
332
|
+
if (other !== component)
|
|
333
|
+
budget -= other.size;
|
|
323
334
|
for (const module of dii.modules) {
|
|
335
|
+
/*
|
|
336
|
+
* **大きすぎるモジュールは組み立てない。** 来ないものとして一覧から外す
|
|
337
|
+
* (受け側が待ち続けないように)
|
|
338
|
+
*/
|
|
339
|
+
if (module.moduleSize > MAX_MODULE_SIZE ||
|
|
340
|
+
component.size + module.moduleSize > budget) {
|
|
341
|
+
component.abandoned.add(module.moduleId);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
324
344
|
component.size += module.moduleSize;
|
|
325
345
|
component.building.set(module.moduleId, {
|
|
326
346
|
module,
|
|
@@ -360,8 +380,8 @@ export class BmlDecoder {
|
|
|
360
380
|
/**
|
|
361
381
|
* **来ないモジュールを諦める** (`GIVE_UP_ROUNDS`)。待っている受け側 (`Resources` の `wait`) は、
|
|
362
382
|
* DII に無いモジュールを「もう来ない」と決める (`gone`) ので、諦めたものを除いた一覧を配り直して
|
|
363
|
-
* 待ちを終わらせる。前の DII
|
|
364
|
-
* 組み立てるのをやめるだけ。次に DII
|
|
383
|
+
* 待ちを終わらせる。前の DII で配り終えたバージョンのままのもの (受け側が持っている) は一覧から外さず、
|
|
384
|
+
* 組み立てるのをやめるだけ。次に DII のバージョンが変われば、また待つ
|
|
365
385
|
*/
|
|
366
386
|
giveUp(component) {
|
|
367
387
|
const limit = component.size * GIVE_UP_ROUNDS;
|
|
@@ -386,7 +406,7 @@ export class BmlDecoder {
|
|
|
386
406
|
for (const waiting of component.building.values())
|
|
387
407
|
waiting.quietBytes += ddb.block.length;
|
|
388
408
|
const held = component.building.get(ddb.moduleId);
|
|
389
|
-
//
|
|
409
|
+
// バージョンが食い違うブロックを混ぜると壊れたファイルができる
|
|
390
410
|
if (held === undefined || held.builder.moduleVersion !== ddb.moduleVersion)
|
|
391
411
|
return;
|
|
392
412
|
const before = held.builder.blocks;
|
|
@@ -403,7 +423,7 @@ export class BmlDecoder {
|
|
|
403
423
|
// 次の DII が来るまで組み立て直さない
|
|
404
424
|
component.building.delete(moduleId);
|
|
405
425
|
/*
|
|
406
|
-
* **同じものを配り直さない。**
|
|
426
|
+
* **同じものを配り直さない。** カルーセルは回り続けるので、バージョンが上がって
|
|
407
427
|
* いなくても同じモジュールが何度も揃う。そのたびに配ると、受け側は
|
|
408
428
|
* 中身の変わらない画面を組み直し続ける
|
|
409
429
|
*/
|
|
@@ -481,7 +501,8 @@ export function inflate(data, compression) {
|
|
|
481
501
|
if (compression.type !== COMPRESSION_ZLIB)
|
|
482
502
|
return null;
|
|
483
503
|
try {
|
|
484
|
-
|
|
504
|
+
// 伸ばした大きさにも上限を置く。小さな DDB から何 GB にも伸びるものを作れる
|
|
505
|
+
return inflateSync(data, { maxOutputLength: MAX_MODULE_SIZE });
|
|
485
506
|
}
|
|
486
507
|
catch {
|
|
487
508
|
return null;
|
package/dist/caption-text.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* **字幕の文字の配置** (
|
|
2
|
+
* **字幕の文字の配置** (バージョン 1)。サーバが放送の字幕 (ARIB STD-B24) をデコードして置き場所まで
|
|
3
3
|
* 決め、受け側 (ブラウザ・テレビのアプリ) はこれを**描くだけ**にする。形式の取り決めは
|
|
4
4
|
* [api.md](https://github.com/danything/denpa/blob/main/docs/api.md#字幕の文字の配置) に書いてある (ここと同じものを、Kotlin でも読む)。
|
|
5
5
|
*
|
|
6
6
|
* 座標はすべて**字幕の面の単位** (`plane`。ふつう 960x540)。受け側は映像の枠に
|
|
7
7
|
* 合わせて縦横それぞれ伸ばす。色は `#rrggbbaa`。
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
9
|
+
/** 形式のバージョン。**互換の無い変え方をしたら上げる** (受け側は知らないバージョンを描かない) */
|
|
10
10
|
export declare const CAPTION_TEXT_VERSION = 1;
|
|
11
11
|
/** 縁取り (ORN) の太さ。面の単位で、字の輪郭の外側へ。libaribcaption の既定 (ffmpeg の `outline_width`) と同じ */
|
|
12
12
|
export declare const STROKE_WIDTH = 1.5;
|
|
@@ -46,6 +46,11 @@ export interface CaptionRun {
|
|
|
46
46
|
*/
|
|
47
47
|
scaleX: number;
|
|
48
48
|
text: string;
|
|
49
|
+
/**
|
|
50
|
+
* `text` 全体で1つの区画 (JIS X 0213 の か゚・æ̀・˩˥ のように、2つのコードポイントで1字のもの)。
|
|
51
|
+
* この run は1字だけで、`text` をまとめて描く (フォントが1字に組む)
|
|
52
|
+
*/
|
|
53
|
+
joined?: true;
|
|
49
54
|
/** 置き換えられなかった外字。`text` は「〓」で、この画像を字の枠いっぱいに描く */
|
|
50
55
|
drcs?: string;
|
|
51
56
|
/** 字の色 */
|
|
@@ -108,7 +113,7 @@ export interface CaptionLanguage {
|
|
|
108
113
|
}
|
|
109
114
|
/**
|
|
110
115
|
* 録画の字幕のうち、第2言語から後ろ (`CaptionPages.others`)。**第1言語は今までどおり `pages`** —
|
|
111
|
-
*
|
|
116
|
+
* バージョン (`v`) を上げずに足したので、知らない受け側は第1言語だけを描く
|
|
112
117
|
*/
|
|
113
118
|
export interface CaptionLanguagePages extends CaptionLanguage {
|
|
114
119
|
pages: {
|
package/dist/caption-text.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { LANGUAGE } from "./language.js";
|
|
2
2
|
/**
|
|
3
|
-
* **字幕の文字の配置** (
|
|
3
|
+
* **字幕の文字の配置** (バージョン 1)。サーバが放送の字幕 (ARIB STD-B24) をデコードして置き場所まで
|
|
4
4
|
* 決め、受け側 (ブラウザ・テレビのアプリ) はこれを**描くだけ**にする。形式の取り決めは
|
|
5
5
|
* [api.md](https://github.com/danything/denpa/blob/main/docs/api.md#字幕の文字の配置) に書いてある (ここと同じものを、Kotlin でも読む)。
|
|
6
6
|
*
|
|
7
7
|
* 座標はすべて**字幕の面の単位** (`plane`。ふつう 960x540)。受け側は映像の枠に
|
|
8
8
|
* 合わせて縦横それぞれ伸ばす。色は `#rrggbbaa`。
|
|
9
9
|
*/
|
|
10
|
-
/**
|
|
10
|
+
/** 形式のバージョン。**互換の無い変え方をしたら上げる** (受け側は知らないバージョンを描かない) */
|
|
11
11
|
export const CAPTION_TEXT_VERSION = 1;
|
|
12
12
|
/** 縁取り (ORN) の太さ。面の単位で、字の輪郭の外側へ。libaribcaption の既定 (ffmpeg の `outline_width`) と同じ */
|
|
13
13
|
export const STROKE_WIDTH = 1.5;
|
package/dist/drcs-smooth.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* **字幕の外字 (DRCS) の画像を、送る前になめらかにする** ([b24caption.ts](b24caption.ts) が JSON に入れるところで使う)。
|
|
3
3
|
*
|
|
4
|
-
* 放送は置き換え表で字にできない外字を、16〜36
|
|
4
|
+
* 放送は置き換え表で字にできない外字を、16〜36 画素の白黒か 4 階調の画像で送ってくる。受け側はそれを字の枠いっぱい
|
|
5
5
|
* (1920x1080 の全画面で 72 画素ほど) に引き伸ばすので、そのまま渡すと 1 点が 4x4 の階段になり、
|
|
6
6
|
* なめらかに引き伸ばしても階段がぼやけて残る (スピーカー 🔊 の外字など)。
|
|
7
7
|
*
|
package/dist/drcs-smooth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* **字幕の外字 (DRCS) の画像を、送る前になめらかにする** ([b24caption.ts](b24caption.ts) が JSON に入れるところで使う)。
|
|
3
3
|
*
|
|
4
|
-
* 放送は置き換え表で字にできない外字を、16〜36
|
|
4
|
+
* 放送は置き換え表で字にできない外字を、16〜36 画素の白黒か 4 階調の画像で送ってくる。受け側はそれを字の枠いっぱい
|
|
5
5
|
* (1920x1080 の全画面で 72 画素ほど) に引き伸ばすので、そのまま渡すと 1 点が 4x4 の階段になり、
|
|
6
6
|
* なめらかに引き伸ばしても階段がぼやけて残る (スピーカー 🔊 の外字など)。
|
|
7
7
|
*
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* **置き換え表の MD5 は元の画像から取る** (b24caption.ts)。ここで作った画像は送るためだけのもの。
|
|
20
20
|
*/
|
|
21
|
+
import { Buffer } from "node:buffer";
|
|
21
22
|
/** 送る画像の一辺。これに届くまで倍にする (全画面の字の枠と同じくらい) */
|
|
22
23
|
const TARGET = 64;
|
|
23
24
|
/** 送る画像の一辺の上限。受け側 (denpa-tv) もこれより大きい画像は読まない */
|
package/dist/dsmcc.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface DiiModule {
|
|
|
43
43
|
}
|
|
44
44
|
export interface Dii {
|
|
45
45
|
downloadId: number;
|
|
46
|
-
/**
|
|
46
|
+
/** カルーセルのバージョン。**変わっていなければ組み立て直さない** */
|
|
47
47
|
transactionId: number;
|
|
48
48
|
blockSize: number;
|
|
49
49
|
modules: DiiModule[];
|
|
@@ -70,23 +70,41 @@ export declare function parseDii(section: Uint8Array): Dii | null;
|
|
|
70
70
|
/** DDB (Download Data Block)。モジュールを割ったブロックが1つ入っている */
|
|
71
71
|
export declare function parseDdb(section: Uint8Array): Ddb | null;
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
73
|
+
* 組み立てるモジュールの大きさの上限 (16MiB)。**これより大きいと言う DII は信じない。**
|
|
74
|
+
*
|
|
75
|
+
* `moduleSize` は32ビットで、そのまま信じると DII 1つで 4GB を確保させられる。
|
|
76
|
+
* 実際のデータ放送のモジュールは数 KB〜数百 KB、衛星のロゴも全局ぶんで数百 KB なので、
|
|
77
|
+
* 十分に余裕を見た値。zlib で伸ばしたあとの大きさにも同じ上限を使う ([bml.ts](bml.ts) の `inflate`)
|
|
78
|
+
*/
|
|
79
|
+
export declare const MAX_MODULE_SIZE: number;
|
|
80
|
+
/**
|
|
81
|
+
* 組み立て中のモジュール。DDB が来るたびに然るべき位置へ置く。
|
|
82
|
+
*
|
|
83
|
+
* **入れ物は届いたところまでしか確保しない。** DII の言う大きさを先に確保すると、
|
|
84
|
+
* 大きいと言うだけの DII でメモリを使い切らせられる。`MAX_MODULE_SIZE` を超える
|
|
85
|
+
* モジュールはブロックを受け取らない (いつまでも揃わない)。
|
|
75
86
|
*
|
|
76
87
|
* 順番は保証されない (途中から見れば真ん中から始まる) ので、
|
|
77
88
|
* **受け取ったブロック番号を数えて**揃ったかを見る。
|
|
78
89
|
*/
|
|
79
90
|
export declare class ModuleBuilder {
|
|
80
91
|
readonly moduleVersion: number;
|
|
92
|
+
private readonly moduleSize;
|
|
81
93
|
private readonly blockSize;
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
private buffer;
|
|
95
|
+
/** 受け取ったブロック番号 (1ブロック1ビット)。最初のブロックが来てから確保する */
|
|
96
|
+
private received;
|
|
97
|
+
private count;
|
|
84
98
|
private readonly total;
|
|
85
99
|
constructor(moduleVersion: number, moduleSize: number, blockSize: number);
|
|
100
|
+
/** 組み立てた中身。まだ届いていないところは 0。**揃ってから読むこと** (読むとモジュールの大きさぶんを確保する) */
|
|
101
|
+
get data(): Uint8Array;
|
|
86
102
|
/** 受け取ったブロックの数 (同じブロックを2度受けても1つ) */
|
|
87
103
|
get blocks(): number;
|
|
88
104
|
/** 揃ったか。**大きさ 0 のモジュールは初めから揃っている** (運ぶブロックが無いので DDB を待たない) */
|
|
89
105
|
get complete(): boolean;
|
|
90
106
|
/** ブロックを1つ置く。**揃ったら true** */
|
|
91
107
|
add(blockNumber: number, block: Uint8Array): boolean;
|
|
108
|
+
/** 入れ物を広げる。何度も写し直さないよう倍々で、モジュールの大きさまで */
|
|
109
|
+
private grow;
|
|
92
110
|
}
|
package/dist/dsmcc.js
CHANGED
|
@@ -153,39 +153,79 @@ export function parseDdb(section) {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
156
|
+
* 組み立てるモジュールの大きさの上限 (16MiB)。**これより大きいと言う DII は信じない。**
|
|
157
|
+
*
|
|
158
|
+
* `moduleSize` は32ビットで、そのまま信じると DII 1つで 4GB を確保させられる。
|
|
159
|
+
* 実際のデータ放送のモジュールは数 KB〜数百 KB、衛星のロゴも全局ぶんで数百 KB なので、
|
|
160
|
+
* 十分に余裕を見た値。zlib で伸ばしたあとの大きさにも同じ上限を使う ([bml.ts](bml.ts) の `inflate`)
|
|
161
|
+
*/
|
|
162
|
+
export const MAX_MODULE_SIZE = 16 * 1024 * 1024;
|
|
163
|
+
/**
|
|
164
|
+
* 組み立て中のモジュール。DDB が来るたびに然るべき位置へ置く。
|
|
165
|
+
*
|
|
166
|
+
* **入れ物は届いたところまでしか確保しない。** DII の言う大きさを先に確保すると、
|
|
167
|
+
* 大きいと言うだけの DII でメモリを使い切らせられる。`MAX_MODULE_SIZE` を超える
|
|
168
|
+
* モジュールはブロックを受け取らない (いつまでも揃わない)。
|
|
158
169
|
*
|
|
159
170
|
* 順番は保証されない (途中から見れば真ん中から始まる) ので、
|
|
160
171
|
* **受け取ったブロック番号を数えて**揃ったかを見る。
|
|
161
172
|
*/
|
|
162
173
|
export class ModuleBuilder {
|
|
163
174
|
moduleVersion;
|
|
175
|
+
moduleSize;
|
|
164
176
|
blockSize;
|
|
165
|
-
|
|
166
|
-
|
|
177
|
+
buffer = new Uint8Array(0);
|
|
178
|
+
/** 受け取ったブロック番号 (1ブロック1ビット)。最初のブロックが来てから確保する */
|
|
179
|
+
received = null;
|
|
180
|
+
count = 0;
|
|
167
181
|
total;
|
|
168
182
|
constructor(moduleVersion, moduleSize, blockSize) {
|
|
169
183
|
this.moduleVersion = moduleVersion;
|
|
184
|
+
this.moduleSize = moduleSize;
|
|
170
185
|
this.blockSize = blockSize;
|
|
171
|
-
this.data = new Uint8Array(moduleSize);
|
|
172
186
|
this.total = Math.ceil(moduleSize / blockSize);
|
|
173
187
|
}
|
|
188
|
+
/** 組み立てた中身。まだ届いていないところは 0。**揃ってから読むこと** (読むとモジュールの大きさぶんを確保する) */
|
|
189
|
+
get data() {
|
|
190
|
+
if (this.buffer.length < this.moduleSize &&
|
|
191
|
+
this.moduleSize <= MAX_MODULE_SIZE)
|
|
192
|
+
this.grow(this.moduleSize);
|
|
193
|
+
return this.buffer;
|
|
194
|
+
}
|
|
174
195
|
/** 受け取ったブロックの数 (同じブロックを2度受けても1つ) */
|
|
175
196
|
get blocks() {
|
|
176
|
-
return this.
|
|
197
|
+
return this.count;
|
|
177
198
|
}
|
|
178
199
|
/** 揃ったか。**大きさ 0 のモジュールは初めから揃っている** (運ぶブロックが無いので DDB を待たない) */
|
|
179
200
|
get complete() {
|
|
180
|
-
return this.
|
|
201
|
+
return this.count >= this.total;
|
|
181
202
|
}
|
|
182
203
|
/** ブロックを1つ置く。**揃ったら true** */
|
|
183
204
|
add(blockNumber, block) {
|
|
205
|
+
if (this.moduleSize > MAX_MODULE_SIZE)
|
|
206
|
+
return false;
|
|
207
|
+
// blockSize を超えるブロックは受けない。重なったブロックで受け取る量が膨らむ
|
|
208
|
+
if (blockNumber >= this.total || block.length > this.blockSize)
|
|
209
|
+
return false;
|
|
184
210
|
const at = this.blockSize * blockNumber;
|
|
185
|
-
if (at + block.length > this.
|
|
211
|
+
if (at + block.length > this.moduleSize)
|
|
186
212
|
return false;
|
|
187
|
-
this.
|
|
188
|
-
|
|
213
|
+
if (at + block.length > this.buffer.length)
|
|
214
|
+
this.grow(at + block.length);
|
|
215
|
+
this.buffer.set(block, at);
|
|
216
|
+
this.received ??= new Uint8Array(Math.ceil(this.total / 8));
|
|
217
|
+
const index = blockNumber >> 3;
|
|
218
|
+
const bit = 1 << (blockNumber & 7);
|
|
219
|
+
if ((this.received[index] & bit) === 0) {
|
|
220
|
+
this.received[index] = this.received[index] | bit;
|
|
221
|
+
this.count++;
|
|
222
|
+
}
|
|
189
223
|
return this.complete;
|
|
190
224
|
}
|
|
225
|
+
/** 入れ物を広げる。何度も写し直さないよう倍々で、モジュールの大きさまで */
|
|
226
|
+
grow(length) {
|
|
227
|
+
const next = new Uint8Array(Math.min(this.moduleSize, Math.max(length, this.buffer.length * 2)));
|
|
228
|
+
next.set(this.buffer);
|
|
229
|
+
this.buffer = next;
|
|
230
|
+
}
|
|
191
231
|
}
|