7

I am trying to upload a video to server, and on client end. I am reading it using FileReader's readAsBinaryString().

Now, my problem is, I don't know how to read duration of this video file.

If i try reading the file, and assigning the reader's data to a video tag's source, then none of the events associated to the video tag are fired. I need to find the duration of file uploaded on client end.

Can somebody please suggest me something?

ShivangiBilora
  • 2,912
  • 4
  • 20
  • 27

2 Answers2

18

You can do something like this for that to work:

  • read the file as ArrayBuffer (this can be posted directly to server as a binary stream later)
  • wrap it in a Blob object
  • create an object URL for the blob
  • and finally set the url as the video source.

When the video object triggers the loadedmetadata event you should be able to read the duration.

You could use data-uri too, but notice that browsers may apply size limits (as well as other disadvantages) for them which is essential when it comes to video files, and there is a significant encoding/decoding overhead due to the Base-64 process.

Example

Select a video file you know the browser can handle (in production you should of course filter accepted file types based on video.canPlayType()).

The duration will show after the above steps has performed (no error handling included in the example, adjust as needed).

var fileEl = document.querySelector("input");
fileEl.onchange = function(e) {

  var file = e.target.files[0],                               // selected file
      mime = file.type,                                       // store mime for later
      rd = new FileReader();                                  // create a FileReader
  
  rd.onload = function(e) {                                   // when file has read:
    
    var blob = new Blob([e.target.result], {type: mime}),     // create a blob of buffer
        url = (URL || webkitURL).createObjectURL(blob),       // create o-URL of blob
        video = document.createElement("video");              // create video element

    video.preload = "metadata";                               // preload setting
    video.addEventListener("loadedmetadata", function() {     // when enough data loads
      document.querySelector("div")
          .innerHTML = "Duration: " + video.duration + "s";   // show duration
      (URL || webkitURL).revokeObjectURL(url);                // clean up

      // ... continue from here ...

    });
    video.src = url;                                          // start video load
  };
  rd.readAsArrayBuffer(file);                                 // read file object
};
<input type="file"><br><div></div>
Community
  • 1
  • 1
  • 2
    Worth to notice as well: very large (~250 mb+) video files are not suited for this. For those, upload directly to server, load the video file back from server as source for a video element, let browser cache it (metadata), extract duration and send back time to update server/database with time (or use ffmpeg or something similar on server side to read out the time from the file directly). There is the option of manually parsing the file header, but that would be out of scope for SO. –  May 06 '15 at 10:48
  • @K3N +1 Lifesaver. This is the only working solution I could find without having to load the whole video into memory. How easy would it be to extract an image from the video using a solution similar to this? – GFoley83 Apr 22 '16 at 04:09
  • @GFoley83 it's pretty straight forward. Just set the time at where you want to grab a frame using `currentTime`. When the `timeupdate` event triggers simply draw to a canvas element using the video element as image source. –  Apr 22 '16 at 14:12
  • @K3N Thanks. I remember trying that before and having issues with tainted canvas but I'll give it another go. For your solution above, is there any work around for really large files without having to upload them first? I just need the video height E.g. Like maybe taking a chunk from the middle of the `File` object and passing that to the `FileReader`? – GFoley83 Apr 22 '16 at 18:47
  • 1
    @K3N @Shivi I've run some tests on reading the bytes from the start of video to get the metadata. If you add `file = file.slice(0, 500000); // .5MB` before `rd.readAsArrayBuffer(file);` you can parse the metadata (which should always be located at the start of the file for video) from even large files e.g. > 12GB in my tests, as you're only loading a small chunk. Example here: https://jsfiddle.net/gavinfoley/9saxaoak/ – GFoley83 Apr 24 '16 at 08:02
  • This gives like twenty 404 requests for me. – Zane Hitchcox Jan 14 '18 at 17:03
  • 1
    URL.createObjectURL can be used directly on the `file`, at least in 2018 with modern browsers. – Albin Apr 27 '18 at 03:24
6

you can do something like below, the trick is to use readAsDataURL:

var reader = new FileReader();
reader.onload = function() {
    var media = new Audio(reader.result);
    media.onloadedmetadata = function(){
         media.duration; // this would give duration of the video/audio file
    };    
};
reader.readAsDataURL(file); 

Fiddle Demo

mido
  • 24,198
  • 15
  • 92
  • 117
  • 3
    Just note that data-uri's has size limits (varies depending on browser) which may be essential with video files, and has a noticeable encoding/decoding (base-64) overhead. –  May 06 '15 at 10:21