Restores software MP3 decoding

This commit is contained in:
Yury Delendik 2015-01-08 10:08:09 -06:00
Родитель 2747d727e1
Коммит 5b711fb316
5 изменённых файлов: 249 добавлений и 216 удалений

101
lib/mp3/mp3worker.js Normal file
Просмотреть файл

@ -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' />