ajax-progressive.js 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. JSMpeg.Source.AjaxProgressive = (function(){ "use strict";
  2. var AjaxProgressiveSource = function(url, options) {
  3. this.url = url;
  4. this.destination = null;
  5. this.request = null;
  6. this.completed = false;
  7. this.established = false;
  8. this.progress = 0;
  9. this.fileSize = 0;
  10. this.loadedSize = 0;
  11. this.chunkSize = options.chunkSize || 1024*1024;
  12. this.isLoading = false;
  13. this.loadStartTime = 0;
  14. this.throttled = options.throttled !== false;
  15. this.aborted = false;
  16. };
  17. AjaxProgressiveSource.prototype.connect = function(destination) {
  18. this.destination = destination;
  19. };
  20. AjaxProgressiveSource.prototype.start = function() {
  21. this.request = new XMLHttpRequest();
  22. this.request.onreadystatechange = function() {
  23. if (this.request.readyState === this.request.DONE) {
  24. this.fileSize = parseInt(
  25. this.request.getResponseHeader("Content-Length")
  26. );
  27. this.loadNextChunk();
  28. }
  29. }.bind(this);
  30. this.request.onprogress = this.onProgress.bind(this);
  31. this.request.open('HEAD', this.url);
  32. this.request.send();
  33. };
  34. AjaxProgressiveSource.prototype.resume = function(secondsHeadroom) {
  35. if (this.isLoading || !this.throttled) {
  36. return;
  37. }
  38. // Guess the worst case loading time with lots of safety margin. This is
  39. // somewhat arbitrary...
  40. var worstCaseLoadingTime = this.loadTime * 8 + 2;
  41. if (worstCaseLoadingTime > secondsHeadroom) {
  42. this.loadNextChunk();
  43. }
  44. };
  45. AjaxProgressiveSource.prototype.destroy = function() {
  46. this.request.abort();
  47. this.aborted = true;
  48. };
  49. AjaxProgressiveSource.prototype.loadNextChunk = function() {
  50. var start = this.loadedSize,
  51. end = Math.min(this.loadedSize + this.chunkSize-1, this.fileSize-1);
  52. if (start >= this.fileSize || this.aborted) {
  53. this.completed = true;
  54. return;
  55. }
  56. this.isLoading = true;
  57. this.loadStartTime = JSMpeg.Now();
  58. this.request = new XMLHttpRequest();
  59. this.request.onreadystatechange = function() {
  60. if (
  61. this.request.readyState === this.request.DONE &&
  62. this.request.status >= 200 && this.request.status < 300
  63. ) {
  64. this.onChunkLoad(this.request.response);
  65. }
  66. else if (this.request.readyState === this.request.DONE) {
  67. // Retry?
  68. if (this.loadFails++ < 3) {
  69. this.loadNextChunk();
  70. }
  71. }
  72. }.bind(this);
  73. if (start === 0) {
  74. this.request.onprogress = this.onProgress.bind(this);
  75. }
  76. this.request.open('GET', this.url+'?'+start+"-"+end);
  77. this.request.setRequestHeader("Range", "bytes="+start+"-"+end);
  78. this.request.responseType = "arraybuffer";
  79. this.request.send();
  80. };
  81. AjaxProgressiveSource.prototype.onProgress = function(ev) {
  82. this.progress = (ev.loaded / ev.total);
  83. };
  84. AjaxProgressiveSource.prototype.onChunkLoad = function(data) {
  85. this.established = true;
  86. this.progress = 1;
  87. this.loadedSize += data.byteLength;
  88. this.loadFails = 0;
  89. this.isLoading = false;
  90. if (this.destination) {
  91. this.destination.write(data);
  92. }
  93. this.loadTime = JSMpeg.Now() - this.loadStartTime;
  94. if (!this.throttled) {
  95. this.loadNextChunk();
  96. }
  97. };
  98. return AjaxProgressiveSource;
  99. })();