libaribhtml5 0.1.1 → 0.1.3
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 +7 -0
- package/dist/sdk/libaribhtml5.js +9 -9
- package/dist/sdk/libaribhtml5.mjs +760 -494
- package/dist/types/index.d.ts +6 -4
- package/dist/types/receiver-host.d.ts +32 -1
- package/dist/types/runtime/install.d.ts +10 -7
- package/dist/types/runtime/stream-event.d.ts +21 -0
- package/dist/types/runtime/system-information.d.ts +12 -0
- package/dist/types/service-worker-vfs.d.ts +18 -0
- package/docs/android-program-guide.md +9 -72
- package/docs/application-lifecycle.md +95 -0
- package/docs/browser-iframe-compositing.md +93 -0
- package/docs/media-plane-integration.md +68 -0
- package/docs/receiver-host-contracts.md +12 -2
- package/package.json +1 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export { AribReceiverHost } from './receiver-host';
|
|
2
|
-
export type { AribCaptionPacket, AribBroadcastClock, AribReceiverLifecycleEvent, AribReceiverHostOptions, } from './receiver-host';
|
|
2
|
+
export type { AribApplicationInformation, AribApplicationReplaceHandler, AribApplicationReplaceRequest, AribCaptionPacket, AribBroadcastClock, AribExitManagedStateHandler, AribReceiverLifecycleEvent, AribReceiverHostOptions, } from './receiver-host';
|
|
3
3
|
export { BehindIframeMediaPlaneAdapter, DomObjectMediaPlaneAdapter, } from './media-plane';
|
|
4
4
|
export type { AribMediaPlane, AribMediaPlaneAdapter, AribMediaPlaneLayer, AribMediaPlaneStackEntry, AribMediaPlaneUnmountReason, AribVideoPlane, BehindIframeMediaPlaneAdapterOptions, DomObjectMediaPlaneAdapterOptions, } from './media-plane';
|
|
5
5
|
export { installRuntime } from './runtime/install';
|
|
6
|
-
export { createReceiverSystemInformation, RECEIVER_SYSTEM_IDENTITY, } from './runtime/system-information';
|
|
6
|
+
export { createReceiverSystemInformation, readReceiverInformationArray, RECEIVER_SYSTEM_IDENTITY, synchronizeReceiverCompatibilityStorage, } from './runtime/system-information';
|
|
7
7
|
export type { ReceiverSystemIdentity, ReceiverSystemInformation, ReceiverSystemInformationOverrides, } from './runtime/system-information';
|
|
8
8
|
export { cloneProgramInfo } from './program-info';
|
|
9
9
|
export type { ProgramInfo } from './program-info';
|
|
10
|
-
export
|
|
10
|
+
export { runtimeEventMatchesSelector } from './runtime/stream-event';
|
|
11
|
+
export type { RuntimeEvent, RuntimeEventSelector, RuntimeEventSource, } from './runtime/stream-event';
|
|
12
|
+
export type { RuntimeApplicationInformation, RuntimeOptions, RuntimeWindow, } from './runtime/install';
|
|
11
13
|
export { DEFAULT_RECEIVER_DEVICE_IDENTIFIER, resolveReceiverDeviceIdentifier, } from './device-identifier';
|
|
12
14
|
export type { ReceiverDeviceIdentifierProvider } from './device-identifier';
|
|
13
15
|
export type { BroadcastResourceCacheEvent, BroadcastResourceCacheListener, BroadcastResourceChange, BroadcastResourceStore, } from './runtime/resources';
|
|
@@ -15,7 +17,7 @@ export { installBroadcastClock } from './runtime/clock';
|
|
|
15
17
|
export type { BroadcastNowProvider } from './runtime/clock';
|
|
16
18
|
export { installAribSymbolFont } from './runtime/fonts';
|
|
17
19
|
export { DEFAULT_BROADCAST_BASE_PATH, deriveBroadcastRootUrl, normalizeBroadcastBaseUrl, resolveBroadcastUrl, } from './broadcast-url';
|
|
18
|
-
export { deferRomSoundMarkup, installRomSoundProtocol, resolveRomSoundUrl, } from './runtime/romsound';
|
|
20
|
+
export { deferRomSoundMarkup, installRomSoundProtocol, resolveRomSoundUrl, } from './runtime/romsound.js';
|
|
19
21
|
export { prefixBroadcastRootAttributes, prepareBroadcastHtml, prepareBroadcastStylesheet, rewriteBroadcastObjectMarkup, } from './broadcast-document';
|
|
20
22
|
export type { BroadcastDocumentOptions } from './broadcast-document';
|
|
21
23
|
export { BroadcastVfsSession, ServiceWorkerBroadcastVfs } from './service-worker-vfs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RuntimeEvent, type RuntimeWindow } from './runtime/install';
|
|
1
|
+
import { type RuntimeApplicationInformation, type RuntimeEvent, type RuntimeWindow } from './runtime/install';
|
|
2
2
|
import { type ProgramInfo } from './program-info';
|
|
3
3
|
import type { ReceiverSystemInformationOverrides } from './runtime/system-information';
|
|
4
4
|
import type { ReceiverDeviceIdentifierProvider } from './device-identifier';
|
|
@@ -13,6 +13,17 @@ export type AribCaptionPacket = {
|
|
|
13
13
|
tmd?: string;
|
|
14
14
|
data?: string;
|
|
15
15
|
};
|
|
16
|
+
export type AribCaptionSubscription = {
|
|
17
|
+
componentTags: number[];
|
|
18
|
+
};
|
|
19
|
+
export type AribApplicationInformation = RuntimeApplicationInformation;
|
|
20
|
+
export type AribApplicationReplaceRequest = {
|
|
21
|
+
organizationId: number;
|
|
22
|
+
applicationId: number;
|
|
23
|
+
aitUrl: string | null;
|
|
24
|
+
};
|
|
25
|
+
export type AribApplicationReplaceHandler = (request: AribApplicationReplaceRequest) => void | Promise<void>;
|
|
26
|
+
export type AribExitManagedStateHandler = (url: string) => void | Promise<void>;
|
|
16
27
|
export type AribBroadcastClock = {
|
|
17
28
|
/** Absolute broadcast time at the anchor, as Unix epoch milliseconds. */
|
|
18
29
|
epochMilliseconds: number;
|
|
@@ -52,6 +63,8 @@ export type AribReceiverHostOptions = {
|
|
|
52
63
|
onStatus?: (status: string) => void;
|
|
53
64
|
/** Machine-readable application/runtime lifecycle; never branch on onStatus text. */
|
|
54
65
|
onLifecycle?: (event: AribReceiverLifecycleEvent) => void;
|
|
66
|
+
/** Time to wait for a loaded document to install the receiver runtime. 0 disables the fallback. */
|
|
67
|
+
applicationLoadTimeoutMs?: number;
|
|
55
68
|
onUrlChange?: (url: string) => void;
|
|
56
69
|
onMediaPlane?: (plane: AribMediaPlane) => void;
|
|
57
70
|
/** @deprecated Use onMediaPlane. */
|
|
@@ -65,6 +78,12 @@ export type AribReceiverHostOptions = {
|
|
|
65
78
|
resourceStore?: BroadcastResourceStore;
|
|
66
79
|
/** Receiver/WebView bridge which opens the native EPG or a program detail page. */
|
|
67
80
|
onOpenProgramGuide?: AribProgramGuideHandler;
|
|
81
|
+
/** Active caption components requested by the current broadcast document. */
|
|
82
|
+
onCaptionSubscription?: (subscription: AribCaptionSubscription) => void;
|
|
83
|
+
/** Resolve Application.replaceApplication() through the receiver's current MH-AIT. */
|
|
84
|
+
onReplaceApplication?: AribApplicationReplaceHandler;
|
|
85
|
+
/** Transfer an application to an unmanaged general-application URL. */
|
|
86
|
+
onExitManagedState?: AribExitManagedStateHandler;
|
|
68
87
|
/** Override the default browser alert when the receiver cannot open the EPG. */
|
|
69
88
|
onProgramGuideUnavailable?: AribProgramGuideUnavailableHandler;
|
|
70
89
|
/** Receiver identity/capabilities exposed to the broadcast application. */
|
|
@@ -80,6 +99,7 @@ export declare class AribReceiverHost {
|
|
|
80
99
|
private readonly origin;
|
|
81
100
|
private readonly onStatus?;
|
|
82
101
|
private readonly onLifecycle?;
|
|
102
|
+
private readonly applicationLoadTimeoutMs;
|
|
83
103
|
private readonly onUrlChange?;
|
|
84
104
|
private readonly onMediaPlane?;
|
|
85
105
|
private readonly onVideoPlane?;
|
|
@@ -89,6 +109,9 @@ export declare class AribReceiverHost {
|
|
|
89
109
|
private readonly broadcastBaseUrl;
|
|
90
110
|
private readonly resourceStore?;
|
|
91
111
|
private readonly onOpenProgramGuide?;
|
|
112
|
+
private readonly onCaptionSubscription?;
|
|
113
|
+
private readonly onReplaceApplication?;
|
|
114
|
+
private readonly onExitManagedState?;
|
|
92
115
|
private readonly onProgramGuideUnavailable?;
|
|
93
116
|
private readonly systemInformation?;
|
|
94
117
|
private readonly getDeviceIdentifier?;
|
|
@@ -97,14 +120,19 @@ export declare class AribReceiverHost {
|
|
|
97
120
|
private logicalWidth;
|
|
98
121
|
private logicalHeight;
|
|
99
122
|
private captionComponentTags;
|
|
123
|
+
private applicationInformation;
|
|
100
124
|
private programInfo;
|
|
125
|
+
private readonly pendingStreamEvents;
|
|
101
126
|
private broadcastClock;
|
|
102
127
|
private video;
|
|
103
128
|
private mediaPlaneEnabled;
|
|
104
129
|
private applicationExited;
|
|
130
|
+
private applicationLoadTimer;
|
|
105
131
|
private destroyed;
|
|
106
132
|
constructor(options: AribReceiverHostOptions);
|
|
107
133
|
installRuntime(target: RuntimeWindow): void;
|
|
134
|
+
/** Set the MH-AIT identity exposed by applicationManager.getOwnerApplication(). */
|
|
135
|
+
setApplicationInformation(application: AribApplicationInformation | null): void;
|
|
108
136
|
/** @deprecated Pass an AribMediaPlaneAdapter to the constructor. */
|
|
109
137
|
attachVideo(video: HTMLVideoElement): void;
|
|
110
138
|
loadApplication(url: string, status?: string): void;
|
|
@@ -138,6 +166,9 @@ export declare class AribReceiverHost {
|
|
|
138
166
|
private applyMediaPlane;
|
|
139
167
|
private readMediaLayer;
|
|
140
168
|
private postToRuntime;
|
|
169
|
+
private armApplicationLoadTimer;
|
|
170
|
+
private failApplicationLoad;
|
|
171
|
+
private clearApplicationLoadTimer;
|
|
141
172
|
private invalidateRuntime;
|
|
142
173
|
private fitBroadcastCanvas;
|
|
143
174
|
private assertAlive;
|
|
@@ -4,14 +4,8 @@ import { type BroadcastResourceStore } from './resources';
|
|
|
4
4
|
import { type ReceiverSystemInformationOverrides } from './system-information';
|
|
5
5
|
import { type ReceiverDeviceIdentifierProvider } from '../device-identifier';
|
|
6
6
|
export type { ProgramInfo } from '../program-info';
|
|
7
|
+
export type { RuntimeEvent, RuntimeEventSelector, RuntimeEventSource, } from './stream-event';
|
|
7
8
|
export type RuntimeWindow = Window & typeof globalThis & Record<string, unknown>;
|
|
8
|
-
export type RuntimeEvent = {
|
|
9
|
-
source: {
|
|
10
|
-
event_message_tag: number;
|
|
11
|
-
};
|
|
12
|
-
message_id: number;
|
|
13
|
-
private_data_byte: string;
|
|
14
|
-
};
|
|
15
9
|
export type RuntimeOptions = {
|
|
16
10
|
/** Permit HTTP requests outside the receiver-managed application origin. */
|
|
17
11
|
allowExternalNetwork?: boolean;
|
|
@@ -27,5 +21,14 @@ export type RuntimeOptions = {
|
|
|
27
21
|
systemInformation?: ReceiverSystemInformationOverrides;
|
|
28
22
|
/** Resolve receiver/CAS identifiers by receiverDevice identifier kind. */
|
|
29
23
|
getDeviceIdentifier?: ReceiverDeviceIdentifierProvider;
|
|
24
|
+
/** MH-AIT metadata for the application which owns this document. */
|
|
25
|
+
application?: RuntimeApplicationInformation;
|
|
26
|
+
};
|
|
27
|
+
export type RuntimeApplicationInformation = {
|
|
28
|
+
type?: string;
|
|
29
|
+
organizationId?: number;
|
|
30
|
+
applicationId?: number;
|
|
31
|
+
controlCode?: string;
|
|
32
|
+
autostartPriority?: number;
|
|
30
33
|
};
|
|
31
34
|
export declare function installRuntime(target: RuntimeWindow, options?: RuntimeOptions): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type RuntimeEventSource = {
|
|
2
|
+
original_network_id?: number;
|
|
3
|
+
tlv_stream_id?: number;
|
|
4
|
+
service_id?: number;
|
|
5
|
+
event_message_tag: number;
|
|
6
|
+
};
|
|
7
|
+
export type RuntimeEvent = {
|
|
8
|
+
source: RuntimeEventSource;
|
|
9
|
+
message_group_id: number;
|
|
10
|
+
message_id: number;
|
|
11
|
+
message_version: number;
|
|
12
|
+
private_data_byte: string;
|
|
13
|
+
};
|
|
14
|
+
export type RuntimeEventSelector = {
|
|
15
|
+
source?: Partial<RuntimeEventSource>;
|
|
16
|
+
message_group_id?: number;
|
|
17
|
+
message_id?: number;
|
|
18
|
+
message_version?: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function runtimeEventMatchesSelector(event: RuntimeEvent, selector: RuntimeEventSelector): boolean;
|
|
21
|
+
export declare function sameRuntimeEventSelector(first: RuntimeEventSelector, second: RuntimeEventSelector): boolean;
|
|
@@ -12,3 +12,15 @@ export type ReceiverSystemInformation = ReceiverSystemIdentity & {
|
|
|
12
12
|
export type ReceiverSystemInformationOverrides = Partial<ReceiverSystemIdentity> & Record<string, unknown>;
|
|
13
13
|
export declare const RECEIVER_SYSTEM_IDENTITY: Readonly<ReceiverSystemIdentity>;
|
|
14
14
|
export declare function createReceiverSystemInformation(baseurl: string, overrides?: ReceiverSystemInformationOverrides): ReceiverSystemInformation;
|
|
15
|
+
/**
|
|
16
|
+
* Mirror receiver-owned regional data into compatibility keys used directly
|
|
17
|
+
* by broadcaster libraries. NHK's nhksh.getZipCode(), for example, reads the
|
|
18
|
+
* historical `_zipcode` localStorage key instead of receiverinfo NVRAM.
|
|
19
|
+
*/
|
|
20
|
+
export declare function synchronizeReceiverCompatibilityStorage(storage: Pick<Storage, 'setItem' | 'removeItem'>, systemInformation: ReceiverSystemInformation): void;
|
|
21
|
+
/**
|
|
22
|
+
* Project receiver-owned regional settings into the legacy receiverinfo Ureg.
|
|
23
|
+
* The host remains the sole owner of persistence; broadcast applications only
|
|
24
|
+
* receive the snapshot supplied in systemInformation.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readReceiverInformationArray(namespace: string, structure: string, systemInformation: ReceiverSystemInformation): unknown[] | null;
|
|
@@ -38,12 +38,19 @@ export declare class BroadcastVfsSession {
|
|
|
38
38
|
private drainScheduled;
|
|
39
39
|
private ioTail;
|
|
40
40
|
private sessionReady;
|
|
41
|
+
private disposed;
|
|
41
42
|
constructor(backend: BroadcastVfsBackend, options?: BroadcastVfsSessionOptions);
|
|
42
43
|
beginSession(): Promise<void>;
|
|
43
44
|
enqueue(resource: BroadcastVfsResource): number;
|
|
44
45
|
waitFor(revision: number): Promise<void>;
|
|
45
46
|
/** Ensure a staged path is readable, replaying the page-owned mirror if needed. */
|
|
46
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>;
|
|
47
54
|
private appendIo;
|
|
48
55
|
private scheduleDrain;
|
|
49
56
|
private drainOne;
|
|
@@ -55,12 +62,23 @@ export declare class ServiceWorkerBroadcastVfs {
|
|
|
55
62
|
private readonly uniqueBasenameFallback;
|
|
56
63
|
private registration;
|
|
57
64
|
private ready;
|
|
65
|
+
private readonly inFlight;
|
|
66
|
+
private disposing;
|
|
67
|
+
private disposed;
|
|
58
68
|
constructor(options: ServiceWorkerBroadcastVfsOptions);
|
|
59
69
|
initialize(): Promise<ServiceWorkerRegistration>;
|
|
60
70
|
begin(): Promise<void>;
|
|
61
71
|
put(resource: BroadcastVfsResource): Promise<void>;
|
|
62
72
|
canRead(path: string): Promise<boolean>;
|
|
63
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>;
|
|
64
80
|
private request;
|
|
81
|
+
private performRequest;
|
|
82
|
+
private performRequestWithRegistration;
|
|
65
83
|
private waitForActivation;
|
|
66
84
|
}
|
|
@@ -1,80 +1,17 @@
|
|
|
1
1
|
# Android の「放送予定」と番組表連携
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 受信機所有の EPG へ渡す
|
|
4
4
|
|
|
5
|
-
「放送予定」は放送 HTML
|
|
6
|
-
|
|
5
|
+
「放送予定」は放送 HTML 内へ任意の番組表 URL を開く操作ではなく、受信機が所有する
|
|
6
|
+
番組詳細・予約 UI へ番組識別子を渡す host operation として扱います。
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
3. Android TV の検索結果を選ぶと `SearchableProgramInfo` が
|
|
13
|
-
`TvTunerService` を起動し、番組情報と `future` を渡す。
|
|
14
|
-
4. `TvTunerService` は future の場合に `StartMode.Reserve` を選び、別パッケージの
|
|
15
|
-
`jp.pixela.atv_app.MainActivity` を起動する。
|
|
16
|
-
5. その Activity の WebView は次の内蔵ページを開く。
|
|
8
|
+
現在時刻と番組の開始・終了時刻から `live` / `future` を判定し、放送種別、network ID、
|
|
9
|
+
service ID、event ID を receiver bridge へ渡します。受信機側は識別子を現在の EIT/EPG と
|
|
10
|
+
照合してから、自アプリ内の画面を開いてください。放送 application から渡された URL を
|
|
11
|
+
そのまま WebView や外部 Activity へ転送してはいけません。
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
#/?program_id=...&broadcast_type=...&service_id=...&event_id=...
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
したがって、これは任意の外部 URL を開く機能ではありません。検索結果から受信機所有の
|
|
24
|
-
番組詳細・予約 UI へ移る、明示的なホスト機能です。
|
|
25
|
-
|
|
26
|
-
### future 判定と検索データ
|
|
27
|
-
|
|
28
|
-
`ProgramInfoOpenHelper` の判定は単純な派生状態です。番組時刻は DB 内では秒単位で、
|
|
29
|
-
比較時にミリ秒へ変換されます。
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
now >= start + duration -> 検索結果に入れない
|
|
33
|
-
start <= now < end -> live
|
|
34
|
-
now < start -> future(最大 50 件)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
future の `suggest_text_1` は `[放送予定] 番組名` になります。同じ放送種別、
|
|
38
|
-
service ID、event ID の有効な予約があれば `[予約済]` も追加されます。
|
|
39
|
-
`suggest_intent_extra_data` には番組 JSON と `#future`、live の場合は `#live` が入り、
|
|
40
|
-
クリック後の `TvTunerService` はこの suffix で遷移を分けます。
|
|
41
|
-
|
|
42
|
-
Provider は次の標準 suggestion 列を公開します。
|
|
43
|
-
|
|
44
|
-
- `suggest_text_1` / `suggest_text_2`
|
|
45
|
-
- `suggest_result_card_image`(ジャンル別 320 x 180 画像)
|
|
46
|
-
- `suggest_is_live`
|
|
47
|
-
- `suggest_intent_extra_data`
|
|
48
|
-
- `suggest_intent_data_id`
|
|
49
|
-
|
|
50
|
-
Katniss 側のローカル suggestion 変換では、タイトル、説明、カード画像、intent extra data
|
|
51
|
-
をそのまま読み、`suggest_is_live` 自体では分岐していません。したがって、この機種で
|
|
52
|
-
future を決定している本体は `ProgramInfoOpenHelper` と `#future` の受け側です。
|
|
53
|
-
|
|
54
|
-
また、APK の日本語リソースに存在する原文は `[放送予定]` で、括弧なしの
|
|
55
|
-
`放送予定` という独立した文字列リソースは見つかりません。スクリーンショットの括弧なし
|
|
56
|
-
表示は検索 UI 側のプレゼンテーションであり、放送 HTML やフォントが生成する状態では
|
|
57
|
-
ありません。
|
|
58
|
-
|
|
59
|
-
### EPG の保存と更新
|
|
60
|
-
|
|
61
|
-
検索時に放送波を直接スキャンしているわけではありません。ネイティブチューナーが持つ
|
|
62
|
-
`program_info.sqlite3` の更新時刻を監視し、検索用の `program_info.db` へコピーします。
|
|
63
|
-
検索要求ごとに一時 `suggest_content.db` を作り直し、番組名・説明とチャンネル名を検索します。
|
|
64
|
-
|
|
65
|
-
バックグラウンド更新は起動後 5 分、その後 30 分ごとに判定されます。EPG スキャンを
|
|
66
|
-
実行する JST の時間帯はコード上で次の二つです。
|
|
67
|
-
|
|
68
|
-
- 04:38–07:37
|
|
69
|
-
- 16:38–19:37
|
|
70
|
-
|
|
71
|
-
更新サービスは `StartMode.UpdateEPG` でチューナーを初期化し、EPG cache target `0` を
|
|
72
|
-
更新して最大 120 秒待ちます。録画対応機では録画コンテンツ target `5` と予約 target `6`
|
|
73
|
-
も更新します。テレビアプリが前面へ出た場合、バックグラウンド更新は中止されます。
|
|
74
|
-
|
|
75
|
-
なお、ARIB STD-B62 Volume 2 には `receiverDevice.scheduleToTune()` などの
|
|
76
|
-
視聴・録画予約 API もありますが、上記の Android 検索結果から番組詳細を開く処理とは
|
|
77
|
-
別のインターフェースです。
|
|
13
|
+
ARIB STD-B62 Volume 2 の `receiverDevice.scheduleToTune()` 等の視聴・録画予約 API と、
|
|
14
|
+
この host 固有の番組表画面を開く操作は別のインターフェースです。
|
|
78
15
|
|
|
79
16
|
## libaribhtml5 のホストインターフェース
|
|
80
17
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# MH-AIT アプリケーションライフサイクル
|
|
2
|
+
|
|
3
|
+
この文書は、ARIB HTML5 データ放送の自動起動、表示用アプリケーションへの切替、終了、
|
|
4
|
+
および EMT の責務境界を receiver host 向けにまとめます。放送局固有の URL や EMT ID を
|
|
5
|
+
プレーヤーへハードコードしないための実装条件です。
|
|
6
|
+
|
|
7
|
+
## 制御の所有者
|
|
8
|
+
|
|
9
|
+
アプリケーションの起動と終了は MH-AIT の application control code が制御します。
|
|
10
|
+
|
|
11
|
+
| control code | 値 | receiver の動作 |
|
|
12
|
+
| --- | ---: | --- |
|
|
13
|
+
| `AUTOSTART` | `0x01` | entry resource が利用可能になった時点で自動起動する |
|
|
14
|
+
| `PRESENT` | `0x02` | 実行可能として保持する。新しいページを自動起動しない |
|
|
15
|
+
| `KILL` | `0x04` | 対象の managed application を直ちに終了する |
|
|
16
|
+
| `PREFETCH` | `0x05` | resource を取得・保持する。表示や起動は行わない |
|
|
17
|
+
|
|
18
|
+
根拠は ARIB STD-B60 1.5版 10.3.2.1 表10-8の application control code と、同規格
|
|
19
|
+
10.3.3.1 の MH-application descriptor です。
|
|
20
|
+
|
|
21
|
+
`AUTOSTART` は起動を指示する control code であり、CSS の表示状態を指示しません。
|
|
22
|
+
ユーザーへの可視性は別フィールドである MH-application descriptor の `visibility` で決まります。
|
|
23
|
+
|
|
24
|
+
| visibility | receiver の動作 |
|
|
25
|
+
| ---: | --- |
|
|
26
|
+
| `00` | エラー報告等を除き、ユーザーと他 application の双方へ不可視 |
|
|
27
|
+
| `01` | ユーザーへ不可視。他 application からは API 等を介して可視 |
|
|
28
|
+
| `10` | reserved |
|
|
29
|
+
| `11` | ユーザーと他 application の双方へ可視 |
|
|
30
|
+
|
|
31
|
+
`00` / `01` でも document、timer、通信、EMT listener は実行を続けます。iframe を `display:none`
|
|
32
|
+
にして実行を止めず、receiver compositor の application plane だけをユーザーへ合成しません。
|
|
33
|
+
`11` の AUTOSTART は通常どおり画面を描画できます。
|
|
34
|
+
|
|
35
|
+
別 application へ切り替える必要がある場合、実行中のページが
|
|
36
|
+
`navigator.applicationManager.getOwnerApplication().replaceApplication(...)` を呼びます。
|
|
37
|
+
|
|
38
|
+
## replaceApplication
|
|
39
|
+
|
|
40
|
+
ARIB STD-B62 第二編 3.3.7–3.3.8 は `ApplicationManager` と `Application` を定義します。
|
|
41
|
+
`replaceApplication(organization_id, application_id, ait_url)` は現在の application を終了し、
|
|
42
|
+
指定した application を起動する受信機要求です。EMT を受信した host が URL を推測する機能ではありません。
|
|
43
|
+
|
|
44
|
+
`libaribhtml5` はこの要求を `AribReceiverHostOptions.onReplaceApplication` へ渡します。
|
|
45
|
+
host は現在の MH-AIT から `(organizationId, applicationId)` を検索し、entry resource の VFS commit を
|
|
46
|
+
待ってから、同じ `AribReceiverHost` / iframe で `loadApplication()` を実行してください。
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const host = new AribReceiverHost({
|
|
50
|
+
iframe,
|
|
51
|
+
viewport,
|
|
52
|
+
onReplaceApplication: async request => {
|
|
53
|
+
const target = currentApplications.get(
|
|
54
|
+
`${request.organizationId}:${request.applicationId}`,
|
|
55
|
+
)
|
|
56
|
+
if (!target) throw new Error('Application is not present in the current MH-AIT')
|
|
57
|
+
await vfs.waitFor(target.entryRevision)
|
|
58
|
+
host.setApplicationInformation(target.information)
|
|
59
|
+
host.loadApplication(target.entryUrl, 'アプリケーション切替中')
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`exitFromManagedState(url)` は放送管理外の general application へ移る別の操作です。
|
|
65
|
+
許可境界と外部 network policy を receiver 側で検証し、対応する場合だけ
|
|
66
|
+
`onExitManagedState` で処理します。
|
|
67
|
+
|
|
68
|
+
## AUTOSTART と PRESENT の同居
|
|
69
|
+
|
|
70
|
+
一つの tuning/session generation につき managed application slot は一つにします。
|
|
71
|
+
|
|
72
|
+
1. active application がない時だけ、ready になった `AUTOSTART` を読み込む。
|
|
73
|
+
2. `PRESENT` は data button 用の候補として保持するが、それだけで iframe を reload しない。
|
|
74
|
+
3. 同じ application が AUTOSTART から PRESENT へ更新され、継続条件を満たす場合は既存 runtime を再利用する。
|
|
75
|
+
4. `KILL`、AIT からの消失、AIT location descriptor の消失、tune/session reset で runtime と listener を破棄する。
|
|
76
|
+
5. 404 または runtime bootstrap 不成立が確定した時点で直ちに破棄する。固定の再試行待ち時間中、
|
|
77
|
+
壊れたページを表示し続けない。
|
|
78
|
+
|
|
79
|
+
application state、resource revision、EMT listener、version deduplication はすべて tuning generation に
|
|
80
|
+
所属させます。前チャンネルの非同期完了や event を新しい iframe へ渡してはいけません。
|
|
81
|
+
|
|
82
|
+
## EMT は起動コマンドではない
|
|
83
|
+
|
|
84
|
+
ARIB STD-B60 1.5版 11章の Event Message Table は、実行中 application の登録済み selector へ
|
|
85
|
+
event message を指定時刻に配送する仕組みです。規格には即時、UTC、NPT、相対時刻等の time mode が
|
|
86
|
+
あります。現在の runtime が保証するのは即時配送 (`time_mode=0`) だけであり、その他の ignition time は
|
|
87
|
+
receiver clock scheduler を実装するまで未対応です。
|
|
88
|
+
|
|
89
|
+
- EMT `40/5` 等を見て host が表示ページ URL を決めない。
|
|
90
|
+
- application が listener を登録する前に到着した event を、起動後に独自再生しない。
|
|
91
|
+
- 同じ message ID/version は重複配送しない。
|
|
92
|
+
- listener 登録後に受信した、または放送側が再送した event だけを application へ渡す。
|
|
93
|
+
|
|
94
|
+
したがって紅白等の連動ページは、MH-AIT による AUTOSTART、ページ内 listener 登録、EMT 配送、
|
|
95
|
+
ページによる `replaceApplication()` の順に進みます。
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ブラウザ iframe 合成の注意事項
|
|
2
|
+
|
|
3
|
+
この文書は、通常ブラウザでデータ放送 iframe と既存プレーヤーの `<video>` を合成する際の
|
|
4
|
+
ブラウザ固有の制約をまとめます。ARIB のプレーン順序そのものは
|
|
5
|
+
[メディアプレーン統合ガイド](media-plane-integration.md)を参照してください。
|
|
6
|
+
|
|
7
|
+
## 必須のレイヤー構成
|
|
8
|
+
|
|
9
|
+
外部映像面を使う場合は、同じ 16:9 container の sibling として次の順に置きます。
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
z=0 receiver-owned background plane
|
|
13
|
+
z=1 プレーヤーが所有する既存 video
|
|
14
|
+
z=2 データ放送 iframe
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
video の複製や canvas への 1:1 コピーは行いません。MSE に接続済みのプレーヤー所有 video を
|
|
18
|
+
media object の矩形へ移動・リサイズして使います。video を iframe より前へ上げると、字幕説明、
|
|
19
|
+
ダイアログ、字幕ボタンなど本来映像より前にある application UI まで隠れるため禁止です。
|
|
20
|
+
|
|
21
|
+
iframe 内の `object[type="video/x-arib2-broadcast"]` は映像そのものではなく、外部 video plane の
|
|
22
|
+
位置・寸法・可視状態を宣言する geometry anchor として残します。external adapter は object の描画を
|
|
23
|
+
`opacity: 0 !important` で隠します。さらに `#vstream > object#av` のように、object と同じ矩形で
|
|
24
|
+
object だけを子に持つ media-only wrapper は wrapper 自身も隠します。wrapper が高い `z-index` の
|
|
25
|
+
独立した compositing layer を作る場合、背景だけを透明化しても Chromium が外部 video の上へその layer を
|
|
26
|
+
残すためです。字幕、ダイアログ、操作 UI を含む祖先は media-only wrapper として扱いません。
|
|
27
|
+
|
|
28
|
+
`display: none`、`visibility: hidden`、`content-visibility: hidden` は矩形計測または可視判定を壊すため
|
|
29
|
+
使用しません。runtime は隠す前の computed opacity を media-plane report に使い、host 側の video は
|
|
30
|
+
object の CSS を複製せず、報告された `getBoundingClientRect()` を logical viewport に対する割合へ
|
|
31
|
+
変換して配置します。
|
|
32
|
+
|
|
33
|
+
## Chromium の白い iframe backdrop
|
|
34
|
+
|
|
35
|
+
Chromium は iframe owner と iframe 内 root の used `color-scheme` が異なる場合、透明 iframe の
|
|
36
|
+
代わりに scheme に対応する不透明 backdrop を描画します。dark theme の host に、標準の light
|
|
37
|
+
canvas を使う放送ページを埋め込むと白になります。
|
|
38
|
+
|
|
39
|
+
この白は DOM の背景ではないため、次をすべて確認しても残ります。
|
|
40
|
+
|
|
41
|
+
- iframe、`html`、`body` の computed `background-color` が透明
|
|
42
|
+
- 背景画像と疑似要素がない
|
|
43
|
+
- iframe の `opacity` や `z-index` を変更した
|
|
44
|
+
|
|
45
|
+
この判定を無効化する公開 feature flag はありません。receiver が管理する iframe owner と
|
|
46
|
+
放送ページ root の双方に同じ scheme を明示します。
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
iframe.style.colorScheme = 'only light'
|
|
50
|
+
iframe.contentDocument.documentElement.style.setProperty('color-scheme', 'only light', 'important')
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
これは clickjacking/XSS 対策による透明 iframe の禁止ではありません。Blink は owner と embedded
|
|
54
|
+
document の scheme が異なる場合に canvas backdrop を意図的に描画しています。
|
|
55
|
+
|
|
56
|
+
## application background と media hole
|
|
57
|
+
|
|
58
|
+
external adapter は video を iframe の背後に置くため、application canvas の `html` / `body` を
|
|
59
|
+
透明化します。ただし元の stage 背景色まで捨てると、映像領域外に host アプリの背景が露出します。
|
|
60
|
+
runtime は透明化前の stage 色を host へ報告し、host はその色を z=0 の receiver background plane
|
|
61
|
+
へ置きます。`body` が白でも `#backscreen` のような不透明要素が application canvas 全体を覆う場合は、
|
|
62
|
+
その全画布要素を実際の stage 色として優先します。色を取得できない間の実装 fallback は黒です。
|
|
63
|
+
|
|
64
|
+
iframe 内では、media object 自身を透明にしても、その祖先や object より前にある装飾用背景要素が
|
|
65
|
+
同じ矩形を塗ると外部 video を隠します。external runtime は次の背景だけを一時的に透明化します。
|
|
66
|
+
|
|
67
|
+
- media object の不透明な祖先背景
|
|
68
|
+
- media object の矩形全体を覆う application element の不透明な背景
|
|
69
|
+
|
|
70
|
+
要素自体は削除せず、`background-color` と `background-image` だけを退避して透明化します。
|
|
71
|
+
media object が消える、ページが遷移する、または application が終了すると元の inline style を
|
|
72
|
+
復元します。要素内の文字、画像、字幕、ダイアログ、操作 UI は削除せず、透明な debug overlay
|
|
73
|
+
も変更しません。media slot 全体を覆わないボタンやパネルの背景も対象外です。
|
|
74
|
+
|
|
75
|
+
runtime が一度透明化した背景要素は、media slot を覆っている間は管理対象として保持します。
|
|
76
|
+
透明化後の computed style だけを見て対象外にすると、MutationObserver の各走査で
|
|
77
|
+
「透明化 → 元へ復元」を繰り返し、映像面と application 背景が点滅します。
|
|
78
|
+
|
|
79
|
+
## DevTools での切り分け
|
|
80
|
+
|
|
81
|
+
白い全面シートでは、まず owner/root の scheme を確認します。
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const frame = document.querySelector('iframe.dplayer-tlv-data-broadcast')
|
|
85
|
+
console.table({
|
|
86
|
+
owner: getComputedStyle(frame).colorScheme,
|
|
87
|
+
root: getComputedStyle(frame.contentDocument.documentElement).colorScheme,
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
黒い media slot では、video の `readyState` と矩形を確認した後、iframe 内で media object より前に
|
|
92
|
+
ある covering element の背景を確認します。DevTools の青い半透明表示は選択要素の highlight で、
|
|
93
|
+
実際の video frame や application background ではありません。
|
|
@@ -13,6 +13,68 @@
|
|
|
13
13
|
caption ページを正しく表示するなら、1 番目または 2 番目が必要です。
|
|
14
14
|
`BehindIframeMediaPlaneAdapter` では実現できません。
|
|
15
15
|
|
|
16
|
+
## 背景プレーンの制約
|
|
17
|
+
|
|
18
|
+
BS4K/BS8K の背景は、データ放送ページが任意に選ぶ壁紙ではありません。受信機は
|
|
19
|
+
マルチメディアプレーンより後ろに、画面全体を覆う単色の背景プレーンを持ちます。
|
|
20
|
+
前面のプレーンが透明、または何も描画していない画素だけに、この色が見えます。
|
|
21
|
+
|
|
22
|
+
MMT の source of truth は LCT です。実装時の優先順位は次の通りです。
|
|
23
|
+
|
|
24
|
+
1. LCT の `Background_Color_Descriptor` があれば、その RGB 各 8 bit の値を使う。
|
|
25
|
+
2. LCT の色をまだ受け取っていない間は、アプリケーションが報告した stage の単色を互換
|
|
26
|
+
fallback として使ってよい。ただし、これはアプリケーション CSS を LCT と解釈するものではない。
|
|
27
|
+
3. どちらも得られない間の receiver-owned fallback は黒 (`#000`) とする。
|
|
28
|
+
|
|
29
|
+
この fallback は規格上の既定色ではありません。白いブラウザ canvas の露出を避けるための
|
|
30
|
+
実装上の選択です。確認に使った NHK BS8K の `8k.mmts` から抽出したファイルも、
|
|
31
|
+
`top/source/top.css` の `body` と `#vstream`、および
|
|
32
|
+
`caption/source/caption.css` の `#backscreen` と `.bs8k #container` に黒を指定しています。
|
|
33
|
+
したがって、このサンプル用に背景画像やグラデーションを追加しません。
|
|
34
|
+
|
|
35
|
+
背景の実装には、さらに次の制約があります。
|
|
36
|
+
|
|
37
|
+
- 背景プレーンは viewport 全体を覆い、映像、アプリケーション、字幕、文字スーパーより後ろに置く。
|
|
38
|
+
- LCT の色はアプリケーションやサービスの切り替え時に引き継がない。新しい LCT を受け取るまでは
|
|
39
|
+
fallback に戻す。
|
|
40
|
+
- `in-object` adapter では、runtime はアプリケーションの `html` / `body` を透明化しない。
|
|
41
|
+
放送アプリケーションが指定した背景色や背景画像は multimedia plane の描画として保持する。
|
|
42
|
+
- `external` adapter では、外部映像面を iframe 越しに見せるため application canvas を透明化する。
|
|
43
|
+
その場合は receiver 背景、外部映像、iframe を別々の sibling layer として、この順に重ねる。
|
|
44
|
+
receiver 背景と iframe を同じ wrapper に入れると、外部映像を両者の間へ合成できない。
|
|
45
|
+
application canvas を透明化する前の stage 色は receiver viewport の最背面に移し、映像領域外に
|
|
46
|
+
host アプリの背景が露出しないようにする。stage 色を取得できない間の fallback は黒とする。
|
|
47
|
+
- dark theme の host に light canvas の放送ページを埋め込む場合、iframe owner と iframe 内 root に
|
|
48
|
+
`color-scheme: only light` を明示する。Chromium は owner と iframe 内の root で used color scheme が
|
|
49
|
+
異なると、`html` / `body` の computed background が透明でも、不透明な scheme backdrop
|
|
50
|
+
(light の場合は白)を frame canvas に描画する。これは CSS の `background`、iframe の
|
|
51
|
+
`z-index`、`opacity` では除去できない。
|
|
52
|
+
- `#vstream` 等、media object の祖先が不透明な背景を描く場合、external runtime はその祖先の
|
|
53
|
+
背景だけを media object の存続中に透明化する。兄弟要素の UI は変更しない。video を iframe
|
|
54
|
+
より前へ上げて穴を作ってはならない。そうすると字幕説明など、映像より前にあるべき
|
|
55
|
+
application UI まで隠れる。
|
|
56
|
+
- `#vstream > object#av` のように media object と同じ矩形で object だけを子に持つ wrapper は、
|
|
57
|
+
背景だけでなく wrapper 自身を `opacity: 0` にする。高い `z-index` の純 media wrapper が作る
|
|
58
|
+
空の compositing layer を残さないためであり、字幕や操作 UI を含む wrapper には適用しない。
|
|
59
|
+
- 映像の位置と寸法は、どちらの adapter でも放送アプリケーションが宣言した video object に従う。
|
|
60
|
+
- 放送アプリケーション内の背景画像、要素背景、半透明 UI は multimedia plane の描画であり、
|
|
61
|
+
LCT の背景色を置き換えない。
|
|
62
|
+
- native compositor でも DOM 実装でも、LCT の単色を iframe 内の装飾要素として実装しない。
|
|
63
|
+
動画領域の切り抜きや iframe のページ遷移から独立した、受信機所有の最背面レイヤーにする。
|
|
64
|
+
|
|
65
|
+
Chromium の `color-scheme` backdrop、external media hole、DevTools での切り分けは
|
|
66
|
+
[ブラウザ iframe 合成の注意事項](browser-iframe-compositing.md)も参照してください。
|
|
67
|
+
|
|
68
|
+
根拠は `../ARIB-docs` の次の条項です。
|
|
69
|
+
|
|
70
|
+
- ARIB STD-B62 1.5版 第一編 第1部 5.2(4): 背景プレーンは最背面で、前面が透明または
|
|
71
|
+
未描画の部分に指定色を表示する。
|
|
72
|
+
- 同 5.3: MMT 方式の受信機は LCT で指定された色を背景プレーンへ描画して合成する。
|
|
73
|
+
- 同 6.1.1: 背景プレーンは 7680x4320、BT.2020 系の RGB 各 8 bit として定義される。
|
|
74
|
+
- ARIB STD-B60 1.5版 7.3.3.3 および 7.4.3.3: LCT と
|
|
75
|
+
`Background_Color_Descriptor` (`descriptor_tag=0x8002`) を定義し、24 bit を RGB 各 8 bit
|
|
76
|
+
の背景色として扱う。
|
|
77
|
+
|
|
16
78
|
## ブラウザへ組み込む最小コード
|
|
17
79
|
|
|
18
80
|
普通の動画表示領域とデータ放送 iframe を同じ viewport に置きます。
|
|
@@ -48,6 +110,9 @@ const viewport = document.querySelector<HTMLElement>('#viewport')!
|
|
|
48
110
|
const normalPlayer = document.querySelector<HTMLElement>('#normal-player')!
|
|
49
111
|
const video = document.querySelector<HTMLVideoElement>('#video')!
|
|
50
112
|
|
|
113
|
+
// dark theme の host でも、放送ページの標準 light canvas と scheme を一致させる。
|
|
114
|
+
iframe.style.colorScheme = 'light'
|
|
115
|
+
|
|
51
116
|
const host = new AribReceiverHost({
|
|
52
117
|
iframe,
|
|
53
118
|
viewport,
|
|
@@ -218,6 +283,9 @@ iframe 内の背景 < 映像 < iframe 内の字幕説明ボタン
|
|
|
218
283
|
|
|
219
284
|
D Data の字幕コンテンツはアプリケーション DOM です。`setCaptionTracks()` と
|
|
220
285
|
`pushCaption()` で iframe へ渡し、通常の放送字幕として親側へ二重描画しないでください。
|
|
286
|
+
アプリケーションが `addCaptionListener()` を登録している間は
|
|
287
|
+
`onCaptionSubscription` に要求中の component tag が通知されます。ネイティブ受信機は
|
|
288
|
+
この通知を使い、同じ字幕を通常の字幕 renderer で二重表示しないようにできます。
|
|
221
289
|
|
|
222
290
|
---
|
|
223
291
|
|
|
@@ -53,6 +53,11 @@ example `sh4`) themselves. The current document mount is used only when mapping
|
|
|
53
53
|
root-relative resources such as `/60/...`; it is not included in `baseurl`.
|
|
54
54
|
An injected `baseurl` cannot override the actual VFS scope.
|
|
55
55
|
|
|
56
|
+
The regional fields `zipcode`, `prefecture`, and `regioncode` are also exposed
|
|
57
|
+
read-only through the legacy `receiverinfo` Ureg used by broadcaster apps. The
|
|
58
|
+
host owns persistence and passes a snapshot; writes inside the app do not flow
|
|
59
|
+
back into the host's settings.
|
|
60
|
+
|
|
56
61
|
## Device identifiers
|
|
57
62
|
|
|
58
63
|
The default package combination remains `huggy`, `Huggy ARIB HTML5 Receiver`,
|
|
@@ -81,12 +86,17 @@ const host = new AribReceiverHost({
|
|
|
81
86
|
viewport,
|
|
82
87
|
onStatus: text => statusLabel.textContent = text,
|
|
83
88
|
onLifecycle(event) {
|
|
84
|
-
if (event.type === 'installed') clearApplicationTimeout()
|
|
85
|
-
if (event.type === 'navigating') armApplicationTimeout()
|
|
86
89
|
if (event.type === 'error') reportRuntimeError(event.message)
|
|
87
90
|
},
|
|
88
91
|
})
|
|
89
92
|
```
|
|
90
93
|
|
|
94
|
+
`loadApplication()` とアプリケーション内遷移で読み込まれた文書が receiver runtime を
|
|
95
|
+
導入しなかった場合は、iframe の `load` 直後に `about:blank` へ戻し、media plane を
|
|
96
|
+
通常視聴状態へ復元して `error`、`exited` の順に通知します。これにより VFS の未命中や
|
|
97
|
+
通常の 404 文書を画面へ残しません。文書の `load` 自体が発生しない要求には既定 5 秒の
|
|
98
|
+
watchdog を適用します。待機時間は `applicationLoadTimeoutMs` で変更でき、`0` を指定した
|
|
99
|
+
場合は watchdog だけを無効化します。
|
|
100
|
+
|
|
91
101
|
Lifecycle event types are `loading`, `installed`, `navigating`, `exited`,
|
|
92
102
|
`navigation-blocked`, `frame-blocked`, and `error`.
|