javascriptroom blog

Where is FPS Stored in HTML5 Video Metadata? Guide for Custom Video Player Developers

For developers building custom video players, understanding video metadata is critical to delivering a seamless user experience. One key piece of metadata is Frames Per Second (FPS)—the number of still frames displayed per second. FPS directly impacts playback smoothness, syncing with audio/subtitles, and adaptive streaming logic (e.g., adjusting quality based on device capabilities).

Yet, unlike duration or dimensions, FPS is not explicitly exposed in the HTML5 Video API by default. This can confuse developers trying to access it directly from the <video> element. In this guide, we’ll demystify where FPS is stored, why it’s hidden, and practical methods to extract it for your custom player.

2026-02

Table of Contents#

  1. Understanding HTML5 Video Metadata

    • 1.1 What is Video Metadata?
    • 1.2 Exposed vs. Hidden Metadata in HTML5
  2. The Challenge: Why FPS Isn’t Directly Accessible

    • 2.1 HTML5 Spec Limitations
    • 2.2 Container-Level vs. API-Exposed Metadata
  3. How Browsers Handle Video Metadata and FPS

    • 3.1 Browser Inconsistencies
    • 3.2 Why FPS Isn’t Standardized
  4. Practical Methods to Extract FPS from HTML5 Video

    • 4.1 Method 1: Parse Media Manifests (HLS/DASH)
    • 4.2 Method 2: Third-Party Metadata Parsing Libraries
    • 4.3 Method 3: Frame Capture + Timing (Fallback)
  5. Challenges and Limitations

    • 5.1 Variable Frame Rate (VFR) Content
    • 5.2 Encrypted/DRM-Protected Content
    • 5.3 Browser and Format Inconsistencies
  6. Best Practices for Custom Video Player Developers

  7. Conclusion

  8. References

1. Understanding HTML5 Video Metadata#

1.1 What is Video Metadata?#

Video metadata is structured data embedded in or associated with a video file that describes its content and technical properties. It includes:

  • Technical metadata: Duration, resolution (width/height), bitrate, codec, and FPS.
  • Descriptive metadata: Title, author, captions, or chapter markers.

For custom video players, technical metadata like FPS is critical for optimizing playback (e.g., ensuring the player renders frames at the correct rate to avoid stuttering).

1.2 Exposed vs. Hidden Metadata in HTML5#

The HTML5 <video> element exposes basic metadata via its properties and events. For example:

  • video.duration: Total length of the video (available after loadedmetadata event).
  • video.videoWidth/videoHeight: Native dimensions of the video.
  • video.audioTracks/video.videoTracks: Lists of audio/video tracks.

However, not all technical metadata is exposed. FPS, for instance, is typically stored in the video file’s container metadata (e.g., MP4’s moov box, WebM’s Info element) but is not required to be exposed by the HTML5 spec. This leaves developers without a standard API to access it directly.

2. The Challenge: Why FPS Isn’t Directly Accessible#

2.1 HTML5 Spec Limitations#

The W3C HTML5 Spec defines the <video> element’s API but does not mandate exposing frame rate. The VideoTrack interface (part of the spec) includes properties like id, kind, and label, but no frameRate attribute. Browsers are not required to surface this data, leading to inconsistency.

2.2 Container-Level vs. API-Exposed Metadata#

FPS is stored at the container level (e.g., MP4, WebM) or codec level (e.g., H.264, VP9). For example:

  • In MP4 files, FPS is often stored in the stts (sample-to-time) box or tkhd (track header) box.
  • In WebM, it’s in the Video track’s CodecPrivate field or Info element.

The HTML5 Video API does not parse these low-level container structures, so FPS remains hidden unless explicitly exposed by the browser (which is rare).

3. How Browsers Handle Video Metadata and FPS#

3.1 Browser Inconsistencies#

Browsers prioritize performance and security over exposing low-level metadata. For example:

  • Chrome/Edge: May expose limited track metadata via videoTracks, but not FPS.
  • Firefox: Similarly does not expose FPS in the standard API.
  • Safari: Focuses on energy efficiency, with no public API for FPS extraction.

No major browser currently exposes FPS via the native <video> element API.

3.2 Why FPS Isn’t Standardized#

FPS variability (e.g., 23.976 vs. 24 FPS, variable frame rate content) and the complexity of parsing container metadata (which can slow down video loading) likely explain why the spec omits it. Browsers prioritize fast playback over exposing niche metadata like FPS.

4. Practical Methods to Extract FPS from HTML5 Video#

Despite the lack of a standard API, developers can use workarounds to extract FPS. Below are the most reliable methods:

4.1 Method 1: Parse Media Manifests (HLS/DASH)#

If your video is streamed via HLS (m3u8) or DASH (mpd), manifest files often include FPS in their metadata. For example:

HLS Manifest Example:#

HLS streams use .m3u8 playlists with #EXT-X-STREAM-INF tags that may specify FRAME-RATE:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1280x720,FRAME-RATE=24.0
720p/stream.m3u8

DASH Manifest Example:#

DASH’s MPD (Media Presentation Description) includes FrameRate in track metadata:

<Representation id="1" codecs="avc1.4d401f" width="1280" height="720" frameRate="24/1" bandwidth="2000000">

Implementation Steps:

  1. Fetch the HLS/DASH manifest.
  2. Parse the manifest to extract FRAME-RATE (HLS) or frameRate (DASH).
  3. Use this value in your player logic.

Code Example (HLS Manifest Parsing):

async function getFpsFromHlsManifest(manifestUrl) {
  const response = await fetch(manifestUrl);
  const manifest = await response.text();
  const streamInfRegex = /#EXT-X-STREAM-INF:.*FRAME-RATE=(\d+\.?\d*)/;
  const match = manifest.match(streamInfRegex);
  return match ? parseFloat(match[1]) : null;
}
 
// Usage:
getFpsFromHlsManifest("https://example.com/stream.m3u8").then(fps => {
  console.log("Extracted FPS:", fps); // e.g., 24.0
});

4.2 Method 2: Third-Party Metadata Parsing Libraries#

Libraries like mediainfo.js or ffmpeg.wasm parse the video file’s binary data to extract low-level metadata, including FPS. These libraries bypass the HTML5 API and read container/codec metadata directly.

Example with mediainfo.js:#

mediainfo.js is a port of the popular MediaInfo tool, optimized for browsers. It can extract FPS from local files or URLs.

Steps:

  1. Load mediainfo.js via CDN or npm.
  2. Pass the video file/URL to MediaInfo() and parse the metadata.

Code Example:

<!-- Load mediainfo.js -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mediainfo.min.js"></script>
 
<script>
async function getFpsWithMediainfo(videoUrl) {
  const MediaInfo = await window.mediainfo();
  const mi = new MediaInfo();
  
  // Fetch video file as ArrayBuffer
  const response = await fetch(videoUrl);
  const buffer = await response.arrayBuffer();
  
  // Parse metadata
  const result = await mi.analyzeData(() => buffer.byteLength, (offset, size) => 
    new Uint8Array(buffer, offset, size)
  );
  
  const metadata = JSON.parse(result);
  const videoTrack = metadata.media.track.find(track => track['@type'] === 'Video');
  return videoTrack ? parseFloat(videoTrack.FrameRate) : null;
}
 
// Usage:
getFpsWithMediainfo("https://example.com/video.mp4").then(fps => {
  console.log("mediainfo.js FPS:", fps); // e.g., 23.976
});
</script>

Pros: Reliable for local files or non-streaming videos.
Cons: Adds ~2MB to your bundle (mediainfo.js) and requires CORS access to the video file.

4.3 Method 3: Frame Capture + Timing (Fallback)#

If manifests or libraries aren’t feasible, you can estimate FPS by capturing frames via <canvas> and measuring time between captures. This is a fallback and less accurate.

How It Works:

  • Play the video and draw frames to a canvas at regular intervals.
  • Measure the time between frame captures and calculate FPS as framesCaptured / totalTime.

Code Example:

async function estimateFpsWithCanvas(videoElement) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const framesToCapture = 10; // More frames = better accuracy
  let framesCaptured = 0;
  let startTime;
 
  return new Promise((resolve) => {
    videoElement.play();
    startTime = performance.now();
 
    const captureFrame = () => {
      if (framesCaptured < framesToCapture) {
        // Draw current frame to canvas
        canvas.width = videoElement.videoWidth;
        canvas.height = videoElement.videoHeight;
        ctx.drawImage(videoElement, 0, 0);
        framesCaptured++;
        requestAnimationFrame(captureFrame);
      } else {
        videoElement.pause();
        const endTime = performance.now();
        const duration = (endTime - startTime) / 1000; // seconds
        const estimatedFps = framesCaptured / duration;
        resolve(estimatedFps.toFixed(2));
      }
    };
 
    captureFrame();
  });
}
 
// Usage:
const video = document.getElementById('myVideo');
video.addEventListener('loadeddata', async () => {
  const estimatedFps = await estimateFpsWithCanvas(video);
  console.log("Estimated FPS:", estimatedFps); // e.g., 24.12
});

Pros: No external libraries; works with any video.
Cons: Inaccurate for variable frame rate (VFR) content; requires playback (may interrupt user experience).

5. Challenges and Limitations#

5.1 Variable Frame Rate (VFR) Content#

Many modern videos (e.g., smartphone recordings) use VFR, where frame rate varies (e.g., 24–30 FPS). Libraries like mediainfo.js will return an average, but estimates via canvas may be misleading.

5.2 Encrypted/DRM-Protected Content#

DRM-protected videos (e.g., Widevine, PlayReady) encrypt container metadata, making it impossible to parse with libraries like mediainfo.js. You’ll need to rely on manifest data (HLS/DASH) if available.

5.3 Browser and Format Inconsistencies#

  • MP4 vs. WebM: Container structures differ, so parsing logic varies.
  • CORS Restrictions: Libraries like mediainfo.js require the video file to have CORS headers, which may not be available for third-party content.

6. Best Practices for Custom Video Player Developers#

  • Prioritize Manifests for Streaming: If using HLS/DASH, parse FPS from the manifest (most reliable).
  • Cache FPS Values: Extract FPS once (e.g., on loadedmetadata) and cache it to avoid redundant parsing.
  • Handle Missing FPS Gracefully: Default to common values (24, 30 FPS) if extraction fails.
  • Test Across Browsers/Formats: Validate FPS extraction with MP4, WebM, and VFR content.

7. Conclusion#

FPS is critical for custom video players but is not exposed via the HTML5 Video API. Developers must use workarounds like parsing HLS/DASH manifests, third-party libraries (mediainfo.js), or canvas-based estimation. For streaming content, manifests are the most reliable; for local/non-streaming videos, libraries like mediainfo.js offer accuracy. Always handle edge cases (VFR, DRM) and test across browsers to ensure smooth playback.

8. References#