@danything/arib 0.0.0-stage → 0.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/dist/synth.js ADDED
@@ -0,0 +1,508 @@
1
+ /**
2
+ * TS のセクションを組み立てる。
3
+ *
4
+ * 実チューナーが無いので、解析のテストにも偽エージェントにも「それらしい TS」が要る。
5
+ * 読む側 (psi.ts・eit.ts・dsmcc.ts・logo.ts・ait.ts など) と対になる書く側で、ここだけが仕様の写し。
6
+ */
7
+ import { joinBytes } from "./bytes.js";
8
+ import { PACKET, SYNC, withCrc } from "./psi.js";
9
+ // 組み立てる道具と一緒にここから取るテストがある (logo.test.ts) ので、そのまま出しておく
10
+ export { withCrc };
11
+ const be = (value) => [(value >> 8) & 0xff, value & 0xff];
12
+ const be32 = (value) => [...be(value >>> 16), ...be(value & 0xffff)];
13
+ /** セクションの頭8バイト (長さは `withCrc` が埋める)。版 0・節 0/0 */
14
+ function head(tableId, extension) {
15
+ return [tableId, 0x00, 0x00, ...be(extension), 0xc1, 0x00, 0x00];
16
+ }
17
+ /** PCR の基準部 (90kHz) を適応フィールドに書く (旗は呼ぶ側)。拡張部 (27MHz の端数) は読む側が見ていないので 0 */
18
+ export function putPcr(packet, pcr) {
19
+ const base = Math.round(pcr) % 2 ** 33;
20
+ packet[6] = Math.floor(base / 2 ** 25) & 0xff;
21
+ packet[7] = Math.floor(base / 2 ** 17) & 0xff;
22
+ packet[8] = Math.floor(base / 2 ** 9) & 0xff;
23
+ packet[9] = Math.floor(base / 2) & 0xff;
24
+ packet[10] = ((base & 1) << 7) | 0x7e;
25
+ packet[11] = 0x00;
26
+ }
27
+ /**
28
+ * Unicode → JIS X 0208 の区点。**読む側の裏返し。**
29
+ *
30
+ * 表を抱え込まずに、EUC-JP のデコーダを1回だけ総当たりして作る
31
+ * (84区 × 94点 = 7896 文字)。テストと偽放送局からしか呼ばないので、
32
+ * 最初に使うときまで作らない。
33
+ */
34
+ let jis = null;
35
+ function jisTable() {
36
+ if (jis !== null)
37
+ return jis;
38
+ const decoder = new TextDecoder("euc-jp");
39
+ jis = new Map();
40
+ for (let ku = 0x21; ku <= 0x74; ku++) {
41
+ for (let ten = 0x21; ten <= 0x7e; ten++) {
42
+ const char = decoder.decode(Uint8Array.of(ku | 0x80, ten | 0x80));
43
+ if (char.length !== 1 || char === "�")
44
+ continue;
45
+ if (!jis.has(char))
46
+ jis.set(char, (ku << 8) | ten);
47
+ }
48
+ }
49
+ return jis;
50
+ }
51
+ /**
52
+ * 文字列を ARIB STD-B24 の8単位符号にする。
53
+ *
54
+ * 初期状態 (G0=漢字) から始めて、ASCII のところだけ英数集合へ切り替える。
55
+ * 本物の放送はもっと凝った切り替え方をするが、**読む側が同じ経路を通れば足りる**。
56
+ * 文字の大きさは触らない (字幕は呼ぶ側が MSZ などを前に置く)。SI に書くなら `encodeSiText`
57
+ */
58
+ export function encodeAribText(text, half = false) {
59
+ const out = [];
60
+ let alnum = false;
61
+ for (const char of text) {
62
+ const code = char.codePointAt(0) ?? 0;
63
+ if (code < 0x80) {
64
+ // ESC ( J で英数 (半角で読ませるなら MSZ で中型に)
65
+ if (!alnum)
66
+ out.push(0x1b, 0x28, 0x4a, ...(half ? [0x89] : []));
67
+ alnum = true;
68
+ out.push(code);
69
+ continue;
70
+ }
71
+ // ESC $ B で漢字 (中型にしていたら NSZ で標準に戻す)
72
+ if (alnum)
73
+ out.push(0x1b, 0x24, 0x42, ...(half ? [0x8a] : []));
74
+ alnum = false;
75
+ const kuten = jisTable().get(char);
76
+ // 表に無い字は下駄で埋める (2区14点)。長さが変わらないので位置がずれない
77
+ out.push(...be(kuten ?? 0x222e));
78
+ }
79
+ return out;
80
+ }
81
+ /**
82
+ * 番組名・局名 (SI) に書く形式。**ASCII は中型 (MSZ) で挟んで半角で読ませる** — 標準の大きさの
83
+ * 英数はテレビが全角で描く (読む側 `aribtext.ts` も全角にする)。全角の英数 (「Venue」) は
84
+ * 漢字の集合の字として書くので、読めば書いたとおりの文字列に戻る
85
+ */
86
+ function encodeSiText(text) {
87
+ return encodeAribText(text, true);
88
+ }
89
+ /** 言語の符号 "jpn" (ISO 639-2) */
90
+ const JPN = [0x6a, 0x70, 0x6e];
91
+ /** 長さ1バイトを頭に付けた文字列。SI の記述子はどこもこの形式 */
92
+ const sized = (text) => {
93
+ const body = encodeSiText(text);
94
+ return [body.length, ...body];
95
+ };
96
+ export function sdtSection(transportStreamId, originalNetworkId, services) {
97
+ const body = [
98
+ ...head(0x42, transportStreamId),
99
+ ...be(originalNetworkId),
100
+ 0xff,
101
+ ];
102
+ for (const [serviceId, serviceType, name] of services) {
103
+ // service_descriptor (0x48): 種別 + 事業者名 + サービス名
104
+ const payload = [serviceType, 0x00, ...sized(name ?? "")];
105
+ const descriptor = [0x48, payload.length, ...payload];
106
+ body.push(...be(serviceId), 0xfc, ...be(0x8000 | descriptor.length), ...descriptor);
107
+ }
108
+ return withCrc(body);
109
+ }
110
+ /** 2桁の10進を BCD の1バイトに */
111
+ const bcd = (value) => ((Math.floor(value / 10) << 4) | (value % 10)) & 0xff;
112
+ /** epoch ms → MJD + BCD の5バイト。放送の時刻は日本時間で書く */
113
+ function mjdTime(at) {
114
+ const jst = Math.floor(at / 1000) + 9 * 3600;
115
+ const mjd = Math.floor(jst / 86400) + 40587;
116
+ const rest = ((jst % 86400) + 86400) % 86400;
117
+ return [
118
+ ...be(mjd),
119
+ bcd(Math.floor(rest / 3600)),
120
+ bcd(Math.floor(rest / 60) % 60),
121
+ bcd(rest % 60),
122
+ ];
123
+ }
124
+ /** ms → BCD 3バイトの尺 */
125
+ function bcdDuration(ms) {
126
+ const total = Math.floor(ms / 1000);
127
+ return [
128
+ bcd(Math.floor(total / 3600)),
129
+ bcd(Math.floor(total / 60) % 60),
130
+ bcd(total % 60),
131
+ ];
132
+ }
133
+ /**
134
+ * デジタルコピー制御記述子 (0xC1)。EIT の番組と PMT に載る。
135
+ * 中身は1バイトだけ: 記録制御 (2) 最大ビットレートの有無 (1) 部品ごとの制御の有無 (1) 出力の制御 (4)
136
+ */
137
+ export function copyControlDescriptor(control) {
138
+ return [0xc1, 0x01, ((control & 0b11) << 6) | 0b0100];
139
+ }
140
+ function eventDescriptors(event) {
141
+ const out = [];
142
+ const short = [...JPN, ...sized(event.name)];
143
+ short.push(...sized(event.description ?? ""));
144
+ out.push(0x4d, short.length, ...short);
145
+ const extended = Object.entries(event.extended ?? {});
146
+ if (extended.length > 0) {
147
+ const items = extended.flatMap(([heading, text]) => [
148
+ ...sized(heading),
149
+ ...sized(text),
150
+ ]);
151
+ const body = [0x00, ...JPN, items.length, ...items, 0x00];
152
+ out.push(0x4e, body.length, ...body);
153
+ }
154
+ if (event.rawDescriptors !== undefined)
155
+ out.push(...event.rawDescriptors);
156
+ if (event.copyControl !== undefined)
157
+ out.push(...copyControlDescriptor(event.copyControl));
158
+ if (event.genres !== undefined && event.genres.length > 0) {
159
+ const body = event.genres.flatMap(([lv1, lv2]) => [(lv1 << 4) | lv2, 0xff]);
160
+ out.push(0x54, body.length, ...body);
161
+ }
162
+ if (event.video !== undefined) {
163
+ const [streamContent, componentType] = event.video;
164
+ out.push(0x50, 0x06, 0xf0 | streamContent, componentType, 0x00, ...JPN);
165
+ }
166
+ for (const [index, audio] of (event.audios ?? []).entries()) {
167
+ /*
168
+ * stream_content / component_type / component_tag / stream_type /
169
+ * simulcast_group_tag / フラグ / 言語 / **放送が付けた名前**。
170
+ *
171
+ * フラグは 多言語(1) 主音声(1) 品質(2) 標本化周波数(3) reserved(1)。
172
+ * 名前は、種別も言語も同じ音声が2本あるとき**唯一の見分け**になる
173
+ */
174
+ const lang = [...(audio.lang ?? "jpn")].map((c) => c.charCodeAt(0));
175
+ const text = encodeSiText(audio.text ?? "");
176
+ const flags = ((audio.main ?? true) ? 0b0100_0000 : 0) | 0b0000_1110;
177
+ const body = [
178
+ 0x02,
179
+ audio.type,
180
+ 0x10 + index,
181
+ 0x0f,
182
+ 0xff,
183
+ flags,
184
+ ...lang,
185
+ ...text,
186
+ ];
187
+ out.push(0xc4, body.length, ...body);
188
+ }
189
+ return out;
190
+ }
191
+ /** EIT。番組表そのもの。table_id で p/f (0x4E) と schedule (0x50〜) が分かれる */
192
+ export function eitSection(options) {
193
+ const body = [
194
+ options.tableId,
195
+ 0x00,
196
+ 0x00,
197
+ ...be(options.serviceId),
198
+ 0xc1 | ((options.version ?? 0) << 1),
199
+ options.sectionNumber ?? 0,
200
+ options.lastSectionNumber ?? 0,
201
+ ...be(options.transportStreamId),
202
+ ...be(options.originalNetworkId),
203
+ options.segmentLastSectionNumber ?? options.lastSectionNumber ?? 0,
204
+ options.lastTableId ?? options.tableId,
205
+ ];
206
+ for (const event of options.events) {
207
+ const descriptors = eventDescriptors(event);
208
+ body.push(...be(event.eventId), ...mjdTime(event.startAt), ...bcdDuration(event.duration), ((event.runningStatus ?? 0) << 5) |
209
+ (event.isFree === false ? 0x10 : 0x00) |
210
+ ((descriptors.length >> 8) & 0x0f), descriptors.length & 0xff, ...descriptors);
211
+ }
212
+ return withCrc(body);
213
+ }
214
+ export function nitSection(networkId, remoteControlKeyId, streams) {
215
+ const body = [...head(0x40, networkId), ...be(0xf000)];
216
+ const loop = [];
217
+ for (const [transportStreamId, originalNetworkId, services] of streams) {
218
+ const descriptors = [];
219
+ // TS information descriptor (0xCD)
220
+ if (remoteControlKeyId !== null)
221
+ descriptors.push(0xcd, 0x02, remoteControlKeyId, 0x00);
222
+ const serviceList = services.flatMap(([id, type]) => [...be(id), type]);
223
+ descriptors.push(0x41, serviceList.length, ...serviceList);
224
+ loop.push(...be(transportStreamId), ...be(originalNetworkId), ...be(0xf000 | descriptors.length), ...descriptors);
225
+ }
226
+ body.push(...be(0xf000 | loop.length), ...loop);
227
+ return withCrc(body);
228
+ }
229
+ /** セクションを TS パケットに詰める。頭のパケットには pointer_field が付く */
230
+ export function packetize(pid, section, counter = 0) {
231
+ const packets = [];
232
+ let data = [0x00, ...section]; // pointer_field = 0
233
+ let first = true;
234
+ while (data.length > 0) {
235
+ const chunk = data.slice(0, PACKET - 4);
236
+ data = data.slice(PACKET - 4);
237
+ packets.push(SYNC, (first ? 0x40 : 0x00) | (pid >> 8), pid & 0xff, 0x10 | (counter & 0x0f));
238
+ packets.push(...chunk, ...new Array(PACKET - 4 - chunk.length).fill(0xff));
239
+ counter++;
240
+ first = false;
241
+ }
242
+ return Uint8Array.from(packets);
243
+ }
244
+ /** 部品を1本のバイト列に繋ぐ。組んだパケットを流し込むときの糊 */
245
+ export function stream(...parts) {
246
+ return joinBytes(parts);
247
+ }
248
+ /** PCR を1つ運ぶだけのパケット */
249
+ export function pcrPacket(pid, seconds, counter = 0) {
250
+ const packet = new Uint8Array(PACKET).fill(0xff);
251
+ packet[0] = SYNC;
252
+ packet[1] = pid >> 8;
253
+ packet[2] = pid & 0xff;
254
+ // 適応フィールドのみ (中身なし)
255
+ packet[3] = 0x20 | (counter & 0x0f);
256
+ packet[4] = PACKET - 5;
257
+ packet[5] = 0x10; // PCR_flag
258
+ putPcr(packet, seconds * 90_000);
259
+ return packet;
260
+ }
261
+ /** 放送の実時刻 (TDT) を1パケットに。BML の `getCurrentDateTime` が見る */
262
+ export function tdtPacket(unixMs) {
263
+ return packetize(0x0014, Uint8Array.from([0x70, 0x70, 0x05, ...mjdTime(unixMs)]));
264
+ }
265
+ /** PAT。サービスID → PMT の PID */
266
+ export function patSection(programs, transportStreamId = 1) {
267
+ const body = head(0x00, transportStreamId);
268
+ for (const [serviceId, pmtPid] of programs) {
269
+ body.push(...be(serviceId), 0xe0 | (pmtPid >> 8), pmtPid & 0xff);
270
+ }
271
+ return withCrc(body);
272
+ }
273
+ /**
274
+ * PMT。ロゴのカルーセルが乗る ES を1つだけ持たせる。
275
+ * 目印は stream_identifier_descriptor (0x52) の component_tag
276
+ */
277
+ export function pmtSection(serviceId, esPid, componentTag) {
278
+ return withCrc([
279
+ ...head(0x02, serviceId),
280
+ 0xe0,
281
+ 0x00, // PCR_PID
282
+ 0xf0,
283
+ 0x00, // program_info_length = 0
284
+ 0x0d, // stream_type: DSM-CC セクション
285
+ 0xe0 | (esPid >> 8),
286
+ esPid & 0xff,
287
+ 0xf0,
288
+ 0x03,
289
+ 0x52,
290
+ 0x01,
291
+ componentTag,
292
+ ]);
293
+ }
294
+ /**
295
+ * ふつうの PMT。映像・音声のような ES を並べる。
296
+ *
297
+ * ロゴ用の `pmtSection` と違って中身を選べる。局を選り分ける
298
+ * [service-filter.ts](../../../packages/arib/src/service-filter.ts) を試すのに要る
299
+ */
300
+ export function programMap(serviceId, pcrPid,
301
+ /** `[種別, PID]`。**3つ目は記述子**の並び (字幕の部品タグなど) */
302
+ streams, ecmPid,
303
+ /** 番組ぜんたいに掛かる記述子 (program_info) に足すもの。コピー制御 ({@link copyControlDescriptor}) など */
304
+ extra = []) {
305
+ const programInfo = [
306
+ ...(ecmPid === undefined
307
+ ? []
308
+ : [0x09, 0x04, 0x00, 0x05, 0xe0 | (ecmPid >> 8), ecmPid & 0xff]),
309
+ ...extra,
310
+ ];
311
+ const body = [
312
+ ...head(0x02, serviceId),
313
+ 0xe0 | (pcrPid >> 8),
314
+ pcrPid & 0xff,
315
+ ...be(0xf000 | programInfo.length),
316
+ ...programInfo,
317
+ ];
318
+ for (const [streamType, pid, info = []] of streams) {
319
+ body.push(streamType, 0xe0 | (pid >> 8), pid & 0xff, ...be(0xf000 | info.length), ...info);
320
+ }
321
+ return withCrc(body);
322
+ }
323
+ /**
324
+ * データ符号化方式記述子 (0xFD)。**BML が載っている ES の目印。**
325
+ *
326
+ * 中身は「入口かどうか」と、カルーセルの回 (dataEventId)。
327
+ * 既定は地上波 (0x0C) の入口つき・960x540
328
+ */
329
+ export function bxmlDescriptor(options = {}) {
330
+ const { dataComponentId = 0x0c, entryPoint = true } = options;
331
+ const body = [
332
+ ...be(dataComponentId),
333
+ // 伝送方式00 / 入口 / 自動起動 / 解像度0011 (960x540)
334
+ ...(entryPoint ? [0x33, 0x70] : [0x00, 0xff]),
335
+ 0xf8, // dataEventId 0xF / event_section_flag
336
+ 0xa0, // 即時取得あり / 蓄積不可 / 起動優先
337
+ ];
338
+ return [0xfd, body.length, ...body];
339
+ }
340
+ /** DSM-CC のセクションで包む */
341
+ function dsmccSection(tableId, message) {
342
+ return withCrc([...head(tableId, 0), ...message]);
343
+ }
344
+ /**
345
+ * DII。「このモジュールは何バイトで、名前は何か」を伝える。
346
+ *
347
+ * **複数のモジュールを載せられる。** 本物の BS はここに `LOGO-05` と
348
+ * `CS_LOGO-05` を並べて流していて (実機の `BS15_0` で確認)、1つしか
349
+ * 載せられない作りでは CS を取りこぼす経路を試せない。
350
+ */
351
+ export function diiSection(downloadId, blockSize, modules,
352
+ /** カルーセルの版。**同じ番号で送り直しても組み立て直されない** */
353
+ transactionId = 1) {
354
+ const list = Array.isArray(modules) ? modules : [modules];
355
+ const entries = list.flatMap((module) => {
356
+ const info = [];
357
+ if (module.name !== undefined) {
358
+ const name = [...new TextEncoder().encode(module.name)];
359
+ info.push(0x02, name.length, ...name);
360
+ }
361
+ if (module.contentType !== undefined) {
362
+ const type = [...new TextEncoder().encode(module.contentType)];
363
+ info.push(0x01, type.length, ...type);
364
+ }
365
+ if (module.compression !== undefined) {
366
+ const { type, originalSize } = module.compression;
367
+ info.push(0xc2, 5, type & 0xff, ...be32(originalSize));
368
+ }
369
+ return [
370
+ ...be(module.moduleId),
371
+ ...be32(module.moduleSize),
372
+ module.moduleVersion,
373
+ info.length,
374
+ ...info,
375
+ ];
376
+ });
377
+ const rest = [
378
+ ...be32(downloadId),
379
+ ...be(blockSize),
380
+ 0x00, // windowSize
381
+ 0x00, // ackPeriod
382
+ ...be32(0), // tCDownloadWindow
383
+ ...be32(0), // tCDownloadScenario
384
+ ...be(0), // compatibilityDescriptor: 長さ0
385
+ ...be(list.length), // numberOfModules
386
+ ...entries,
387
+ ...be(0), // privateDataLength
388
+ ];
389
+ return dsmccSection(0x3b, [
390
+ 0x11, // protocolDiscriminator
391
+ 0x03, // dsmccType
392
+ ...be(0x1002), // messageId: DII
393
+ ...be32(transactionId),
394
+ 0xff,
395
+ 0x00, // reserved / adaptationLength
396
+ ...be(rest.length),
397
+ ...rest,
398
+ ]);
399
+ }
400
+ /** DDB。モジュールを割ったブロックを1つ運ぶ */
401
+ export function ddbSection(downloadId, moduleId, moduleVersion, blockNumber, block) {
402
+ return dsmccSection(0x3c, [
403
+ 0x11,
404
+ 0x03,
405
+ ...be(0x1003), // messageId: DDB
406
+ ...be32(downloadId),
407
+ 0xff,
408
+ 0x00, // reserved / adaptationLength
409
+ ...be(block.length + 6),
410
+ ...be(moduleId),
411
+ moduleVersion,
412
+ 0xff,
413
+ ...be(blockNumber),
414
+ ...block,
415
+ ]);
416
+ }
417
+ /** ロゴデータモジュール (ARIB STD-B21)。カルーセルで運ばれる中身 */
418
+ export function logoModule(logoType, logos) {
419
+ const body = [logoType, ...be(logos.length)];
420
+ for (const logo of logos) {
421
+ body.push(...be(logo.logoId), logo.services.length);
422
+ for (const [networkId, serviceId] of logo.services) {
423
+ body.push(...be(networkId), ...be(0), ...be(serviceId));
424
+ }
425
+ body.push(...be(logo.data.length), ...logo.data);
426
+ }
427
+ return Uint8Array.from(body);
428
+ }
429
+ /*
430
+ * --- データ放送のテストで使う定番の割り振り ---
431
+ * bml / data-timeline / recorded-bml の3つのテストが、同じ表と同じ組み立てを
432
+ * それぞれに書いていた。番号そのものに意味は無く、揃っていることに意味がある。
433
+ */
434
+ export const BML_TS = {
435
+ service: 1024,
436
+ pmtPid: 0x1f0,
437
+ videoPid: 0x0111,
438
+ bmlPid: 0x0800,
439
+ downloadId: 0xf0000001,
440
+ componentTag: 0x40,
441
+ };
442
+ /** BML の ES が1本だけ載った PMT (`BML_TS` の割り振り) */
443
+ export function bmlPmt() {
444
+ return programMap(BML_TS.service, BML_TS.videoPid, [
445
+ [0x02, BML_TS.videoPid, [0x52, 0x01, 0x00]],
446
+ [
447
+ 0x0d,
448
+ BML_TS.bmlPid,
449
+ [0x52, 0x01, BML_TS.componentTag, ...bxmlDescriptor()],
450
+ ],
451
+ ]);
452
+ }
453
+ /** PAT と PMT。データ放送のストリームの頭 */
454
+ export function bmlHead() {
455
+ return [
456
+ packetize(0x0000, patSection([[BML_TS.service, BML_TS.pmtPid]])),
457
+ packetize(BML_TS.pmtPid, bmlPmt()),
458
+ ];
459
+ }
460
+ /**
461
+ * モジュール1つぶんの中身。**放送は multipart/mixed で複数のファイルを詰める。**
462
+ * 本物と同じく、頭に Content-Type、境界で区切って各ファイルに Content-Location
463
+ */
464
+ export function multipartModule(files) {
465
+ const boundary = "denpa";
466
+ let out = `Content-Type: multipart/mixed; boundary=${boundary}\r\n\r\n`;
467
+ for (const [location, type, body] of files) {
468
+ out += `--${boundary}\r\nContent-Type: ${type}\r\nContent-Location: ${location}\r\n\r\n${body}\r\n`;
469
+ }
470
+ out += `--${boundary}--\r\n`;
471
+ return new TextEncoder().encode(out);
472
+ }
473
+ /** PMT の ES に付ける application_signalling_descriptor (0x6F)。**AIT の在り処の印** */
474
+ export function aitSignallingDescriptor(applicationType = 0x0010, version = 1) {
475
+ return [0x6f, 0x03, ...be(applicationType), version & 0x1f];
476
+ }
477
+ /**
478
+ * AIT (table_id 0x74)。**Hybridcast のアプリのアドレス (URL) が載っている表。**
479
+ *
480
+ * 中身そのものは電波に乗っていないので、ここに入るのは URL と名前だけ
481
+ */
482
+ export function aitSection(applications, applicationType = 0x0010) {
483
+ const loop = [];
484
+ for (const app of applications) {
485
+ const info = [];
486
+ if (app.name !== undefined) {
487
+ const name = encodeSiText(app.name);
488
+ // application_name_descriptor: 言語 + 長さ + 中身
489
+ info.push(0x01, 4 + name.length, ...JPN, name.length, ...name);
490
+ }
491
+ if (app.base !== undefined) {
492
+ const base = [...new TextEncoder().encode(app.base)];
493
+ // transport_protocol_descriptor: protocol_id=3 (通信路)、label、URL の頭
494
+ info.push(0x02, 5 + base.length, ...be(0x0003), 0x01, base.length, ...base, 0x00);
495
+ }
496
+ if (app.path !== undefined) {
497
+ const path = [...new TextEncoder().encode(app.path)];
498
+ info.push(0x15, path.length, ...path);
499
+ }
500
+ loop.push(...be32(app.organisationId), ...be(app.applicationId), app.controlCode ?? 0x01, ...be(0xf000 | info.length), ...info);
501
+ }
502
+ return withCrc([
503
+ ...head(0x74, 0x8000 | applicationType),
504
+ ...be(0xf000), // common_descriptors_length = 0
505
+ ...be(0xf000 | loop.length),
506
+ ...loop,
507
+ ]);
508
+ }
package/package.json CHANGED
@@ -1,6 +1,32 @@
1
1
  {
2
2
  "name": "@danything/arib",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "日本のデジタル放送 (ARIB) の TS を読む: PSI・PES・EIT・ARIB の文字・字幕・データ放送",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/danything/denpa.git",
9
+ "directory": "packages/arib"
10
+ },
11
+ "keywords": [
12
+ "arib",
13
+ "isdb",
14
+ "mpeg-ts",
15
+ "eit",
16
+ "epg",
17
+ "b24",
18
+ "caption",
19
+ "bml"
20
+ ],
21
+ "type": "module",
22
+ "sideEffects": false,
23
+ "exports": {
24
+ "./*": {
25
+ "types": "./dist/*.d.ts",
26
+ "default": "./dist/*.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ]
32
+ }