@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,168 @@
|
|
|
1
|
+
import { compileAudioMixProgram, type VideoProject } from '@pictor-sdk/video-core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deciding what a preview should be doing about audio.
|
|
5
|
+
*
|
|
6
|
+
* Pure, and separate from the hook that acts on it, because every rule worth
|
|
7
|
+
* getting right here is a rule about state rather than about React: whether a
|
|
8
|
+
* project needs a mix at all, whether the mix on hand is the one the document
|
|
9
|
+
* describes, whether a result that just arrived is still wanted, and whether
|
|
10
|
+
* two players have drifted far enough to be worth correcting.
|
|
11
|
+
*
|
|
12
|
+
* ## Two sources of sound, never both
|
|
13
|
+
*
|
|
14
|
+
* Without music, the video player's own audio is the preview and nothing here
|
|
15
|
+
* applies. With music, the mix covers the *whole* timeline — including the
|
|
16
|
+
* audio of the clips themselves — so playing the clips' audio as well would
|
|
17
|
+
* play everything twice. There is no state in which both are audible, and the
|
|
18
|
+
* transition between them is the thing this file exists to sequence.
|
|
19
|
+
*
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** What a preview's audio is doing. @experimental */
|
|
24
|
+
export type PreviewAudioStatus =
|
|
25
|
+
/** No music in the project. The video player's own audio is the preview. */
|
|
26
|
+
| 'source'
|
|
27
|
+
/** Music exists and the mix for *this* document is not ready. */
|
|
28
|
+
| 'preparing'
|
|
29
|
+
/** The mix on hand matches the document. Source audio is muted. */
|
|
30
|
+
| 'ready'
|
|
31
|
+
/** The mix could not be prepared. Nothing is played rather than the wrong thing. */
|
|
32
|
+
| 'failed'
|
|
33
|
+
|
|
34
|
+
/** @experimental */
|
|
35
|
+
export interface PreviewAudioDecision {
|
|
36
|
+
status: PreviewAudioStatus
|
|
37
|
+
/** The key the document wants. Empty when there is no music. */
|
|
38
|
+
wantedKey: string
|
|
39
|
+
/** Whether a job should be started for `wantedKey`. */
|
|
40
|
+
shouldPrepare: boolean
|
|
41
|
+
/** Whether the video player's own audio must be silenced. */
|
|
42
|
+
muteSourceAudio: boolean
|
|
43
|
+
/** Whether playback may run right now. */
|
|
44
|
+
canPlay: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @experimental */
|
|
48
|
+
export interface PreviewAudioInputs {
|
|
49
|
+
/** The key the current document compiles to, or '' when it has no music. */
|
|
50
|
+
wantedKey: string
|
|
51
|
+
/** The key of the mix already prepared and loaded, if any. */
|
|
52
|
+
readyKey: string | null
|
|
53
|
+
/** The key a job is currently preparing, if any. */
|
|
54
|
+
preparingKey: string | null
|
|
55
|
+
/** The key whose preparation failed, if any. */
|
|
56
|
+
failedKey: string | null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether a project needs a mixed preview at all.
|
|
61
|
+
*
|
|
62
|
+
* True when anything in the compiled program is audible *and comes from an
|
|
63
|
+
* audio item* — a project whose only sound is clip audio does not need a mix,
|
|
64
|
+
* because the video player already plays exactly that and does it in realtime.
|
|
65
|
+
*
|
|
66
|
+
* @experimental
|
|
67
|
+
*/
|
|
68
|
+
export function previewAudioNeeded(project: VideoProject): boolean {
|
|
69
|
+
return project.audio.length > 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What to do, given what is wanted and what is on hand.
|
|
74
|
+
*
|
|
75
|
+
* @experimental
|
|
76
|
+
*/
|
|
77
|
+
export function decidePreviewAudio(inputs: PreviewAudioInputs): PreviewAudioDecision {
|
|
78
|
+
const { wantedKey, readyKey, preparingKey, failedKey } = inputs
|
|
79
|
+
|
|
80
|
+
if (wantedKey === '') {
|
|
81
|
+
// No music. The video player's audio is the preview, exactly as it was
|
|
82
|
+
// before any of this existed.
|
|
83
|
+
return {
|
|
84
|
+
status: 'source',
|
|
85
|
+
wantedKey,
|
|
86
|
+
shouldPrepare: false,
|
|
87
|
+
muteSourceAudio: false,
|
|
88
|
+
canPlay: true,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (readyKey === wantedKey) {
|
|
93
|
+
return {
|
|
94
|
+
status: 'ready',
|
|
95
|
+
wantedKey,
|
|
96
|
+
shouldPrepare: false,
|
|
97
|
+
// The mix contains the clips' audio too. Leaving the source audible
|
|
98
|
+
// would play the whole timeline twice.
|
|
99
|
+
muteSourceAudio: true,
|
|
100
|
+
canPlay: true,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (failedKey === wantedKey) {
|
|
105
|
+
/*
|
|
106
|
+
Refusing rather than falling back.
|
|
107
|
+
|
|
108
|
+
The tempting fallback is "play the clip audio instead", and it is wrong:
|
|
109
|
+
the person would hear a preview missing its music, with no reliable way to
|
|
110
|
+
tell that from a project whose music is silent. A named failure is worse
|
|
111
|
+
to look at and better to act on.
|
|
112
|
+
*/
|
|
113
|
+
return {
|
|
114
|
+
status: 'failed',
|
|
115
|
+
wantedKey,
|
|
116
|
+
shouldPrepare: false,
|
|
117
|
+
muteSourceAudio: true,
|
|
118
|
+
canPlay: false,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
Preparing. A mix for an older document may still be loaded, and it must not
|
|
124
|
+
play: it is the previous edit's sound, which is the most convincing kind of
|
|
125
|
+
wrong. Source audio stays muted throughout, so the transition is silence
|
|
126
|
+
rather than a burst of un-mixed clip audio.
|
|
127
|
+
*/
|
|
128
|
+
return {
|
|
129
|
+
status: 'preparing',
|
|
130
|
+
wantedKey,
|
|
131
|
+
shouldPrepare: preparingKey !== wantedKey,
|
|
132
|
+
muteSourceAudio: true,
|
|
133
|
+
canPlay: false,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The key a project's audio compiles to, given resolved sources. @experimental */
|
|
138
|
+
export function previewProgramJson(project: VideoProject): string {
|
|
139
|
+
return JSON.stringify(compileAudioMixProgram(project))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
The drift comparison itself lives in native, not here.
|
|
144
|
+
|
|
145
|
+
It used to be a function in this file, and it was never wired to a caller —
|
|
146
|
+
measured, a clip seam left the mix 240 ms ahead of the picture and it stayed
|
|
147
|
+
there. Reimplementing it here now would be worse than that: both anchors are
|
|
148
|
+
published on one monotonic clock in native, and a JavaScript twin would have
|
|
149
|
+
no way to read them at the same instant, so the two would disagree about what
|
|
150
|
+
"at the same time" meant. See `PreviewTrace.driftCheck`.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The default tolerance.
|
|
155
|
+
*
|
|
156
|
+
* The drift this used to chase is prevented rather than corrected: `PreviewSync`
|
|
157
|
+
* holds both transports through a stall and releases them aligned, which is
|
|
158
|
+
* where all of it came from. A correcting seek during playback costs 200-340 ms
|
|
159
|
+
* on a Pixel 7 — the same size as the error it was chasing, so it oscillated
|
|
160
|
+
* between +264 ms and -198 ms — and realignment now happens while everything is
|
|
161
|
+
* held, where a seek has nothing to chase.
|
|
162
|
+
*
|
|
163
|
+
* So this is back to the tolerance the gates were written against. It is a
|
|
164
|
+
* backstop for something nobody has seen, not the mechanism.
|
|
165
|
+
*
|
|
166
|
+
* @experimental
|
|
167
|
+
*/
|
|
168
|
+
export const PREVIEW_DRIFT_TOLERANCE_US = 60_000
|