7

I'm writing a user script for downloading videos. The flash player of the web site use a JSON file.
The purpose of my script is to get the url of the video by downloading and parsing the video according to the web page. Currently it can download an extract the URL of the videos successfully.

The important part of the JSON file look like this :

    {
        "ammProfile": "AMM-HBBTV",
        "version": "VF",
        "versionProg": "1",
        "VFO": "HBBTV",
        "VMT": "mp4",
        "VUR": "http://vo.llnwd.net/v2/am/HBBTV/051332-074-A_SQ_2_VF_01464306_MP4-2200_AMM-HBBTV.mp4"
    }, {
        "ammProfile": "AMM-HBBTV",
        "version": "VF",
        "versionProg": "1",
        "VFO": "HBBTV",
        "VMT": "mp4",
        "VUR": "http://vo.llnwd.net/v2/am/HBBTV/051332-074-A_EQ_2_VF_01464315_MP4-1500_AMM-HBBTV.mp4"
    }

Both URL here are about the same video, this just is just the resolution which change.

So, How I can parse the relevant metadata without downloading the whole file? The standard for the H.264 video codec is very hard to read.

user2284570
  • 2,891
  • 3
  • 26
  • 74
  • Note : this look possible *(at least)* for audio files with [mp4js](https://github.com/lennart/mp4). There are no constraint about the same origin policy, so AJAX can work *(That's how I'm getting the url of the files)*. The link provided here are no the real links, because I can't share them publicly. The video encoding used for all the files is **H.264**, but software patents doesn't apply in my region, so it's fine to decode metadata. – user2284570 Sep 21 '14 at 15:50

3 Answers3

8

you don't need to load the whole video to get the height:

function getVideoHeight(url, fnCallback){

var video=document.createElement("video");
  video.autoplay=true;
  video.oncanplay=function(){
     fnCallback(this.offsetWidth, this.offsetHeight);
     this.src="about:blank";
     document.body.removeChild(video);   
  };

  document.body.appendChild(video);
  video.src=url;

}


//test:

getVideoHeight(
  "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4", 
  function(w,h){ alert( w+"X"+h); }
); // shows: "640X360"
dandavis
  • 16,370
  • 5
  • 40
  • 36
  • 1
    A large portion of the browser market doesn't support the H.264 video codec... The HTML5 solution require the browser to support the codec... – user2284570 Sep 21 '14 at 15:45
  • @user2284570: actually it's pretty much just firefox on OSX that would have trouble, but most mac users use Safari or Chrome anyway. if you have a flash player, you can do the same style of load-check-unload using flash. see http://caniuse.com/#feat=mpeg4 for browser details. – dandavis Sep 21 '14 at 18:45
  • Ok : according to you 30% of peoples who use internet is a negligible share? The purpose of my user script is to avoid using flash on their site. – user2284570 Sep 21 '14 at 19:09
  • can i use pegs H.264 support 84%, and we can add an additional 8% to that in a few weeks as firefox adds H.264 for all in the next popular release. i never said it's negligible, but at <10%, I wouldn't consider the compat hole to be "a large portion". For that last 10%, you can try to fallback to flash using the same simple stratgy as my HTML5 code demonstrates. Other wise, you'll be looking at proof of concept scripts, not mainstream simple utilities... – dandavis Sep 21 '14 at 23:19
  • [Ok](http://caniuse.com/#feat=mpeg4) : It seems chrome usage is increasing. but It include chromium as supporting mp4 which is not always true. and 10% still represent million of users. Similarly, many web site bowser with workaround for Opera. I won't provide flash fallback, since the purpose of my user script is to avoid using it. [Swiffy](https://www.google.com/doubleclick/studio/swiffy/) seems to have a tiny JavaScript only wrapper for playing flash videos *(which often use H.264)*. but I can't find how to use it due to Google's obfuscator. – user2284570 Sep 22 '14 at 23:00
3

You can use the Range HTTP header via XMLHttpRequest to get only a portion of the file:

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

For example:

xhr.setRequestHeader ('Range', 'bytes=0-' + (fragment_size - 1))
xhr.setRequestHeader ('Content-Length', fragment_size) // This part isn't absolutely required on most (all?) browsers.
Agamemnus
  • 1,395
  • 4
  • 17
  • 41
  • ... and then parse the fragment of binary (MP4) data in mostly unprivileged JavaScript? How does one do *that* exactly. Can you somehow used typed arrays to get at the bytes? – David-SkyMesh Sep 21 '14 at 03:31
  • 1
    It would be a problem if the videos were.. "downloaded".. from the user, but presumably we're using Ajax here, which can definitely read data as bytes and feed it into typed arrays. Even if it was entirely client-side (no internet access), you could perhaps install some sort of fake HTTP server... – Agamemnus Sep 21 '14 at 07:31
  • @Agamemnus : It looks like [very hard](http://stackoverflow.com/a/6477652/2284570). – user2284570 Sep 28 '14 at 14:43
2

I use the xhr range header to download partial content, and then get the file info using videoconverter.js, a JS version of ffmpeg (whose license you should check if you plan to use any of this).

var videoUrl = 'https://dl.dropboxusercontent.com/u/17698405/bla.mp4';

var cmd = '-i myfile.mp4';
var args = parseArguments(cmd);
var sizeToDownload = 200*1024;

retrieveVideo(videoUrl, sizeToDownload, function(fileData) {
 ffmpeg_run({
  arguments: args,
  files: [{
   name: 'myfile.mp4',
   data: fileData
  }],
  print: print,
  printErr: print 
 }); 
});

function parseArguments(text) {
  text = text.replace(/\s+/g, ' ');
  var args = [];
  text.split('"').forEach(function(t, i) {
    t = t.trim();
    if ((i % 2) === 1) {
      args.push(t);
    } else {
      args = args.concat(t.split(" "));
    }
  });
  return args;
}


function retrieveVideo(path, fragment_size, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", path, true);
  xhr.responseType = "arraybuffer";
  xhr.setRequestHeader ('Range', 'bytes=0-' + (fragment_size - 1));

  xhr.onload = function (oEvent) {
    var arrayBuffer = xhr.response;
    if (arrayBuffer) {
      callback(new Uint8Array(arrayBuffer));
    }
  };

  xhr.send(null);
}

var textarea = document.getElementsByTagName('textarea')[0];

function print(text) {
 textarea.value += '> ' + text + '\n';
}
* { box-sizing: border-box }
html,body { height: 100%; margin: 0; padding: 0; overflow: hidden }
textarea { width: 100%; height: 100%; }
<script src="https://rawgit.com/bgrins/videoconverter.js/master/build/ffmpeg-all-codecs.js"></script>
<textarea></textarea>
antishok
  • 2,910
  • 16
  • 21