最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How do I get the frame rate of an HTML video with JavaScript? - Stack Overflow

programmeradmin7浏览0评论

Edit: I was able to figure this out, scroll down to the top answer to see it. AFAIK no other post on Stack (or anywhere online) with this question had a working and consistent answer.

Anyway...

I’m building a retiming tool for videos on YouTube and other websites, and finding the frame rate of the video element is necessary for frame-by-frame seeking and rounding reasons.

The way I’m doing it so far is by using requestVideoFrameCallback() and finding the smallest difference between two frame times: (sorry for bad formatting, I’m on mobile)

var v = document.querySelector(“video”)
var c = 0; // time of last frame 
var framelength = 1; // length of one frame 
var fps;
function check(t, m) {
  var diff = Math.abs(m.mediaTime - c); // difference between this frame and the last
  if (diff && diff < framelength) {
    framelength = diff;
    fps = Math.round( 1 / framelength)
  }
  c = m.mediaTime;
  v.requestVideoFrameCallback(check);
}
v.requestVideoFrameCallback(check);

However, there are sometimes rounding issues due to the mediaTime having only 3 decimal places, so is there a better way to find the FPS with JavaScript?

Edit: I was able to figure this out, scroll down to the top answer to see it. AFAIK no other post on Stack (or anywhere online) with this question had a working and consistent answer.

Anyway...

I’m building a retiming tool for videos on YouTube and other websites, and finding the frame rate of the video element is necessary for frame-by-frame seeking and rounding reasons.

The way I’m doing it so far is by using requestVideoFrameCallback() and finding the smallest difference between two frame times: (sorry for bad formatting, I’m on mobile)

var v = document.querySelector(“video”)
var c = 0; // time of last frame 
var framelength = 1; // length of one frame 
var fps;
function check(t, m) {
  var diff = Math.abs(m.mediaTime - c); // difference between this frame and the last
  if (diff && diff < framelength) {
    framelength = diff;
    fps = Math.round( 1 / framelength)
  }
  c = m.mediaTime;
  v.requestVideoFrameCallback(check);
}
v.requestVideoFrameCallback(check);

However, there are sometimes rounding issues due to the mediaTime having only 3 decimal places, so is there a better way to find the FPS with JavaScript?

Share Improve this question edited Jul 24, 2022 at 0:23 derder56 asked Jul 15, 2022 at 17:33 derder56derder56 3613 silver badges10 bronze badges 5
  • "How do I get the frame rate of an HTML video with JavaScript?" You cannot. Also you're talking about content that exists on external servers so CORS could be an issue. The only way is to check the video file's bytes, so you need a direct link to the MP4 (and PHP to avoid CORS when reading the file bytes back into JS) then check these bytes for the FPS (depends on format like MP4 or WebM or OGV). PS: Youtube mentions frame rate in their video's page (not in embed-version), so load the page source with PHP then look for first occurrence of "fps": and then extract next following 2 digits. – VC.One Commented Jul 16, 2022 at 21:14
  • Well, actually I was able to tweak my code and get a consistent FPS that pretty much matched the video every time, sooo. YAY – derder56 Commented Jul 17, 2022 at 0:50
  • Does this answer your question? How to determine the intended frame-rate on an html video element – Ethan Commented Jul 24, 2022 at 0:12
  • Nope, I already figured it out, check the answers for this question @Ethan – derder56 Commented Jul 24, 2022 at 0:14
  • I've deleted my ment. The MediaTrackSettings method always returns 30 fps. I'm going to instead go with using mediainfo.js (github./buzz/mediainfo.js) which can actually parse the video file itself for information. – Samuel Commented Jan 18, 2023 at 1:07
Add a ment  | 

1 Answer 1

Reset to default 7

Using a mix of some hacky requestVideoFrameCallback stuff, as well as making an array and rounding it, I was able to get a consistent and accurate FPS for any video. I wish I had been able to found this online when I needed it, but here it is for anybody else who had the same question as me:

Step 1: Set variables

  • vid is the HTML video element
  • last_media_time and last_frame_run will be used later to determine differences between frames and get the FPS
  • fps is obvious
  • fps_rounder is for rounding and stopping stuttering video frames from messing up the FPS, it actually contains the differences between frames, we make it have like 50 items to be sure, more about this in step 2
  • frame_not_seeked is to stop users from moving around the video and messing up FPS, more about this in step 3
var vid = document.querySelector("video");
var last_media_time, last_frame_num, fps;
var fps_rounder = [];
var frame_not_seeked = true;

Step 2: Set a ticker function with rVFC

We use .mediaTime and .presentedFrames to get a good diff, which would be either 0.016 or 0.017, assuming the video is 60fps. Then we shove it all in fps_rounder so we can actually have it even out to 0.016666ish so we can be sure it's 60fps and not like 59 or 61. I would remend not trying to reference the FPS in the code unless fps_rounder has at least 50 items in it, just so you can be sure that stuttering won't mess it up.

function ticker(useless, metadata) {
  var media_time_diff = Math.abs(metadata.mediaTime - last_media_time);
  var frame_num_diff = Math.abs(metadata.presentedFrames - last_frame_num);
  var diff = media_time_diff / frame_num_diff;
  if (diff && diff < 1 && frame_not_seeked && fps_rounder.length < 50 && vid.playbackRate === 1 && document.hasFocus()) {
    fps_rounder.push(diff);
    fps = Math.round(1 / get_fps_average());
  }
  frame_not_seeked = true;
  last_media_time = metadata.mediaTime;
  last_frame_num = metadata.presentedFrames;
  vid.requestVideoFrameCallback(ticker);
}
vid.requestVideoFrameCallback(ticker);

Step 3: Stop seeking from messing it up

As you might have seen in the above code, it references frame_not_seeked. This is because when a user clicks and changes the timestamp of a video, it can sometimes mess things up since .mediaTime is changing a lot but .presentedFrames is only changing by 1. So when the video is seeked, we remove the last item from fps_rounder, and stop the next diff from being added to fps_rounder by setting frame_not_seeked to false, just to be safe.

vid.addEventListener("seeked", function () {
  fps_rounder.pop();
  frame_not_seeked = false;
});

Step 4: Make an averaging function

This is mentioned in step 2, so obviously we need it to exist. It's just a simple function that averages an array using .reduce(), in this case getting the average of fps_rounder and therefore an accurate FPS.

function get_fps_average() {
  return fps_rounder.reduce((a, b) => a + b) / fps_rounder.length;
}

Putting it all together

    // Part 1:
    var vid = document.querySelector("video");
    var last_media_time, last_frame_num, fps;
    var fps_rounder = [];
    var frame_not_seeked = true;
    // Part 2 (with some modifications):
    function ticker(useless, metadata) {
      var media_time_diff = Math.abs(metadata.mediaTime - last_media_time);
      var frame_num_diff = Math.abs(metadata.presentedFrames - last_frame_num);
      var diff = media_time_diff / frame_num_diff;
      if (
        diff &&
        diff < 1 &&
        frame_not_seeked &&
        fps_rounder.length < 50 &&
        vid.playbackRate === 1 &&
        document.hasFocus()
      ) {
        fps_rounder.push(diff);
        fps = Math.round(1 / get_fps_average());
        document.querySelector("p").textContent = "FPS: " + fps + ", certainty: " + fps_rounder.length * 2 + "%";
      }
      frame_not_seeked = true;
      last_media_time = metadata.mediaTime;
      last_frame_num = metadata.presentedFrames;
      vid.requestVideoFrameCallback(ticker);
    }
    vid.requestVideoFrameCallback(ticker);
    // Part 3:
    vid.addEventListener("seeked", function () {
      fps_rounder.pop();
      frame_not_seeked = false;
    });
    // Part 4:
    function get_fps_average() {
      return fps_rounder.reduce((a, b) => a + b) / fps_rounder.length;
    }
<p>The FPS will appear here!</p>
<video id="myVideo" width="320" height="176" controls>
  <source src="https://www.w3schools./tags/mov_bbb.mp4" type="video/mp4">
</video>

Have fun!

发布评论

评论列表(0)

  1. 暂无评论