1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview 7 * Class implement the video frame recorder extension client. 8 */ 9 10'use strict'; 11 12/** @suppress {duplicate} */ 13var remoting = remoting || {}; 14 15/** 16 * @constructor 17 * @param {remoting.ClientPlugin} plugin 18 */ 19remoting.VideoFrameRecorder = function(plugin) { 20 this.fileWriter_ = null; 21 this.isRecording_ = false; 22 this.plugin_ = plugin; 23}; 24 25/** 26 * Starts or stops video recording. 27 */ 28remoting.VideoFrameRecorder.prototype.startStopRecording = function() { 29 var data = {}; 30 if (this.isRecording_) { 31 this.isRecording_ = false; 32 data = { type: 'stop' } 33 34 chrome.fileSystem.chooseEntry( 35 {type: 'saveFile', suggestedName: 'videoRecording.pb'}, 36 this.onFileChosen_.bind(this)); 37 } else { 38 this.isRecording_ = true; 39 data = { type: 'start' } 40 } 41 this.plugin_.sendClientMessage('video-recorder', JSON.stringify(data)); 42} 43 44/** 45 * Returns true if the session is currently being recorded. 46 * @return {boolean} 47 */ 48remoting.VideoFrameRecorder.prototype.isRecording = function() { 49 return this.isRecording_; 50} 51 52/** 53 * Handles 'video-recorder' extension messages and returns true. Returns 54 * false for all other message types. 55 * @param {string} type Type of extension message. 56 * @param {string} data Content of the extension message. 57 * @return {boolean} 58 */ 59remoting.VideoFrameRecorder.prototype.handleMessage = 60 function(type, data) { 61 if (type != 'video-recorder') { 62 return false; 63 } 64 65 var message = getJsonObjectFromString(data); 66 var messageType = getStringAttr(message, 'type'); 67 var messageData = getStringAttr(message, 'data'); 68 69 if (messageType == 'next-frame-reply') { 70 if (!this.fileWriter_) { 71 console.log("Received frame but have no writer"); 72 return true; 73 } 74 if (!messageData) { 75 console.log("Finished receiving frames"); 76 this.fileWriter_ = null; 77 return true; 78 } 79 80 console.log("Received frame"); 81 /* jscompile gets confused if you refer to this as just atob(). */ 82 var videoPacketString = /** @type {string?} */ window.atob(messageData); 83 84 console.log("Converted from Base64 - length:" + videoPacketString.length); 85 var byteArrays = []; 86 87 for (var offset = 0; offset < videoPacketString.length; offset += 512) { 88 var slice = videoPacketString.slice(offset, offset + 512); 89 var byteNumbers = new Array(slice.length); 90 for (var i = 0; i < slice.length; i++) { 91 byteNumbers[i] = slice.charCodeAt(i); 92 } 93 var byteArray = new Uint8Array(byteNumbers); 94 byteArrays.push(byteArray); 95 } 96 97 console.log("Writing frame"); 98 videoPacketString = null; 99 /** 100 * Our current compiler toolchain only understands the old (deprecated) 101 * Blob constructor, which does not accept any parameters. 102 * TODO(wez): Remove this when compiler is updated (see crbug.com/405298). 103 * @suppress {checkTypes} 104 * @param {Array} parts 105 * @return {Blob} 106 */ 107 var makeBlob = function(parts) { 108 return new Blob(parts); 109 } 110 var videoPacketBlob = makeBlob(byteArrays); 111 byteArrays = null; 112 113 this.fileWriter_.write(videoPacketBlob); 114 115 return true; 116 } 117 118 console.log("Unrecognized message: " + messageType); 119 return true; 120} 121 122/** @param {FileEntry} fileEntry */ 123remoting.VideoFrameRecorder.prototype.onFileChosen_ = function(fileEntry) { 124 if (!fileEntry) { 125 console.log("Cancelled save of video frames."); 126 } else { 127 /** @type {function(string):void} */ 128 chrome.fileSystem.getDisplayPath(fileEntry, function(path) { 129 console.log("Saving video frames to:" + path); 130 }); 131 fileEntry.createWriter(this.onFileWriter_.bind(this)); 132 } 133} 134 135/** @param {FileWriter} fileWriter */ 136remoting.VideoFrameRecorder.prototype.onFileWriter_ = function(fileWriter) { 137 console.log("Obtained FileWriter for video frame write"); 138 fileWriter.onwriteend = this.onWriteComplete_.bind(this); 139 this.fileWriter_ = fileWriter; 140 this.fetchNextFrame_(); 141} 142 143remoting.VideoFrameRecorder.prototype.onWriteComplete_ = function(e) { 144 console.log("Video frame write complete"); 145 this.fetchNextFrame_(); 146} 147 148remoting.VideoFrameRecorder.prototype.fetchNextFrame_ = function() { 149 console.log("Request next video frame"); 150 var data = { type: 'next-frame' } 151 this.plugin_.sendClientMessage('video-recorder', JSON.stringify(data)); 152} 153