You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Cloud Functions中调整视频尺寸并压缩视频?

How to Resize, Compress, and Limit Video Size in Cloud Functions for iOS Apps

Great question—handling video resizing and compression in Cloud Functions is totally manageable, and I’ve helped build similar workflows for iOS-focused apps before. Let’s break down exactly how to meet all your requirements, step by step.

First: Choose Your Core Tool

For video processing, FFmpeg is the industry standard—it’s flexible, powerful, and works seamlessly in Cloud Functions. We’ll use the fluent-ffmpeg Node.js wrapper (plus a pre-built FFmpeg binary for Cloud Functions) to simplify code.

1. Resize Videos for Consistent iPhone Display

To ensure videos look uniform across all iPhones, pick a target resolution that balances compatibility and quality. The sweet spot for iOS is 1080p (1920x1080, 16:9)—it’s supported by every modern iPhone, and matches the default video capture resolution for most models.

Key rules for resizing:

  • Never stretch the video—preserve the original aspect ratio
  • For videos with non-16:9 aspect ratios (e.g., 4:3), add black bars (letterboxing/pillarboxing) to fill the 16:9 frame

Here’s the FFmpeg filter to achieve this:

scale=w=1920:h=1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2
  • scale shrinks the video to fit within 1920x1080 without stretching
  • pad adds black bars to center the video in the 16:9 frame

2. Compress Videos & Enforce 50MB Limit (Prioritize HD Quality)

A 30-second 1080p HD video can easily stay under 50MB with smart encoding choices. Here’s how to balance quality and file size:

Use H.264 Encoding (Best Compatibility + Quality)

H.264 is supported by all iPhones and offers excellent compression. We’ll use two key settings:

  • CRF (Constant Rate Factor): Controls quality directly. A value of 23-25 gives great HD quality while keeping file sizes small. Lower = better quality, larger file; higher = smaller file, slightly reduced quality.
  • Preset: Balances compression speed and efficiency. medium is a great middle ground—fast enough for Cloud Functions, with solid compression.

Audio Compression

Don’t forget audio! Use AAC encoding (iOS’s preferred format) with a 128kbps bitrate—this is more than enough for clear audio without wasting space.

Enforce 50MB Hard Limit

To guarantee you never exceed 50MB, calculate the maximum allowed bitrate:
50MB = (50 * 8 * 1024 * 1024) bits / 30 seconds ≈ 13.98 Mbps

Add these FFmpeg options to cap the bitrate:

-maxrate 13M -bufsize 26M

bufsize ensures the bitrate stays consistent across variable-content videos (e.g., fast action vs. static scenes).

3. Full Cloud Functions Implementation (Node.js)

Here’s a complete, production-ready function that triggers when a video is uploaded to Cloud Storage, processes it, and saves the result.

Step 1: Install Dependencies

In your Cloud Functions project folder, run:

npm install @ffmpeg-installer/ffmpeg fluent-ffmpeg @google-cloud/storage

Step 2: The Function Code

const functions = require('firebase-functions');
const ffmpeg = require('fluent-ffmpeg');
const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const { Storage } = require('@google-cloud/storage');
const storage = new Storage();
const os = require('os');
const path = require('path');
const fs = require('fs');

// Set FFmpeg path for Cloud Functions environment
ffmpeg.setFfmpegPath(ffmpegPath);

exports.processUserVideo = functions.storage.object().onFinalize(async (object) => {
  // Skip non-video files and avatar images
  if (!object.contentType.startsWith('video/') || object.name.includes('avatar')) {
    functions.logger.log('Skipping non-video or avatar file');
    return null;
  }

  // Validate video duration (optional but recommended)
  // You can add a check here using FFmpeg to get duration and reject if over 30s

  const bucket = storage.bucket(object.bucket);
  const filePath = object.name;
  const fileName = path.basename(filePath);
  // Temp storage for Cloud Functions (auto-cleaned after function finishes)
  const tempLocalFile = path.join(os.tmpdir(), fileName);
  const processedFileName = `processed/${fileName}`;
  const tempLocalProcessedFile = path.join(os.tmpdir(), processedFileName);

  try {
    // Download original video to temp directory
    await bucket.file(filePath).download({ destination: tempLocalFile });
    functions.logger.log('Downloaded source file to:', tempLocalFile);

    // Run FFmpeg processing
    await new Promise((resolve, reject) => {
      ffmpeg(tempLocalFile)
        // Resize to 1080p with aspect ratio preservation
        .videoFilter('scale=w=1920:h=1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2')
        // Video encoding settings for HD quality + compression
        .outputOptions('-c:v libx264')
        .outputOptions('-crf 24')
        .outputOptions('-preset medium')
        // Bitrate cap to ensure <50MB for 30s videos
        .outputOptions('-maxrate 13M')
        .outputOptions('-bufsize 26M')
        // Audio compression
        .outputOptions('-c:a aac')
        .outputOptions('-b:a 128k')
        // Output to temp processed file
        .output(tempLocalProcessedFile)
        .on('end', resolve)
        .on('error', (err) => {
          functions.logger.error('FFmpeg processing failed:', err);
          reject(err);
        })
        .run();
    });

    // Upload processed video back to Cloud Storage
    await bucket.upload(tempLocalProcessedFile, {
      destination: processedFileName,
      contentType: 'video/mp4',
      metadata: {
        cacheControl: 'public, max-age=31536000'
      }
    });
    functions.logger.log('Successfully uploaded processed video to:', processedFileName);

    // Optional: Delete original file to save storage space
    // await bucket.file(filePath).delete();

    return null;
  } catch (err) {
    functions.logger.error('Overall processing failed:', err);
    return err;
  } finally {
    // Clean up temp files to avoid storage leaks
    if (fs.existsSync(tempLocalFile)) fs.unlinkSync(tempLocalFile);
    if (fs.existsSync(tempLocalProcessedFile)) fs.unlinkSync(tempLocalProcessedFile);
  }
});

Critical Configuration Tips

  • Memory & Timeout: Video processing is resource-heavy. Set your function to use 2GB of memory and a 540-second timeout (the maximum allowed for Cloud Functions) to handle 4K source videos.
  • Permissions: Ensure your Cloud Functions service account has storage.objects.get and storage.objects.create permissions for your Cloud Storage bucket.
  • Testing: Test with diverse videos (4K, 1080p, 4:3 aspect ratio) to verify sizing, quality, and file size stay under 50MB.
  • H.265 Alternative: If you need even smaller files (and your app supports iOS 11+), replace libx264 with libx265 and use CRF 28—this cuts file sizes by ~50% with nearly identical quality.

内容的提问来源于stack exchange,提问作者Nat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:32:38