@pictor-sdk/video-android 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/LICENSE +21 -0
- package/README.md +40 -0
- package/android/CMakeLists.txt +30 -0
- package/android/build.gradle +119 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/cpp/pictor_overlaystage.cpp +372 -0
- package/android/src/main/cpp/pictor_publish.cpp +87 -0
- package/android/src/main/cpp/pictor_videosurface.cpp +735 -0
- package/android/src/main/java/dev/pictor/video/AacPriming.kt +261 -0
- package/android/src/main/java/dev/pictor/video/AudioMixer.kt +1112 -0
- package/android/src/main/java/dev/pictor/video/AudioProbe.kt +162 -0
- package/android/src/main/java/dev/pictor/video/CadenceShaderProgram.kt +337 -0
- package/android/src/main/java/dev/pictor/video/CodecLedger.kt +126 -0
- package/android/src/main/java/dev/pictor/video/CompositionSpike.kt +978 -0
- package/android/src/main/java/dev/pictor/video/Diagnostics.kt +54 -0
- package/android/src/main/java/dev/pictor/video/EncoderGate.kt +476 -0
- package/android/src/main/java/dev/pictor/video/ExportGate.kt +1974 -0
- package/android/src/main/java/dev/pictor/video/ExportHeartbeat.kt +72 -0
- package/android/src/main/java/dev/pictor/video/ExportProgress.kt +103 -0
- package/android/src/main/java/dev/pictor/video/FrameClock.kt +591 -0
- package/android/src/main/java/dev/pictor/video/FramePending.kt +181 -0
- package/android/src/main/java/dev/pictor/video/GateAudio.kt +456 -0
- package/android/src/main/java/dev/pictor/video/GateDestination.kt +445 -0
- package/android/src/main/java/dev/pictor/video/GateFrames.kt +662 -0
- package/android/src/main/java/dev/pictor/video/GateMux.kt +185 -0
- package/android/src/main/java/dev/pictor/video/ManagedSources.kt +333 -0
- package/android/src/main/java/dev/pictor/video/Media3Player.kt +934 -0
- package/android/src/main/java/dev/pictor/video/MediaStoreSave.kt +419 -0
- package/android/src/main/java/dev/pictor/video/OverlayResources.kt +169 -0
- package/android/src/main/java/dev/pictor/video/PictorVideoModule.kt +1612 -0
- package/android/src/main/java/dev/pictor/video/PictorVideoPackage.kt +26 -0
- package/android/src/main/java/dev/pictor/video/PlaybackAnchor.kt +161 -0
- package/android/src/main/java/dev/pictor/video/PreviewAudioPlayer.kt +291 -0
- package/android/src/main/java/dev/pictor/video/PreviewConfig.kt +96 -0
- package/android/src/main/java/dev/pictor/video/PreviewMix.kt +396 -0
- package/android/src/main/java/dev/pictor/video/PreviewSync.kt +374 -0
- package/android/src/main/java/dev/pictor/video/PreviewTrace.kt +478 -0
- package/android/src/main/java/dev/pictor/video/PrimingTrim.kt +337 -0
- package/android/src/main/java/dev/pictor/video/RepeatingEncoderFactory.kt +97 -0
- package/android/src/main/java/dev/pictor/video/SceneProgram.kt +169 -0
- package/android/src/main/java/dev/pictor/video/SceneProgramNative.kt +299 -0
- package/android/src/main/java/dev/pictor/video/SkiaPassThroughEffect.kt +695 -0
- package/android/src/main/java/dev/pictor/video/SurfaceAttachment.kt +110 -0
- package/android/src/main/java/dev/pictor/video/ThumbnailDecoder.kt +171 -0
- package/android/src/main/java/dev/pictor/video/VideoSurface.kt +394 -0
- package/app.plugin.js +43 -0
- package/dist/index.d.mts +3622 -0
- package/dist/index.d.ts +3622 -0
- package/dist/index.js +3737 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3625 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +82 -0
- package/plugin/gradleProperties.d.ts +19 -0
- package/plugin/gradleProperties.js +64 -0
- package/react-native.config.js +13 -0
- package/src/NativePictorVideo.ts +574 -0
- package/src/audioProbe.ts +72 -0
- package/src/compositionSpike.ts +305 -0
- package/src/convergence.ts +112 -0
- package/src/encoderGate.ts +66 -0
- package/src/exportDestination.ts +149 -0
- package/src/exportJob.ts +232 -0
- package/src/exportProgress.ts +391 -0
- package/src/exportProgressPump.ts +190 -0
- package/src/exporter.ts +655 -0
- package/src/index.ts +276 -0
- package/src/managedSources.ts +252 -0
- package/src/media3.ts +412 -0
- package/src/mediaStore.ts +177 -0
- package/src/memoryGuard.ts +118 -0
- package/src/mixSources.ts +82 -0
- package/src/overlayStaging.ts +216 -0
- package/src/playbackLifecycle.ts +137 -0
- package/src/player.ts +39 -0
- package/src/playlistSession.ts +125 -0
- package/src/previewAudioDiagnostics.ts +67 -0
- package/src/previewAudioSession.ts +168 -0
- package/src/previewCadence.ts +950 -0
- package/src/previewMix.ts +212 -0
- package/src/previewTrace.ts +348 -0
- package/src/spikeExcursions.ts +405 -0
- package/src/submissionScoring.ts +680 -0
- package/src/surfaceOwner.ts +264 -0
- package/src/surfaceRecovery.ts +121 -0
- package/src/syncTrace.ts +189 -0
- package/src/thumbnailProvider.ts +163 -0
- package/src/tracePhases.ts +217 -0
- package/src/useCompositionPlayback.ts +327 -0
- package/src/useMedia3Playback.ts +492 -0
- package/src/usePreviewAudio.ts +305 -0
- package/src/useVideoSurface.ts +282 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-wrapper-object-types --
|
|
2
|
+
* React Native's codegen recognises the upper-cased `Object` as its
|
|
3
|
+
* `UnsafeObject` type and rejects the primitive `object`. This file is a
|
|
4
|
+
* codegen spec before it is TypeScript, so the generator's spelling wins.
|
|
5
|
+
*/
|
|
6
|
+
import { TurboModuleRegistry, type TurboModule } from 'react-native'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The native surface. **Internal** until the API stabilises.
|
|
10
|
+
*
|
|
11
|
+
* Numbers, strings and plain objects only. `ExoPlayer`, `MediaItem`, `Surface`
|
|
12
|
+
* and every other Android or Media3 type stops at the Kotlin side of this
|
|
13
|
+
* boundary and never appears in a Pictor signature.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here is per frame. Frames reach the renderer through the
|
|
16
|
+
* `__pictorVideoSurface` host object on the UI runtime, so the JS thread is not
|
|
17
|
+
* in the picture's path at all; these calls are controls and a throttled
|
|
18
|
+
* snapshot.
|
|
19
|
+
*/
|
|
20
|
+
export interface Spec extends TurboModule {
|
|
21
|
+
/** Publishes `__pictorVideoSurface` on the JS runtime. */
|
|
22
|
+
installVideoSurface(): boolean
|
|
23
|
+
/**
|
|
24
|
+
* Publishes `__pictorOverlayStaging` on the JS runtime.
|
|
25
|
+
*
|
|
26
|
+
* Native chooses the directory; JS never supplies a destination.
|
|
27
|
+
*/
|
|
28
|
+
installOverlayStaging(): boolean
|
|
29
|
+
|
|
30
|
+
/** Open a source — local `android.resource:`/`file:` or remote — and render it. */
|
|
31
|
+
media3Prepare(uri: string): boolean
|
|
32
|
+
/** Diagnostics: a player with no video output, for heap attribution. */
|
|
33
|
+
media3PrepareNoSurface(uri: string): boolean
|
|
34
|
+
/** Diagnostics: point the existing player at another source. */
|
|
35
|
+
media3Rebind(uri: string): boolean
|
|
36
|
+
/** Diagnostics: end the source, keep the player. */
|
|
37
|
+
media3StopKeepingPlayer(): void
|
|
38
|
+
/**
|
|
39
|
+
* Replace the playlist. Two parallel arrays: sources, then four numbers per
|
|
40
|
+
* entry — clipping start and end in microseconds, speed in milli-units, and
|
|
41
|
+
* muted as 0 or 1.
|
|
42
|
+
*/
|
|
43
|
+
media3SetPlaylist(
|
|
44
|
+
uris: string[],
|
|
45
|
+
bounds: number[],
|
|
46
|
+
startWindow: number,
|
|
47
|
+
startPositionUs: number,
|
|
48
|
+
): boolean
|
|
49
|
+
/** Seek in the player's coordinates: window index plus offset. */
|
|
50
|
+
media3SeekToWindow(windowIndex: number, positionUs: number): void
|
|
51
|
+
/**
|
|
52
|
+
* **V3.3 gate only.** Encode one source through Transformer, once.
|
|
53
|
+
*
|
|
54
|
+
* Not an exporter and not reachable from `VideoExporter`: it takes a single
|
|
55
|
+
* URI rather than a project, writes to an adapter-private temporary, and
|
|
56
|
+
* exists to answer whether Skia can draw inside Transformer's frame processor
|
|
57
|
+
* without changing the picture. `withSkia` selects the effect chain — false is
|
|
58
|
+
* the vanilla run that establishes what the codec alone costs.
|
|
59
|
+
*
|
|
60
|
+
* Returns a JSON string rather than a shaped object because the shape is a
|
|
61
|
+
* gate's report, not an API, and freezing it here would be pretending
|
|
62
|
+
* otherwise.
|
|
63
|
+
*
|
|
64
|
+
* @experimental
|
|
65
|
+
*/
|
|
66
|
+
exportGateRun(uri: string, withSkia: boolean, timeoutSeconds: number): string
|
|
67
|
+
/**
|
|
68
|
+
* **V3.3 gate only.** Execute one serialized, versioned `VideoScene`.
|
|
69
|
+
*
|
|
70
|
+
* Native receives a scene, never a project: ids, indices and numbers that
|
|
71
|
+
* `resolveVideoScene` already settled. It validates the schema version, the
|
|
72
|
+
* command order and every identity before Transformer starts, and refuses
|
|
73
|
+
* anything it cannot execute faithfully rather than skipping it.
|
|
74
|
+
*
|
|
75
|
+
* @experimental
|
|
76
|
+
*/
|
|
77
|
+
exportGateRunScene(
|
|
78
|
+
uri: string,
|
|
79
|
+
sceneJson: string,
|
|
80
|
+
timeoutSeconds: number,
|
|
81
|
+
/** `-1` never fails; `0` fails after GL init before the first frame; `n` after n frames. */
|
|
82
|
+
failAfterFrames: number,
|
|
83
|
+
): string
|
|
84
|
+
/**
|
|
85
|
+
* **V3.3 gate only.** Execute a compiled scene *program* over a sequence.
|
|
86
|
+
*
|
|
87
|
+
* One document for the whole export: sorted, contiguous, half-open segments
|
|
88
|
+
* compiled from the project's clip placements and overlay spans. Native
|
|
89
|
+
* validates coverage, then selects a segment per frame by binary search — a
|
|
90
|
+
* lookup, not an interpretation.
|
|
91
|
+
*
|
|
92
|
+
* @experimental
|
|
93
|
+
*/
|
|
94
|
+
exportGateRunProgram(uris: string[], programJson: string, timeoutSeconds: number): string
|
|
95
|
+
/** **V3.3 gate only.** Delete every temporary the gate created. @experimental */
|
|
96
|
+
/**
|
|
97
|
+
* **V3.3 gate only.** A sequence whose clipping and speed TypeScript chose.
|
|
98
|
+
*
|
|
99
|
+
* `itemsJson` is `{ uri, startMs, endMs, speed, durationUs?, forceAudioTrack? }[]`.
|
|
100
|
+
* Native hands these to Media3 and computes none of them: which part of a
|
|
101
|
+
* source plays, and how fast, is timeline interpretation and belongs to the
|
|
102
|
+
* compiler.
|
|
103
|
+
*
|
|
104
|
+
* `forceAudioTrack` is sequence-level and honoured if any item sets it. A
|
|
105
|
+
* source with no audio track fails outright at any rate other than 1 without
|
|
106
|
+
* it — Media3 drives its speed path through audio.
|
|
107
|
+
*
|
|
108
|
+
* @experimental
|
|
109
|
+
*/
|
|
110
|
+
exportGateRunItems(itemsJson: string, programJson: string, timeoutSeconds: number): string
|
|
111
|
+
exportGateReleaseTemporary(): number
|
|
112
|
+
/**
|
|
113
|
+
* **V3.3 gate only.** Compare two files in **display space**.
|
|
114
|
+
*
|
|
115
|
+
* Display space is the only space the question means anything in: a file
|
|
116
|
+
* storing 1080×1920 with a rotation flag and one storing 1920×1080 with the
|
|
117
|
+
* orientation baked in display identically, and comparing stored dimensions
|
|
118
|
+
* would call that a difference.
|
|
119
|
+
*
|
|
120
|
+
* Returns a JSON report of per-timestamp statistics. It states no verdict —
|
|
121
|
+
* the threshold is frozen by the caller, from the control, before the thing
|
|
122
|
+
* being tested is looked at.
|
|
123
|
+
*
|
|
124
|
+
* @experimental
|
|
125
|
+
*/
|
|
126
|
+
exportGateCompare(leftUri: string, rightUri: string, timestampsUs: number[]): string
|
|
127
|
+
/**
|
|
128
|
+
* **V3.3 gate only.** Mean colour of a central region, per timestamp.
|
|
129
|
+
*
|
|
130
|
+
* Reports numbers and draws no conclusion. Which source a colour implies, and
|
|
131
|
+
* which frame of it, is arithmetic the TypeScript oracle owns.
|
|
132
|
+
*
|
|
133
|
+
* @experimental
|
|
134
|
+
*/
|
|
135
|
+
exportGateSample(uri: string, timestampsUs: number[]): string
|
|
136
|
+
/**
|
|
137
|
+
* **V3.3 gate only.** Muxed sample timestamps and per-track durations.
|
|
138
|
+
*
|
|
139
|
+
* A frame count and a whole-file duration cannot tell a held tail frame from a
|
|
140
|
+
* correctly-timed one, or a gap from a slow rate. The written timestamps can.
|
|
141
|
+
* Reports them; judges nothing.
|
|
142
|
+
*
|
|
143
|
+
* @experimental
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* **V3.4 gate only.** Mean and extremes over caller-chosen rectangles.
|
|
147
|
+
*
|
|
148
|
+
* `probesJson` is `{ timeUs, x, y, w, h }[]`. The fixed centre region
|
|
149
|
+
* {@link exportGateSample} uses straddles a transformed clip's edge and
|
|
150
|
+
* averages source pixels with cleared ones; the caller has the corner oracle
|
|
151
|
+
* and passes rectangles it has confirmed are covered.
|
|
152
|
+
*
|
|
153
|
+
* @experimental
|
|
154
|
+
*/
|
|
155
|
+
/**
|
|
156
|
+
* **V3.5 gate only.** Decoded audio: loudness and spectrum per window.
|
|
157
|
+
*
|
|
158
|
+
* A pitch-shifting speed change and a pitch-preserving one produce files whose
|
|
159
|
+
* duration and sample count are identical, so counting cannot tell them apart.
|
|
160
|
+
* Also reports AAC encoder delay, padding and the first presentation time,
|
|
161
|
+
* which a seam alignment check needs.
|
|
162
|
+
*
|
|
163
|
+
* @experimental
|
|
164
|
+
*/
|
|
165
|
+
exportGateAudio(uri: string, windowsJson: string): string
|
|
166
|
+
exportGateProbe(uri: string, probesJson: string): string
|
|
167
|
+
exportGateTracks(uri: string): string
|
|
168
|
+
/**
|
|
169
|
+
* **V3.4 gate only.** One decoded frame as base64 PNG, in display space.
|
|
170
|
+
*
|
|
171
|
+
* @experimental
|
|
172
|
+
*/
|
|
173
|
+
exportGateFramePng(uri: string, timeUs: number): string
|
|
174
|
+
/**
|
|
175
|
+
* **V3.4 gate only.** Encode a still through the treatment's encoder.
|
|
176
|
+
*
|
|
177
|
+
* A comparison between two renderers is only about the renderers if
|
|
178
|
+
* everything after them is the same, so the reference is encoded and decoded
|
|
179
|
+
* exactly as the treatment is.
|
|
180
|
+
*
|
|
181
|
+
* @experimental
|
|
182
|
+
*/
|
|
183
|
+
exportGateEncodeStill(pngBase64: string, frames: number): string
|
|
184
|
+
/**
|
|
185
|
+
* **V3.4 gate only.** Geometry and interior colour, measured separately.
|
|
186
|
+
*
|
|
187
|
+
* A maximum channel delta is near-useless at a transformed edge: one row of
|
|
188
|
+
* pixels on the far side of a bright/black boundary reads 255 while the
|
|
189
|
+
* picture is right. Returns mask overlap, edge displacement and interior
|
|
190
|
+
* colour error, and draws no conclusion about any of them.
|
|
191
|
+
*
|
|
192
|
+
* @experimental
|
|
193
|
+
*/
|
|
194
|
+
exportGateGeometry(
|
|
195
|
+
leftUri: string,
|
|
196
|
+
leftUs: number,
|
|
197
|
+
rightUri: string,
|
|
198
|
+
rightUs: number,
|
|
199
|
+
maskThreshold: number,
|
|
200
|
+
): string
|
|
201
|
+
/**
|
|
202
|
+
* **V3.3 gate only.** Per-sample checksums of the compressed video payload.
|
|
203
|
+
*
|
|
204
|
+
* Transmuxing must move encoded bytes and change only timestamps and container
|
|
205
|
+
* metadata. Decoded pixels cannot show that — a re-encode that happens to look
|
|
206
|
+
* identical would pass — so the payload itself is checksummed.
|
|
207
|
+
*
|
|
208
|
+
* @experimental
|
|
209
|
+
*/
|
|
210
|
+
exportGateSampleHashes(uri: string): string
|
|
211
|
+
/**
|
|
212
|
+
* **V3.3 gate only.** One Transformer job per clip, then a transmux concat.
|
|
213
|
+
*
|
|
214
|
+
* `shardsJson` is `{ uri, startMs, endMs, speed, timelineOffsetUs,
|
|
215
|
+
* forceAudioTrack, frameDropFps?, encoderRepeatUs?, emitsPerInput?,
|
|
216
|
+
* outputPtsUs?, cadenceFailAfterEmits? }[]`. The cadence arrays come straight
|
|
217
|
+
* from `resampleClip`; native walks them and computes no grid arithmetic. Native adds `timelineOffsetUs` to each local
|
|
218
|
+
* presentation timestamp and does segment lookup; it computes no placement,
|
|
219
|
+
* duration or rate. Intermediates are deleted on every path.
|
|
220
|
+
*
|
|
221
|
+
* @experimental
|
|
222
|
+
*/
|
|
223
|
+
exportGateRunShards(
|
|
224
|
+
shardsJson: string,
|
|
225
|
+
programJson: string,
|
|
226
|
+
timeoutSeconds: number,
|
|
227
|
+
overlaysJson: string,
|
|
228
|
+
): string
|
|
229
|
+
/**
|
|
230
|
+
* **V3.4 gate only.** Write raw RGBA bytes to a sidecar and checksum them.
|
|
231
|
+
*
|
|
232
|
+
* Pixels never travel in scene JSON. This stages them beside it and returns
|
|
233
|
+
* the path, length and CRC32 a descriptor must declare.
|
|
234
|
+
*
|
|
235
|
+
* @experimental
|
|
236
|
+
*/
|
|
237
|
+
/**
|
|
238
|
+
* **V3.5 gate only.** Video shards, an audio timeline, then a muxer copy.
|
|
239
|
+
*
|
|
240
|
+
* The two pipelines never meet until the last step, and that step decodes
|
|
241
|
+
* nothing: `MediaMuxer` writes both sets of encoded samples unchanged, so
|
|
242
|
+
* neither takes a second generation.
|
|
243
|
+
*
|
|
244
|
+
* @experimental
|
|
245
|
+
*/
|
|
246
|
+
/**
|
|
247
|
+
* Run an export off the bridge so it can be cancelled while it runs.
|
|
248
|
+
*
|
|
249
|
+
* Resolves with the same JSON outcome the blocking call returns. @experimental
|
|
250
|
+
*/
|
|
251
|
+
exportRunProjectAsync(
|
|
252
|
+
jobId: string,
|
|
253
|
+
shardsJson: string,
|
|
254
|
+
programJson: string,
|
|
255
|
+
overlaysJson: string,
|
|
256
|
+
mixProgramJson: string,
|
|
257
|
+
mixUrisJson: string,
|
|
258
|
+
timeoutSeconds: number,
|
|
259
|
+
/** `""` for a temporary, or `{kind:'file',path,overwrite}`. @experimental */
|
|
260
|
+
destinationJson: string,
|
|
261
|
+
): Promise<string>
|
|
262
|
+
/**
|
|
263
|
+
* Save a finished export into the device's video collection.
|
|
264
|
+
*
|
|
265
|
+
* Inserts pending, streams, verifies, then publishes. Settles once: the
|
|
266
|
+
* published `content:` uri, or an error with the pending row already
|
|
267
|
+
* deleted. @experimental
|
|
268
|
+
*/
|
|
269
|
+
mediaSaveAsync(jobId: string, sourceUri: string): Promise<string>
|
|
270
|
+
/**
|
|
271
|
+
* Offer a saved video to the system share sheet.
|
|
272
|
+
*
|
|
273
|
+
* The durable `content:` uri only, with a read grant and nothing else.
|
|
274
|
+
* Returns what the intent carried, so a gate can inspect it. @experimental
|
|
275
|
+
*/
|
|
276
|
+
mediaShare(uri: string): string
|
|
277
|
+
/** Ask a running save to stop. Idempotent. @experimental */
|
|
278
|
+
mediaSaveCancel(jobId: string): string
|
|
279
|
+
/** Delete this app's own stale pending rows. Never a published item. @experimental */
|
|
280
|
+
mediaSaveSweepPending(): string
|
|
281
|
+
/** Confirm a saved item still opens. @experimental */
|
|
282
|
+
mediaSaveReadable(uri: string): string
|
|
283
|
+
/**
|
|
284
|
+
* Release one temporary output this module produced. Idempotent.
|
|
285
|
+
*
|
|
286
|
+
* Refuses any path it did not create, so it cannot delete a caller's file.
|
|
287
|
+
* @experimental
|
|
288
|
+
*/
|
|
289
|
+
exportReleaseTemporaryOutput(uri: string): string
|
|
290
|
+
/**
|
|
291
|
+
* Remove `.pictor-staging-*.tmp` orphans a killed process left behind.
|
|
292
|
+
*
|
|
293
|
+
* Positively identified by prefix and suffix; never a wildcard. @experimental
|
|
294
|
+
*/
|
|
295
|
+
exportSweepStagingOrphans(directory: string): string
|
|
296
|
+
/** Counted work plus the beat total, for the progress pump. @experimental */
|
|
297
|
+
exportProgressSnapshot(): string
|
|
298
|
+
/**
|
|
299
|
+
* What a picked audio file actually contains.
|
|
300
|
+
*
|
|
301
|
+
* Duration, audio-track presence, seekability and MIME, read by opening the
|
|
302
|
+
* file. The host asks this before a sound may enter a project: the mix
|
|
303
|
+
* compiler has no decoder and believes whatever the document declares, and a
|
|
304
|
+
* document that claimed audio a file lacked cost a whole export. Returns
|
|
305
|
+
* `{ok, durationUs, hasAudioTrack, seekable, mimeType}` or `{ok:false,
|
|
306
|
+
* reason}` — never throws, because an unreadable pick is an answer.
|
|
307
|
+
* @experimental
|
|
308
|
+
*/
|
|
309
|
+
/**
|
|
310
|
+
* Prepare the audio a preview plays, off the JS thread.
|
|
311
|
+
*
|
|
312
|
+
* The same mixer an export runs, over the same compiled program. Resolves
|
|
313
|
+
* `{ok, uri, key, cached, byteLength}`, or `{ok:false, superseded}` when a
|
|
314
|
+
* newer request replaced it — which a caller must never publish.
|
|
315
|
+
* @experimental
|
|
316
|
+
*/
|
|
317
|
+
previewMixPrepare(
|
|
318
|
+
jobId: string,
|
|
319
|
+
programJson: string,
|
|
320
|
+
urisJson: string,
|
|
321
|
+
): Promise<string>
|
|
322
|
+
/**
|
|
323
|
+
* Load a prepared mix into the mixed-audio player, stamped with an epoch.
|
|
324
|
+
*
|
|
325
|
+
* The epoch is how a start or a seek proves it belongs to the mix currently
|
|
326
|
+
* playing; one carrying an older epoch is dropped. @experimental
|
|
327
|
+
*/
|
|
328
|
+
previewAudioLoad(uri: string, epoch: number): string
|
|
329
|
+
/** Start, if the epoch still matches. @experimental */
|
|
330
|
+
previewAudioPlay(epoch: number): string
|
|
331
|
+
/** @experimental */
|
|
332
|
+
previewAudioPause(): string
|
|
333
|
+
/** Seek to a global timeline position, if the epoch still matches. @experimental */
|
|
334
|
+
previewAudioSeek(positionUs: number, epoch: number): string
|
|
335
|
+
/** Preview rate. Clip speed is already compiled into the mix. @experimental */
|
|
336
|
+
previewAudioSetRate(rate: number): string
|
|
337
|
+
/** Position and the wall clock it was sampled at, for drift. @experimental */
|
|
338
|
+
previewAudioState(): string
|
|
339
|
+
/**
|
|
340
|
+
* Silence the video player's own audio while a mixed preview plays.
|
|
341
|
+
*
|
|
342
|
+
* The mix contains the clips' audio too, so both audible plays the timeline
|
|
343
|
+
* twice. @experimental
|
|
344
|
+
*/
|
|
345
|
+
previewMuteSourceAudio(muted: boolean): string
|
|
346
|
+
/** @experimental */
|
|
347
|
+
previewAudioRelease(): string
|
|
348
|
+
/**
|
|
349
|
+
* Gate-only: encode a fixed, natively generated PCM fixture and describe the
|
|
350
|
+
* result — access units, timestamps, CSD, payload hashes and decoded metrics.
|
|
351
|
+
*
|
|
352
|
+
* The inputs are formulas rather than files, so a baseline taken before a
|
|
353
|
+
* change to the encoder loop is comparable with one taken after it.
|
|
354
|
+
* @experimental
|
|
355
|
+
*/
|
|
356
|
+
encoderGateRun(fixture: string, maxInputSize: number): string
|
|
357
|
+
/** Gate-only: where samples go, stage by stage, for one marker source. @experimental */
|
|
358
|
+
primingAttribution(): string
|
|
359
|
+
/** Gate-only: the file-measurable Music gates, in one run. @experimental */
|
|
360
|
+
musicGates(): string
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Gate-only: mix the marker fixture at a chosen timeline start, decode the
|
|
364
|
+
* result and report where the markers actually landed. @experimental
|
|
365
|
+
*/
|
|
366
|
+
previewAlignmentGate(startUs: number): string
|
|
367
|
+
|
|
368
|
+
/** Gate-only: arm the paired player trace and sample every `intervalMs`. @experimental */
|
|
369
|
+
previewTraceArm(on: boolean, intervalMs: number): string
|
|
370
|
+
/** Gate-only: label the trace so a harness can find its own cases. @experimental */
|
|
371
|
+
previewTraceMark(label: string): string
|
|
372
|
+
/** Gate-only: drain trace events from a sequence number, paged. @experimental */
|
|
373
|
+
previewTraceDrain(fromSeq: number, limit: number): string
|
|
374
|
+
/**
|
|
375
|
+
* Compare both players on one clock and correct the mix past a tolerance.
|
|
376
|
+
*
|
|
377
|
+
* Native because that is where both anchors are published; measuring across
|
|
378
|
+
* the bridge would carry the hop's own jitter into the answer. @experimental
|
|
379
|
+
*/
|
|
380
|
+
previewDriftCheck(toleranceUs: number): string
|
|
381
|
+
/**
|
|
382
|
+
* Stop playback for a host reason — background, picker, unmount.
|
|
383
|
+
*
|
|
384
|
+
* Overrides any automatic resume the stall coordinator is holding, so
|
|
385
|
+
* playback cannot restart at a moment nobody asked for. @experimental
|
|
386
|
+
*/
|
|
387
|
+
previewSyncStop(reason: string): string
|
|
388
|
+
/** What the stall coordinator has done, for assertions. @experimental */
|
|
389
|
+
previewSyncCounters(): string
|
|
390
|
+
/**
|
|
391
|
+
* Frames offered, queued and drawn.
|
|
392
|
+
*
|
|
393
|
+
* Read-only accounting, available in release, so the question "is the trace
|
|
394
|
+
* disturbing the picture?" can be asked without the trace. @experimental
|
|
395
|
+
*/
|
|
396
|
+
previewFramePipeline(): string
|
|
397
|
+
/**
|
|
398
|
+
* Gate-only: the shared-clock playback spike.
|
|
399
|
+
*
|
|
400
|
+
* `spikeStart` takes `{items:[{uri,startUs,endUs,speed}], mixUri, width,
|
|
401
|
+
* height}` straight from `playlistOf()`, so the compiler decides the timing
|
|
402
|
+
* and native maps it onto media items without interpreting the project.
|
|
403
|
+
*/
|
|
404
|
+
spikeStart(specJson: string): string
|
|
405
|
+
spikePlay(): string
|
|
406
|
+
spikePause(): string
|
|
407
|
+
spikeSeek(positionUs: number): string
|
|
408
|
+
spikeClaim(): string
|
|
409
|
+
spikeState(): string
|
|
410
|
+
spikeDrain(fromSeq: number, limit: number): string
|
|
411
|
+
spikeRelease(): string
|
|
412
|
+
/**
|
|
413
|
+
* Gate-only: turn parts of the music preview off, to bisect a failure.
|
|
414
|
+
*
|
|
415
|
+
* `{loadPlayer, startMix, positionPump, coordinator}`, all true in
|
|
416
|
+
* production. @experimental
|
|
417
|
+
*/
|
|
418
|
+
previewConfigSet(json: string): string
|
|
419
|
+
/** What the configuration currently is. @experimental */
|
|
420
|
+
previewConfigGet(): string
|
|
421
|
+
/** Every codec mix preparation owns, and what became of it. @experimental */
|
|
422
|
+
previewCodecLedger(): string
|
|
423
|
+
/** Stop whatever is preparing. @experimental */
|
|
424
|
+
previewMixCancel(): string
|
|
425
|
+
/** The cache key for a program, without preparing it. @experimental */
|
|
426
|
+
previewMixKey(programJson: string, urisJson: string): string
|
|
427
|
+
/** Drop one prepared mix. @experimental */
|
|
428
|
+
previewMixRelease(key: string): string
|
|
429
|
+
/** Keep only these keys; remove the rest and every partial. @experimental */
|
|
430
|
+
previewMixSweep(keepKeysJson: string): string
|
|
431
|
+
/** What is prepared, for assertions. @experimental */
|
|
432
|
+
previewMixLedger(): string
|
|
433
|
+
/**
|
|
434
|
+
* Copy a picked source into app-owned durable storage.
|
|
435
|
+
*
|
|
436
|
+
* A picker returns a reference, not a file: a `content://` grant dies with
|
|
437
|
+
* the activity result and a cache copy dies whenever Android wants storage
|
|
438
|
+
* back. The project persists what this returns, never what a picker did.
|
|
439
|
+
* Content-addressed, so the same song added twice is stored once. Returns
|
|
440
|
+
* `{ok, uri, token, byteLength, sha256, created}` or `{ok:false, reason}`.
|
|
441
|
+
* @experimental
|
|
442
|
+
*/
|
|
443
|
+
ingestManagedSource(uri: string): string
|
|
444
|
+
/** Drop one managed source by token. @experimental */
|
|
445
|
+
releaseManagedSource(token: string): string
|
|
446
|
+
/**
|
|
447
|
+
* Remove every managed source not named in `keepTokensJson`, and every
|
|
448
|
+
* partial a killed process left. The only thing that deletes media.
|
|
449
|
+
* @experimental
|
|
450
|
+
*/
|
|
451
|
+
sweepManagedSources(keepTokensJson: string): string
|
|
452
|
+
/**
|
|
453
|
+
* Every filename in the managed directory, including partials and strays.
|
|
454
|
+
*
|
|
455
|
+
* Read-only accounting. `managedSourceLedger` reports only well-formed
|
|
456
|
+
* sources, which cannot show what a sweep left behind. @experimental
|
|
457
|
+
*/
|
|
458
|
+
managedSourceList(): string
|
|
459
|
+
/** Gate-only: sweep with an unparsed keep list. @experimental */
|
|
460
|
+
managedSourceGateSweepRaw(rawKeep: string): string
|
|
461
|
+
/** Gate-only: seed a file with an exact name, to exercise the sweep. @experimental */
|
|
462
|
+
managedSourceGateSeed(name: string, bytes: number): string
|
|
463
|
+
/** Tokens and sizes of what is stored, for assertions. @experimental */
|
|
464
|
+
managedSourceLedger(): string
|
|
465
|
+
probeAudioSource(uri: string): string
|
|
466
|
+
/** Confirm the destination is closed and parseable. @experimental */
|
|
467
|
+
exportProbeOutput(uri: string): string
|
|
468
|
+
/** Gate-only: hold inside the media copy, so a kill can land there. @experimental */
|
|
469
|
+
exportGateHoldMediaCopy(enabled: boolean): string
|
|
470
|
+
/** Gate-only: wedge the cadence program after N frames. -1 is off. @experimental */
|
|
471
|
+
exportGateStallAfterFrames(frames: number): string
|
|
472
|
+
/**
|
|
473
|
+
* Arm the gate-only controls. Everything gate-only is inert until this is
|
|
474
|
+
* called, and the production exporter never calls it. @experimental
|
|
475
|
+
*/
|
|
476
|
+
exportGateEnableDiagnostics(enabled: boolean): string
|
|
477
|
+
/** Gate-only: fail publication at a chosen point. @experimental */
|
|
478
|
+
exportGatePublishFault(mode: string): string
|
|
479
|
+
/** Gate-only: does this directory's filesystem have an atomic no-replace? @experimental */
|
|
480
|
+
exportGateProbeNoReplace(directory: string): string
|
|
481
|
+
/** Gate-only: size and CRC32, to prove a file did or did not change. @experimental */
|
|
482
|
+
exportGateFileDigest(path: string): string
|
|
483
|
+
/** Gate-only: which temporaries are still registered, by name. @experimental */
|
|
484
|
+
exportGateLiveTemporaries(): string
|
|
485
|
+
/** Gate-only: remove a file, so a case can start from a known state. @experimental */
|
|
486
|
+
exportGateDeleteFile(path: string): string
|
|
487
|
+
/** Gate-only: a writable directory that is not the gallery. @experimental */
|
|
488
|
+
exportGateExternalFilesDir(): string
|
|
489
|
+
/** Gate-only: the current heartbeat epoch. @experimental */
|
|
490
|
+
exportGateBeatEpoch(): string
|
|
491
|
+
/** Gate-only: offer a beat for `epoch`; reports acceptance. @experimental */
|
|
492
|
+
exportGateTestBeat(source: string, epoch: number): string
|
|
493
|
+
/** Gate-only: enable sample hashing for conformance checks. @experimental */
|
|
494
|
+
exportGateProvenance(enabled: boolean): string
|
|
495
|
+
/** Heartbeat counts and the maximum gap per source. @experimental */
|
|
496
|
+
exportGateHeartbeats(): string
|
|
497
|
+
/** Gate-only: started/completed/failed/released accounting. @experimental */
|
|
498
|
+
exportGateAccounting(): string
|
|
499
|
+
/** Stop `jobId` if it is the one running. Idempotent. @experimental */
|
|
500
|
+
exportCancelJob(jobId: string): string
|
|
501
|
+
exportGateRunProject(
|
|
502
|
+
shardsJson: string,
|
|
503
|
+
programJson: string,
|
|
504
|
+
overlaysJson: string,
|
|
505
|
+
mixProgramJson: string,
|
|
506
|
+
mixUrisJson: string,
|
|
507
|
+
timeoutSeconds: number,
|
|
508
|
+
destinationJson: string,
|
|
509
|
+
): string
|
|
510
|
+
/**
|
|
511
|
+
* **V3.5 gate only.** Mix a compiled `AudioMixProgram` and encode it once.
|
|
512
|
+
*
|
|
513
|
+
* `urisJson` maps span id to a host-resolved URI. Native reads only the plan:
|
|
514
|
+
* no audio kinds, no duration policy, no project.
|
|
515
|
+
*
|
|
516
|
+
* @experimental
|
|
517
|
+
*/
|
|
518
|
+
exportGateMixAudio(programJson: string, urisJson: string): string
|
|
519
|
+
/**
|
|
520
|
+
* **V3.5 gate only.** Decode one window and report every stage.
|
|
521
|
+
*
|
|
522
|
+
* Running the identical window at 1x and another rate settles which stage is
|
|
523
|
+
* responsible: matching cropped counts with differing stretched counts point
|
|
524
|
+
* at the stretch, differing cropped counts at the decode.
|
|
525
|
+
*
|
|
526
|
+
* @experimental
|
|
527
|
+
*/
|
|
528
|
+
exportGateDecodeProbe(uri: string, fromUs: number, toUs: number, speed: number): string
|
|
529
|
+
/** Per-window dominant-frequency trajectory and harmonic band energies. @experimental */
|
|
530
|
+
exportGateTrajectory(uri: string, spec: string): string
|
|
531
|
+
/** Copy a gate output to external files, for the human gates. @experimental */
|
|
532
|
+
exportGateStage(uri: string, name: string): string
|
|
533
|
+
/** Measure this encoder configuration's priming from a burst at `at`. @experimental */
|
|
534
|
+
exportGatePrimingProbe(rate: number, channels: number, bitrate: number, at: number): string
|
|
535
|
+
/** Decode a window under a simulated decoder warm-up requirement. @experimental */
|
|
536
|
+
exportGatePrerollProbe(uri: string, fromUs: number, toUs: number, warmupUs: number): string
|
|
537
|
+
/**
|
|
538
|
+
* **V3.5 gate only.** Run the time-stretch alone on synthetic PCM.
|
|
539
|
+
*
|
|
540
|
+
* @experimental
|
|
541
|
+
*/
|
|
542
|
+
exportGateStretchProbe(rate: number, channels: number, frames: number, speed: number): string
|
|
543
|
+
exportGateStageResource(base64: string, width: number, height: number): string
|
|
544
|
+
media3Play(): void
|
|
545
|
+
media3Pause(): void
|
|
546
|
+
/** Microseconds, like every other time in Pictor. */
|
|
547
|
+
media3Seek(positionUs: number): void
|
|
548
|
+
media3SetRate(rate: number): void
|
|
549
|
+
/** Media3's own recovery after a source failure: prepare again. */
|
|
550
|
+
media3Retry(): void
|
|
551
|
+
media3Release(): void
|
|
552
|
+
/** A throttled snapshot: position, size, counters and any source error. */
|
|
553
|
+
media3State(): Object
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Start decoding one still frame. Returns a token to poll or cancel with.
|
|
557
|
+
*
|
|
558
|
+
* Not a Promise: a strip issues and abandons requests in bursts, and a
|
|
559
|
+
* rejected Promise per abandoned request is noisy and hard to cancel. A
|
|
560
|
+
* token the caller owns makes "stop this one" expressible.
|
|
561
|
+
*/
|
|
562
|
+
thumbnailRequest(uri: string, sourceTimeUs: number, widthPx: number): number
|
|
563
|
+
thumbnailCancel(token: number): void
|
|
564
|
+
/** Base64 JPEG, or null while it is still decoding. Consumes the result. */
|
|
565
|
+
thumbnailTake(token: number): string | null
|
|
566
|
+
thumbnailError(token: number): string | null
|
|
567
|
+
thumbnailRunning(token: number): boolean
|
|
568
|
+
thumbnailReleaseSource(uri: string): void
|
|
569
|
+
thumbnailReleaseAll(): void
|
|
570
|
+
/** `{ decoded, cancelled, failed, running, retrievers }`. */
|
|
571
|
+
thumbnailCounters(): Object
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export default TurboModuleRegistry.get<Spec>('PictorVideo')
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AudioProbe } from '@pictor-sdk/video-core'
|
|
2
|
+
import { requireNative } from './player'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reading what a picked audio file actually contains.
|
|
6
|
+
*
|
|
7
|
+
* The structural half of {@link AudioProbe} lives in `@pictor-sdk/video-core`,
|
|
8
|
+
* which has no decoder and never will; this is the adapter that can answer it.
|
|
9
|
+
* A host calls this once, before adding, and passes the result to
|
|
10
|
+
* `acceptAudioSource`.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here identifies the file. The display name is the host's to carry —
|
|
13
|
+
* a picker URI embeds an id and sometimes a user's own folder names.
|
|
14
|
+
*
|
|
15
|
+
* @experimental
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** A probe that failed, with a reason fit for a log rather than a person. @experimental */
|
|
19
|
+
export interface AudioProbeFailure {
|
|
20
|
+
ok: false
|
|
21
|
+
/** Why the file could not be read. Diagnostic, not a user-facing sentence. */
|
|
22
|
+
reason: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What native learned, minus the display name only the host knows. @experimental */
|
|
26
|
+
export type AudioProbeResult = ({ ok: true } & Omit<AudioProbe, 'displayName'>) | AudioProbeFailure
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Open `uri` and report its audio.
|
|
30
|
+
*
|
|
31
|
+
* Never throws for an unreadable file: a pick that cannot be read is an answer,
|
|
32
|
+
* and a host that had to wrap this in a `try` would be handling a normal
|
|
33
|
+
* outcome as an exception. It throws only if the adapter is not installed,
|
|
34
|
+
* which is a wiring mistake rather than a file.
|
|
35
|
+
*
|
|
36
|
+
* @experimental
|
|
37
|
+
*/
|
|
38
|
+
export function probeAudioSource(uri: string): AudioProbeResult {
|
|
39
|
+
let raw: string
|
|
40
|
+
try {
|
|
41
|
+
raw = requireNative().probeAudioSource(uri)
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let parsed: unknown
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(raw)
|
|
49
|
+
} catch {
|
|
50
|
+
return { ok: false, reason: 'probe returned unparseable JSON' }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const value = parsed as Partial<AudioProbe> & { ok?: boolean; reason?: string }
|
|
54
|
+
if (value.ok !== true) {
|
|
55
|
+
return { ok: false, reason: typeof value.reason === 'string' ? value.reason : 'probe failed' }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
Normalised here, not trusted as-is.
|
|
60
|
+
|
|
61
|
+
Everything downstream treats these as decided facts — `hasAudioTrack` ends
|
|
62
|
+
up in `metadata.hasAudio`, which is what the encoder is told. A missing or
|
|
63
|
+
malformed field must not arrive as `undefined` and read as permission.
|
|
64
|
+
*/
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
durationUs: Number.isFinite(value.durationUs) ? Number(value.durationUs) : 0,
|
|
68
|
+
hasAudioTrack: value.hasAudioTrack === true,
|
|
69
|
+
seekable: value.seekable === true,
|
|
70
|
+
mimeType: typeof value.mimeType === 'string' ? value.mimeType : '',
|
|
71
|
+
}
|
|
72
|
+
}
|