diff --git a/bricks/recorder.js b/bricks/recorder.js new file mode 100644 index 0000000..c102f9a --- /dev/null +++ b/bricks/recorder.js @@ -0,0 +1,173 @@ +var bricks = window.bricks || {}; + +bricks.MediaRecorder = class extends bricks.Popup { + constructor(opts){ + super(opts); + opts.fps = opts.fps || 60; + this.task_period = 1 / this.fps; + this.task = null; + this.stream = null; + this.normal_stop = false; + this.mimetype = 'audio/wav'; + this.preview = new bricks.VBox({width: '100%', css: 'filler'}); + this.controls = new bricks.HBox({width: '100%', cheight: 2.5}); + this.toggle_record = new bricks.Svg({ + url: bricks_resource('/imgs/start_recording.svg'), + tip: 'start or stop record', + rate: 2 + }); + this.timepass = new bricks.Text({text:'', cheight: 10}); + var filler = new bricks.HFiller({}); + var cancel = new bricks.Svg({ + url: '/imgs/delete.svg', + rate:2, + tip: 'cancel recording'}); + cancel.bind('click', this.cancel_record.bind(this)) + this.add_widget(this.preview); + this.add_widget(this.controls); + this.controls.add_widget(this.toggle_record); + this.controls.add_widget(this.timepass); + this.controls.add_widget(filler); + this.controls.add_widget(cancel); + this.record_status = 'standby'; + this.toggle_record.bind('click', this.switch_record.bind(this)); + this.toggle_record.disabled(true); + schedule_once(this.open_recorder.bind(this), 0.1); + } + async switch_record(){ + console.log('toggle_record called'); + } + cancel_record(){ + this.toggle_record.disabled = true; + this.close_recorder(); + } + async open_recorder(){ + console.debug('open recorder for record'); + } + async start_recorder(){ + this.normal_stop = false; + this.mediaRecorder = new MediaRecorder(stream, + {mimeType: this.mimetype}); + this.recordedChunks = []; + this.mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + this.recordedChunks.push(event.data); + this.time_diff = bricks.timeDiff(this.start_time); + this.timepass.set_text(this.time_diff); + } + }; + this.mediaRecorder.onstop = async () => { + if (!this.normal_stop) return; + const blob = new Blob(this.recordedChunks, + { type: this.mimetype }); + // 1. 在本地播放 + const videoURL = URL.createObjectURL(blob); + // 2. 转成 File 对象 + const file = new File([blob], + "recorded_video.webm", + { type: this.mimetype }); + this.dispatch('record_end', file); + }; + + this.start_time = Date.now(); + this.mediaRecorder.start(); + this.dispatch('record_started') + console.log("Recording started..."); + } + + stop_recorder(){ + this.normal_stop = true; + this.time_diff = bricks.timeDiff(this.start_time); + this.mediaRecorder.stop(); + this.timepass.set_text(this.time_diff); + this.mediaRecorder = null; + this.close_recorder(); + console.log("Recording stopped."); + } + + close_recorder(){ + if (this.stream){ + if this.mediaRecorder){ + this.mediaRecorder.stop(); + this.mediaRecorder = null; + } + this.stream.getTracks().forEach(track => track.stop()); + this.stream = null; + } + } +} + +class bricks.WidgetRecorder = bricks.MediaRecorder { + async open_recorder(){ + var widget = bricks.getWidgetById(this.opts.widgetid,bricks.app); + if (widget.dom_element.tagName == 'VIDEO'){ + this.mimetype = 'video/mp4'; + } else if (widget.dom_element.tagName == 'AUDIO'){ + this.mimetype = 'audio/wav'; + } else { + throw 'Error, not a media element'; + } + this.stream = source.captureStream(); + } +} +class bricks.SysAudioRecorder = bricks.MediaRecorder { + async open_recorder(){ + var options = {} + options.audio = true; + this.mimetype = 'audio/wav'; + this.stream = await navigator.mediaDevices.getUserMedia(options); + this.toggle_record.disabled(false); + this.preview.disabled(true); + } +} +class bricks.SysVideoRecorder = bricks.MediaRecorder { + async open_recorder(){ + var options = { + audio: true, + video: true + } + this.mimetype = 'video/mp4'; + this.stream = await navigator.mediaDevices.getUserMedia(options); + this.imgw = new bricks.Image({width: '100%'}); + this.preview.add_widget(imgw); + this.task = schedule_once(this.show_picture.bind(this), this.task_period); + } + show_picture(){ + if (this.task_period == 0){ + return; + } + var canvas = document.createElement('canvas'); + canvas.height = this.video.videoHeight; + canvas.width = this.video.videoWidth; + const context = canvas.getContext('2d'); + context.drawImage(this.video, 0, 0); + this.dataurl = canvas.toDataURL('image/jpeg', 0.95); + this.imgw.set_url(this.dataurl); + this.task = schedule_once(this.show_picture.bind(this), + this.task_period); + this.show_cnt += 1; + } + close_recorder(){ + super.close_recorder(); + if (this.task){ + this.task.cancel(); + this.task = null; + } + } +} +class bricks.TakePhoto= bricks.SysVideoRecorder { + switch_record(){ + event.stopPropagation(); + if (this.task){ + task.cancel(); + this.task = null; + } + this.task_period = 0; + this.task = null; + this.dispatch('shot', this.dataurl); + } +} +bricks.Factory.register('SysCamera', bricks.SysCamera); +bricks.Factory.register('WidgetRecorder', bricks.WidgetRecorder); +bricks.Factory.register('SysAudioRecorder', bricks.SysAudioRecorder); +bricks.Factory.register('SysVideoRecorder', bricks.SysVideoRecorder);