VideoViewer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /**
  2. * Depends on Framework7
  3. */
  4. export class VideoViewer {
  5. //
  6. constructor(video, fps, frameCount, frameChanged) {
  7. this.video = video;
  8. this.fps = fps;
  9. this.frameCount = frameCount;
  10. this.currentFrame = 0;
  11. this.playing = false;
  12. this.timeout = null;
  13. this.currentKey = null;
  14. this.frameChanged = frameChanged;
  15. let that = this;
  16. video.css({
  17. 'position':'relative'
  18. });
  19. this.overflay = $$('<div>');
  20. this.overflay.css({
  21. 'display': 'flex',
  22. 'flex-shrink': '1',
  23. 'flex-direction': 'column',
  24. 'justify-content': 'flex-end',
  25. 'position':'absolute',
  26. 'width':'100%',
  27. 'height':'100%',
  28. 'left': '0',
  29. 'top': '0',
  30. 'color': 'white',
  31. 'font-size':'16px',
  32. //'z-index': '101'
  33. });
  34. video.parent().append(this.overflay);
  35. let row = $$('<div>');
  36. row.css({
  37. 'display': 'flex',
  38. 'flex-shrink': '1',
  39. 'flex-direction': 'row',
  40. 'justify-content': 'space-between',
  41. 'background-color': 'rgba(0,0,0,0.5)',
  42. 'color': 'white',
  43. 'padding': '2px',
  44. 'position':'absolute',
  45. 'bottom': '0',
  46. 'left': '0',
  47. 'width':'calc(100% - 4px)',
  48. 'z-index': '104'
  49. });
  50. this.overflay.append(row);
  51. // Play
  52. this.playButton = $$('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  53. this.playButton.click(function() {
  54. that.playing = !that.playing;
  55. if(that.playing) {
  56. that.playButton.html('<a class="link"><span class="icon material-icons">pause</span></a>');
  57. that.nextFrame();
  58. }
  59. else {
  60. that.playButton.html('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  61. }
  62. });
  63. // Stop
  64. let stopButton = $$('<a class="link"><span class="icon material-icons">stop</span></a>');
  65. stopButton.click(function() {
  66. that.stop();
  67. that.currentFrame=0;
  68. that.updateTime();
  69. });
  70. let prevButton = $$('<a class="link"><span class="icon material-icons">skip_previous</span></a>');
  71. prevButton.click(function() {
  72. that.stop();
  73. that.previousFrame();
  74. });
  75. let nextButton = $$('<a class="link"><span class="icon material-icons">skip_next</span></a>');
  76. nextButton.click(function() {
  77. that.stop();
  78. that.nextFrame();
  79. });
  80. document.addEventListener('keydown', function(key) {
  81. if(!that.currentKey) {
  82. that.stop();
  83. if (key.code=='ArrowRight' || key.code=='ArrowLeft') {
  84. that.currentKey = key.code;
  85. if (key.code=='ArrowRight') {
  86. that.playing = 1;
  87. that.nextFrame();
  88. }
  89. else if(key.code=='ArrowLeft') {
  90. that.playing = 2;
  91. that.previousFrame();
  92. }
  93. }
  94. }
  95. that.currentKey = key;
  96. });
  97. document.addEventListener('keyup', function(key) {
  98. that.currentKey = null;
  99. that.stop();
  100. });
  101. this.frameCounter = $$('<span>');
  102. // Video time changed
  103. video[0].ontimeupdate = function() {
  104. if(that.playing) {
  105. var delta = Date.now() - that.startTime;
  106. let frameTime = 1.0/that.fps*1000;
  107. frameTime -= delta;
  108. if(frameTime<0) {
  109. console.log('delayed by', -frameTime);
  110. frameTime = 0;
  111. }
  112. that.timeout = setTimeout(function() {
  113. if(that.playing===2) {
  114. that.previousFrame();
  115. }
  116. else if(that.playing===1 || that.playing===true) {
  117. that.nextFrame();
  118. }
  119. }, frameTime);
  120. }
  121. };
  122. let col1 = $$('<div>');
  123. col1.css({
  124. 'display': 'flex',
  125. 'flex-shrink': '1',
  126. 'flex-direction': 'row',
  127. });
  128. col1.append(this.playButton);
  129. col1.append(stopButton);
  130. row.append(col1);
  131. let mid0 = $$('<div>');
  132. mid0.css({
  133. 'display': 'flex',
  134. 'flex-shrink': '1',
  135. 'width': 'calc(100% - 200px)',
  136. 'flex-direction': 'row',
  137. });
  138. let mid = $$('<div class="range-slider">');
  139. let range = $$('<input type="range" min="0" max="'+(this.frameCount-1)+'" step="1" value="0">');
  140. mid.mousedown(function() {
  141. that.sliderMouseDown = true;
  142. });
  143. mid.mouseup(function() {
  144. that.sliderMouseDown = null;
  145. });
  146. mid.append(range);
  147. mid0.append(mid);
  148. row.append(mid0);
  149. this.slider = global.app.range.create({
  150. el: '.range-slider',
  151. on: {
  152. change: function () {
  153. if(that.sliderMouseDown) {
  154. that.stop();
  155. that.currentFrame=that.slider.getValue();
  156. that.updateTime();
  157. }
  158. }
  159. }
  160. });
  161. let col2 = $$('<div>');
  162. col2.css({
  163. 'display': 'flex',
  164. 'flex-shrink': '1',
  165. 'flex-direction': 'row',
  166. });
  167. col2.append(prevButton);
  168. col2.append(that.frameCounter);
  169. col2.append(nextButton);
  170. row.append(col2);
  171. that.updatePosition();
  172. }
  173. clean() {
  174. this.overflay.remove();
  175. }
  176. stop() {
  177. this.playing = false;
  178. clearTimeout(this.timeout);
  179. this.playButton.html('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  180. }
  181. previousFrame() {
  182. this.currentFrame--;
  183. if(this.currentFrame<0) {
  184. this.currentFrame=this.frameCount-1;
  185. }
  186. this.updateTime();
  187. }
  188. nextFrame() {
  189. this.currentFrame++;
  190. if(this.currentFrame>=this.frameCount) {
  191. this.currentFrame=0;
  192. }
  193. this.updateTime();
  194. }
  195. updatePosition() {
  196. this.slider.setValue(this.currentFrame);
  197. this.frameCounter.text((this.currentFrame+1)+'/'+this.frameCount);
  198. }
  199. updateTime() {
  200. let frameTime = 1.0/this.fps;
  201. let pos = this.currentFrame * frameTime + frameTime/2.0;
  202. this.startTime = Date.now();
  203. this.video[0].currentTime = pos;
  204. this.updatePosition();
  205. if(this.frameChanged) {
  206. this.frameChanged(this.currentFrame);
  207. }
  208. }
  209. }
  210. /*
  211. export class VideoViewer {
  212. //
  213. constructor(video, fps, frameCount) {
  214. this.video = video;
  215. this.fps = fps;
  216. this.frameCount = frameCount;
  217. this.currentFrame = 0;
  218. this.playing = false;
  219. this.timeout = null;
  220. this.currentKey = null;
  221. let that = this;
  222. video.css({
  223. 'position':'relative'
  224. });
  225. this.overflay = $$('<div>');
  226. this.overflay.css({
  227. 'display': 'flex',
  228. 'flex-shrink': '1',
  229. 'flex-direction': 'column',
  230. 'justify-content': 'flex-end',
  231. 'position':'absolute',
  232. 'width':'100%',
  233. 'height':'100%',
  234. 'left': '0',
  235. 'top': '0',
  236. 'color': 'white',
  237. 'font-size':'16px'
  238. });
  239. video.parent().append(this.overflay);
  240. let row = $$('<div>');
  241. row.css({
  242. 'display': 'flex',
  243. 'flex-shrink': '1',
  244. 'flex-direction': 'row',
  245. 'justify-content': 'space-between',
  246. 'background-color': 'rgba(0,0,0,0.5)',
  247. 'color': 'white',
  248. 'padding': '2px',
  249. });
  250. this.overflay.append(row);
  251. // Play
  252. this.playButton = $$('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  253. this.playButton.click(function() {
  254. that.playing = !that.playing;
  255. if(that.playing) {
  256. that.playButton.html('<a class="link"><span class="icon material-icons">pause</span></a>');
  257. that.nextFrame();
  258. }
  259. else {
  260. that.playButton.html('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  261. }
  262. });
  263. // Stop
  264. let stopButton = $$('<a class="link"><span class="icon material-icons">stop</span></a>');
  265. stopButton.click(function() {
  266. that.stop();
  267. that.currentFrame=0;
  268. that.updateTime();
  269. });
  270. let prevButton = $$('<a class="link"><span class="icon material-icons">skip_previous</span></a>');
  271. prevButton.click(function() {
  272. that.stop();
  273. that.previousFrame();
  274. });
  275. let nextButton = $$('<a class="link"><span class="icon material-icons">skip_next</span></a>');
  276. nextButton.click(function() {
  277. that.stop();
  278. that.nextFrame();
  279. });
  280. document.addEventListener('keydown', function(key) {
  281. if(!that.currentKey) {
  282. that.stop();
  283. if (key.code=='ArrowRight' || key.code=='ArrowLeft') {
  284. console.log('down OK');
  285. that.currentKey = key.code;
  286. if (key.code=='ArrowRight') {
  287. that.playing = 1;
  288. that.nextFrame();
  289. }
  290. else if(key.code=='ArrowLeft') {
  291. that.playing = 2;
  292. that.previousFrame();
  293. }
  294. }
  295. }
  296. that.currentKey = key;
  297. });
  298. document.addEventListener('keyup', function(key) {
  299. that.currentKey = null;
  300. that.stop();
  301. });
  302. this.frameCounter = $$('<span>');
  303. // Video time changed
  304. video[0].ontimeupdate = function() {
  305. console.log(that.playing);
  306. if(that.playing) {
  307. that.timeout = setTimeout(function() {
  308. if(that.playing===2) {
  309. that.previousFrame();
  310. }
  311. else if(that.playing===1 || that.playing===true) {
  312. that.nextFrame();
  313. }
  314. }, 1.0/that.fps*1000);
  315. }
  316. };
  317. let col1 = $$('<div>');
  318. col1.css({
  319. 'display': 'flex',
  320. 'flex-shrink': '1',
  321. 'flex-direction': 'row',
  322. });
  323. col1.append(this.playButton);
  324. col1.append(stopButton);
  325. row.append(col1);
  326. let mid0 = $$('<div>');
  327. mid0.css({
  328. 'display': 'flex',
  329. 'flex-shrink': '1',
  330. 'width': 'calc(100% - 200px)',
  331. 'flex-direction': 'row',
  332. });
  333. let mid = $$('<div class="range-slider">');
  334. let range = $$('<input type="range" min="0" max="'+(this.frameCount-1)+'" step="1" value="0">');
  335. mid.mousedown(function() {
  336. that.sliderMouseDown = true;
  337. });
  338. mid.mouseup(function() {
  339. that.sliderMouseDown = null;
  340. });
  341. mid.append(range);
  342. mid0.append(mid);
  343. row.append(mid0);
  344. this.slider = global.app.range.create({
  345. el: '.range-slider',
  346. on: {
  347. change: function () {
  348. if(that.sliderMouseDown) {
  349. that.stop();
  350. that.currentFrame=that.slider.getValue();
  351. that.updateTime();
  352. }
  353. }
  354. }
  355. });
  356. let col2 = $$('<div>');
  357. col2.css({
  358. 'display': 'flex',
  359. 'flex-shrink': '1',
  360. 'flex-direction': 'row',
  361. });
  362. col2.append(prevButton);
  363. col2.append(that.frameCounter);
  364. col2.append(nextButton);
  365. row.append(col2);
  366. that.updatePosition();
  367. }
  368. clean() {
  369. this.overflay.remove();
  370. }
  371. stop() {
  372. this.playing = false;
  373. clearTimeout(this.timeout);
  374. this.playButton.html('<a class="link"><span class="icon material-icons">play_arrow</span></a>');
  375. }
  376. previousFrame() {
  377. this.currentFrame--;
  378. if(this.currentFrame<0) {
  379. this.currentFrame=this.frameCount-1;
  380. }
  381. this.updateTime();
  382. }
  383. nextFrame() {
  384. this.currentFrame++;
  385. if(this.currentFrame>=this.frameCount) {
  386. this.currentFrame=0;
  387. }
  388. this.updateTime();
  389. }
  390. updatePosition() {
  391. this.slider.setValue(this.currentFrame);
  392. this.frameCounter.text((this.currentFrame+1)+'/'+this.frameCount);
  393. }
  394. updateTime() {
  395. let frameTime = 1.0/this.fps;
  396. let pos = this.currentFrame * frameTime + frameTime/2.0;
  397. this.video[0].currentTime = pos;
  398. this.updatePosition();
  399. }
  400. }
  401. */