Instrument video probe with raw-value metrics for bitrate/HDR validation

This commit is contained in:
vineyardbovines
2026-06-25 18:10:11 -04:00
parent 3f2c106ecb
commit 127d0288b4
4 changed files with 56 additions and 4 deletions
+17
View File
@@ -252,6 +252,23 @@ export type Events = {
'composer:image:edit': { 'composer:image:edit': {
platform: Platform['OS'] platform: Platform['OS']
} }
'composer:video:probe': {
mimeType: string
codec: string
width: number
height: number
duration: number
bitrate: number
fileSize: number
hasAudio: boolean
frameRate: number
rotation: number
isHDR: boolean
wouldCompress: boolean
}
'composer:video:probeFailed': {
safeMessage: string
}
'composerPrompt:press': {} 'composerPrompt:press': {}
'composerPrompt:camera:press': {} 'composerPrompt:camera:press': {}
'composerPrompt:gallery:press': {} 'composerPrompt:gallery:press': {}
+12 -2
View File
@@ -6,7 +6,11 @@ import {
VIDEO_MAX_SIZE, VIDEO_MAX_SIZE,
} from '#/lib/constants' } from '#/lib/constants'
import {logger} from '#/logger' import {logger} from '#/logger'
import {compress, probe} from '../../../../modules/expo-bluesky-video-compress' import {
compress,
probe,
type VideoMetadata,
} from '../../../../modules/expo-bluesky-video-compress'
import { import {
COMPRESSION_MAX_DIMENSION, COMPRESSION_MAX_DIMENSION,
COMPRESSION_PASSTHROUGH_BITRATE, COMPRESSION_PASSTHROUGH_BITRATE,
@@ -19,6 +23,8 @@ export async function compressVideo(
opts?: { opts?: {
signal?: AbortSignal signal?: AbortSignal
onProgress?: (progress: number) => void onProgress?: (progress: number) => void
onProbe?: (metadata: VideoMetadata, wouldCompress: boolean) => void
onProbeFailed?: (error: unknown) => void
}, },
): Promise<CompressedVideo> { ): Promise<CompressedVideo> {
if (file.mimeType === 'image/gif') { if (file.mimeType === 'image/gif') {
@@ -36,6 +42,7 @@ export async function compressVideo(
logger.debug('probe failed, falling through to passthrough', { logger.debug('probe failed, falling through to passthrough', {
safeMessage: e, safeMessage: e,
}) })
opts?.onProbeFailed?.(e)
return { return {
uri: file.uri, uri: file.uri,
size: file.fileSize ?? -1, size: file.fileSize ?? -1,
@@ -43,7 +50,10 @@ export async function compressVideo(
} }
} }
if (!shouldCompress(metadata, isAcceptableFormat)) { const willCompress = shouldCompress(metadata, isAcceptableFormat)
opts?.onProbe?.(metadata, willCompress)
if (!willCompress) {
return { return {
uri: file.uri, uri: file.uri,
size: metadata.fileSize, size: metadata.fileSize,
+4 -2
View File
@@ -412,9 +412,10 @@ export const ComposePost = ({
currentDid, currentDid,
abortController.signal, abortController.signal,
i18n, i18n,
ax.metric,
) )
}, },
[i18n, agent, currentDid, composerDispatch], [i18n, agent, currentDid, composerDispatch, ax.metric],
) )
const onInitVideo = useNonReactiveCallback(() => { const onInitVideo = useNonReactiveCallback(() => {
@@ -559,6 +560,7 @@ export const ComposePost = ({
currentDid, currentDid,
abortController.signal, abortController.signal,
i18n, i18n,
ax.metric,
) )
} catch (e) { } catch (e) {
logger.error('Failed to restore video from draft', { logger.error('Failed to restore video from draft', {
@@ -567,7 +569,7 @@ export const ComposePost = ({
}) })
} }
}, },
[i18n, agent, currentDid, composerDispatch], [i18n, agent, currentDid, composerDispatch, ax.metric],
) )
const handleSelectDraft = useCallback( const handleSelectDraft = useCallback(
+23
View File
@@ -16,6 +16,7 @@ import {uploadVideo} from '#/lib/media/video/upload'
import {createVideoAgent} from '#/lib/media/video/util' import {createVideoAgent} from '#/lib/media/video/util'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {logger} from '#/logger' import {logger} from '#/logger'
import {type AnalyticsContextType} from '#/analytics'
type CaptionsTrack = {lang: string; file: File} type CaptionsTrack = {lang: string; file: File}
@@ -265,6 +266,7 @@ export async function processVideo(
did: string, did: string,
signal: AbortSignal, signal: AbortSignal,
i18n: I18n, i18n: I18n,
metric: AnalyticsContextType['metric'],
) { ) {
let video: CompressedVideo | undefined let video: CompressedVideo | undefined
try { try {
@@ -273,6 +275,27 @@ export async function processVideo(
dispatch({type: 'update_progress', progress: trunc2dp(num), signal}) dispatch({type: 'update_progress', progress: trunc2dp(num), signal})
}, },
signal, signal,
onProbe: (metadata, wouldCompress) => {
metric('composer:video:probe', {
mimeType: metadata.mimeType,
codec: metadata.codec,
width: metadata.width,
height: metadata.height,
duration: metadata.duration,
bitrate: metadata.bitrate,
fileSize: metadata.fileSize,
hasAudio: metadata.hasAudio,
frameRate: metadata.frameRate,
rotation: metadata.rotation,
isHDR: metadata.isHDR,
wouldCompress,
})
},
onProbeFailed: e => {
metric('composer:video:probeFailed', {
safeMessage: e instanceof Error ? e.message : String(e),
})
},
}) })
} catch (e) { } catch (e) {
const message = getCompressErrorMessage(e, i18n) const message = getCompressErrorMessage(e, i18n)