├── .editorconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── server.js
├── src
├── ReactH265Player.js
├── common.js
├── decoder.js
├── downloader.js
├── libffmpeg.js
├── libffmpeg.wasm
├── pcm-player.js
├── player.js
└── webgl.js
├── webpack.config.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | indent_size = 2
7 |
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | lib
3 | scripts
4 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
5 |
6 | # dependencies
7 | /node_modules
8 | /.pnp
9 | .pnp.js
10 |
11 | # testing
12 | /coverage
13 |
14 | # production
15 | /build
16 |
17 | # misc
18 | .DS_Store
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 |
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .editorconfig
2 | .eslintrc
3 | .travis.yml
4 |
5 | karma.conf.js
6 | server.js
7 | webpack.config.js
8 |
9 | demo
10 | scripts
11 | src
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache:
3 | directories:
4 | - node_modules
5 | notifications:
6 | email: false
7 | node_js:
8 | - "iojs"
9 | - "4.1"
10 | - "4.0"
11 | - "0.12"
12 | - "0.11"
13 | - "0.10"
14 |
15 | env:
16 | global:
17 | - NODE_ENV=test
18 |
19 | before_install:
20 | - npm i -g npm@^2.0.0
21 | script:
22 | - npm test
23 | before_script:
24 | - npm prune
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Bhargav Anand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React H265 WASM Player
2 |
3 | A higher-level react component to play h265 video
4 |
5 |
6 | ## About
7 |
8 | ## Usage
9 |
10 | ### NPM and Webpack/Browserify
11 | Install via `npm`. Use `--save` to include it in your *package.json*.
12 |
13 | ```bash
14 | npm install react-h265-wasm-player
15 | ```
16 |
17 | Start by importing/requiring react-h265-wasm-player within your react code.
18 |
19 | ```js
20 | import ReactH265Player from 'react-h265-wasm-player';
21 |
22 | import React, { useState } from 'react';
23 | import ReactH265Player from './ReactH265Player'
24 |
25 | function App() {
26 | const [playerRef, setPlayerRef] = useState(null);
27 |
28 | return (
29 |
57 | );
58 | }
59 |
60 | export default App;
61 | ```
62 |
63 | ### In `index.html`
64 |
65 | Because decode job is distributed in JS worker, for better performance
66 | You need to add these to your html container.
67 |
68 | ```
69 |
70 |
71 |
72 |
73 | ```
74 |
75 | ## Todo
76 | - Add vendor prefixes
77 | - Find a good way for wasm loading
78 |
79 | ## Issues
80 | Feel free to contribute. Submit a Pull Request or open an issue for further discussion.
81 |
82 | ## License
83 | MIT © [tcper][tcper]
84 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-h265-wasm-player",
3 | "version": "0.0.1",
4 | "description": "Workable h265 player for react",
5 | "main": "src/ReactH265Player",
6 | "scripts": {},
7 | "repository": {
8 | "type": "git",
9 | "url": "git+git@github.com:tcper/react-h265-wasm-player.git"
10 | },
11 | "author": "whoisandie",
12 | "license": "MIT",
13 | "bugs": {
14 | "url": "https://github.com/tcper/react-h265-wasm-player/issues"
15 | },
16 | "homepage": "https://github.com/tcper/react-h265-wasm-player",
17 | "dependencies": {
18 | "react": "^16.13.1",
19 | "react-dom": "^16.13.1"
20 | },
21 | "devDependencies": {
22 | "webpack": "^4.42.1",
23 | "webpack-dev-server": "^3.10.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | contentBase: __dirname,
7 | hot: true,
8 | inline: true,
9 | stats: {
10 | chunkModules: false,
11 | colors: true,
12 | },
13 | }).listen(config.port, config.ip, function (err) {
14 | if (err) {
15 | console.log(err);
16 | }
17 |
18 | console.log('Listening at ' + config.ip + ':' + config.port);
19 | });
20 |
--------------------------------------------------------------------------------
/src/ReactH265Player.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | class ReactH265Player extends Component {
4 | ref = React.createRef();
5 | player = new window.Player();
6 | state = {
7 | width: this.props.width,
8 | height: this.props.height
9 | };
10 |
11 | componentDidMount() {
12 | // console.log(this.ref.current);
13 | const {
14 | passRef,
15 | url = "",
16 | bufferSize = 512 * 1024,
17 | isStream = false,
18 | errorHandler
19 | } = this.props;
20 | if (passRef) {
21 | passRef(this.player);
22 | }
23 | this.player.play(url, this.ref.current, errorHandler, bufferSize, isStream);
24 | }
25 |
26 | componentWillUnmount() {
27 | this.player.stop();
28 | }
29 |
30 | render() {
31 | const { width, height } = this.state;
32 | return ;
33 | }
34 | }
35 |
36 | export default ReactH265Player;
37 |
--------------------------------------------------------------------------------
/src/common.js:
--------------------------------------------------------------------------------
1 | //Player request.
2 | const kPlayVideoReq = 0;
3 | const kPauseVideoReq = 1;
4 | const kStopVideoReq = 2;
5 |
6 | //Player response.
7 | const kPlayVideoRsp = 0;
8 | const kAudioInfo = 1;
9 | const kVideoInfo = 2;
10 | const kAudioData = 3;
11 | const kVideoData = 4;
12 |
13 | //Downloader request.
14 | const kGetFileInfoReq = 0;
15 | const kDownloadFileReq = 1;
16 | const kCloseDownloaderReq = 2;
17 |
18 | //Downloader response.
19 | const kGetFileInfoRsp = 0;
20 | const kFileData = 1;
21 |
22 | //Downloader Protocol.
23 | const kProtoHttp = 0;
24 | const kProtoWebsocket = 1;
25 |
26 | //Decoder request.
27 | const kInitDecoderReq = 0;
28 | const kUninitDecoderReq = 1;
29 | const kOpenDecoderReq = 2;
30 | const kCloseDecoderReq = 3;
31 | const kFeedDataReq = 4;
32 | const kStartDecodingReq = 5;
33 | const kPauseDecodingReq = 6;
34 | const kSeekToReq = 7;
35 |
36 | //Decoder response.
37 | const kInitDecoderRsp = 0;
38 | const kUninitDecoderRsp = 1;
39 | const kOpenDecoderRsp = 2;
40 | const kCloseDecoderRsp = 3;
41 | const kVideoFrame = 4;
42 | const kAudioFrame = 5;
43 | const kStartDecodingRsp = 6;
44 | const kPauseDecodingRsp = 7;
45 | const kDecodeFinishedEvt = 8;
46 | const kRequestDataEvt = 9;
47 | const kSeekToRsp = 10;
48 |
49 | function Logger(module) {
50 | this.module = module;
51 | }
52 |
53 | Logger.prototype.log = function (line) {
54 | console.log("[" + this.currentTimeStr() + "][" + this.module + "]" + line);
55 | }
56 |
57 | Logger.prototype.logError = function (line) {
58 | console.log("[" + this.currentTimeStr() + "][" + this.module + "][ER] " + line);
59 | }
60 |
61 | Logger.prototype.logInfo = function (line) {
62 | console.log("[" + this.currentTimeStr() + "][" + this.module + "][IF] " + line);
63 | }
64 |
65 | Logger.prototype.logDebug = function (line) {
66 | console.log("[" + this.currentTimeStr() + "][" + this.module + "][DT] " + line);
67 | }
68 |
69 | Logger.prototype.currentTimeStr = function () {
70 | var now = new Date(Date.now());
71 | var year = now.getFullYear();
72 | var month = now.getMonth() + 1;
73 | var day = now.getDate();
74 | var hour = now.getHours();
75 | var min = now.getMinutes();
76 | var sec = now.getSeconds();
77 | var ms = now.getMilliseconds();
78 | return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec + ":" + ms;
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/src/decoder.js:
--------------------------------------------------------------------------------
1 | self.Module = {
2 | onRuntimeInitialized: function () {
3 | onWasmLoaded();
4 | }
5 | };
6 |
7 | self.importScripts("common.js");
8 | self.importScripts("libffmpeg.js");
9 |
10 | function Decoder() {
11 | this.logger = new Logger("Decoder");
12 | this.coreLogLevel = 1;
13 | this.accurateSeek = true;
14 | this.wasmLoaded = false;
15 | this.tmpReqQue = [];
16 | this.cacheBuffer = null;
17 | this.decodeTimer = null;
18 | this.videoCallback = null;
19 | this.audioCallback = null;
20 | this.requestCallback = null;
21 | }
22 |
23 | Decoder.prototype.initDecoder = function (fileSize, chunkSize) {
24 | var ret = Module._initDecoder(fileSize, this.coreLogLevel);
25 | this.logger.logInfo("initDecoder return " + ret + ".");
26 | if (0 == ret) {
27 | this.cacheBuffer = Module._malloc(chunkSize);
28 | }
29 | var objData = {
30 | t: kInitDecoderRsp,
31 | e: ret
32 | };
33 | self.postMessage(objData);
34 | };
35 |
36 | Decoder.prototype.uninitDecoder = function () {
37 | var ret = Module._uninitDecoder();
38 | this.logger.logInfo("Uninit ffmpeg decoder return " + ret + ".");
39 | if (this.cacheBuffer != null) {
40 | Module._free(this.cacheBuffer);
41 | this.cacheBuffer = null;
42 | }
43 | };
44 |
45 | Decoder.prototype.openDecoder = function () {
46 | var paramCount = 7, paramSize = 4;
47 | var paramByteBuffer = Module._malloc(paramCount * paramSize);
48 | var ret = Module._openDecoder(paramByteBuffer, paramCount, this.videoCallback, this.audioCallback, this.requestCallback);
49 | this.logger.logInfo("openDecoder return " + ret);
50 |
51 | if (ret == 0) {
52 | var paramIntBuff = paramByteBuffer >> 2;
53 | var paramArray = Module.HEAP32.subarray(paramIntBuff, paramIntBuff + paramCount);
54 | var duration = paramArray[0];
55 | var videoPixFmt = paramArray[1];
56 | var videoWidth = paramArray[2];
57 | var videoHeight = paramArray[3];
58 | var audioSampleFmt = paramArray[4];
59 | var audioChannels = paramArray[5];
60 | var audioSampleRate = paramArray[6];
61 |
62 | var objData = {
63 | t: kOpenDecoderRsp,
64 | e: ret,
65 | v: {
66 | d: duration,
67 | p: videoPixFmt,
68 | w: videoWidth,
69 | h: videoHeight
70 | },
71 | a: {
72 | f: audioSampleFmt,
73 | c: audioChannels,
74 | r: audioSampleRate
75 | }
76 | };
77 | self.postMessage(objData);
78 | } else {
79 | var objData = {
80 | t: kOpenDecoderRsp,
81 | e: ret
82 | };
83 | self.postMessage(objData);
84 | }
85 | Module._free(paramByteBuffer);
86 | };
87 |
88 | Decoder.prototype.closeDecoder = function () {
89 | this.logger.logInfo("closeDecoder.");
90 | if (this.decodeTimer) {
91 | clearInterval(this.decodeTimer);
92 | this.decodeTimer = null;
93 | this.logger.logInfo("Decode timer stopped.");
94 | }
95 |
96 | var ret = Module._closeDecoder();
97 | this.logger.logInfo("Close ffmpeg decoder return " + ret + ".");
98 |
99 | var objData = {
100 | t: kCloseDecoderRsp,
101 | e: 0
102 | };
103 | self.postMessage(objData);
104 | };
105 |
106 | Decoder.prototype.startDecoding = function (interval) {
107 | //this.logger.logInfo("Start decoding.");
108 | if (this.decodeTimer) {
109 | clearInterval(this.decodeTimer);
110 | }
111 | this.decodeTimer = setInterval(this.decode, interval);
112 | };
113 |
114 | Decoder.prototype.pauseDecoding = function () {
115 | //this.logger.logInfo("Pause decoding.");
116 | if (this.decodeTimer) {
117 | clearInterval(this.decodeTimer);
118 | this.decodeTimer = null;
119 | }
120 | };
121 |
122 | Decoder.prototype.decode = function () {
123 | var ret = Module._decodeOnePacket();
124 | if (ret == 7) {
125 | self.decoder.logger.logInfo("Decoder finished.");
126 | self.decoder.pauseDecoding();
127 | var objData = {
128 | t: kDecodeFinishedEvt,
129 | };
130 | self.postMessage(objData);
131 | }
132 |
133 | while (ret == 9) {
134 | //self.decoder.logger.logInfo("One old frame");
135 | ret = Module._decodeOnePacket();
136 | }
137 | };
138 |
139 | Decoder.prototype.sendData = function (data) {
140 | var typedArray = new Uint8Array(data);
141 | Module.HEAPU8.set(typedArray, this.cacheBuffer);
142 | Module._sendData(this.cacheBuffer, typedArray.length);
143 | };
144 |
145 | Decoder.prototype.seekTo = function (ms) {
146 | var accurateSeek = this.accurateSeek ? 1 : 0;
147 | var ret = Module._seekTo(ms, accurateSeek);
148 | var objData = {
149 | t: kSeekToRsp,
150 | r: ret
151 | };
152 | self.postMessage(objData);
153 | };
154 |
155 | Decoder.prototype.processReq = function (req) {
156 | //this.logger.logInfo("processReq " + req.t + ".");
157 | switch (req.t) {
158 | case kInitDecoderReq:
159 | this.initDecoder(req.s, req.c);
160 | break;
161 | case kUninitDecoderReq:
162 | this.uninitDecoder();
163 | break;
164 | case kOpenDecoderReq:
165 | this.openDecoder();
166 | break;
167 | case kCloseDecoderReq:
168 | this.closeDecoder();
169 | break;
170 | case kStartDecodingReq:
171 | this.startDecoding(req.i);
172 | break;
173 | case kPauseDecodingReq:
174 | this.pauseDecoding();
175 | break;
176 | case kFeedDataReq:
177 | this.sendData(req.d);
178 | break;
179 | case kSeekToReq:
180 | this.seekTo(req.ms);
181 | break;
182 | default:
183 | this.logger.logError("Unsupport messsage " + req.t);
184 | }
185 | };
186 |
187 | Decoder.prototype.cacheReq = function (req) {
188 | if (req) {
189 | this.tmpReqQue.push(req);
190 | }
191 | };
192 |
193 | Decoder.prototype.onWasmLoaded = function () {
194 | this.logger.logInfo("Wasm loaded.");
195 | this.wasmLoaded = true;
196 |
197 | this.videoCallback = Module.addFunction(function (buff, size, timestamp) {
198 | var outArray = Module.HEAPU8.subarray(buff, buff + size);
199 | var data = new Uint8Array(outArray);
200 | var objData = {
201 | t: kVideoFrame,
202 | s: timestamp,
203 | d: data
204 | };
205 | self.postMessage(objData, [objData.d.buffer]);
206 | }, 'viid');
207 |
208 | this.audioCallback = Module.addFunction(function (buff, size, timestamp) {
209 | var outArray = Module.HEAPU8.subarray(buff, buff + size);
210 | var data = new Uint8Array(outArray);
211 | var objData = {
212 | t: kAudioFrame,
213 | s: timestamp,
214 | d: data
215 | };
216 | self.postMessage(objData, [objData.d.buffer]);
217 | }, 'viid');
218 |
219 | this.requestCallback = Module.addFunction(function (offset, availble) {
220 | var objData = {
221 | t: kRequestDataEvt,
222 | o: offset,
223 | a: availble
224 | };
225 | self.postMessage(objData);
226 | }, 'vii');
227 |
228 | while (this.tmpReqQue.length > 0) {
229 | var req = this.tmpReqQue.shift();
230 | this.processReq(req);
231 | }
232 | };
233 |
234 | self.decoder = new Decoder;
235 |
236 | self.onmessage = function (evt) {
237 | if (!self.decoder) {
238 | console.log("[ER] Decoder not initialized!");
239 | return;
240 | }
241 |
242 | var req = evt.data;
243 | if (!self.decoder.wasmLoaded) {
244 | self.decoder.cacheReq(req);
245 | self.decoder.logger.logInfo("Temp cache req " + req.t + ".");
246 | return;
247 | }
248 |
249 | self.decoder.processReq(req);
250 | };
251 |
252 | function onWasmLoaded() {
253 | if (self.decoder) {
254 | self.decoder.onWasmLoaded();
255 | } else {
256 | console.log("[ER] No decoder!");
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/src/downloader.js:
--------------------------------------------------------------------------------
1 | self.importScripts("common.js");
2 |
3 | function Downloader() {
4 | this.logger = new Logger("Downloader");
5 | this.ws = null;
6 | }
7 |
8 | Downloader.prototype.appendBuffer = function (buffer1, buffer2) {
9 | var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
10 | tmp.set(new Uint8Array(buffer1), 0);
11 | tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
12 | return tmp.buffer;
13 | };
14 |
15 | Downloader.prototype.reportFileSize = function (sz, st) {
16 | var objData = {
17 | t: kGetFileInfoRsp,
18 | i: {
19 | sz: sz,
20 | st: st
21 | }
22 | };
23 |
24 | //this.logger.logInfo("File size " + sz + " bytes.");
25 | self.postMessage(objData);
26 | };
27 |
28 | Downloader.prototype.reportData = function (start, end, seq, data) {
29 | var objData = {
30 | t: kFileData,
31 | s: start,
32 | e: end,
33 | d: data,
34 | q: seq
35 | };
36 | self.postMessage(objData, [objData.d]);
37 | };
38 |
39 | // Http implement.
40 | Downloader.prototype.getFileInfoByHttp = function (url) {
41 | this.logger.logInfo("Getting file size " + url + ".");
42 | var size = 0;
43 | var status = 0;
44 | var reported = false;
45 |
46 | var xhr = new XMLHttpRequest();
47 | xhr.open('get', url, true);
48 | var self = this;
49 | xhr.onreadystatechange = () => {
50 | var len = xhr.getResponseHeader("Content-Length");
51 | if (len) {
52 | size = len;
53 | }
54 |
55 | if (xhr.status) {
56 | status = xhr.status;
57 | }
58 |
59 | //Completed.
60 | if (!reported && ((size > 0 && status > 0) || xhr.readyState == 4)) {
61 | self.reportFileSize(size, status);
62 | reported = true;
63 | xhr.abort();
64 | }
65 | };
66 | xhr.send();
67 | };
68 |
69 | Downloader.prototype.downloadFileByHttp = function (url, start, end, seq) {
70 | //this.logger.logInfo("Downloading file " + url + ", bytes=" + start + "-" + end + ".");
71 | var xhr = new XMLHttpRequest;
72 | xhr.open('get', url, true);
73 | xhr.responseType = 'arraybuffer';
74 | xhr.setRequestHeader("Range", "bytes=" + start + "-" + end);
75 | var self = this;
76 | xhr.onload = function () {
77 | self.reportData(start, end, seq, xhr.response);
78 | };
79 | xhr.send();
80 | };
81 |
82 | // Websocket implement, NOTICE MUST call requestWebsocket serially, MUST wait
83 | // for result of last websocket request(cb called) for there's only one stream
84 | // exists.
85 | Downloader.prototype.requestWebsocket = function (url, msg, cb) {
86 | if (this.ws == null) {
87 | this.ws = new WebSocket(url);
88 | this.ws.binaryType = 'arraybuffer';
89 |
90 | var self = this;
91 | this.ws.onopen = function(evt) {
92 | self.logger.logInfo("Ws connected.");
93 | self.ws.send(msg);
94 | };
95 |
96 | this.ws.onerror = function(evt) {
97 | self.logger.logError("Ws connect error " + evt.data);
98 | }
99 |
100 | this.ws.onmessage = cb.onmessage;
101 | } else {
102 | this.ws.onmessage = cb.onmessage;
103 | this.ws.send(msg);
104 | }
105 | };
106 |
107 | Downloader.prototype.getFileInfoByWebsocket = function (url) {
108 | //this.logger.logInfo("Getting file size " + url + ".");
109 |
110 | // TBD, consider tcp sticky package.
111 | var data = null;
112 | var expectLength = 4;
113 | var self = this;
114 | var cmd = {
115 | url : url,
116 | cmd : "size",
117 | };
118 | this.requestWebsocket(url, JSON.stringify(cmd), {
119 | onmessage : function(evt) {
120 | if (data != null) {
121 | data = self.appendBuffer(data, evt.data);
122 | } else if (evt.data.byteLength < expectLength) {
123 | data = evt.data.slice(0);
124 | } else {
125 | data = evt.data;
126 | }
127 |
128 | // Assume 4 bytes header as file size.
129 | if (data.byteLength == expectLength) {
130 | let int32array = new Int32Array(data, 0, 1);
131 | let size = int32array[0];
132 | self.reportFileSize(size, 200);
133 | //self.logger.logInfo("Got file size " + self.fileSize + ".");
134 | }
135 | }
136 | });
137 | };
138 |
139 | Downloader.prototype.downloadFileByWebsocket = function (url, start, end, seq) {
140 | //this.logger.logInfo("Downloading file " + url + ", bytes=" + start + "-" + end + ".");
141 | var data = null;
142 | var expectLength = end - start + 1;
143 | var self = this;
144 | var cmd = {
145 | url : url,
146 | cmd : "data",
147 | start : start,
148 | end : end
149 | };
150 | this.requestWebsocket(url, JSON.stringify(cmd), {
151 | onmessage : function(evt) {
152 | if (data != null) {
153 | data = self.appendBuffer(data, evt.data);
154 | } else if (evt.data.byteLength < expectLength) {
155 | data = evt.data.slice(0);
156 | } else {
157 | data = evt.data;
158 | }
159 |
160 | // Wait for expect data length.
161 | if (data.byteLength == expectLength) {
162 | self.reportData(start, end, seq, data);
163 | }
164 | }
165 | });
166 | };
167 |
168 | // Interface.
169 | Downloader.prototype.getFileInfo = function (proto, url) {
170 | switch (proto) {
171 | case kProtoHttp:
172 | this.getFileInfoByHttp(url);
173 | break;
174 | case kProtoWebsocket:
175 | this.getFileInfoByWebsocket(url);
176 | break;
177 | default:
178 | this.logger.logError("Invalid protocol " + proto);
179 | break;
180 | }
181 | };
182 |
183 | Downloader.prototype.downloadFile = function (proto, url, start, end, seq) {
184 | switch (proto) {
185 | case kProtoHttp:
186 | this.downloadFileByHttp(url, start, end, seq);
187 | break;
188 | case kProtoWebsocket:
189 | this.downloadFileByWebsocket(url, start, end, seq);
190 | break;
191 | default:
192 | this.logger.logError("Invalid protocol " + proto);
193 | break;
194 | }
195 | }
196 |
197 | self.downloader = new Downloader();
198 |
199 | self.onmessage = function (evt) {
200 | if (!self.downloader) {
201 | console.log("[ER] Downloader not initialized!");
202 | return;
203 | }
204 |
205 | var objData = evt.data;
206 | switch (objData.t) {
207 | case kGetFileInfoReq:
208 | self.downloader.getFileInfo(objData.p, objData.u);
209 | break;
210 | case kDownloadFileReq:
211 | self.downloader.downloadFile(objData.p, objData.u, objData.s, objData.e, objData.q);
212 | break;
213 | case kCloseDownloaderReq:
214 | //Nothing to do.
215 | break;
216 | default:
217 | self.downloader.logger.logError("Unsupport messsage " + objData.t);
218 | }
219 | };
220 |
--------------------------------------------------------------------------------
/src/libffmpeg.js:
--------------------------------------------------------------------------------
1 | var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_HAS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_HAS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_NODE=ENVIRONMENT_HAS_NODE&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){scriptDirectory=__dirname+"/";read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];function dynamicAlloc(size){var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=ret+size+15&-16;if(end>_emscripten_get_heap_size()){abort()}HEAP32[DYNAMICTOP_PTR>>2]=end;return ret}function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return 4}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0,"getNativeTypeSize invalid bits "+bits+", type "+type);return bits/8}else{return 0}}}}function convertJsFunctionToWasm(func,sig){if(typeof WebAssembly.Function==="function"){var typeNames={"i":"i32","j":"i64","f":"f32","d":"f64"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":1731,"maximum":1731+14,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var ALLOC_NORMAL=0;var ALLOC_NONE=3;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[_malloc,stackAlloc,dynamicAlloc][allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var stop;ptr=ret;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr>2]=0}stop=ret+size;while(ptr>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var PAGE_SIZE=16384;var WASM_PAGE_SIZE=65536;var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var DYNAMIC_BASE=6533824,DYNAMICTOP_PTR=1290784;var INITIAL_TOTAL_MEMORY=Module["TOTAL_MEMORY"]||67108864;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE,"maximum":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_TOTAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="libffmpeg.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"env":asmLibraryArg,"wasi_snapshot_preview1":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"])js+="\n"+Module["extraStackTrace"]();return demangleAll(js)}function ___lock(){}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node}return node},getFileDataAsRegularArray:function(node){if(node.contents&&node.contents.subarray){var arr=[];for(var i=0;i=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(new ArrayBuffer(newSize));if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var err=FS.mayLookup(parent);if(err){throw new FS.ErrnoError(err,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){if(!FS.FSNode){FS.FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};FS.FSNode.prototype={};var readMode=292|73;var writeMode=146;Object.defineProperties(FS.FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}})}var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var err=FS.nodePermissions(dir,"x");if(err)return err;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var err=FS.nodePermissions(dir,"wx");if(err){return err}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}})}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){console.log("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(err){FS.syncFSRequests--;return callback(err)}function done(err){if(err){if(!done.errored){done.errored=true;return doCallback(err)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var err=FS.mayCreate(parent,name);if(err){throw new FS.ErrnoError(err)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;i"})},staticInit:function(){FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:function(input,output,error){FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:function(){FS.init.initialized=false;var fflush=Module["_fflush"];if(fflush)fflush(0);for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;console.log("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){if(!FS.forceLoadFile(node)){throw new FS.ErrnoError(29)}var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags){var buffer=new Uint8Array(HEAPU8.subarray(addr,addr+len));FS.msync(stream,buffer,0,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:0,get:function(varargs){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(){var ret=UTF8ToString(SYSCALLS.get());return ret},getStreamFromFD:function(fd){if(fd===undefined)fd=SYSCALLS.get();var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(){var low=SYSCALLS.get(),high=SYSCALLS.get();return low},getZero:function(){SYSCALLS.get()}};function ___syscall10(which,varargs){SYSCALLS.varargs=varargs;try{var path=SYSCALLS.getStr();FS.unlink(path);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall195(which,varargs){SYSCALLS.varargs=varargs;try{var path=SYSCALLS.getStr(),buf=SYSCALLS.get();return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall196(which,varargs){SYSCALLS.varargs=varargs;try{var path=SYSCALLS.getStr(),buf=SYSCALLS.get();return SYSCALLS.doStat(FS.lstat,path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall197(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),buf=SYSCALLS.get();return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall220(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),dirp=SYSCALLS.get(),count=SYSCALLS.get();if(!stream.getdents){stream.getdents=FS.readdir(stream.path)}var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var idx=Math.floor(off/struct_size);while(idx>>0,(tempDouble=id,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos>>2]=tempI64[0],HEAP32[dirp+pos+4>>2]=tempI64[1];tempI64=[(idx+1)*struct_size>>>0,(tempDouble=(idx+1)*struct_size,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos+8>>2]=tempI64[0],HEAP32[dirp+pos+12>>2]=tempI64[1];HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18>>0]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall221(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),cmd=SYSCALLS.get();switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:___setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall3(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),buf=SYSCALLS.get(),count=SYSCALLS.get();return FS.read(stream,HEAP8,buf,count)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall33(which,varargs){SYSCALLS.varargs=varargs;try{var path=SYSCALLS.getStr(),amode=SYSCALLS.get();return SYSCALLS.doAccess(path,amode)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall38(which,varargs){SYSCALLS.varargs=varargs;try{var old_path=SYSCALLS.getStr(),new_path=SYSCALLS.getStr();FS.rename(old_path,new_path);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall4(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),buf=SYSCALLS.get(),count=SYSCALLS.get();return FS.write(stream,HEAP8,buf,count)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall40(which,varargs){SYSCALLS.varargs=varargs;try{var path=SYSCALLS.getStr();FS.rmdir(path);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall5(which,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(),flags=SYSCALLS.get(),mode=SYSCALLS.get();var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall54(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),op=SYSCALLS.get();switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___unlock(){}function _abort(){abort()}function _clock(){if(_clock.start===undefined)_clock.start=Date.now();return(Date.now()-_clock.start)*(1e6/1e3)|0}function _emscripten_get_now(){abort()}function _emscripten_get_now_is_monotonic(){return 0||ENVIRONMENT_IS_NODE||typeof dateNow!=="undefined"||1}function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if(clk_id===1&&_emscripten_get_now_is_monotonic()){now=_emscripten_get_now()}else{___setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _emscripten_get_heap_size(){return HEAP8.length}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){abortOnCannotGrowMemory(requestedSize)}var ENV={};function _emscripten_get_environ(){if(!_emscripten_get_environ.strings){var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8","_":thisProgram};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}_emscripten_get_environ.strings=strings}return _emscripten_get_environ.strings}function _environ_get(__environ,environ_buf){var strings=_emscripten_get_environ();var bufSize=0;strings.forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=_emscripten_get_environ();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4;HEAP8[pbuf>>0]=type;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _ftime(p){var millis=Date.now();HEAP32[p>>2]=millis/1e3|0;HEAP16[p+4>>1]=millis%1e3;HEAP16[p+6>>1]=0;HEAP16[p+8>>1]=0;return 0}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}var ___tm_timezone=(stringToUTF8("GMT",1290848,4),1290848);function _gmtime_r(time,tmPtr){var date=new Date(HEAP32[time>>2]*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();HEAP32[tmPtr+36>>2]=0;HEAP32[tmPtr+32>>2]=0;var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+40>>2]=___tm_timezone;return tmPtr}function _tzset(){if(_tzset.called)return;_tzset.called=true;HEAP32[__get_timezone()>>2]=(new Date).getTimezoneOffset()*60;var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);HEAP32[__get_daylight()>>2]=Number(winter.getTimezoneOffset()!=summer.getTimezoneOffset());function extractZone(date){var match=date.toTimeString().match(/\(([A-Za-z ]+)\)$/);return match?match[1]:"GMT"}var winterName=extractZone(winter);var summerName=extractZone(summer);var winterNamePtr=allocate(intArrayFromString(winterName),"i8",ALLOC_NORMAL);var summerNamePtr=allocate(intArrayFromString(summerName),"i8",ALLOC_NORMAL);if(summer.getTimezoneOffset()>2]=winterNamePtr;HEAP32[__get_tzname()+4>>2]=summerNamePtr}else{HEAP32[__get_tzname()>>2]=summerNamePtr;HEAP32[__get_tzname()+4>>2]=winterNamePtr}}function _localtime_r(time,tmPtr){_tzset();var date=new Date(HEAP32[time>>2]*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var start=new Date(date.getFullYear(),0,1);var yday=(date.getTime()-start.getTime())/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst;var zonePtr=HEAP32[__get_tzname()+(dst?4:0)>>2];HEAP32[tmPtr+40>>2]=zonePtr;return tmPtr}function _usleep(useconds){var msec=useconds/1e3;if((ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&self["performance"]&&self["performance"]["now"]){var start=self["performance"]["now"]();while(self["performance"]["now"]()-start>2];var nanoseconds=HEAP32[rqtp+4>>2];if(nanoseconds<0||nanoseconds>999999999||seconds<0){___setErrNo(28);return-1}if(rmtp!==0){HEAP32[rmtp>>2]=0;HEAP32[rmtp+4>>2]=0}return _usleep(seconds*1e6+nanoseconds/1e3)}function _pthread_cond_destroy(){return 0}function _pthread_cond_init(){return 0}function _pthread_create(){return 6}function _pthread_join(){}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]);return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value==="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};for(var rule in EXPANSION_RULES_2){if(pattern.indexOf(rule)>=0){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:var maxHeapSize=2*1024*1024*1024-65536;maxHeapSize=HEAPU8.length;return maxHeapSize/PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(28);return-1}FS.staticInit();Module["FS_createFolder"]=FS.createFolder;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createLink"]=FS.createLink;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function _emscripten_get_now_actual(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else _emscripten_get_now=function(){return performance["now"]()};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"z":___lock,"l":___syscall10,"E":___syscall195,"G":___syscall196,"F":___syscall197,"u":___syscall220,"f":___syscall221,"w":___syscall3,"x":___syscall33,"C":___syscall38,"v":___syscall4,"k":___syscall40,"j":___syscall5,"B":___syscall54,"e":___unlock,"a":_abort,"J":_clock,"m":_clock_gettime,"q":_emscripten_memcpy_big,"r":_emscripten_resize_heap,"s":_environ_get,"t":_environ_sizes_get,"d":_fd_close,"i":_fd_fdstat_get,"y":_fd_read,"p":_fd_seek,"A":_fd_write,"L":_ftime,"I":_gettimeofday,"K":_gmtime_r,"D":_localtime_r,"memory":wasmMemory,"H":_nanosleep,"b":_pthread_cond_destroy,"c":_pthread_cond_init,"g":_pthread_create,"h":_pthread_join,"o":_strftime,"n":_sysconf,"table":wasmTable};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return Module["asm"]["M"].apply(null,arguments)};var _initDecoder=Module["_initDecoder"]=function(){return Module["asm"]["N"].apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return Module["asm"]["O"].apply(null,arguments)};var _uninitDecoder=Module["_uninitDecoder"]=function(){return Module["asm"]["P"].apply(null,arguments)};var _openDecoder=Module["_openDecoder"]=function(){return Module["asm"]["Q"].apply(null,arguments)};var _closeDecoder=Module["_closeDecoder"]=function(){return Module["asm"]["R"].apply(null,arguments)};var _sendData=Module["_sendData"]=function(){return Module["asm"]["S"].apply(null,arguments)};var _decodeOnePacket=Module["_decodeOnePacket"]=function(){return Module["asm"]["T"].apply(null,arguments)};var _seekTo=Module["_seekTo"]=function(){return Module["asm"]["U"].apply(null,arguments)};var _main=Module["_main"]=function(){return Module["asm"]["V"].apply(null,arguments)};var _free=Module["_free"]=function(){return Module["asm"]["W"].apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return Module["asm"]["X"].apply(null,arguments)};var __get_tzname=Module["__get_tzname"]=function(){return Module["asm"]["Y"].apply(null,arguments)};var __get_daylight=Module["__get_daylight"]=function(){return Module["asm"]["Z"].apply(null,arguments)};var __get_timezone=Module["__get_timezone"]=function(){return Module["asm"]["_"].apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return Module["asm"]["$"].apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return Module["asm"]["aa"].apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return Module["asm"]["ba"].apply(null,arguments)};Module["asm"]=asm;Module["getMemory"]=getMemory;Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createFolder"]=FS.createFolder;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createLink"]=FS.createLink;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["addFunction"]=addFunction;Module["calledRun"]=calledRun;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}var calledMain=false;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args){var entryFunction=Module["_main"];var argc=0;var argv=0;try{var ret=entryFunction(argc,argv);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="unwind"){noExitRuntime=true;return}else{var toLog=e;if(e&&typeof e==="object"&&e.stack){toLog=[e,e.stack]}err("exception thrown: "+toLog);quit_(1,e)}}finally{calledMain=true}}function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&noExitRuntime&&status===0){return}if(noExitRuntime){}else{ABORT=true;EXITSTATUS=status;exitRuntime();if(Module["onExit"])Module["onExit"](status)}quit_(status,new ExitStatus(status))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;noExitRuntime=true;run();
2 |
--------------------------------------------------------------------------------
/src/libffmpeg.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tcper/react-h265-wasm-player/57e03681a08e7ccfa36a45d317a74740b8d072c1/src/libffmpeg.wasm
--------------------------------------------------------------------------------
/src/pcm-player.js:
--------------------------------------------------------------------------------
1 | function PCMPlayer(option) {
2 | this.init(option);
3 | }
4 |
5 | PCMPlayer.prototype.init = function(option) {
6 | var defaults = {
7 | encoding: '16bitInt',
8 | channels: 1,
9 | sampleRate: 8000,
10 | flushingTime: 1000
11 | };
12 | this.option = Object.assign({}, defaults, option);
13 | this.samples = new Float32Array();
14 | this.flush = this.flush.bind(this);
15 | this.interval = setInterval(this.flush, this.option.flushingTime);
16 | this.maxValue = this.getMaxValue();
17 | this.typedArray = this.getTypedArray();
18 | this.createContext();
19 | };
20 |
21 | PCMPlayer.prototype.getMaxValue = function () {
22 | var encodings = {
23 | '8bitInt': 128,
24 | '16bitInt': 32768,
25 | '32bitInt': 2147483648,
26 | '32bitFloat': 1
27 | }
28 |
29 | return encodings[this.option.encoding] ? encodings[this.option.encoding] : encodings['16bitInt'];
30 | };
31 |
32 | PCMPlayer.prototype.getTypedArray = function () {
33 | var typedArrays = {
34 | '8bitInt': Int8Array,
35 | '16bitInt': Int16Array,
36 | '32bitInt': Int32Array,
37 | '32bitFloat': Float32Array
38 | }
39 |
40 | return typedArrays[this.option.encoding] ? typedArrays[this.option.encoding] : typedArrays['16bitInt'];
41 | };
42 |
43 | PCMPlayer.prototype.createContext = function() {
44 | this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
45 | this.gainNode = this.audioCtx.createGain();
46 | this.gainNode.gain.value = 1;
47 | this.gainNode.connect(this.audioCtx.destination);
48 | this.startTime = this.audioCtx.currentTime;
49 | };
50 |
51 | PCMPlayer.prototype.isTypedArray = function(data) {
52 | return (data.byteLength && data.buffer && data.buffer.constructor == ArrayBuffer);
53 | };
54 |
55 | PCMPlayer.prototype.feed = function(data) {
56 | if (!this.isTypedArray(data)) return;
57 | data = this.getFormatedValue(data);
58 | var tmp = new Float32Array(this.samples.length + data.length);
59 | tmp.set(this.samples, 0);
60 | tmp.set(data, this.samples.length);
61 | this.samples = tmp;
62 | };
63 |
64 | PCMPlayer.prototype.getFormatedValue = function(data) {
65 | var data = new this.typedArray(data.buffer),
66 | float32 = new Float32Array(data.length),
67 | i;
68 |
69 | for (i = 0; i < data.length; i++) {
70 | float32[i] = data[i] / this.maxValue;
71 | }
72 | return float32;
73 | };
74 |
75 | PCMPlayer.prototype.volume = function(volume) {
76 | this.gainNode.gain.value = volume;
77 | };
78 |
79 | PCMPlayer.prototype.destroy = function() {
80 | if (this.interval) {
81 | clearInterval(this.interval);
82 | }
83 | this.samples = null;
84 | this.audioCtx.close();
85 | this.audioCtx = null;
86 | };
87 |
88 | PCMPlayer.prototype.flush = function() {
89 | if (!this.samples.length) return;
90 | var bufferSource = this.audioCtx.createBufferSource(),
91 | length = this.samples.length / this.option.channels,
92 | audioBuffer = this.audioCtx.createBuffer(this.option.channels, length, this.option.sampleRate),
93 | audioData,
94 | channel,
95 | offset,
96 | i,
97 | decrement;
98 |
99 | for (channel = 0; channel < this.option.channels; channel++) {
100 | audioData = audioBuffer.getChannelData(channel);
101 | offset = channel;
102 | decrement = 50;
103 | for (i = 0; i < length; i++) {
104 | audioData[i] = this.samples[offset];
105 | /* fadein */
106 | if (i < 50) {
107 | audioData[i] = (audioData[i] * i) / 50;
108 | }
109 | /* fadeout*/
110 | if (i >= (length - 51)) {
111 | audioData[i] = (audioData[i] * decrement--) / 50;
112 | }
113 | offset += this.option.channels;
114 | }
115 | }
116 |
117 | if (this.startTime < this.audioCtx.currentTime) {
118 | this.startTime = this.audioCtx.currentTime;
119 | }
120 | //console.log('start vs current '+this.startTime+' vs '+this.audioCtx.currentTime+' duration: '+audioBuffer.duration);
121 | bufferSource.buffer = audioBuffer;
122 | bufferSource.connect(this.gainNode);
123 | bufferSource.start(this.startTime);
124 | this.startTime += audioBuffer.duration;
125 | this.samples = new Float32Array();
126 | };
127 |
128 | PCMPlayer.prototype.getTimestamp = function () {
129 | if (this.audioCtx) {
130 | return this.audioCtx.currentTime;
131 | } else {
132 | return 0;
133 | }
134 | };
135 |
136 | PCMPlayer.prototype.play = function (data) {
137 | if (!this.isTypedArray(data)) {
138 | return;
139 | }
140 |
141 | data = this.getFormatedValue(data);
142 | if (!data.length) {
143 | return;
144 | }
145 |
146 | var bufferSource = this.audioCtx.createBufferSource(),
147 | length = data.length / this.option.channels,
148 | audioBuffer = this.audioCtx.createBuffer(this.option.channels, length, this.option.sampleRate),
149 | audioData,
150 | channel,
151 | offset,
152 | i,
153 | decrement;
154 |
155 | for (channel = 0; channel < this.option.channels; channel++) {
156 | audioData = audioBuffer.getChannelData(channel);
157 | offset = channel;
158 | decrement = 50;
159 | for (i = 0; i < length; i++) {
160 | audioData[i] = data[offset];
161 | /* fadein */
162 | if (i < 50) {
163 | audioData[i] = (audioData[i] * i) / 50;
164 | }
165 | /* fadeout*/
166 | if (i >= (length - 51)) {
167 | audioData[i] = (audioData[i] * decrement--) / 50;
168 | }
169 | offset += this.option.channels;
170 | }
171 | }
172 |
173 | if (this.startTime < this.audioCtx.currentTime) {
174 | this.startTime = this.audioCtx.currentTime;
175 | }
176 | //console.log('start vs current '+this.startTime+' vs '+this.audioCtx.currentTime+' duration: '+audioBuffer.duration);
177 | bufferSource.buffer = audioBuffer;
178 | bufferSource.connect(this.gainNode);
179 | bufferSource.start(this.startTime);
180 | this.startTime += audioBuffer.duration;
181 | };
182 |
183 | PCMPlayer.prototype.pause = function () {
184 | if (this.audioCtx.state === 'running') {
185 | this.audioCtx.suspend()
186 | }
187 | }
188 |
189 | PCMPlayer.prototype.resume = function () {
190 | if (this.audioCtx.state === 'suspended') {
191 | this.audioCtx.resume()
192 | }
193 | }
194 |
195 |
196 |
--------------------------------------------------------------------------------
/src/player.js:
--------------------------------------------------------------------------------
1 | //Decoder states.
2 | const decoderStateIdle = 0;
3 | const decoderStateInitializing = 1;
4 | const decoderStateReady = 2;
5 | const decoderStateFinished = 3;
6 |
7 | //Player states.
8 | const playerStateIdle = 0;
9 | const playerStatePlaying = 1;
10 | const playerStatePausing = 2;
11 |
12 | //Constant.
13 | const maxBufferTimeLength = 1.0;
14 | const downloadSpeedByteRateCoef = 2.0;
15 |
16 | String.prototype.startWith = function(str) {
17 | var reg = new RegExp("^" + str);
18 | return reg.test(this);
19 | };
20 |
21 | function FileInfo(url) {
22 | this.url = url;
23 | this.size = 0;
24 | this.offset = 0;
25 | this.chunkSize = 65536;
26 | }
27 |
28 | function Player() {
29 | this.fileInfo = null;
30 | this.pcmPlayer = null;
31 | this.canvas = null;
32 | this.webglPlayer = null;
33 | this.callback = null;
34 | this.waitHeaderLength = 524288;
35 | this.duration = 0;
36 | this.pixFmt = 0;
37 | this.videoWidth = 0;
38 | this.videoHeight = 0;
39 | this.yLength = 0;
40 | this.uvLength = 0;
41 | this.beginTimeOffset = 0;
42 | this.decoderState = decoderStateIdle;
43 | this.playerState = playerStateIdle;
44 | this.decoding = false;
45 | this.decodeInterval = 5;
46 | this.videoRendererTimer = null;
47 | this.downloadTimer = null;
48 | this.chunkInterval = 200;
49 | this.downloadSeqNo = 0;
50 | this.downloading = false;
51 | this.downloadProto = kProtoHttp;
52 | this.timeLabel = null;
53 | this.timeTrack = null;
54 | this.trackTimer = null;
55 | this.trackTimerInterval = 500;
56 | this.displayDuration = "00:00:00";
57 | this.audioEncoding = "";
58 | this.audioChannels = 0;
59 | this.audioSampleRate = 0;
60 | this.seeking = false; // Flag to preventing multi seek from track.
61 | this.justSeeked = false; // Flag to preventing multi seek from ffmpeg.
62 | this.urgent = false;
63 | this.seekWaitLen = 524288; // Default wait for 512K, will be updated in onVideoParam.
64 | this.seekReceivedLen = 0;
65 | this.loadingDiv = null;
66 | this.buffering = false;
67 | this.frameBuffer = [];
68 | this.isStream = false;
69 | this.streamReceivedLen = 0;
70 | this.firstAudioFrame = true;
71 | this.fetchController = null;
72 | this.streamPauseParam = null;
73 | this.logger = new Logger("Player");
74 | this.initDownloadWorker();
75 | this.initDecodeWorker();
76 | }
77 |
78 | Player.prototype.initDownloadWorker = function () {
79 | var self = this;
80 | this.downloadWorker = new Worker("downloader.js");
81 | this.downloadWorker.onmessage = function (evt) {
82 | var objData = evt.data;
83 | switch (objData.t) {
84 | case kGetFileInfoRsp:
85 | self.onGetFileInfo(objData.i);
86 | break;
87 | case kFileData:
88 | self.onFileData(objData.d, objData.s, objData.e, objData.q);
89 | break;
90 | }
91 | }
92 | };
93 |
94 | Player.prototype.initDecodeWorker = function () {
95 | var self = this;
96 | this.decodeWorker = new Worker("decoder.js");
97 | this.decodeWorker.onmessage = function (evt) {
98 | var objData = evt.data;
99 | switch (objData.t) {
100 | case kInitDecoderRsp:
101 | self.onInitDecoder(objData);
102 | break;
103 | case kOpenDecoderRsp:
104 | self.onOpenDecoder(objData);
105 | break;
106 | case kVideoFrame:
107 | self.onVideoFrame(objData);
108 | break;
109 | case kAudioFrame:
110 | self.onAudioFrame(objData);
111 | break;
112 | case kDecodeFinishedEvt:
113 | self.onDecodeFinished(objData);
114 | break;
115 | case kRequestDataEvt:
116 | self.onRequestData(objData.o, objData.a);
117 | break;
118 | case kSeekToRsp:
119 | self.onSeekToRsp(objData.r);
120 | break;
121 | }
122 | }
123 | };
124 |
125 | Player.prototype.play = function (url, canvas, callback, waitHeaderLength, isStream) {
126 | this.logger.logInfo("Play " + url + ".");
127 |
128 | var ret = {
129 | e: 0,
130 | m: "Success"
131 | };
132 |
133 | var success = true;
134 | do {
135 | if (this.playerState == playerStatePausing) {
136 | ret = this.resume();
137 | break;
138 | }
139 |
140 | if (this.playerState == playerStatePlaying) {
141 | break;
142 | }
143 |
144 | if (!url) {
145 | ret = {
146 | e: -1,
147 | m: "Invalid url"
148 | };
149 | success = false;
150 | this.logger.logError("[ER] playVideo error, url empty.");
151 | break;
152 | }
153 |
154 | if (!canvas) {
155 | ret = {
156 | e: -2,
157 | m: "Canvas not set"
158 | };
159 | success = false;
160 | this.logger.logError("[ER] playVideo error, canvas empty.");
161 | break;
162 | }
163 |
164 | if (!this.downloadWorker) {
165 | ret = {
166 | e: -3,
167 | m: "Downloader not initialized"
168 | };
169 | success = false;
170 | this.logger.logError("[ER] Downloader not initialized.");
171 | break
172 | }
173 |
174 | if (!this.decodeWorker) {
175 | ret = {
176 | e: -4,
177 | m: "Decoder not initialized"
178 | };
179 | success = false;
180 | this.logger.logError("[ER] Decoder not initialized.");
181 | break
182 | }
183 |
184 | if (url.startWith("ws://") || url.startWith("wss://")) {
185 | this.downloadProto = kProtoWebsocket;
186 | } else {
187 | this.downloadProto = kProtoHttp;
188 | }
189 |
190 | this.fileInfo = new FileInfo(url);
191 | this.canvas = canvas;
192 | this.callback = callback;
193 | this.waitHeaderLength = waitHeaderLength || this.waitHeaderLength;
194 | this.playerState = playerStatePlaying;
195 | this.isStream = isStream;
196 | this.startTrackTimer();
197 | this.displayLoop();
198 |
199 | //var playCanvasContext = playCanvas.getContext("2d"); //If get 2d, webgl will be disabled.
200 | this.webglPlayer = new WebGLPlayer(this.canvas, {
201 | preserveDrawingBuffer: false
202 | });
203 |
204 | if (!this.isStream) {
205 | var req = {
206 | t: kGetFileInfoReq,
207 | u: url,
208 | p: this.downloadProto
209 | };
210 | this.downloadWorker.postMessage(req);
211 | } else {
212 | this.requestStream(url);
213 | this.onGetFileInfo({
214 | sz: -1,
215 | st: 200
216 | });
217 | }
218 |
219 | var self = this;
220 | this.registerVisibilityEvent(function(visible) {
221 | if (visible) {
222 | self.resume();
223 | } else {
224 | self.pause();
225 | }
226 | });
227 |
228 | this.buffering = true;
229 | this.showLoading();
230 | } while (false);
231 |
232 | return ret;
233 | };
234 |
235 | Player.prototype.pauseStream = function () {
236 | if (this.playerState != playerStatePlaying) {
237 | var ret = {
238 | e: -1,
239 | m: "Not playing"
240 | };
241 | return ret;
242 | }
243 |
244 | this.streamPauseParam = {
245 | url: this.fileInfo.url,
246 | canvas: this.canvas,
247 | callback: this.callback,
248 | waitHeaderLength: this.waitHeaderLength
249 | }
250 |
251 | this.logger.logInfo("Stop in stream pause.");
252 | this.stop();
253 |
254 | var ret = {
255 | e: 0,
256 | m: "Success"
257 | };
258 |
259 | return ret;
260 | }
261 |
262 | Player.prototype.pause = function () {
263 | if (this.isStream) {
264 | return this.pauseStream();
265 | }
266 |
267 | this.logger.logInfo("Pause.");
268 |
269 | if (this.playerState != playerStatePlaying) {
270 | var ret = {
271 | e: -1,
272 | m: "Not playing"
273 | };
274 | return ret;
275 | }
276 |
277 | //Pause video rendering and audio flushing.
278 | this.playerState = playerStatePausing;
279 |
280 | //Pause audio context.
281 | if (this.pcmPlayer) {
282 | this.pcmPlayer.pause();
283 | }
284 |
285 | //Pause decoding.
286 | this.pauseDecoding();
287 |
288 | //Stop track timer.
289 | this.stopTrackTimer();
290 |
291 | //Do not stop downloader for background buffering.
292 | var ret = {
293 | e: 0,
294 | m: "Success"
295 | };
296 |
297 | return ret;
298 | };
299 |
300 | Player.prototype.resumeStream = function () {
301 | if (this.playerState != playerStateIdle || !this.streamPauseParam) {
302 | var ret = {
303 | e: -1,
304 | m: "Not pausing"
305 | };
306 | return ret;
307 | }
308 |
309 | this.logger.logInfo("Play in stream resume.");
310 | this.play(this.streamPauseParam.url,
311 | this.streamPauseParam.canvas,
312 | this.streamPauseParam.callback,
313 | this.streamPauseParam.waitHeaderLength,
314 | true);
315 | this.streamPauseParam = null;
316 |
317 | var ret = {
318 | e: 0,
319 | m: "Success"
320 | };
321 |
322 | return ret;
323 | }
324 |
325 | Player.prototype.resume = function (fromSeek) {
326 | if (this.isStream) {
327 | return this.resumeStream();
328 | }
329 |
330 | this.logger.logInfo("Resume.");
331 |
332 | if (this.playerState != playerStatePausing) {
333 | var ret = {
334 | e: -1,
335 | m: "Not pausing"
336 | };
337 | return ret;
338 | }
339 |
340 | if (!fromSeek) {
341 | //Resume audio context.
342 | this.pcmPlayer.resume();
343 | }
344 |
345 | //If there's a flying video renderer op, interrupt it.
346 | if (this.videoRendererTimer != null) {
347 | clearTimeout(this.videoRendererTimer);
348 | this.videoRendererTimer = null;
349 | }
350 |
351 | //Restart video rendering and audio flushing.
352 | this.playerState = playerStatePlaying;
353 |
354 | //Restart decoding.
355 | this.startDecoding();
356 |
357 | //Restart track timer.
358 | if (!this.seeking) {
359 | this.startTrackTimer();
360 | }
361 |
362 | var ret = {
363 | e: 0,
364 | m: "Success"
365 | };
366 | return ret;
367 | };
368 |
369 | Player.prototype.stop = function () {
370 | this.logger.logInfo("Stop.");
371 | if (this.playerState == playerStateIdle) {
372 | var ret = {
373 | e: -1,
374 | m: "Not playing"
375 | };
376 | return ret;
377 | }
378 |
379 | if (this.videoRendererTimer != null) {
380 | clearTimeout(this.videoRendererTimer);
381 | this.videoRendererTimer = null;
382 | this.logger.logInfo("Video renderer timer stopped.");
383 | }
384 |
385 | this.stopDownloadTimer();
386 | this.stopTrackTimer();
387 | this.hideLoading();
388 |
389 | this.fileInfo = null;
390 | this.canvas = null;
391 | this.webglPlayer = null;
392 | this.callback = null;
393 | this.duration = 0;
394 | this.pixFmt = 0;
395 | this.videoWidth = 0;
396 | this.videoHeight = 0;
397 | this.yLength = 0;
398 | this.uvLength = 0;
399 | this.beginTimeOffset = 0;
400 | this.decoderState = decoderStateIdle;
401 | this.playerState = playerStateIdle;
402 | this.decoding = false;
403 | this.frameBuffer = [];
404 | this.buffering = false;
405 | this.streamReceivedLen = 0;
406 | this.firstAudioFrame = true;
407 | this.urgent = false;
408 | this.seekReceivedLen = 0;
409 |
410 | if (this.pcmPlayer) {
411 | this.pcmPlayer.destroy();
412 | this.pcmPlayer = null;
413 | this.logger.logInfo("Pcm player released.");
414 | }
415 |
416 | if (this.timeTrack) {
417 | this.timeTrack.value = 0;
418 | }
419 | if (this.timeLabel) {
420 | this.timeLabel.innerHTML = this.formatTime(0) + "/" + this.displayDuration;
421 | }
422 |
423 | this.logger.logInfo("Closing decoder.");
424 | this.decodeWorker.postMessage({
425 | t: kCloseDecoderReq
426 | });
427 |
428 |
429 | this.logger.logInfo("Uniniting decoder.");
430 | this.decodeWorker.postMessage({
431 | t: kUninitDecoderReq
432 | });
433 |
434 | if (this.fetchController) {
435 | this.fetchController.abort();
436 | this.fetchController = null;
437 | }
438 |
439 | return ret;
440 | };
441 |
442 | Player.prototype.seekTo = function(ms) {
443 | if (this.isStream) {
444 | return;
445 | }
446 |
447 | // Pause playing.
448 | this.pause();
449 |
450 | // Stop download.
451 | this.stopDownloadTimer();
452 |
453 | // Clear frame buffer.
454 | this.frameBuffer.length = 0;
455 |
456 | // Request decoder to seek.
457 | this.decodeWorker.postMessage({
458 | t: kSeekToReq,
459 | ms: ms
460 | });
461 |
462 | // Reset begin time offset.
463 | this.beginTimeOffset = ms / 1000;
464 | this.logger.logInfo("seekTo beginTimeOffset " + this.beginTimeOffset);
465 |
466 | this.seeking = true;
467 | this.justSeeked = true;
468 | this.urgent = true;
469 | this.seekReceivedLen = 0;
470 | this.startBuffering();
471 | };
472 |
473 | Player.prototype.fullscreen = function () {
474 | if (this.webglPlayer) {
475 | this.webglPlayer.fullscreen();
476 | }
477 | };
478 |
479 | Player.prototype.getState = function () {
480 | return this.playerState;
481 | };
482 |
483 | Player.prototype.setTrack = function (timeTrack, timeLabel) {
484 | this.timeTrack = timeTrack;
485 | this.timeLabel = timeLabel;
486 |
487 | if (this.timeTrack) {
488 | var self = this;
489 | this.timeTrack.oninput = function () {
490 | if (!self.seeking) {
491 | self.seekTo(self.timeTrack.value);
492 | }
493 | }
494 | this.timeTrack.onchange = function () {
495 | if (!self.seeking) {
496 | self.seekTo(self.timeTrack.value);
497 | }
498 | }
499 | }
500 | };
501 |
502 | Player.prototype.onGetFileInfo = function (info) {
503 | if (this.playerState == playerStateIdle) {
504 | return;
505 | }
506 |
507 | this.logger.logInfo("Got file size rsp:" + info.st + " size:" + info.sz + " byte.");
508 | if (info.st == 200) {
509 | this.fileInfo.size = Number(info.sz);
510 | this.logger.logInfo("Initializing decoder.");
511 | var req = {
512 | t: kInitDecoderReq,
513 | s: this.fileInfo.size,
514 | c: this.fileInfo.chunkSize
515 | };
516 | this.decodeWorker.postMessage(req);
517 | } else {
518 | this.reportPlayError(-1, info.st);
519 | }
520 | };
521 |
522 | Player.prototype.onFileData = function (data, start, end, seq) {
523 | //this.logger.logInfo("Got data bytes=" + start + "-" + end + ".");
524 | this.downloading = false;
525 |
526 | if (this.playerState == playerStateIdle) {
527 | return;
528 | }
529 |
530 | if (seq != this.downloadSeqNo) {
531 | return; // Old data.
532 | }
533 |
534 | if (this.playerState == playerStatePausing) {
535 | if (this.seeking) {
536 | this.seekReceivedLen += data.byteLength;
537 | let left = this.fileInfo.size - this.fileInfo.offset;
538 | let seekWaitLen = Math.min(left, this.seekWaitLen);
539 | if (this.seekReceivedLen >= seekWaitLen) {
540 | this.logger.logInfo("Resume in seek now");
541 | setTimeout(() => {
542 | this.resume(true);
543 | }, 0);
544 | }
545 | } else {
546 | return;
547 | }
548 | }
549 |
550 | var len = end - start + 1;
551 | this.fileInfo.offset += len;
552 |
553 | var objData = {
554 | t: kFeedDataReq,
555 | d: data
556 | };
557 | this.decodeWorker.postMessage(objData, [objData.d]);
558 |
559 | switch (this.decoderState) {
560 | case decoderStateIdle:
561 | this.onFileDataUnderDecoderIdle();
562 | break;
563 | case decoderStateInitializing:
564 | this.onFileDataUnderDecoderInitializing();
565 | break;
566 | case decoderStateReady:
567 | this.onFileDataUnderDecoderReady();
568 | break;
569 | }
570 |
571 | if (this.urgent) {
572 | setTimeout(() => {
573 | this.downloadOneChunk();
574 | }, 0);
575 | }
576 | };
577 |
578 | Player.prototype.onFileDataUnderDecoderIdle = function () {
579 | if (this.fileInfo.offset >= this.waitHeaderLength || (!this.isStream && this.fileInfo.offset == this.fileInfo.size)) {
580 | this.logger.logInfo("Opening decoder.");
581 | this.decoderState = decoderStateInitializing;
582 | var req = {
583 | t: kOpenDecoderReq
584 | };
585 | this.decodeWorker.postMessage(req);
586 | }
587 |
588 | this.downloadOneChunk();
589 | };
590 |
591 | Player.prototype.onFileDataUnderDecoderInitializing = function () {
592 | this.downloadOneChunk();
593 | };
594 |
595 | Player.prototype.onFileDataUnderDecoderReady = function () {
596 | //this.downloadOneChunk();
597 | };
598 |
599 | Player.prototype.onInitDecoder = function (objData) {
600 | if (this.playerState == playerStateIdle) {
601 | return;
602 | }
603 |
604 | this.logger.logInfo("Init decoder response " + objData.e + ".");
605 | if (objData.e == 0) {
606 | if (!this.isStream) {
607 | this.downloadOneChunk();
608 | }
609 | } else {
610 | this.reportPlayError(objData.e);
611 | }
612 | };
613 |
614 | Player.prototype.onOpenDecoder = function (objData) {
615 | if (this.playerState == playerStateIdle) {
616 | return;
617 | }
618 |
619 | this.logger.logInfo("Open decoder response " + objData.e + ".");
620 | if (objData.e == 0) {
621 | this.onVideoParam(objData.v);
622 | this.onAudioParam(objData.a);
623 | this.decoderState = decoderStateReady;
624 | this.logger.logInfo("Decoder ready now.");
625 | this.startDecoding();
626 | } else {
627 | this.reportPlayError(objData.e);
628 | }
629 | };
630 |
631 | Player.prototype.onVideoParam = function (v) {
632 | if (this.playerState == playerStateIdle) {
633 | return;
634 | }
635 |
636 | this.logger.logInfo("Video param duation:" + v.d + " pixFmt:" + v.p + " width:" + v.w + " height:" + v.h + ".");
637 | this.duration = v.d;
638 | this.pixFmt = v.p;
639 | //this.canvas.width = v.w;
640 | //this.canvas.height = v.h;
641 | this.videoWidth = v.w;
642 | this.videoHeight = v.h;
643 | this.yLength = this.videoWidth * this.videoHeight;
644 | this.uvLength = (this.videoWidth / 2) * (this.videoHeight / 2);
645 |
646 | /*
647 | //var playCanvasContext = playCanvas.getContext("2d"); //If get 2d, webgl will be disabled.
648 | this.webglPlayer = new WebGLPlayer(this.canvas, {
649 | preserveDrawingBuffer: false
650 | });
651 | */
652 |
653 | if (this.timeTrack) {
654 | this.timeTrack.min = 0;
655 | this.timeTrack.max = this.duration;
656 | this.timeTrack.value = 0;
657 | this.displayDuration = this.formatTime(this.duration / 1000);
658 | }
659 |
660 | var byteRate = 1000 * this.fileInfo.size / this.duration;
661 | var targetSpeed = downloadSpeedByteRateCoef * byteRate;
662 | var chunkPerSecond = targetSpeed / this.fileInfo.chunkSize;
663 | this.chunkInterval = 1000 / chunkPerSecond;
664 | this.seekWaitLen = byteRate * maxBufferTimeLength * 2;
665 | this.logger.logInfo("Seek wait len " + this.seekWaitLen);
666 |
667 | if (!this.isStream) {
668 | this.startDownloadTimer();
669 | }
670 |
671 | this.logger.logInfo("Byte rate:" + byteRate + " target speed:" + targetSpeed + " chunk interval:" + this.chunkInterval + ".");
672 | };
673 |
674 | Player.prototype.onAudioParam = function (a) {
675 | if (this.playerState == playerStateIdle) {
676 | return;
677 | }
678 |
679 | this.logger.logInfo("Audio param sampleFmt:" + a.f + " channels:" + a.c + " sampleRate:" + a.r + ".");
680 |
681 | var sampleFmt = a.f;
682 | var channels = a.c;
683 | var sampleRate = a.r;
684 |
685 | var encoding = "16bitInt";
686 | switch (sampleFmt) {
687 | case 0:
688 | encoding = "8bitInt";
689 | break;
690 | case 1:
691 | encoding = "16bitInt";
692 | break;
693 | case 2:
694 | encoding = "32bitInt";
695 | break;
696 | case 3:
697 | encoding = "32bitFloat";
698 | break;
699 | default:
700 | this.logger.logError("Unsupported audio sampleFmt " + sampleFmt + "!");
701 | }
702 | this.logger.logInfo("Audio encoding " + encoding + ".");
703 |
704 | this.pcmPlayer = new PCMPlayer({
705 | encoding: encoding,
706 | channels: channels,
707 | sampleRate: sampleRate,
708 | flushingTime: 5000
709 | });
710 |
711 | this.audioEncoding = encoding;
712 | this.audioChannels = channels;
713 | this.audioSampleRate = sampleRate;
714 | };
715 |
716 | Player.prototype.restartAudio = function () {
717 | if (this.pcmPlayer) {
718 | this.pcmPlayer.destroy();
719 | this.pcmPlayer = null;
720 | }
721 |
722 | this.pcmPlayer = new PCMPlayer({
723 | encoding: this.audioEncoding,
724 | channels: this.audioChannels,
725 | sampleRate: this.audioSampleRate,
726 | flushingTime: 5000
727 | });
728 | };
729 |
730 | Player.prototype.bufferFrame = function (frame) {
731 | // If not decoding, it may be frame before seeking, should be discarded.
732 | if (!this.decoding) {
733 | return;
734 | }
735 | this.frameBuffer.push(frame);
736 | //this.logger.logInfo("bufferFrame " + frame.s + ", seq " + frame.q);
737 | if (this.getBufferTimerLength() >= maxBufferTimeLength || this.decoderState == decoderStateFinished) {
738 | if (this.decoding) {
739 | //this.logger.logInfo("Frame buffer time length >= " + maxBufferTimeLength + ", pause decoding.");
740 | this.pauseDecoding();
741 | }
742 | if (this.buffering) {
743 | this.stopBuffering();
744 | }
745 | }
746 | }
747 |
748 | Player.prototype.displayAudioFrame = function (frame) {
749 | if (this.playerState != playerStatePlaying) {
750 | return false;
751 | }
752 |
753 | if (this.seeking) {
754 | this.restartAudio();
755 | this.startTrackTimer();
756 | this.hideLoading();
757 | this.seeking = false;
758 | this.urgent = false;
759 | }
760 |
761 | if (this.isStream && this.firstAudioFrame) {
762 | this.firstAudioFrame = false;
763 | this.beginTimeOffset = frame.s;
764 | }
765 |
766 | this.pcmPlayer.play(new Uint8Array(frame.d));
767 | return true;
768 | };
769 |
770 | Player.prototype.onAudioFrame = function (frame) {
771 | this.bufferFrame(frame);
772 | };
773 |
774 | Player.prototype.onDecodeFinished = function (objData) {
775 | this.pauseDecoding();
776 | this.decoderState = decoderStateFinished;
777 | };
778 |
779 | Player.prototype.getBufferTimerLength = function() {
780 | if (!this.frameBuffer || this.frameBuffer.length == 0) {
781 | return 0;
782 | }
783 |
784 | let oldest = this.frameBuffer[0];
785 | let newest = this.frameBuffer[this.frameBuffer.length - 1];
786 | return newest.s - oldest.s;
787 | };
788 |
789 | Player.prototype.onVideoFrame = function (frame) {
790 | this.bufferFrame(frame);
791 | };
792 |
793 | Player.prototype.displayVideoFrame = function (frame) {
794 | if (this.playerState != playerStatePlaying) {
795 | return false;
796 | }
797 |
798 | if (this.seeking) {
799 | this.restartAudio();
800 | this.startTrackTimer();
801 | this.hideLoading();
802 | this.seeking = false;
803 | this.urgent = false;
804 | }
805 |
806 | var audioCurTs = this.pcmPlayer.getTimestamp();
807 | var audioTimestamp = audioCurTs + this.beginTimeOffset;
808 | var delay = frame.s - audioTimestamp;
809 |
810 | //this.logger.logInfo("displayVideoFrame delay=" + delay + "=" + " " + frame.s + " - (" + audioCurTs + " + " + this.beginTimeOffset + ")" + "->" + audioTimestamp);
811 |
812 | if (audioTimestamp <= 0 || delay <= 0) {
813 | var data = new Uint8Array(frame.d);
814 | this.renderVideoFrame(data);
815 | return true;
816 | }
817 | return false;
818 | };
819 |
820 | Player.prototype.onSeekToRsp = function (ret) {
821 | if (ret != 0) {
822 | this.justSeeked = false;
823 | this.seeking = false;
824 | }
825 | };
826 |
827 | Player.prototype.onRequestData = function (offset, available) {
828 | if (this.justSeeked) {
829 | this.logger.logInfo("Request data " + offset + ", available " + available);
830 | if (offset == -1) {
831 | // Hit in buffer.
832 | let left = this.fileInfo.size - this.fileInfo.offset;
833 | if (available >= left) {
834 | this.logger.logInfo("No need to wait");
835 | this.resume();
836 | } else {
837 | this.startDownloadTimer();
838 | }
839 | } else {
840 | if (offset >= 0 && offset < this.fileInfo.size) {
841 | this.fileInfo.offset = offset;
842 | }
843 | this.startDownloadTimer();
844 | }
845 |
846 | //this.restartAudio();
847 | this.justSeeked = false;
848 | }
849 | };
850 |
851 | Player.prototype.displayLoop = function() {
852 | if (this.playerState !== playerStateIdle) {
853 | requestAnimationFrame(this.displayLoop.bind(this));
854 | }
855 | if (this.playerState != playerStatePlaying) {
856 | return;
857 | }
858 |
859 | if (this.frameBuffer.length == 0) {
860 | return;
861 | }
862 |
863 | if (this.buffering) {
864 | return;
865 | }
866 |
867 | // requestAnimationFrame may be 60fps, if stream fps too large,
868 | // we need to render more frames in one loop, otherwise display
869 | // fps won't catch up with source fps, leads to memory increasing,
870 | // set to 2 now.
871 | for (i = 0; i < 2; ++i) {
872 | var frame = this.frameBuffer[0];
873 | switch (frame.t) {
874 | case kAudioFrame:
875 | if (this.displayAudioFrame(frame)) {
876 | this.frameBuffer.shift();
877 | }
878 | break;
879 | case kVideoFrame:
880 | if (this.displayVideoFrame(frame)) {
881 | this.frameBuffer.shift();
882 | }
883 | break;
884 | default:
885 | return;
886 | }
887 |
888 | if (this.frameBuffer.length == 0) {
889 | break;
890 | }
891 | }
892 |
893 | if (this.getBufferTimerLength() < maxBufferTimeLength / 2) {
894 | if (!this.decoding) {
895 | //this.logger.logInfo("Buffer time length < " + maxBufferTimeLength / 2 + ", restart decoding.");
896 | this.startDecoding();
897 | }
898 | }
899 |
900 | if (this.bufferFrame.length == 0) {
901 | if (this.decoderState == decoderStateFinished) {
902 | this.reportPlayError(1, 0, "Finished");
903 | this.stop();
904 | } else {
905 | this.startBuffering();
906 | }
907 | }
908 | };
909 |
910 | Player.prototype.startBuffering = function () {
911 | this.buffering = true;
912 | this.showLoading();
913 | this.pause();
914 | }
915 |
916 | Player.prototype.stopBuffering = function () {
917 | this.buffering = false;
918 | this.hideLoading();
919 | this.resume();
920 | }
921 |
922 | Player.prototype.renderVideoFrame = function (data) {
923 | this.webglPlayer.renderFrame(data, this.videoWidth, this.videoHeight, this.yLength, this.uvLength);
924 | };
925 |
926 | Player.prototype.downloadOneChunk = function () {
927 | if (this.downloading || this.isStream) {
928 | return;
929 | }
930 |
931 | var start = this.fileInfo.offset;
932 | if (start >= this.fileInfo.size) {
933 | this.logger.logError("Reach file end.");
934 | this.stopDownloadTimer();
935 | return;
936 | }
937 |
938 | var end = this.fileInfo.offset + this.fileInfo.chunkSize - 1;
939 | if (end >= this.fileInfo.size) {
940 | end = this.fileInfo.size - 1;
941 | }
942 |
943 | var len = end - start + 1;
944 | if (len > this.fileInfo.chunkSize) {
945 | console.log("Error: request len:" + len + " > chunkSize:" + this.fileInfo.chunkSize);
946 | return;
947 | }
948 |
949 | var req = {
950 | t: kDownloadFileReq,
951 | u: this.fileInfo.url,
952 | s: start,
953 | e: end,
954 | q: this.downloadSeqNo,
955 | p: this.downloadProto
956 | };
957 | this.downloadWorker.postMessage(req);
958 | this.downloading = true;
959 | };
960 |
961 | Player.prototype.startDownloadTimer = function () {
962 | var self = this;
963 | this.downloadSeqNo++;
964 | this.downloadTimer = setInterval(function () {
965 | self.downloadOneChunk();
966 | }, this.chunkInterval);
967 | };
968 |
969 | Player.prototype.stopDownloadTimer = function () {
970 | if (this.downloadTimer != null) {
971 | clearInterval(this.downloadTimer);
972 | this.downloadTimer = null;
973 | }
974 | this.downloading = false;
975 | };
976 |
977 | Player.prototype.startTrackTimer = function () {
978 | var self = this;
979 | this.trackTimer = setInterval(function () {
980 | self.updateTrackTime();
981 | }, this.trackTimerInterval);
982 | };
983 |
984 | Player.prototype.stopTrackTimer = function () {
985 | if (this.trackTimer != null) {
986 | clearInterval(this.trackTimer);
987 | this.trackTimer = null;
988 | }
989 | };
990 |
991 | Player.prototype.updateTrackTime = function () {
992 | if (this.playerState == playerStatePlaying && this.pcmPlayer) {
993 | var currentPlayTime = this.pcmPlayer.getTimestamp() + this.beginTimeOffset;
994 | if (this.timeTrack) {
995 | this.timeTrack.value = 1000 * currentPlayTime;
996 | }
997 |
998 | if (this.timeLabel) {
999 | this.timeLabel.innerHTML = this.formatTime(currentPlayTime) + "/" + this.displayDuration;
1000 | }
1001 | }
1002 | };
1003 |
1004 | Player.prototype.startDecoding = function () {
1005 | var req = {
1006 | t: kStartDecodingReq,
1007 | i: this.urgent ? 0 : this.decodeInterval,
1008 | };
1009 | this.decodeWorker.postMessage(req);
1010 | this.decoding = true;
1011 | };
1012 |
1013 | Player.prototype.pauseDecoding = function () {
1014 | var req = {
1015 | t: kPauseDecodingReq
1016 | };
1017 | this.decodeWorker.postMessage(req);
1018 | this.decoding = false;
1019 | };
1020 |
1021 | Player.prototype.formatTime = function (s) {
1022 | var h = Math.floor(s / 3600) < 10 ? '0' + Math.floor(s / 3600) : Math.floor(s / 3600);
1023 | var m = Math.floor((s / 60 % 60)) < 10 ? '0' + Math.floor((s / 60 % 60)) : Math.floor((s / 60 % 60));
1024 | var s = Math.floor((s % 60)) < 10 ? '0' + Math.floor((s % 60)) : Math.floor((s % 60));
1025 | return result = h + ":" + m + ":" + s;
1026 | };
1027 |
1028 | Player.prototype.reportPlayError = function (error, status, message) {
1029 | var e = {
1030 | error: error || 0,
1031 | status: status || 0,
1032 | message: message
1033 | };
1034 |
1035 | if (this.callback) {
1036 | this.callback(e);
1037 | }
1038 | };
1039 |
1040 | Player.prototype.setLoadingDiv = function (loadingDiv) {
1041 | this.loadingDiv = loadingDiv;
1042 | }
1043 |
1044 | Player.prototype.hideLoading = function () {
1045 | if (this.loadingDiv != null) {
1046 | loading.style.display = "none";
1047 | }
1048 | };
1049 |
1050 | Player.prototype.showLoading = function () {
1051 | if (this.loadingDiv != null) {
1052 | loading.style.display = "block";
1053 | }
1054 | };
1055 |
1056 | Player.prototype.registerVisibilityEvent = function (cb) {
1057 | var hidden = "hidden";
1058 |
1059 | // Standards:
1060 | if (hidden in document) {
1061 | document.addEventListener("visibilitychange", onchange);
1062 | } else if ((hidden = "mozHidden") in document) {
1063 | document.addEventListener("mozvisibilitychange", onchange);
1064 | } else if ((hidden = "webkitHidden") in document) {
1065 | document.addEventListener("webkitvisibilitychange", onchange);
1066 | } else if ((hidden = "msHidden") in document) {
1067 | document.addEventListener("msvisibilitychange", onchange);
1068 | } else if ("onfocusin" in document) {
1069 | // IE 9 and lower.
1070 | document.onfocusin = document.onfocusout = onchange;
1071 | } else {
1072 | // All others.
1073 | window.onpageshow = window.onpagehide = window.onfocus = window.onblur = onchange;
1074 | }
1075 |
1076 | function onchange (evt) {
1077 | var v = true;
1078 | var h = false;
1079 | var evtMap = {
1080 | focus:v,
1081 | focusin:v,
1082 | pageshow:v,
1083 | blur:h,
1084 | focusout:h,
1085 | pagehide:h
1086 | };
1087 |
1088 | evt = evt || window.event;
1089 | var visible = v;
1090 | if (evt.type in evtMap) {
1091 | visible = evtMap[evt.type];
1092 | } else {
1093 | visible = this[hidden] ? h : v;
1094 | }
1095 | cb(visible);
1096 | }
1097 |
1098 | // set the initial state (but only if browser supports the Page Visibility API)
1099 | if( document[hidden] !== undefined ) {
1100 | onchange({type: document[hidden] ? "blur" : "focus"});
1101 | }
1102 | }
1103 |
1104 | Player.prototype.onStreamDataUnderDecoderIdle = function (length) {
1105 | if (this.streamReceivedLen >= this.waitHeaderLength) {
1106 | this.logger.logInfo("Opening decoder.");
1107 | this.decoderState = decoderStateInitializing;
1108 | var req = {
1109 | t: kOpenDecoderReq
1110 | };
1111 | this.decodeWorker.postMessage(req);
1112 | } else {
1113 | this.streamReceivedLen += length;
1114 | }
1115 | };
1116 |
1117 | Player.prototype.requestStream = function (url) {
1118 | var self = this;
1119 | this.fetchController = new AbortController();
1120 | const signal = this.fetchController.signal;
1121 |
1122 | fetch(url, {signal}).then(async function respond(response) {
1123 | const reader = response.body.getReader();
1124 | reader.read().then(function processData({done, value}) {
1125 | if (done) {
1126 | self.logger.logInfo("Stream done.");
1127 | return;
1128 | }
1129 |
1130 | if (self.playerState != playerStatePlaying) {
1131 | return;
1132 | }
1133 |
1134 | var dataLength = value.byteLength;
1135 | var offset = 0;
1136 | if (dataLength > self.fileInfo.chunkSize) {
1137 | do {
1138 | let len = Math.min(self.fileInfo.chunkSize, dataLength);
1139 | var data = value.buffer.slice(offset, offset + len);
1140 | dataLength -= len;
1141 | offset += len;
1142 | var objData = {
1143 | t: kFeedDataReq,
1144 | d: data
1145 | };
1146 | self.decodeWorker.postMessage(objData, [objData.d]);
1147 | } while (dataLength > 0)
1148 | } else {
1149 | var objData = {
1150 | t: kFeedDataReq,
1151 | d: value.buffer
1152 | };
1153 | self.decodeWorker.postMessage(objData, [objData.d]);
1154 | }
1155 |
1156 | if (self.decoderState == decoderStateIdle) {
1157 | self.onStreamDataUnderDecoderIdle(dataLength);
1158 | }
1159 |
1160 | return reader.read().then(processData);
1161 | });
1162 | }).catch(err => {
1163 | });
1164 | };
1165 |
--------------------------------------------------------------------------------
/src/webgl.js:
--------------------------------------------------------------------------------
1 | function Texture(gl) {
2 | this.gl = gl;
3 | this.texture = gl.createTexture();
4 | gl.bindTexture(gl.TEXTURE_2D, this.texture);
5 |
6 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
7 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
8 |
9 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
10 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
11 | }
12 |
13 | Texture.prototype.bind = function (n, program, name) {
14 | var gl = this.gl;
15 | gl.activeTexture([gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2][n]);
16 | gl.bindTexture(gl.TEXTURE_2D, this.texture);
17 | gl.uniform1i(gl.getUniformLocation(program, name), n);
18 | };
19 |
20 | Texture.prototype.fill = function (width, height, data) {
21 | var gl = this.gl;
22 | gl.bindTexture(gl.TEXTURE_2D, this.texture);
23 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
24 | };
25 |
26 | function WebGLPlayer(canvas, options) {
27 | this.canvas = canvas;
28 | this.gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
29 | this.initGL(options);
30 | }
31 |
32 | WebGLPlayer.prototype.initGL = function (options) {
33 | if (!this.gl) {
34 | console.log("[ER] WebGL not supported.");
35 | return;
36 | }
37 |
38 | var gl = this.gl;
39 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
40 | var program = gl.createProgram();
41 | var vertexShaderSource = [
42 | "attribute highp vec4 aVertexPosition;",
43 | "attribute vec2 aTextureCoord;",
44 | "varying highp vec2 vTextureCoord;",
45 | "void main(void) {",
46 | " gl_Position = aVertexPosition;",
47 | " vTextureCoord = aTextureCoord;",
48 | "}"
49 | ].join("\n");
50 | var vertexShader = gl.createShader(gl.VERTEX_SHADER);
51 | gl.shaderSource(vertexShader, vertexShaderSource);
52 | gl.compileShader(vertexShader);
53 | var fragmentShaderSource = [
54 | "precision highp float;",
55 | "varying lowp vec2 vTextureCoord;",
56 | "uniform sampler2D YTexture;",
57 | "uniform sampler2D UTexture;",
58 | "uniform sampler2D VTexture;",
59 | "const mat4 YUV2RGB = mat4",
60 | "(",
61 | " 1.1643828125, 0, 1.59602734375, -.87078515625,",
62 | " 1.1643828125, -.39176171875, -.81296875, .52959375,",
63 | " 1.1643828125, 2.017234375, 0, -1.081390625,",
64 | " 0, 0, 0, 1",
65 | ");",
66 | "void main(void) {",
67 | " gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
68 | "}"
69 | ].join("\n");
70 |
71 | var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
72 | gl.shaderSource(fragmentShader, fragmentShaderSource);
73 | gl.compileShader(fragmentShader);
74 | gl.attachShader(program, vertexShader);
75 | gl.attachShader(program, fragmentShader);
76 | gl.linkProgram(program);
77 | gl.useProgram(program);
78 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
79 | console.log("[ER] Shader link failed.");
80 | }
81 | var vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition");
82 | gl.enableVertexAttribArray(vertexPositionAttribute);
83 | var textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
84 | gl.enableVertexAttribArray(textureCoordAttribute);
85 |
86 | var verticesBuffer = gl.createBuffer();
87 | gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
88 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0]), gl.STATIC_DRAW);
89 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
90 | var texCoordBuffer = gl.createBuffer();
91 | gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
92 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]), gl.STATIC_DRAW);
93 | gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
94 |
95 | gl.y = new Texture(gl);
96 | gl.u = new Texture(gl);
97 | gl.v = new Texture(gl);
98 | gl.y.bind(0, program, "YTexture");
99 | gl.u.bind(1, program, "UTexture");
100 | gl.v.bind(2, program, "VTexture");
101 | }
102 |
103 | WebGLPlayer.prototype.renderFrame = function (videoFrame, width, height, uOffset, vOffset) {
104 | if (!this.gl) {
105 | console.log("[ER] Render frame failed due to WebGL not supported.");
106 | return;
107 | }
108 |
109 | var gl = this.gl;
110 | gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
111 | gl.clearColor(0.0, 0.0, 0.0, 0.0);
112 | gl.clear(gl.COLOR_BUFFER_BIT);
113 |
114 | gl.y.fill(width, height, videoFrame.subarray(0, uOffset));
115 | gl.u.fill(width >> 1, height >> 1, videoFrame.subarray(uOffset, uOffset + vOffset));
116 | gl.v.fill(width >> 1, height >> 1, videoFrame.subarray(uOffset + vOffset, videoFrame.length));
117 |
118 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
119 | };
120 |
121 | WebGLPlayer.prototype.fullscreen = function () {
122 | var canvas = this.canvas;
123 | if (canvas.RequestFullScreen) {
124 | canvas.RequestFullScreen();
125 | } else if (canvas.webkitRequestFullScreen) {
126 | canvas.webkitRequestFullScreen();
127 | } else if (canvas.mozRequestFullScreen) {
128 | canvas.mozRequestFullScreen();
129 | } else if (canvas.msRequestFullscreen) {
130 | canvas.msRequestFullscreen();
131 | } else {
132 | alert("This browser doesn't supporter fullscreen");
133 | }
134 | };
135 |
136 | WebGLPlayer.prototype.exitfullscreen = function (){
137 | if (document.exitFullscreen) {
138 | document.exitFullscreen();
139 | } else if (document.webkitExitFullscreen) {
140 | document.webkitExitFullscreen();
141 | } else if (document.mozCancelFullScreen) {
142 | document.mozCancelFullScreen();
143 | } else if (document.msExitFullscreen) {
144 | document.msExitFullscreen();
145 | } else {
146 | alert("Exit fullscreen doesn't work");
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var webpack = require('webpack');
5 | var merge = require('webpack-merge');
6 |
7 | var HtmlPlugin = require('html-webpack-plugin');
8 |
9 | var TARGET = process.env.npm_lifecycle_event;
10 | var ROOT_PATH = path.resolve(__dirname);
11 |
12 | var config;
13 | var paths = {
14 | src: path.join(ROOT_PATH, 'src'),
15 | demo: path.join(ROOT_PATH, 'demo'),
16 | };
17 |
18 | var common = {
19 | entry: path.resolve(paths.demo, 'index'),
20 |
21 | resolve: {
22 | extensions: ['', '.js', '.less'],
23 | }
24 | };
25 |
26 | if (TARGET === 'start' || !TARGET) {
27 | config = start();
28 | } else if (TARGET === 'build' || !TARGET) {
29 | config = build();
30 | } else if (TARGET === 'deploy' || !TARGET) {
31 | config = deploy();
32 | }
33 |
34 | function start() {
35 | const IP = '0.0.0.0';
36 | const PORT = 3000;
37 |
38 | return merge(common, {
39 | ip: IP,
40 | port: PORT,
41 | devtool: 'eval-source-map',
42 |
43 | entry: [
44 | 'webpack-dev-server/client?http://' + IP + ':' + PORT,
45 | 'webpack/hot/only-dev-server',
46 | path.join(paths.demo, 'index'),
47 | ],
48 |
49 | output: {
50 | path: __dirname,
51 | filename: 'bundle.js'
52 | },
53 |
54 | resolve: {
55 | alias: {
56 | 'react-blocks$': paths.src,
57 | },
58 | },
59 |
60 | module: {
61 | preLoaders: [
62 | {
63 | test: /\.js$/,
64 | loaders: ['eslint'],
65 | include: [paths.demo, paths.src],
66 | }
67 | ],
68 | loaders: [
69 | {
70 | test: /\.js?$/,
71 | loaders: ['react-hot', 'babel?stage=0'],
72 | include: [paths.demo, paths.src],
73 | },
74 | {
75 | test: /\.less$/,
76 | exclude: /node_modules/,
77 | loaders: ['style', 'css', 'less'],
78 | },
79 | ]
80 | },
81 |
82 | plugins: [
83 | new webpack.DefinePlugin({
84 | 'process.env': {
85 | 'NODE_ENV': JSON.stringify('development'),
86 | }
87 | }),
88 | new HtmlPlugin({
89 | title: 'React Blocks',
90 | inject: true,
91 | }),
92 | new webpack.NoErrorsPlugin(),
93 | new webpack.HotModuleReplacementPlugin(),
94 | ],
95 | });
96 | }
97 |
98 | function build() {
99 | return merge(common, {
100 | entry: path.resolve(paths.src, 'ReactH265Player'),
101 |
102 | output: {
103 | library: 'ReactH265WasmPlayer',
104 | libraryTarget: 'umd'
105 | },
106 |
107 | module: {
108 | loaders: [
109 | {
110 | test: /\.js?$/,
111 | exclude: /node_modules/,
112 | loader: 'babel?stage=0',
113 | }
114 | ]
115 | },
116 |
117 | externals: [
118 | {
119 | "react": {
120 | root: "React",
121 | commonjs2: "react",
122 | commonjs: "react",
123 | amd: "react"
124 | }
125 | }
126 | ]
127 | });
128 | }
129 |
130 | function deploy() {
131 | return merge(common, {
132 | entry: path.resolve(paths.demo, 'index'),
133 |
134 | output: {
135 | path: 'deploy',
136 | filename: 'bundle.js'
137 | },
138 |
139 | resolve: {
140 | alias: {
141 | 'react-blocks$': paths.src,
142 | },
143 | },
144 |
145 | module: {
146 | loaders: [
147 | {
148 | test: /\.js?$/,
149 | exclude: /node_modules/,
150 | loaders: ['babel?stage=0'],
151 | },
152 | {
153 | test: /\.less$/,
154 | exclude: /node_modules/,
155 | loaders: ['style', 'css', 'less']
156 | }
157 | ]
158 | },
159 |
160 | plugins: [
161 | new HtmlPlugin({
162 | title: 'React Blocks',
163 | inject: true,
164 | }),
165 | new webpack.optimize.UglifyJsPlugin({
166 | compress: {
167 | warnings: false
168 | }
169 | }),
170 | new webpack.optimize.OccurenceOrderPlugin(),
171 | ]
172 | });
173 | }
174 |
175 | module.exports = config;
176 |
--------------------------------------------------------------------------------