@danything/arib 2.5.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -501,4 +501,5 @@ export const DRCS_REPLACE = new Map([
501
501
  ['c42450812c184aee2ae01cc5e39ae957', 0x1f4fb], // denpa: ラジオ 📻 (72x72・16 階調)。テレ東 2026-11-01 のアニメの字幕で 1 本に 28 回
502
502
  ['06cb56043b9c4006bcfbe07cc831feaf', 0x1f50a], // denpa: 音の出ているスピーカー 🔊 (72x72・16 階調)。上と同じ番組で 4 回 (テレビ朝日・TBS のものと画像が違う)
503
503
  ['063c95566807d5e7b51ab706426bedf9', 0x1f4f1], // denpa: 携帯電話 📱 (72x72・16 階調)。上と同じ番組で 4 回 (NHK総合のものと画像が違う)
504
+ ['44cbc7dc0af84f6ab857f00a9bc2560a', 0x1f4fa], // denpa: テレビ 📺 (72x72・16 階調)。日テレ 2026-11-14 のらんま1/2 の字幕で 1 本に 2 回 (テレビ朝日のテレビと画像が違う)
504
505
  ]);
@@ -69,6 +69,13 @@ export interface ModuleFile {
69
69
  export interface PMTMessage {
70
70
  type: "pmt";
71
71
  components: ComponentPMT[];
72
+ /**
73
+ * PMT にある音声 (stream_type 0x0f の AAC) の ES の部品タグを、PMT の順に並べたもの。**部品タグの無い ES は null で並ぶ**
74
+ * (bml.ts の `pmtAudios`)。`components` は部品タグの無い ES を落とすので、何本目の音声かはこちらで数える。
75
+ * 画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` も、部品タグに関係なく PMT の順に数える。
76
+ * これを足す前に録った `.bml.jsonl` の PMT には無い
77
+ */
78
+ audios?: (number | null)[];
72
79
  }
73
80
  export interface ModuleDownloadedMessage {
74
81
  type: "moduleDownloaded";
package/dist/bml.d.ts CHANGED
@@ -33,6 +33,11 @@ export declare function parseBxmlInfo(data: Uint8Array): AdditionalAribBXMLInfo;
33
33
  * `BmlDecoder` の外からも使う。denpa のライブは、カルーセルをデコードする前に入口の部品の自動起動の指定をこれで読む
34
34
  */
35
35
  export declare function parsePmt(section: Uint8Array): ComponentPMT[];
36
+ /**
37
+ * PMT にある音声 (AAC) の ES の部品タグを、PMT の順に並べる (`PMTMessage.audios`)。**部品タグの無い ES は null で数に入れる。**
38
+ * `parsePmt` と違って落とさないので、何本目の音声かが画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` にそろう
39
+ */
40
+ export declare function pmtAudios(section: Uint8Array): (number | null)[];
36
41
  /**
37
42
  * 1 局に絞った TS を渡すと、データ放送の通知が出てくる。
38
43
  *
package/dist/bml.js CHANGED
@@ -23,7 +23,7 @@ import { u16, u32 } from "./bytes.js";
23
23
  import { dataEventIdOf, MAX_MODULE_SIZE, ModuleBuilder, parseDdb, parseDii, TABLE_DII, TABLE_STREAM_DESCRIPTOR, } from "./dsmcc.js";
24
24
  import { parseTimeTable, PID_TIME } from "./eit.js";
25
25
  import { parseMediaType, readEntity } from "./multipart.js";
26
- import { descriptors, packetPid, PacketStream, pmtStreams, PmtTracker, SectionAssembler, } from "./psi.js";
26
+ import { descriptors, packetPid, PacketStream, pmtStreams, PmtTracker, SectionAssembler, STREAM_AAC, } from "./psi.js";
27
27
  const DESC_STREAM_IDENTIFIER = 0x52;
28
28
  /** データ符号化方式記述子 (STD-B10 第 2 部 6.2.20) */
29
29
  const DESC_DATA_COMPONENT = 0xfd;
@@ -139,6 +139,23 @@ export function parsePmt(section) {
139
139
  }
140
140
  return components;
141
141
  }
142
+ /**
143
+ * PMT にある音声 (AAC) の ES の部品タグを、PMT の順に並べる (`PMTMessage.audios`)。**部品タグの無い ES は null で数に入れる。**
144
+ * `parsePmt` と違って落とさないので、何本目の音声かが画面の再生 (pes.ts の `readPmt`) と ffmpeg の `0:a:<n>` にそろう
145
+ */
146
+ export function pmtAudios(section) {
147
+ const audios = [];
148
+ for (const [streamType, , info] of pmtStreams(section)) {
149
+ if (streamType !== STREAM_AAC)
150
+ continue;
151
+ let componentId = null;
152
+ for (const [tag, descriptor] of descriptors(info))
153
+ if (tag === DESC_STREAM_IDENTIFIER && descriptor.length >= 1)
154
+ componentId = descriptor[0];
155
+ audios.push(componentId);
156
+ }
157
+ return audios;
158
+ }
142
159
  /** `at` の下位 1 ビットと続く 4 バイトで 33 ビット (STC・NPT) */
143
160
  const u33 = (data, at) => (data[at] & 1) * 0x100000000 + u32(data, at + 1);
144
161
  /** ストリーム記述子 (table_id 0x3D)。NPT と イベントメッセージが載っている */
@@ -252,14 +269,15 @@ export class BmlDecoder {
252
269
  const components = parsePmt(section);
253
270
  if (components.length === 0)
254
271
  return;
272
+ const audios = pmtAudios(section);
255
273
  /*
256
274
  * 放送は同じ表を何度も送る。**変わったときだけ配る。** 受け側は
257
275
  * PMT が来るたびに画面を組み直すので、そのまま通すと出っ放しになる
258
276
  */
259
- const shape = JSON.stringify(components);
277
+ const shape = JSON.stringify([components, audios]);
260
278
  if (this.sent !== shape) {
261
279
  this.sent = shape;
262
- this.send({ type: "pmt", components });
280
+ this.send({ type: "pmt", components, audios });
263
281
  }
264
282
  for (const component of components) {
265
283
  // カルーセルが流れているのは、BML の符号化方式が付いた ES だけ
package/dist/eit.d.ts CHANGED
@@ -70,7 +70,7 @@ export interface EitEvent {
70
70
  /** 尺 (ms)。未定なら null。番組表に入れられるのは決まっているものだけ */
71
71
  duration: number | null;
72
72
  isFree: boolean;
73
- /** 1=まもなく 2=停止中 3=一時停止 4=放送中。p/f の「いま」を見分けるのに使う */
73
+ /** 0=未定義 1=非実行中 2=数秒以内に開始 3=停止中 4=放送中 (STD-B10)。p/f の「いま」を見分けるのに使う */
74
74
  runningStatus: number;
75
75
  name: string;
76
76
  description: string;
@@ -292,7 +292,7 @@ export declare class EpgReader {
292
292
  *
293
293
  * そこで 0 のときは**セクションの番号で決める**。p/f は仕様で
294
294
  * **section 0 が現在・section 1 が次**と決まっていて、こちらは局の都合で
295
- * 変わらない。1 (まもなく) や 2 (停止中) は今までどおり入れない。
295
+ * 変わらない。1〜3 (非実行中・数秒以内に開始・停止中) は今までどおり入れない。
296
296
  */
297
297
  private onAir;
298
298
  /**
package/dist/eit.js CHANGED
@@ -652,7 +652,7 @@ export class EpgReader {
652
652
  *
653
653
  * そこで 0 のときは**セクションの番号で決める**。p/f は仕様で
654
654
  * **section 0 が現在・section 1 が次**と決まっていて、こちらは局の都合で
655
- * 変わらない。1 (まもなく) や 2 (停止中) は今までどおり入れない。
655
+ * 変わらない。1〜3 (非実行中・数秒以内に開始・停止中) は今までどおり入れない。
656
656
  */
657
657
  onAir(section, event) {
658
658
  if (event.runningStatus === 4)
package/dist/mmt-si.d.ts CHANGED
@@ -172,12 +172,21 @@ export declare class TlvServiceReader {
172
172
  private readonly streams;
173
173
  /** package_id (= service_id) → MPT */
174
174
  private readonly mpts;
175
+ /**
176
+ * TLV-NIT[actual]。番号・名前・セクションの番号は最初に来たセクションのもので、`streams` はこれまでに来た全部のセクションの
177
+ * TLV ストリーム (tlv_stream_id ごとに 1 つ。同じ tlv_stream_id は後に来たもので置き換える)
178
+ */
175
179
  network: TlvNetwork | null;
176
180
  transport: MhSdt | null;
177
181
  get complete(): boolean;
178
182
  /** PLT が来て、そこにある package の MPT が全部 1 回は来たか。スキャンで、局ごとの CA_system_id が分かるまで少し長く読むのに使う */
179
183
  get mptsKnown(): boolean;
180
- /** 任意の長さのバイト列を渡す。揃ったら true */
184
+ /**
185
+ * 任意の長さのバイト列を渡す。揃ったら true。
186
+ *
187
+ * 読んだ表は渡されたチャンクの一部を指すことがあり、呼ぶ側はチャンクのバッファを使い回す。**記述子の並び (`descriptors`) と
188
+ * asset_id を持ち続ける MPT と TLV-NIT は、写してから読む**
189
+ */
181
190
  feed(chunk: Uint8Array): boolean;
182
191
  /**
183
192
  * 自分の TLV ストリーム。TLV-NIT[actual] のストリームのうち、PLT にある package (= service_id) をサービスリスト記述子に持つ最初の 1 つ。
package/dist/mmt-si.js CHANGED
@@ -401,6 +401,10 @@ export class TlvServiceReader {
401
401
  streams = new Map();
402
402
  /** package_id (= service_id) → MPT */
403
403
  mpts = new Map();
404
+ /**
405
+ * TLV-NIT[actual]。番号・名前・セクションの番号は最初に来たセクションのもので、`streams` はこれまでに来た全部のセクションの
406
+ * TLV ストリーム (tlv_stream_id ごとに 1 つ。同じ tlv_stream_id は後に来たもので置き換える)
407
+ */
404
408
  network = null;
405
409
  transport = null;
406
410
  get complete() {
@@ -411,24 +415,33 @@ export class TlvServiceReader {
411
415
  return (this.plt !== null &&
412
416
  this.plt.packages.every((entry) => this.mpts.has(entry.packageId)));
413
417
  }
414
- /** 任意の長さのバイト列を渡す。揃ったら true */
418
+ /**
419
+ * 任意の長さのバイト列を渡す。揃ったら true。
420
+ *
421
+ * 読んだ表は渡されたチャンクの一部を指すことがあり、呼ぶ側はチャンクのバッファを使い回す。**記述子の並び (`descriptors`) と
422
+ * asset_id を持ち続ける MPT と TLV-NIT は、写してから読む**
423
+ */
415
424
  feed(chunk) {
416
425
  for (const { table } of this.tables.feed(chunk)) {
417
426
  const tableId = table[0];
418
427
  if (tableId === TABLE_PLT)
419
428
  this.plt = parsePlt(table) ?? this.plt;
420
429
  else if (tableId === TABLE_MPT) {
421
- const mpt = parseMpt(table);
430
+ const mpt = parseMpt(table.slice());
422
431
  if (mpt !== null)
423
432
  this.mpts.set(mpt.packageId, mpt);
424
433
  }
425
434
  else if (tableId === TABLE_TLV_NIT_ACTUAL) {
426
- const network = parseTlvNit(table);
435
+ const network = parseTlvNit(table.slice());
427
436
  if (network === null)
428
437
  continue;
429
- this.network ??= network;
430
438
  for (const stream of network.streams)
431
439
  this.streams.set(stream.tlvStreamId, stream);
440
+ // ネットワークの番号と名前は最初に来たセクションのもの。ストリームは来たセクションの全部
441
+ this.network = {
442
+ ...(this.network ?? network),
443
+ streams: [...this.streams.values()],
444
+ };
432
445
  }
433
446
  else if (tableId === TABLE_MH_SDT_ACTUAL && this.transport === null)
434
447
  this.transport = parseMhSdt(table);
package/dist/pes.js CHANGED
@@ -9,14 +9,13 @@
9
9
  * DOM も WASM も触らないので、ここだけ単体で確かめられる (`pes.test.ts`)。
10
10
  */
11
11
  import { concatBytes, u16 } from "./bytes.js";
12
- import { PACKET, packetPid, PacketStream, PID_PAT, pmtStreams, PmtTracker, } from "./psi.js";
12
+ import { PACKET, packetPid, PacketStream, PID_PAT, pmtStreams, PmtTracker, STREAM_AAC, } from "./psi.js";
13
13
  /** PTS・PCR の刻み (90kHz)。denpa の取り決めの時刻もこの刻み (docs/stream.md#websocket-プロトコル) */
14
14
  export const CLOCK = 90_000;
15
15
  /** PTS の一周 (33 ビット)。26.5 時間 */
16
16
  export const WRAP = 2 ** 33;
17
- /** PMT の stream_type。**放送の映像は MPEG-2、音声は ADTS の AAC** (ARIB STD-B32) */
17
+ /** PMT の stream_type。**放送の映像は MPEG-2、音声は ADTS の AAC** (ARIB STD-B32。AAC は psi.ts の `STREAM_AAC`) */
18
18
  const STREAM_MPEG2 = 0x02;
19
- const STREAM_AAC = 0x0f;
20
19
  /** 5 バイトの PTS / DTS を読む (ISO/IEC 13818-1 2.4.3.7) */
21
20
  export function readStamp(data, at) {
22
21
  return (((data[at] >> 1) & 0x07) * 2 ** 30 +
package/dist/psi.d.ts CHANGED
@@ -120,6 +120,11 @@ export declare class PmtTracker {
120
120
  export declare function descriptors(data: Uint8Array): Generator<[number, Uint8Array]>;
121
121
  /** デジタルコピー制御記述子。EIT の番組ごとと PMT に入る */
122
122
  export declare const DESC_DIGITAL_COPY_CONTROL = 193;
123
+ /**
124
+ * 音声の ES の stream_type (AAC。日本の放送の音声)。主音声の何本目かは、どこでもこの stream_type だけを PMT の順に数える
125
+ * (pes.ts の `readPmt`・bml.ts の `pmtAudios`・データ放送の主音声)。ffmpeg の `0:a:<n>` ともそろう
126
+ */
127
+ export declare const STREAM_AAC = 15;
123
128
  /**
124
129
  * デジタルコピー制御記述子の中身 (タグと長さを除いたもの) から、**コピー禁止か**を読む。
125
130
  *
package/dist/psi.js CHANGED
@@ -314,6 +314,11 @@ export function* descriptors(data) {
314
314
  }
315
315
  /** デジタルコピー制御記述子。EIT の番組ごとと PMT に入る */
316
316
  export const DESC_DIGITAL_COPY_CONTROL = 0xc1;
317
+ /**
318
+ * 音声の ES の stream_type (AAC。日本の放送の音声)。主音声の何本目かは、どこでもこの stream_type だけを PMT の順に数える
319
+ * (pes.ts の `readPmt`・bml.ts の `pmtAudios`・データ放送の主音声)。ffmpeg の `0:a:<n>` ともそろう
320
+ */
321
+ export const STREAM_AAC = 0x0f;
317
322
  /**
318
323
  * デジタルコピー制御記述子の中身 (タグと長さを除いたもの) から、**コピー禁止か**を読む。
319
324
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danything/arib",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "日本のデジタル放送 (ARIB) の TS と MMT/TLV を読む: PSI・PES・EIT・ARIB の文字・字幕・データ放送、TLV・MMTP・MMT-SI・MH-EIT",
5
5
  "license": "MIT",
6
6
  "repository": {