libaribhtml5 0.1.4 → 0.1.7
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 +115 -57
- package/dist/sdk/arib-vfs-sw.js +4 -0
- package/dist/sdk/libaribhtml5.js +10 -10
- package/dist/sdk/libaribhtml5.mjs +1609 -984
- package/dist/types/device-identifier.d.ts +3 -0
- package/dist/types/iframe-input.d.ts +4 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/layout.d.ts +7 -0
- package/dist/types/receiver/canvas-controller.d.ts +30 -0
- package/dist/types/receiver/protocol.d.ts +21 -0
- package/dist/types/receiver/runtime-session-controller.d.ts +40 -0
- package/dist/types/receiver/types.d.ts +91 -0
- package/dist/types/receiver-host.d.ts +25 -103
- package/dist/types/runtime/application-boundary.d.ts +45 -0
- package/dist/types/runtime/application-controller.d.ts +48 -0
- package/dist/types/runtime/caption-controller.d.ts +27 -0
- package/dist/types/runtime/external-media-hole.d.ts +28 -0
- package/dist/types/runtime/external-network.d.ts +7 -0
- package/dist/types/runtime/install.d.ts +3 -7
- package/dist/types/runtime/media-plane-runtime.d.ts +54 -0
- package/dist/types/runtime/platform.d.ts +4 -0
- package/dist/types/service-worker-vfs.d.ts +3 -84
- package/dist/types/vfs/service-worker-backend.d.ts +27 -0
- package/dist/types/vfs/session.d.ts +36 -0
- package/dist/types/vfs/types.d.ts +23 -0
- package/dist/types/viewer-participation.d.ts +29 -0
- package/docs/application-lifecycle.md +34 -0
- package/docs/browser-iframe-compositing.md +20 -0
- package/docs/images/bs8k-caption-dialog.webp +0 -0
- package/docs/images/bs8k-data-broadcast-home.webp +0 -0
- package/docs/images/bs8k-message-removal.webp +0 -0
- package/docs/images/bs8k-usage-survey.webp +0 -0
- package/docs/media-plane-integration.md +23 -0
- package/docs/receiver-compatibility-roadmap.md +300 -0
- package/package.json +3 -2
|
@@ -1,84 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
data: Uint8Array | ArrayBuffer;
|
|
5
|
-
};
|
|
6
|
-
export type ServiceWorkerBroadcastVfsOptions = {
|
|
7
|
-
workerUrl: string | URL;
|
|
8
|
-
/** Same-origin scope used to expose broadcast files. */
|
|
9
|
-
baseUrl?: string | URL;
|
|
10
|
-
/** Compatibility fallback for applications which omit a carousel directory. */
|
|
11
|
-
uniqueBasenameFallback?: boolean;
|
|
12
|
-
};
|
|
13
|
-
export type BroadcastVfsBackend = {
|
|
14
|
-
begin(): Promise<void>;
|
|
15
|
-
put(resource: BroadcastVfsResource): Promise<void>;
|
|
16
|
-
canRead(path: string): Promise<boolean>;
|
|
17
|
-
};
|
|
18
|
-
export type BroadcastVfsSessionOptions = {
|
|
19
|
-
/** Report background write failures; ensure() still rejects to its caller. */
|
|
20
|
-
onError?: (error: unknown) => void;
|
|
21
|
-
/** Override background scheduling, primarily for embedded hosts and tests. */
|
|
22
|
-
schedule?: (task: () => void) => void;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Ordered, recoverable write session above a VFS backend. enqueue() copies
|
|
26
|
-
* callback-lifetime data synchronously and returns a revision barrier.
|
|
27
|
-
*/
|
|
28
|
-
export declare class BroadcastVfsSession {
|
|
29
|
-
private readonly backend;
|
|
30
|
-
private readonly onError?;
|
|
31
|
-
private readonly scheduleTask;
|
|
32
|
-
private readonly mirror;
|
|
33
|
-
private readonly queue;
|
|
34
|
-
private waiters;
|
|
35
|
-
private generation;
|
|
36
|
-
private nextRevision;
|
|
37
|
-
private completedRevision;
|
|
38
|
-
private drainScheduled;
|
|
39
|
-
private ioTail;
|
|
40
|
-
private sessionReady;
|
|
41
|
-
private disposed;
|
|
42
|
-
constructor(backend: BroadcastVfsBackend, options?: BroadcastVfsSessionOptions);
|
|
43
|
-
beginSession(): Promise<void>;
|
|
44
|
-
enqueue(resource: BroadcastVfsResource): number;
|
|
45
|
-
waitFor(revision: number): Promise<void>;
|
|
46
|
-
/** Ensure a staged path is readable, replaying the page-owned mirror if needed. */
|
|
47
|
-
ensure(path: string, revision?: number): Promise<void>;
|
|
48
|
-
/**
|
|
49
|
-
* Stop accepting resources, discard queued writes, and wait for the one
|
|
50
|
-
* backend operation which may already be running. The backend itself remains
|
|
51
|
-
* owned by the caller and can be reset or disposed after this resolves.
|
|
52
|
-
*/
|
|
53
|
-
dispose(): Promise<void>;
|
|
54
|
-
private appendIo;
|
|
55
|
-
private scheduleDrain;
|
|
56
|
-
private drainOne;
|
|
57
|
-
private resolveWaiters;
|
|
58
|
-
}
|
|
59
|
-
export declare class ServiceWorkerBroadcastVfs {
|
|
60
|
-
private readonly workerUrl;
|
|
61
|
-
private readonly scopeUrl;
|
|
62
|
-
private readonly uniqueBasenameFallback;
|
|
63
|
-
private registration;
|
|
64
|
-
private ready;
|
|
65
|
-
private readonly inFlight;
|
|
66
|
-
private disposing;
|
|
67
|
-
private disposed;
|
|
68
|
-
constructor(options: ServiceWorkerBroadcastVfsOptions);
|
|
69
|
-
initialize(): Promise<ServiceWorkerRegistration>;
|
|
70
|
-
begin(): Promise<void>;
|
|
71
|
-
put(resource: BroadcastVfsResource): Promise<void>;
|
|
72
|
-
canRead(path: string): Promise<boolean>;
|
|
73
|
-
reset(): Promise<void>;
|
|
74
|
-
/**
|
|
75
|
-
* Clear the receiver-owned VFS session after outstanding requests finish.
|
|
76
|
-
* The shared Service Worker registration is intentionally retained so other
|
|
77
|
-
* tabs and future receiver instances keep a stable scoped fetch handler.
|
|
78
|
-
*/
|
|
79
|
-
dispose(): Promise<void>;
|
|
80
|
-
private request;
|
|
81
|
-
private performRequest;
|
|
82
|
-
private performRequestWithRegistration;
|
|
83
|
-
private waitForActivation;
|
|
84
|
-
}
|
|
1
|
+
export { BroadcastVfsSession } from './vfs/session.js';
|
|
2
|
+
export { ServiceWorkerBroadcastVfs } from './vfs/service-worker-backend.js';
|
|
3
|
+
export type { BroadcastVfsBackend, BroadcastVfsResource, BroadcastVfsSessionOptions, ServiceWorkerBroadcastVfsOptions, } from './vfs/types.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BroadcastVfsResource, ServiceWorkerBroadcastVfsOptions } from './types.js';
|
|
2
|
+
export declare class ServiceWorkerBroadcastVfs {
|
|
3
|
+
private readonly workerUrl;
|
|
4
|
+
private readonly scopeUrl;
|
|
5
|
+
private readonly uniqueBasenameFallback;
|
|
6
|
+
private registration;
|
|
7
|
+
private ready;
|
|
8
|
+
private readonly inFlight;
|
|
9
|
+
private disposing;
|
|
10
|
+
private disposed;
|
|
11
|
+
constructor(options: ServiceWorkerBroadcastVfsOptions);
|
|
12
|
+
initialize(): Promise<ServiceWorkerRegistration>;
|
|
13
|
+
begin(): Promise<void>;
|
|
14
|
+
put(resource: BroadcastVfsResource): Promise<void>;
|
|
15
|
+
canRead(path: string): Promise<boolean>;
|
|
16
|
+
reset(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Clear the receiver-owned VFS session after outstanding requests finish.
|
|
19
|
+
* The shared Service Worker registration is intentionally retained so other
|
|
20
|
+
* tabs and future receiver instances keep a stable scoped fetch handler.
|
|
21
|
+
*/
|
|
22
|
+
dispose(): Promise<void>;
|
|
23
|
+
private request;
|
|
24
|
+
private performRequest;
|
|
25
|
+
private performRequestWithRegistration;
|
|
26
|
+
private waitForActivation;
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BroadcastVfsBackend, BroadcastVfsResource, BroadcastVfsSessionOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ordered, recoverable write session above a VFS backend. enqueue() copies
|
|
4
|
+
* callback-lifetime data synchronously and returns a revision barrier.
|
|
5
|
+
*/
|
|
6
|
+
export declare class BroadcastVfsSession {
|
|
7
|
+
private readonly backend;
|
|
8
|
+
private readonly onError?;
|
|
9
|
+
private readonly scheduleTask;
|
|
10
|
+
private readonly mirror;
|
|
11
|
+
private readonly queue;
|
|
12
|
+
private waiters;
|
|
13
|
+
private generation;
|
|
14
|
+
private nextRevision;
|
|
15
|
+
private completedRevision;
|
|
16
|
+
private drainScheduled;
|
|
17
|
+
private ioTail;
|
|
18
|
+
private sessionReady;
|
|
19
|
+
private disposed;
|
|
20
|
+
constructor(backend: BroadcastVfsBackend, options?: BroadcastVfsSessionOptions);
|
|
21
|
+
beginSession(): Promise<void>;
|
|
22
|
+
enqueue(resource: BroadcastVfsResource): number;
|
|
23
|
+
waitFor(revision: number): Promise<void>;
|
|
24
|
+
/** Ensure a staged path is readable, replaying the page-owned mirror if needed. */
|
|
25
|
+
ensure(path: string, revision?: number): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Stop accepting resources, discard queued writes, and wait for the one
|
|
28
|
+
* backend operation which may already be running. The backend itself remains
|
|
29
|
+
* owned by the caller and can be reset or disposed after this resolves.
|
|
30
|
+
*/
|
|
31
|
+
dispose(): Promise<void>;
|
|
32
|
+
private appendIo;
|
|
33
|
+
private scheduleDrain;
|
|
34
|
+
private drainOne;
|
|
35
|
+
private resolveWaiters;
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type BroadcastVfsResource = {
|
|
2
|
+
path: string;
|
|
3
|
+
contentType?: string;
|
|
4
|
+
data: Uint8Array | ArrayBuffer;
|
|
5
|
+
};
|
|
6
|
+
export type ServiceWorkerBroadcastVfsOptions = {
|
|
7
|
+
workerUrl: string | URL;
|
|
8
|
+
/** Same-origin scope used to expose broadcast files. */
|
|
9
|
+
baseUrl?: string | URL;
|
|
10
|
+
/** Compatibility fallback for applications which omit a carousel directory. */
|
|
11
|
+
uniqueBasenameFallback?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type BroadcastVfsBackend = {
|
|
14
|
+
begin(): Promise<void>;
|
|
15
|
+
put(resource: BroadcastVfsResource): Promise<void>;
|
|
16
|
+
canRead(path: string): Promise<boolean>;
|
|
17
|
+
};
|
|
18
|
+
export type BroadcastVfsSessionOptions = {
|
|
19
|
+
/** Report background write failures; ensure() still rejects to its caller. */
|
|
20
|
+
onError?: (error: unknown) => void;
|
|
21
|
+
/** Override background scheduling, primarily for embedded hosts and tests. */
|
|
22
|
+
schedule?: (task: () => void) => void;
|
|
23
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type AribApplicationPresentationState = {
|
|
2
|
+
visible: boolean;
|
|
3
|
+
inputActive: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type AribViewerParticipationNotification = {
|
|
6
|
+
contextId: number;
|
|
7
|
+
sourcePacketId: number;
|
|
8
|
+
eventMessageTag: number;
|
|
9
|
+
dataEventId: number;
|
|
10
|
+
messageGroupId: number;
|
|
11
|
+
version: number;
|
|
12
|
+
currentNext: boolean;
|
|
13
|
+
sectionNumber: number;
|
|
14
|
+
lastSectionNumber: number;
|
|
15
|
+
inputOffset: bigint;
|
|
16
|
+
};
|
|
17
|
+
export type AribViewerParticipationEvent = AribViewerParticipationNotification & {
|
|
18
|
+
requiresUserAction: true;
|
|
19
|
+
};
|
|
20
|
+
export declare class ViewerParticipationController {
|
|
21
|
+
private state;
|
|
22
|
+
private generation;
|
|
23
|
+
private readonly delivered;
|
|
24
|
+
get presentation(): AribApplicationPresentationState;
|
|
25
|
+
setPresentation(state: Partial<AribApplicationPresentationState>): void;
|
|
26
|
+
resetPresentation(): void;
|
|
27
|
+
resetSession(): void;
|
|
28
|
+
notify(notification: AribViewerParticipationNotification): AribViewerParticipationEvent | null;
|
|
29
|
+
}
|
|
@@ -65,6 +65,40 @@ const host = new AribReceiverHost({
|
|
|
65
65
|
許可境界と外部 network policy を receiver 側で検証し、対応する場合だけ
|
|
66
66
|
`onExitManagedState` で処理します。
|
|
67
67
|
|
|
68
|
+
## アプリケーション境界と権限
|
|
69
|
+
|
|
70
|
+
MH-AIT の `MH-Application Boundary and Permission Descriptor` (`descriptor_tag=0x802C`)
|
|
71
|
+
は、decoder 側で一つの application に属する loop を平坦化し、次の形で渡します。
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
host.setApplicationInformation({
|
|
75
|
+
organizationId: application.organizationId,
|
|
76
|
+
applicationId: application.applicationId,
|
|
77
|
+
permissionManagedAreas: application.boundaryDescriptors.flatMap(descriptor =>
|
|
78
|
+
descriptor.areas.map(area => ({
|
|
79
|
+
// bitmap ID を含む raw 16-bit 値
|
|
80
|
+
permissionBitmaps: area.permissionBitmaps,
|
|
81
|
+
managedUrls: area.managedUrls,
|
|
82
|
+
})),
|
|
83
|
+
),
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
descriptor がない場合は `permissionManagedAreas` 自体を省略します。この場合は境界が無限で
|
|
88
|
+
全権限です。descriptor が存在し、ある area の `managed_URL_count` が 0 の場合は
|
|
89
|
+
`managedUrls: []`(または `null`)で全 location を表します。より狭い domain/sub-directory の
|
|
90
|
+
設定が一致した場合はそちらを優先します。放送 VFS (`broadcastBaseUrl`) から取得した content は、
|
|
91
|
+
TR-B39 の規定どおり descriptor にかかわらず境界内・全権限として扱います。
|
|
92
|
+
|
|
93
|
+
runtime は bitmap 0/1 を解釈し、bit 12/10/9/8/7/5 を現在実装済みの broadcast media、番組情報、
|
|
94
|
+
persistent array、boundary extension、receiver ID、application/cache/EMT/system-information API に
|
|
95
|
+
適用します。権限がない同期 API は `Error` (`code="NOT_AUTHORIZED_ERR"`) を送出し、AIT 更新後も
|
|
96
|
+
同じ runtime に直ちに反映します。更新によって現在の communication document が境界外になった場合、
|
|
97
|
+
host は application を終了します。
|
|
98
|
+
|
|
99
|
+
境界権限と `allowExternalNetwork` は別条件です。MH-AIT が URL を許可しても、host の network policy が
|
|
100
|
+
offline なら外部 HTTP へは遷移・通信できません。
|
|
101
|
+
|
|
68
102
|
## AUTOSTART と PRESENT の同居
|
|
69
103
|
|
|
70
104
|
一つの tuning/session generation につき managed application slot は一つにします。
|
|
@@ -43,6 +43,26 @@ iframe より前の layer に置きます。標準の `BehindIframeMediaPlaneAda
|
|
|
43
43
|
object の CSS を複製せず、報告された `getBoundingClientRect()` を logical viewport に対する割合へ
|
|
44
44
|
変換して配置します。
|
|
45
45
|
|
|
46
|
+
## プレーヤー操作への pointer 穿透
|
|
47
|
+
|
|
48
|
+
データ放送 iframe は全面が透明でも、一つの不透明な矩形と同じように hit test されます。そのまま
|
|
49
|
+
プレーヤーへ重ねると、特に touch device で下の再生 UI がクリックできなくなります。
|
|
50
|
+
|
|
51
|
+
`AribReceiverHost` は iframe を表示専用の application plane として扱い、既定で `inert`、
|
|
52
|
+
`pointer-events: none !important`、`tabindex="-1"` を設定します。`tabindex="-1"` だけでは iframe 内の
|
|
53
|
+
document が focus を取得できるため不十分です。`inert` によって子 document 全体を focus と hit test の
|
|
54
|
+
対象から外し、下のプレーヤー container へ mouse / touch event と `:hover` を渡します。描画、timer、通信、
|
|
55
|
+
media-plane report は継続します。リモコン入力は iframe の focus に頼らず `host.dispatchKey()` で
|
|
56
|
+
document へ送ってください。
|
|
57
|
+
|
|
58
|
+
iframe 内の pointer event を親へ再送する実装は使いません。iframe の event は親 document へ bubble
|
|
59
|
+
しないため、再送には座標から対象要素を再決定する処理が必要になり、player controller の click、drag、
|
|
60
|
+
pointer capture を壊します。
|
|
61
|
+
|
|
62
|
+
HTML/iframe には SVG の `pointer-events: painted` に相当する、非透明 pixel だけを hit test する
|
|
63
|
+
モードはありません。透明度や root の背景色を見て iframe を `display: none` にすると、debug overlay
|
|
64
|
+
以外の application UI や後から表示される画面まで失われるため使用しません。
|
|
65
|
+
|
|
46
66
|
## Chromium の白い iframe backdrop
|
|
47
67
|
|
|
48
68
|
Chromium は iframe owner と iframe 内 root の used `color-scheme` が異なる場合、透明 iframe の
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -26,6 +26,24 @@ MMT の source of truth は LCT です。実装時の優先順位は次の通り
|
|
|
26
26
|
fallback として使ってよい。ただし、これはアプリケーション CSS を LCT と解釈するものではない。
|
|
27
27
|
3. どちらも得られない間の receiver-owned fallback は黒 (`#000`) とする。
|
|
28
28
|
|
|
29
|
+
`tlvdemux` の WASM 回調は LCT の 24 bit RGB を `backgroundColorRgb` として渡す。
|
|
30
|
+
その値はページ CSS に変換せず、receiver host の背景プレーンへ渡す。
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const demuxer = new TlvDemuxer({
|
|
34
|
+
onLayoutConfiguration(layout) {
|
|
35
|
+
host.setLctBackgroundColor(layout.backgroundColorRgb)
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// 選局・別サービス・別チューニング session へ移る前に前回の LCT を破棄する。
|
|
40
|
+
host.setLctBackgroundColor(null)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
LCT の色が設定されている間、後から到着した application stage の CSS 色は
|
|
44
|
+
背景プレーンを上書きしない。`null` で LCT 色を解除した場合だけ、stage 色または
|
|
45
|
+
黒の fallback へ戻る。
|
|
46
|
+
|
|
29
47
|
この fallback は規格上の既定色ではありません。白いブラウザ canvas の露出を避けるための
|
|
30
48
|
実装上の選択です。確認に使った NHK BS8K の `8k.mmts` から抽出したファイルも、
|
|
31
49
|
`top/source/top.css` の `body` と `#vstream`、および
|
|
@@ -127,6 +145,11 @@ window.__ARIB_HTML5_INSTALL__ = (child: RuntimeWindow) => {
|
|
|
127
145
|
}
|
|
128
146
|
```
|
|
129
147
|
|
|
148
|
+
`AribReceiverHost` は全面 iframe を表示専用の inert overlay として扱うため、データ放送の描画を
|
|
149
|
+
残したまま touch/mouse 操作と `:hover` は下のプレーヤー UI へ通り、iframe 内へ browser focus も
|
|
150
|
+
移りません。`pointer-events: none !important` と `tabindex="-1"` も互換用 fallback として設定します。
|
|
151
|
+
放送アプリケーションのリモコン操作には `host.dispatchKey()` を使用してください。
|
|
152
|
+
|
|
130
153
|
データ放送への出入りは次の二つだけです。
|
|
131
154
|
|
|
132
155
|
```ts
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Receiver compatibility roadmap
|
|
2
|
+
|
|
3
|
+
## Purpose and scope
|
|
4
|
+
|
|
5
|
+
This document records the remaining receiver-runtime work needed to run long-lived ARIB
|
|
6
|
+
HTML5 data services in HonomiTV. The primary target is live and recorded BS4K/BS8K
|
|
7
|
+
playback, including viewer-participation programmes. An isolated demo and Komorebi are useful
|
|
8
|
+
secondary checks, but they are not the integration source of truth.
|
|
9
|
+
|
|
10
|
+
The component boundary is:
|
|
11
|
+
|
|
12
|
+
| Component | Responsibility |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `tlvdemux` | Parse MMT/TLV, MMT-SI, LCT, MH-AIT, EMT, EIT, and carousel resources |
|
|
15
|
+
| DPlayer | Own playback and forward typed TLV events without changing their meaning |
|
|
16
|
+
| `libaribhtml5` | Own iframe/VFS, receiver API objects, application state, permissions, media slots, and receiver input state |
|
|
17
|
+
| HonomiTV | Own live/recorded mode, service/session lifetime, remote-control UI, network proxy, and user-facing notices |
|
|
18
|
+
|
|
19
|
+
Broadcaster directory names such as `sh4`, `sh8`, or `bsfuji4k` are not protocol fields and
|
|
20
|
+
must not become policy. Application entries come from the active MH-AIT/resource context.
|
|
21
|
+
|
|
22
|
+
This status was reviewed on 2026-08-02 against the English ARIB TR-B39 2.5 and STD-B62 2.2
|
|
23
|
+
documents in `../ARIB-docs`, plus extracted NHK BS4K/BS8K applications.
|
|
24
|
+
|
|
25
|
+
## Current coverage
|
|
26
|
+
|
|
27
|
+
The current runtime already covers the receiver API surface exercised by the inspected NHK
|
|
28
|
+
applications:
|
|
29
|
+
|
|
30
|
+
- early runtime bootstrap and `receiverDevice.getSystemInformation(null)`;
|
|
31
|
+
- `getDeviceIdentifier()` and the legacy `bmlCompat.browserPseudo.getIRDID()` path;
|
|
32
|
+
- persistent-array compatibility and receiver-owned regional information;
|
|
33
|
+
- current/following EIT delivery;
|
|
34
|
+
- immediate general event-message delivery;
|
|
35
|
+
- carousel resource retention callbacks;
|
|
36
|
+
- caption existence, subscription, and TTML/resource delivery;
|
|
37
|
+
- MH-AIT identity, application replacement, destruction, and managed-state exit;
|
|
38
|
+
- application boundary/permission bitmap evaluation;
|
|
39
|
+
- broadcast media-slot discovery and external media-plane composition;
|
|
40
|
+
- LCT receiver background colour with an application-stage fallback;
|
|
41
|
+
- receiver clock projection from stream time and media position.
|
|
42
|
+
|
|
43
|
+
The important remaining work is mostly state and lifecycle semantics rather than adding names
|
|
44
|
+
which only return a placeholder value.
|
|
45
|
+
|
|
46
|
+
## P0: required for long-lived interactive broadcasts
|
|
47
|
+
|
|
48
|
+
### Viewer-participation corner notification and input ownership
|
|
49
|
+
|
|
50
|
+
TR-B39 Part 1 Volume 3 section 6.2.5 defines a receiver-level notification for games,
|
|
51
|
+
questionnaires, and viewer voting:
|
|
52
|
+
|
|
53
|
+
- `data_event_id` is `0xF`;
|
|
54
|
+
- `event_msg_group_id` is `0xF00`;
|
|
55
|
+
- the EMT has no event-message descriptor;
|
|
56
|
+
- receiving it does not raise an interrupt event in the broadcast application;
|
|
57
|
+
- when receiver input is not directed to the data application, the receiver may show a notice
|
|
58
|
+
which lets the viewer make the application controllable with a simple operation.
|
|
59
|
+
|
|
60
|
+
This differs from an application `GeneralEventMessageListener`. It must not be injected into
|
|
61
|
+
the page as an invented EMT callback.
|
|
62
|
+
|
|
63
|
+
`Application.show()`, `hide()`, `activateInput()`, and `deactivateInput()` currently return
|
|
64
|
+
success without changing receiver state. They need to update a receiver-owned application
|
|
65
|
+
presentation/input state. `AribReceiverHost` then needs a host-facing viewer-participation
|
|
66
|
+
notification contract. HonomiTV remains responsible for rendering the notice and deciding
|
|
67
|
+
which UI action transfers control to the application.
|
|
68
|
+
|
|
69
|
+
A future API may resemble the following, but these names are not yet a stable contract:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const host = new AribReceiverHost({
|
|
73
|
+
iframe,
|
|
74
|
+
viewport,
|
|
75
|
+
onViewerParticipation(event) {
|
|
76
|
+
if (event.requiresUserAction) showDataBroadcastParticipationNotice()
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
host.notifyViewerParticipationCorner({
|
|
81
|
+
contextId,
|
|
82
|
+
serviceId,
|
|
83
|
+
version,
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Repeated transmissions must be deduplicated by the current tuning generation, service, and
|
|
88
|
+
message version. A service/session reset clears the notification state.
|
|
89
|
+
|
|
90
|
+
Parser prerequisite: the current `tlvdemux` EMT parser emits events only for an event-message
|
|
91
|
+
descriptor (`descriptor_tag=0xF003`). It must expose the descriptor-less `0xF00` receiver
|
|
92
|
+
notification separately instead of fabricating a general event message.
|
|
93
|
+
|
|
94
|
+
Acceptance criteria:
|
|
95
|
+
|
|
96
|
+
1. A hidden or input-inactive application causes one host notification for a new participation
|
|
97
|
+
corner version.
|
|
98
|
+
2. An already controllable application does not cause an unnecessary notice.
|
|
99
|
+
3. Repeated transmission of the same version does not spam the UI.
|
|
100
|
+
4. The notification is never delivered to an application EMT listener.
|
|
101
|
+
5. Changing service or playback session discards the old notification.
|
|
102
|
+
|
|
103
|
+
### Carousel resource update and deletion semantics
|
|
104
|
+
|
|
105
|
+
Real NHK application code uses `storeDataResource("./", listener)` to monitor a directory and
|
|
106
|
+
detect source updates or disappearance. The current cache implementation is centered on an
|
|
107
|
+
exact resolved URL:
|
|
108
|
+
|
|
109
|
+
- directory listeners do not observe descendant resource changes;
|
|
110
|
+
- VFS `put()` does not automatically notify the active receiver runtime;
|
|
111
|
+
- the Service Worker protocol has no single-resource deletion operation;
|
|
112
|
+
- `notifyDataResource()` is a separate manual action which an integrator may omit;
|
|
113
|
+
- a removed resource may therefore remain readable from the VFS mirror.
|
|
114
|
+
|
|
115
|
+
VFS mutation and CacheEvent notification should share one receiver-owned lifecycle. The
|
|
116
|
+
event contract needs atomic `updated`, `deleted`, `store_finished`, and `store_failed`
|
|
117
|
+
semantics for both file and directory targets. A directory listener must match descendants
|
|
118
|
+
without receiving unrelated application resources.
|
|
119
|
+
|
|
120
|
+
A future high-level host contract should cover update, deletion, and session reset in one place,
|
|
121
|
+
for example:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
host.updateBroadcastResource({ path, revision })
|
|
125
|
+
host.removeBroadcastResource(path)
|
|
126
|
+
host.resetBroadcastResources()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The VFS may retain older bytes until a complete new resource is committed, but the runtime
|
|
130
|
+
must not report `updated` before those new bytes are readable.
|
|
131
|
+
|
|
132
|
+
### Complete MH-AIT state and update listeners
|
|
133
|
+
|
|
134
|
+
`setApplicationInformation()` currently describes only the application owning the active
|
|
135
|
+
document. The receiver also needs the complete current MH-AIT so it can implement:
|
|
136
|
+
|
|
137
|
+
- `Application.getOwnerAIT()`;
|
|
138
|
+
- `ApplicationInformationTable.getApplications()`;
|
|
139
|
+
- `streamEvent.addAITUpdateListener()`;
|
|
140
|
+
- `streamEvent.removeAITUpdateListener()`.
|
|
141
|
+
|
|
142
|
+
The host should accept one detached, validated table snapshot containing the table version and
|
|
143
|
+
all applications in transmission order. Updating the table must preserve the currently running
|
|
144
|
+
document when the continuation rules allow it, update its identity/permission snapshot in
|
|
145
|
+
place, and notify registered AIT listeners. A removed or killed active application must exit.
|
|
146
|
+
|
|
147
|
+
HonomiTV already maintains all demuxed application states, so it should pass that snapshot
|
|
148
|
+
rather than reimplementing the AIT object in application code.
|
|
149
|
+
|
|
150
|
+
### Correct event-ID update behaviour
|
|
151
|
+
|
|
152
|
+
TR-B39 requires `addEventIDUpdateListener()` to call the listener once immediately with an
|
|
153
|
+
`ISDBResourceReference`, then again whenever the current event ID changes. The current
|
|
154
|
+
runtime queues a zero-argument callback only after a later `program-info` update.
|
|
155
|
+
|
|
156
|
+
The runtime must additionally:
|
|
157
|
+
|
|
158
|
+
- provide `original_network_id`, `tlv_stream_id`, `service_id`, and `event_id`;
|
|
159
|
+
- use `event_id: null` when no current event exists;
|
|
160
|
+
- distinguish `tlv_stream_id` from the legacy `transport_stream_id` compatibility alias;
|
|
161
|
+
- avoid notifying merely because a title, description, or duration changed;
|
|
162
|
+
- return the present/following fields and `null` values required by the receiver API contract.
|
|
163
|
+
|
|
164
|
+
The inspected NHK application includes a delayed polling fallback when this listener does not
|
|
165
|
+
start, so fixing the semantics removes a real receiver-compatibility workaround rather than an
|
|
166
|
+
abstract completeness issue.
|
|
167
|
+
|
|
168
|
+
### Atomic broadcast-session reset
|
|
169
|
+
|
|
170
|
+
An integrator currently has to clear captions, EIT, the broadcast clock, LCT colour,
|
|
171
|
+
application information, queued EMT events, resource state, and the iframe separately. Missing
|
|
172
|
+
one call leaks old-service state into the next application.
|
|
173
|
+
|
|
174
|
+
Add one generation-changing operation, tentatively `resetBroadcastSession()` or
|
|
175
|
+
`beginBroadcastSession()`, which atomically:
|
|
176
|
+
|
|
177
|
+
- invalidates the previous runtime and asynchronous completions;
|
|
178
|
+
- clears application/AIT and participation state;
|
|
179
|
+
- clears EIT, EMT backlog, UTC/NPT alarms, and broadcast clock;
|
|
180
|
+
- clears caption tracks/subscriptions;
|
|
181
|
+
- clears LCT colour and application-stage fallback;
|
|
182
|
+
- resets retained carousel resources and CacheEvent listeners;
|
|
183
|
+
- restores the ordinary media plane.
|
|
184
|
+
|
|
185
|
+
The individual setters may remain for incremental updates within a session.
|
|
186
|
+
|
|
187
|
+
## P1: compatibility needed after the P0 state model
|
|
188
|
+
|
|
189
|
+
### Capabilities and playback source
|
|
190
|
+
|
|
191
|
+
Implement `navigator.capabilities.hasCapability()` as a host-configurable feature matrix.
|
|
192
|
+
Unknown queries must return `false`; advertising a feature requires the corresponding API and
|
|
193
|
+
host bridge to work.
|
|
194
|
+
|
|
195
|
+
Implement `receiverDevice.streamEvent.sourceIs("onair")`. HonomiTV already knows whether
|
|
196
|
+
the manager was created for live or recorded playback, so the runtime must not guess from a
|
|
197
|
+
carousel path or current URL. TR-B39 recommends this optional API for receivers that execute
|
|
198
|
+
data content during recording playback.
|
|
199
|
+
|
|
200
|
+
### Broadcast video/audio object controls
|
|
201
|
+
|
|
202
|
+
Only caption discovery/listener methods are currently implemented. STD-B62 Volume 2 section
|
|
203
|
+
2.2 also defines:
|
|
204
|
+
|
|
205
|
+
- fullscreen enable, disable, and query;
|
|
206
|
+
- audio mute enable, disable, and query;
|
|
207
|
+
- video/audio source selection and query;
|
|
208
|
+
- caption source selection;
|
|
209
|
+
- caption visibility set/query.
|
|
210
|
+
|
|
211
|
+
These operations should be added to the media-plane adapter contract. The runtime must not
|
|
212
|
+
pretend they succeeded without changing DPlayer/native media state.
|
|
213
|
+
|
|
214
|
+
### Receiver API error and callback conformance
|
|
215
|
+
|
|
216
|
+
Centralize ARIB receiver errors with `INVALID_PARAM_ERR`, `NOT_AUTHORIZED_ERR`,
|
|
217
|
+
`NOT_FOUND_ERR`, `TIMEOUT_ERR`, and `MISC_ERR`. In particular:
|
|
218
|
+
|
|
219
|
+
- asynchronous callbacks must not run after a synchronous API exception;
|
|
220
|
+
- device-identifier acquisition failure returns `null`, not an empty string;
|
|
221
|
+
- `confirmIPNetwork(destination, confirmType, timeout)` needs argument validation and a
|
|
222
|
+
host-provided result rather than only returning the configuration flag;
|
|
223
|
+
- synchronous APIs must validate object shapes and integer ranges consistently.
|
|
224
|
+
|
|
225
|
+
### Storage and network isolation
|
|
226
|
+
|
|
227
|
+
All broadcast documents currently share the HonomiTV origin. Without an emulated application
|
|
228
|
+
storage boundary, different broadcasters can collide in `_wlocal*`, Ureg, and other
|
|
229
|
+
`localStorage` names. Persistence should be scoped by receiver policy and application identity
|
|
230
|
+
while retaining explicitly receiver-owned regional information.
|
|
231
|
+
|
|
232
|
+
Application-boundary permission bit 9 must apply to direct storage access as well as the
|
|
233
|
+
`readPersistentArray()` and `writePersistentArray()` compatibility functions.
|
|
234
|
+
|
|
235
|
+
External traffic should use a typed host request provider. HonomiTV currently rewrites
|
|
236
|
+
`fetch`, XHR, and `sendBeacon` to its same-origin proxy after runtime installation. Moving that
|
|
237
|
+
contract into the receiver host allows permission/boundary checks to precede the proxy and
|
|
238
|
+
avoids competing monkey patches. Subresources, forms, WebSocket/EventSource, redirects, and
|
|
239
|
+
managed-state navigation need an explicit support or rejection policy.
|
|
240
|
+
|
|
241
|
+
## P2: optional and lower-risk receiver functions
|
|
242
|
+
|
|
243
|
+
Implement these only after the session state and parser inputs are stable:
|
|
244
|
+
|
|
245
|
+
- UTC-NPT reference listeners, `getUTCNPT()`, alarms, and cancellation;
|
|
246
|
+
- data-event-ID update listeners;
|
|
247
|
+
- `tuneTo()` through an explicit host callback;
|
|
248
|
+
- viewing/recording scheduling APIs through the receiver EPG/backend;
|
|
249
|
+
- CAS-provider-specific extensions.
|
|
250
|
+
|
|
251
|
+
The runtime must not expose these through `hasCapability()` until their end-to-end behaviour is
|
|
252
|
+
available.
|
|
253
|
+
|
|
254
|
+
## Integration event matrix
|
|
255
|
+
|
|
256
|
+
| Input/state | `tlvdemux`/DPlayer output | `libaribhtml5` action | HonomiTV action |
|
|
257
|
+
| --- | --- | --- | --- |
|
|
258
|
+
| LCT background | layout configuration | Set/reset receiver background plane | Forward event only |
|
|
259
|
+
| MH-AIT | complete table/application states | Update AIT, active application, permissions, listeners | Select lifecycle candidate and pass snapshot |
|
|
260
|
+
| Carousel file | complete resource/revision/delete | Commit VFS mutation and CacheEvent atomically | Forward owned bytes and session generation |
|
|
261
|
+
| MH-EIT p/f | present/following event | Update event object and event-ID listeners | Convert demux time/fields only |
|
|
262
|
+
| General EMT | typed event message | Match registered application listeners | Forward without interpreting broadcaster IDs |
|
|
263
|
+
| Participation EMT `0xF00` | dedicated receiver notification | Evaluate visibility/input state and request a notice | Render notice and transfer control on user action |
|
|
264
|
+
| Liveness source | host playback mode | Implement `sourceIs("onair")` | Supply live versus recorded state |
|
|
265
|
+
| Service/session change | generation change | Atomic receiver-session reset | Call once before replaying the new snapshot |
|
|
266
|
+
|
|
267
|
+
## Validation plan
|
|
268
|
+
|
|
269
|
+
Library tests should cover each contract without requiring broadcaster media:
|
|
270
|
+
|
|
271
|
+
- immediate and change-only event-ID callbacks with structured references;
|
|
272
|
+
- complete AIT snapshots, ordering, version changes, continuation, removal, and KILL;
|
|
273
|
+
- directory CacheEvent matching, atomic update, deletion, and reset;
|
|
274
|
+
- participation notification deduplication and input/visibility combinations;
|
|
275
|
+
- session-reset rejection of late callbacks from the old generation;
|
|
276
|
+
- capability truthfulness and live/recorded `sourceIs()` behaviour;
|
|
277
|
+
- media object state and adapter command failures;
|
|
278
|
+
- application-scoped storage and permission changes.
|
|
279
|
+
|
|
280
|
+
End-to-end replay should then use:
|
|
281
|
+
|
|
282
|
+
- NHK BS Premium 4K for ordinary startup, EIT, receiver API, and long-running resource updates;
|
|
283
|
+
- NHK BS8K for LCT/background, external media holes, caption applications, and 8K viewport state;
|
|
284
|
+
- BS Fuji 4K for a non-NHK application entry and storage/path compatibility check;
|
|
285
|
+
- a synthetic descriptor-less `0xF00` EMT fixture for viewer-participation UI, because an
|
|
286
|
+
archived programme is not guaranteed to contain that notification.
|
|
287
|
+
|
|
288
|
+
Passing unit tests or a local SDK build is not equivalent to proving the deployed HonomiTV
|
|
289
|
+
bundle, browser composition, remote focus, or multi-hour live behaviour.
|
|
290
|
+
|
|
291
|
+
## Standards references
|
|
292
|
+
|
|
293
|
+
- ARIB TR-B39 2.5-E1, Part 1 Volume 3, section 6.2.5 and Appendix 1: viewer-participation
|
|
294
|
+
corner notification and expected receiver UI.
|
|
295
|
+
- ARIB TR-B39 2.5-E1, Part 1 Volume 3, section 10.7: permissions and receiver extended APIs.
|
|
296
|
+
- ARIB STD-B62 2.2-E1, Volume 2, section 2.2: broadcast video/audio object.
|
|
297
|
+
- ARIB STD-B62 2.2-E1, Volume 2, sections 3.3.7 through 3.3.13: application, capabilities,
|
|
298
|
+
receiver device, stream events, and cache control.
|
|
299
|
+
- ARIB STD-B60 and STD-B62: MH-AIT/LCT transport state and receiver media/background-plane
|
|
300
|
+
composition; see the focused lifecycle and media-plane documents in this repository.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libaribhtml5",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Browser runtime and receiver host for ARIB HTML5 data broadcasting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arib",
|
|
@@ -60,9 +60,10 @@
|
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"dev": "vite --host 127.0.0.1",
|
|
63
|
-
"test": "node --experimental-strip-types scripts/test-runtime-contracts.mjs && node --experimental-strip-types scripts/test-vfs-worker.mjs",
|
|
63
|
+
"test": "node --experimental-strip-types scripts/test-runtime-contracts.mjs && node --experimental-strip-types scripts/test-external-network.mjs && node --experimental-strip-types scripts/test-caption-controller.mjs && node --experimental-strip-types scripts/test-runtime-application.mjs && node --experimental-strip-types scripts/test-receiver-protocol.mjs && node --experimental-strip-types scripts/test-receiver-canvas.mjs && node --experimental-strip-types scripts/test-receiver-session.mjs && node --experimental-strip-types scripts/test-media-plane-characterization.mjs && node --experimental-strip-types scripts/test-vfs-worker.mjs",
|
|
64
64
|
"build": "tsc --noEmit && vite build && vite build --config vite.sdk.config.mjs && vite build --config vite.worker.config.mjs",
|
|
65
65
|
"build:sdk": "tsc --noEmit && vite build --config vite.sdk.config.mjs && vite build --config vite.worker.config.mjs && npm run build:types",
|
|
66
|
+
"build:tv": "tsc --noEmit && node --experimental-strip-types scripts/test-tv-input.mjs && vite build --config vite.tv.config.mjs",
|
|
66
67
|
"build:types": "tsc -p tsconfig.build.json && node scripts/rewrite-declaration-imports.mjs",
|
|
67
68
|
"preview": "vite preview --host 127.0.0.1"
|
|
68
69
|
}
|