비디오를 대상 파일 크기로 압축: TypeScript의 비트 전송률 수학

작성자

카테고리:

← 피드로
DEV Community · Noah Chen · 2026-08-03 개발(SW)

A practical calculator for turning an upload limit into a video bitrate, with enough margin for audio and container overhead.

“Make this video smaller” is an open-ended request. “Make this three-minute video fit under 10 MB” is an engineering constraint.

The second version sounds more precise, but a quality slider alone cannot solve it. A quality setting tells an encoder how aggressively to preserve detail. It does not directly tell us how many bytes the final file may contain. If the destination has a hard upload limit, the useful starting point is a bit budget.

This article builds that calculation in TypeScript, then looks at the assumptions that make the answer less exact than the formula first appears.

File Size Is Bitrate Multiplied by Time

A video file contains several streams plus a container. For a simple MP4, the largest pieces are usually:

  • the video stream;
  • the audio stream;
  • container metadata and indexing overhead.

If we ignore overhead for a moment, the relationship is straightforward:

file size in bits = total bitrate in bits per second × duration in seconds

Enter fullscreen mode Exit fullscreen mode

Rearranging it gives us the total bitrate available for a target size:

total bitrate = target size in bits / duration in seconds

Enter fullscreen mode Exit fullscreen mode

That total must cover both video and audio. The approximate video budget is therefore:

video bitrate = total bitrate - audio bitrate - overhead allowance

Enter fullscreen mode Exit fullscreen mode

The result is not a promise. It is a budget that an encoder can aim at.

Be Explicit About MB and MiB

Before writing code, decide what “10 MB” means.

Storage vendors and many web services use decimal megabytes:

1 MB = 1,000,000 bytes

Enter fullscreen mode Exit fullscreen mode

Operating systems and developer tools often display binary mebibytes:

1 MiB = 1,048,576 bytes

Enter fullscreen mode Exit fullscreen mode

The difference is about 4.9%. That is large enough to turn a file that looks safe locally into a rejected upload. For a hard external limit, I prefer to calculate with decimal MB and keep an additional safety margin. For an internal tool where the unit is clearly MiB, I make that choice explicit in the function name or input type.

A Small TypeScript Calculator

The function below accepts a decimal target size, duration, audio bitrate, container allowance, and safety margin. It returns the video bitrate to pass to an encoder.

type BitrateBudgetInput = {
  targetMB: number;
  durationSeconds: number;
  audioKbps?: number;
  containerOverheadFraction?: number;
  safetyMarginFraction?: number;
};

type BitrateBudget = {
  totalKbps: number;
  usableKbps: number;
  audioKbps: number;
  videoKbps: number;
};

export function calculateVideoBitrate({
  targetMB,
  durationSeconds,
  audioKbps = 96,
  containerOverheadFraction = 0.02,
  safetyMarginFraction = 0.03,
}: BitrateBudgetInput): BitrateBudget {
  if (!Number.isFinite(targetMB) || targetMB <= 0) {
    throw new RangeError("targetMB must be greater than zero");
  }

  if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
    throw new RangeError("durationSeconds must be greater than zero");
  }

  const targetBits = targetMB * 1_000_000 * 8;
  const totalKbps = targetBits / durationSeconds / 1_000;

  const reservedFraction =
    containerOverheadFraction + safetyMarginFraction;

  if (reservedFraction < 0 || reservedFraction >= 1) {
    throw new RangeError("reserved fractions must total less than one");
  }

  const usableKbps = totalKbps * (1 - reservedFraction);
  const videoKbps = Math.floor(usableKbps - audioKbps);

  if (videoKbps <= 0) {
    throw new RangeError(
      "The target is too small for this duration and audio bitrate",
    );
  }

  return {
    totalKbps: Math.floor(totalKbps),
    usableKbps: Math.floor(usableKbps),
    audioKbps,
    videoKbps,
  };
}

Enter fullscreen mode Exit fullscreen mode

For a three-minute video with a 10 MB limit:

const budget = calculateVideoBitrate({
  targetMB: 10,
  durationSeconds: 180,
  audioKbps: 96,
});

console.log(budget);

Enter fullscreen mode Exit fullscreen mode

The raw total is roughly 444 kbps. After reserving 5% for overhead and safety, then allocating 96 kbps to audio, the video stream receives about 326 kbps.

That is a tight budget. It may be acceptable for a mostly static 720p screen recording. It will probably look rough for fast camera movement at 1080p. The calculator can tell us whether the numbers fit. It cannot decide whether the visual result is useful.

Add Tests Around the Boundary Conditions

The arithmetic is simple enough that the most valuable tests are about invalid or unrealistic inputs.

import { describe, expect, it } from "vitest";
import { calculateVideoBitrate } from "./bitrate-budget";

describe("calculateVideoBitrate", () => {
  it("returns a positive video budget", () => {
    const result = calculateVideoBitrate({
      targetMB: 25,
      durationSeconds: 120,
      audioKbps: 96,
    });

    expect(result.videoKbps).toBeGreaterThan(0);
    expect(result.videoKbps).toBeLessThan(result.totalKbps);
  });

  it("rejects a zero duration", () => {
    expect(() =>
      calculateVideoBitrate({ targetMB: 10, durationSeconds: 0 }),
    ).toThrow(RangeError);
  });

  it("rejects an impossible audio allocation", () => {
    expect(() =>
      calculateVideoBitrate({
        targetMB: 1,
        durationSeconds: 600,
        audioKbps: 128,
      }),
    ).toThrow(RangeError);
  });
});

Enter fullscreen mode Exit fullscreen mode

An impossible result is useful information. It means at least one constraint must change: shorten the video, increase the file limit, lower the audio bitrate, remove audio, reduce resolution, or accept visibly lower quality.

Why Two-Pass Encoding Gets Closer

A constant bitrate is easy to reason about, but real video does not have constant complexity. A static product screen needs fewer bits than a transition, scrolling page, or camera pan.

Two-pass encoding uses the first pass to analyze where complexity occurs. The second pass spends more of the fixed budget on difficult sections and less on easy ones. It is therefore a sensible choice when file size matters more than encoding speed.

A simplified FFmpeg flow looks like this:

ffmpeg -y -i input.mp4 \
  -c:v libx264 -b:v 326k -pass 1 -an \
  -f mp4 /dev/null

ffmpeg -i input.mp4 \
  -c:v libx264 -b:v 326k -pass 2 \
  -c:a aac -b:a 96k output.mp4

Enter fullscreen mode Exit fullscreen mode

On Windows, the first command uses NUL instead of /dev/null. A production wrapper should also remove the pass-log files and handle failed processes.

Even two-pass encoding can miss an exact target because of muxing overhead, encoder behavior, subtitles, metadata, and rounding. That is why the calculator keeps a safety margin instead of aiming at the final byte.

Codec Choice Changes the Quality, Not the Budget

If the target size and duration are fixed, the total bitrate is fixed too. Switching from H.264 to H.265 does not create a larger bit budget. It tries to produce better visual quality with the same budget.

That tradeoff includes compatibility and encoding cost:

  • H.264 is widely supported and usually encodes faster.
  • H.265 can preserve more detail at low bitrates, especially at higher resolutions.
  • Older browsers, devices, or editing workflows may not handle H.265 as predictably.
  • Re-encoding an already compressed file can introduce additional artifacts regardless of codec.

For a file that must play everywhere, H.264 may be the safer result. For a controlled playback environment and a severe size limit, H.265 may be worth testing.

Resolution Is Often the Most Honest Lever

At some point, there are not enough bits to describe every pixel well.

Trying to keep 4K resolution at a few hundred kilobits per second usually produces a file that is technically 4K but visually worse than a clean 720p version. Downscaling reduces the number of pixels the encoder must describe and can improve perceived quality at the same file size.

For product videos, I use a simple decision order:

  1. Remove dead time and unnecessary scenes.
  2. Keep only the audio quality the content needs.
  3. Calculate the available video bitrate.
  4. Choose a resolution appropriate for that bitrate and viewing context.
  5. Encode with a safety margin.
  6. Inspect text, cursor movement, transitions, and other high-risk frames.

The final inspection matters. A passing file-size check does not mean small interface text remains readable.

When Not to Build the Pipeline

The TypeScript function is useful when compression is part of a repeatable system: an upload service, media queue, desktop tool, or CI job. In that situation, controlling the encoder and recording the exact settings are worth the engineering effort.

For an occasional file, maintaining that pipeline may cost more time than it saves. A browser-based tool such as VideoCompress exposes target-size and advanced controls without requiring a local FFmpeg workflow. The tradeoff is that processing happens in the cloud, so upload time and data sensitivity matter. Its free account also uses monthly credits rather than offering unlimited processing.

That distinction is the practical one: automate recurring workloads; use a focused interface for one-off work; keep sensitive media local.

Treat the Formula as a Constraint, Not a Quality Score

Target-size compression becomes easier to reason about once the upload limit is translated into a bit budget. The arithmetic tells us what is possible. The encoder decides how to distribute those bits. Human inspection determines whether the result is acceptable.

Those are three separate jobs.

A small calculator prevents impossible settings from reaching the encoder and makes tradeoffs visible before a long job starts. It also replaces the vague instruction to “compress it more” with better questions: Can we shorten it? Can we reduce resolution? Is the audio budget too high? Does compatibility require H.264? Is this recurring enough to automate?

That is usually where a reliable video workflow begins.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다