зеркало из https://github.com/mozilla/shumway.git
Restores software MP3 decoding
This commit is contained in:
Родитель
2747d727e1
Коммит
5b711fb316
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2013 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
importScripts('./mp3.js');
|
||||
|
||||
self.addEventListener('message', function (e) {
|
||||
var data = e.data;
|
||||
var sessionId = data.sessionId;
|
||||
try {
|
||||
switch (data.action) {
|
||||
case 'create':
|
||||
var session = new Session(sessionId);
|
||||
sessions[sessionId] = session;
|
||||
break;
|
||||
case 'close':
|
||||
var session = sessions[sessionId];
|
||||
if (session) {
|
||||
session.close();
|
||||
sessions[sessionId] = null;
|
||||
}
|
||||
break;
|
||||
case 'decode':
|
||||
var session = sessions[sessionId];
|
||||
if (!session) {
|
||||
throw new Error('mp3 decoding session is unavailable');
|
||||
}
|
||||
session.decode(data.data);
|
||||
break;
|
||||
}
|
||||
} catch (ex) {
|
||||
self.postMessage({
|
||||
sessionId: sessionId,
|
||||
action: 'error',
|
||||
message: ex.message
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
var sessions = {};
|
||||
|
||||
function Session(id) {
|
||||
this.id = id;
|
||||
if (typeof MP3Decoder === 'undefined') {
|
||||
throw new Error('mp3 decoder is not available');
|
||||
}
|
||||
var decoder = new MP3Decoder();
|
||||
decoder.onframedata = function (frameData, channels, sampleRate, bitRate) {
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'frame',
|
||||
frameData: frameData,
|
||||
channels: channels,
|
||||
sampleRate: sampleRate,
|
||||
bitRate: bitRate
|
||||
});
|
||||
}.bind(this);
|
||||
decoder.onid3tag = function (data) {
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'id3',
|
||||
id3Data: data
|
||||
});
|
||||
}.bind(this);
|
||||
this.decoder = decoder;
|
||||
}
|
||||
Session.prototype = {
|
||||
decode: function (data) {
|
||||
this.decoder.push(data);
|
||||
},
|
||||
close: function () {
|
||||
// flush?
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'closed'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!self.console) {
|
||||
self.console = {
|
||||
log: function (s) {
|
||||
self.postMessage({ action: 'console', method: 'log', message: s });
|
||||
},
|
||||
error: function (s) {
|
||||
self.postMessage({ action: 'console', method: 'error', message: s });
|
||||
},
|
||||
};
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
// Class: MovieClip
|
||||
module Shumway.AVM2.AS.flash.display {
|
||||
import MP3DecoderSession = SWF.MP3DecoderSession;
|
||||
|
||||
var MP3_MIME_TYPE = 'audio/mpeg';
|
||||
|
||||
function openMediaSource(soundStream, mediaSource) {
|
||||
|
@ -70,12 +72,6 @@ module Shumway.AVM2.AS.flash.display {
|
|||
|
||||
var PLAY_USING_AUDIO_TAG = true;
|
||||
|
||||
declare class MP3DecoderSession {
|
||||
public pushAsync(data): void;
|
||||
public onframedata: (frameData) => void;
|
||||
public onerror: (error) => void;
|
||||
}
|
||||
|
||||
export class MovieClipSoundStream {
|
||||
private movieClip: MovieClip;
|
||||
private data;
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference path='references.ts'/>
|
||||
module Shumway.SWF {
|
||||
export var MP3WORKER_PATH: string = '../../lib/mp3/mp3worker.js';
|
||||
|
||||
var mp3Worker: Worker = null;
|
||||
|
||||
function ensureMP3Worker(): Worker {
|
||||
if (!mp3Worker) {
|
||||
mp3Worker = new Worker(MP3WORKER_PATH);
|
||||
mp3Worker.addEventListener('message', function (e) {
|
||||
if (e.data.action === 'console') {
|
||||
console[e.data.method].call(console, e.data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
return mp3Worker;
|
||||
}
|
||||
|
||||
var nextSessionId: number = 0;
|
||||
|
||||
export class MP3DecoderSession {
|
||||
private _sessionId: number;
|
||||
private _onworkermessageBound: (any) => void;
|
||||
private _worker: Worker;
|
||||
|
||||
public onframedata: (frameData: Uint8Array, channels: number, sampleRate: number, bitRate: number) => void;
|
||||
public onid3tag: (tagData: any) => void;
|
||||
public onclosed: () => void;
|
||||
public onerror: (error: string) => void;
|
||||
|
||||
public constructor() {
|
||||
this._sessionId = (nextSessionId++);
|
||||
this._onworkermessageBound = this.onworkermessage.bind(this);
|
||||
this._worker = ensureMP3Worker();
|
||||
this._worker.addEventListener('message', this._onworkermessageBound, false);
|
||||
this._worker.postMessage({
|
||||
sessionId: this._sessionId,
|
||||
action: 'create'
|
||||
});
|
||||
}
|
||||
|
||||
private onworkermessage(e) {
|
||||
if (e.data.sessionId !== this._sessionId)
|
||||
return;
|
||||
var action = e.data.action;
|
||||
switch (action) {
|
||||
case 'closed':
|
||||
if (this.onclosed) {
|
||||
this.onclosed();
|
||||
}
|
||||
this._worker.removeEventListener('message', this._onworkermessageBound, false);
|
||||
this._worker = null;
|
||||
break;
|
||||
case 'frame':
|
||||
this.onframedata(e.data.frameData, e.data.channels,
|
||||
e.data.sampleRate, e.data.bitRate);
|
||||
break;
|
||||
case 'id3':
|
||||
if (this.onid3tag) {
|
||||
this.onid3tag(e.data.id3Data);
|
||||
}
|
||||
break;
|
||||
case 'error':
|
||||
if (this.onerror) {
|
||||
this.onerror(e.data.message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pushAsync(data) {
|
||||
this._worker.postMessage({
|
||||
sessionId: this._sessionId,
|
||||
action: 'decode',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this._worker.postMessage({
|
||||
sessionId: this._sessionId,
|
||||
action: 'close'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static processAll(data: Uint8Array): Promise<{data:Uint8Array; id3Tags: any;}> {
|
||||
var currentBufferSize = 8000;
|
||||
var currentBuffer = new Float32Array(currentBufferSize);
|
||||
var bufferPosition = 0;
|
||||
var id3Tags = [];
|
||||
var sessionAborted = false;
|
||||
|
||||
var promiseWrapper = new PromiseWrapper<{data:Uint8Array; id3Tags: any;}>();
|
||||
var session = new MP3DecoderSession();
|
||||
session.onframedata = function (frameData, channels, sampleRate, bitRate) {
|
||||
var needed = frameData.length + bufferPosition;
|
||||
if (needed > currentBufferSize) {
|
||||
do {
|
||||
currentBufferSize *= 2;
|
||||
} while (needed > currentBufferSize);
|
||||
var newBuffer = new Float32Array(currentBufferSize);
|
||||
newBuffer.set(currentBuffer);
|
||||
currentBuffer = newBuffer;
|
||||
}
|
||||
currentBuffer.set(frameData, bufferPosition);
|
||||
bufferPosition += frameData.length;
|
||||
};
|
||||
session.onid3tag = function (tagData) {
|
||||
id3Tags.push(tagData);
|
||||
};
|
||||
session.onclosed = function () {
|
||||
if (sessionAborted)
|
||||
return;
|
||||
promiseWrapper.resolve({data: currentBuffer.subarray(0, bufferPosition), id3Tags: id3Tags});
|
||||
};
|
||||
session.onerror = function (error) {
|
||||
if (sessionAborted)
|
||||
return;
|
||||
sessionAborted = true;
|
||||
promiseWrapper.reject(error);
|
||||
};
|
||||
session.pushAsync(data);
|
||||
session.close();
|
||||
|
||||
return promiseWrapper.promise;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*global self, importScripts, Worker, MP3Decoder, SHUMWAY_ROOT */
|
||||
|
||||
// TODO: Investigate moving function definitions out of if-blocks
|
||||
/*jshint -W082 */
|
||||
|
||||
var isWorker = typeof window === 'undefined';
|
||||
if (isWorker) {
|
||||
importScripts('../../../lib/mp3/mp3.js');
|
||||
|
||||
self.addEventListener('message', function (e) {
|
||||
var data = e.data;
|
||||
var sessionId = data.sessionId;
|
||||
try {
|
||||
switch (data.action) {
|
||||
case 'create':
|
||||
var session = new Session(sessionId);
|
||||
sessions[sessionId] = session;
|
||||
break;
|
||||
case 'close':
|
||||
var session = sessions[sessionId];
|
||||
if (session) {
|
||||
session.close();
|
||||
sessions[sessionId] = null;
|
||||
}
|
||||
break;
|
||||
case 'decode':
|
||||
var session = sessions[sessionId];
|
||||
if (!session) {
|
||||
throw new Error('mp3 decoding session is unavailable');
|
||||
}
|
||||
session.decode(data.data);
|
||||
break;
|
||||
}
|
||||
} catch (ex) {
|
||||
self.postMessage({
|
||||
sessionId: sessionId,
|
||||
action: 'error',
|
||||
message: ex.message
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
var sessions = {};
|
||||
|
||||
function Session(id) {
|
||||
this.id = id;
|
||||
if (typeof MP3Decoder === 'undefined') {
|
||||
throw new Error('mp3 decoder is not available');
|
||||
}
|
||||
var decoder = new MP3Decoder();
|
||||
decoder.onframedata = function (frameData, channels, sampleRate, bitRate) {
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'frame',
|
||||
frameData: frameData,
|
||||
channels: channels,
|
||||
sampleRate: sampleRate,
|
||||
bitRate: bitRate
|
||||
});
|
||||
}.bind(this);
|
||||
decoder.onid3tag = function (data) {
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'id3',
|
||||
id3Data: data
|
||||
});
|
||||
}.bind(this);
|
||||
this.decoder = decoder;
|
||||
}
|
||||
Session.prototype = {
|
||||
decode: function (data) {
|
||||
this.decoder.push(data);
|
||||
},
|
||||
close: function () {
|
||||
// flush?
|
||||
self.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'closed'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!self.console) {
|
||||
self.console = {
|
||||
log: function (s) {
|
||||
self.postMessage({ action: 'console', method: 'log', message: s });
|
||||
},
|
||||
error: function (s) {
|
||||
self.postMessage({ action: 'console', method: 'error', message: s });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} else {
|
||||
var mp3Worker;
|
||||
|
||||
function createMP3Worker() {
|
||||
var worker = new Worker(SHUMWAY_ROOT + 'swf/mp3worker.js');
|
||||
worker.addEventListener('message', function (e) {
|
||||
if (e.data.action === 'console') {
|
||||
console[e.data.method].call(console, e.data.message);
|
||||
}
|
||||
});
|
||||
return worker;
|
||||
}
|
||||
|
||||
var nextSessionId = 0;
|
||||
function MP3DecoderSession() {
|
||||
mp3Worker = mp3Worker || createMP3Worker();
|
||||
var sessionId = (nextSessionId++);
|
||||
this.id = sessionId;
|
||||
this.onworkermessage = function (e) {
|
||||
if (e.data.sessionId !== sessionId)
|
||||
return;
|
||||
var action = e.data.action;
|
||||
switch (action) {
|
||||
case 'closed':
|
||||
if (this.onclosed)
|
||||
this.onclosed();
|
||||
mp3Worker.removeEventListener('message', this.onworkermessage, false);
|
||||
break;
|
||||
case 'frame':
|
||||
this.onframedata(e.data.frameData, e.data.channels,
|
||||
e.data.sampleRate, e.data.bitRate);
|
||||
break;
|
||||
case 'id3':
|
||||
if (this.onid3tag)
|
||||
this.onid3tag(e.data.id3Data);
|
||||
break;
|
||||
case 'error':
|
||||
if (this.onerror)
|
||||
this.onerror(e.data.message);
|
||||
break;
|
||||
}
|
||||
}.bind(this);
|
||||
mp3Worker.addEventListener('message', this.onworkermessage, false);
|
||||
mp3Worker.postMessage({
|
||||
sessionId: sessionId,
|
||||
action: 'create'
|
||||
});
|
||||
}
|
||||
MP3DecoderSession.prototype = {
|
||||
pushAsync: function (data) {
|
||||
mp3Worker.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'decode',
|
||||
data: data
|
||||
});
|
||||
},
|
||||
close: function () {
|
||||
mp3Worker.postMessage({
|
||||
sessionId: this.id,
|
||||
action: 'close'
|
||||
});
|
||||
},
|
||||
};
|
||||
MP3DecoderSession.processAll = function (data, onloaded) {
|
||||
var currentBufferSize = 8000;
|
||||
var currentBuffer = new Float32Array(currentBufferSize);
|
||||
var bufferPosition = 0;
|
||||
var id3Tags = [];
|
||||
var sessionAborted = false;
|
||||
|
||||
var session = new MP3DecoderSession();
|
||||
session.onframedata = function (frameData, channels, sampleRate, bitRate) {
|
||||
var needed = frameData.length + bufferPosition;
|
||||
if (needed > currentBufferSize) {
|
||||
do {
|
||||
currentBufferSize *= 2;
|
||||
} while (needed > currentBufferSize);
|
||||
var newBuffer = new Float32Array(currentBufferSize);
|
||||
newBuffer.set(currentBuffer);
|
||||
currentBuffer = newBuffer;
|
||||
}
|
||||
currentBuffer.set(frameData, bufferPosition);
|
||||
bufferPosition += frameData.length;
|
||||
};
|
||||
session.onid3tag = function (tagData) {
|
||||
id3Tags.push(tagData);
|
||||
};
|
||||
session.onclosed = function () {
|
||||
if (sessionAborted)
|
||||
return;
|
||||
onloaded(currentBuffer.subarray(0, bufferPosition), id3Tags);
|
||||
};
|
||||
session.onerror = function (error) {
|
||||
if (sessionAborted)
|
||||
return;
|
||||
sessionAborted = true;
|
||||
onloaded(null, null, error);
|
||||
};
|
||||
session.pushAsync(data);
|
||||
session.close();
|
||||
};
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
///<reference path='options.ts'/>
|
||||
///<reference path='jpeg.ts' />
|
||||
///<reference path='stream.ts' />
|
||||
///<reference path='mp3decodersession.ts' />
|
||||
///<reference path='SWFFile.ts' />
|
||||
///<reference path='ImageFile.ts' />
|
||||
///<reference path='FileLoader.ts' />
|
||||
|
|
Загрузка…
Ссылка в новой задаче