Address review: extract shared compression constants, log empty output
- Move TARGET_BITRATE, MAX_DIMENSION, and the 25 MB skip threshold to a new src/lib/media/video/constants.ts so both compress.web.ts and the upcoming native engine use the same values. Codec lists stay local to compress.web.ts since they are mediabunny-specific. - When mediabunny's BufferTarget returns a null buffer after a clean execute(), log via logger.error so Sentry captures the unexpected state. The outer catch still demotes the throw to a warn and falls back to the original blob, so user-visible behavior is unchanged.
This commit is contained in:
@@ -18,12 +18,13 @@ import {VIDEO_MAX_SIZE} from '#/lib/constants'
|
||||
import {VideoTooLargeError} from '#/lib/media/video/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {hasWebCodecs} from '#/view/com/composer/videos/metadata'
|
||||
import {
|
||||
COMPRESSION_MAX_DIMENSION,
|
||||
COMPRESSION_MIN_SIZE_BYTES,
|
||||
COMPRESSION_TARGET_BITRATE,
|
||||
} from './constants'
|
||||
import {type CompressedVideo} from './types'
|
||||
|
||||
const TARGET_BITRATE = 3_000_000 // 3mbps, matches native
|
||||
const MAX_DIMENSION = 1920 // matches native
|
||||
const MIN_SIZE_FOR_COMPRESSION = 25 * 1000 * 1000 // 25mb, matches native
|
||||
|
||||
// Codecs to try in order of preference
|
||||
// avc (H.264) is most compatible, vp9/vp8 are fallbacks for WebM
|
||||
const VIDEO_CODECS: VideoCodec[] = ['avc', 'hevc', 'vp9', 'vp8']
|
||||
@@ -51,12 +52,12 @@ export async function compressVideo(
|
||||
size: blob.size,
|
||||
mimeType: blob.type,
|
||||
isGif,
|
||||
minSizeForCompression: MIN_SIZE_FOR_COMPRESSION,
|
||||
minSizeForCompression: COMPRESSION_MIN_SIZE_BYTES,
|
||||
})
|
||||
|
||||
// Try MediaBunny compression if WebCodecs is available and file is large enough
|
||||
// Skip GIFs - MediaBunny doesn't support them
|
||||
if (hasWebCodecs() && blob.size >= MIN_SIZE_FOR_COMPRESSION && !isGif) {
|
||||
if (hasWebCodecs() && blob.size >= COMPRESSION_MIN_SIZE_BYTES && !isGif) {
|
||||
try {
|
||||
return await doCompression(blob, asset.uri, {onProgress, signal})
|
||||
} catch (e) {
|
||||
@@ -68,7 +69,7 @@ export async function compressVideo(
|
||||
logger.debug('compress: skipping compression', {
|
||||
hasWebCodecs: hasWebCodecs(),
|
||||
blobSize: blob.size,
|
||||
minSize: MIN_SIZE_FOR_COMPRESSION,
|
||||
minSize: COMPRESSION_MIN_SIZE_BYTES,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ async function findEncodableVideoCodec(
|
||||
const canEncode = await canEncodeVideo(codec, {
|
||||
width,
|
||||
height,
|
||||
bitrate: TARGET_BITRATE,
|
||||
bitrate: COMPRESSION_TARGET_BITRATE,
|
||||
})
|
||||
logger.debug('compress: checking video codec', {
|
||||
codec,
|
||||
@@ -182,7 +183,7 @@ async function doCompression(
|
||||
const {width, height} = calculateDimensions(
|
||||
videoTrack.displayWidth,
|
||||
videoTrack.displayHeight,
|
||||
MAX_DIMENSION,
|
||||
COMPRESSION_MAX_DIMENSION,
|
||||
)
|
||||
|
||||
logger.debug('compress: video dimensions', {
|
||||
@@ -232,7 +233,7 @@ async function doCompression(
|
||||
output,
|
||||
video: {
|
||||
codec: codecInfo.codec,
|
||||
bitrate: TARGET_BITRATE,
|
||||
bitrate: COMPRESSION_TARGET_BITRATE,
|
||||
width,
|
||||
height,
|
||||
fit: 'contain',
|
||||
@@ -268,7 +269,13 @@ async function doCompression(
|
||||
const bytes = target.buffer
|
||||
|
||||
if (!bytes) {
|
||||
throw new Error('MediaBunny compression produced no output')
|
||||
// mediabunny's BufferTarget reports a null buffer after a successful
|
||||
// execute(). Should not happen in normal use; recoverable here because
|
||||
// the outer compressVideo() catches and falls back to the original blob,
|
||||
// but worth flagging in Sentry so we can chase the root cause.
|
||||
const err = new Error('Compression produced empty output')
|
||||
logger.error(err, {})
|
||||
throw err
|
||||
}
|
||||
|
||||
const mimeType = codecInfo.useWebM ? 'video/webm' : 'video/mp4'
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Shared compression knobs. Mirrored between native (compress.ts) and web
|
||||
// (compress.web.ts) so both platforms produce videos with the same target.
|
||||
|
||||
export const COMPRESSION_TARGET_BITRATE = 3_000_000 // 3 Mbps
|
||||
export const COMPRESSION_MAX_DIMENSION = 1920
|
||||
// Web skips compression entirely for files under this size; native applies its
|
||||
// own threshold logic inside expo-bluesky-video-compress's probe step.
|
||||
export const COMPRESSION_MIN_SIZE_BYTES = 25_000_000
|
||||
Reference in New Issue
Block a user