import * as loader from "tc-loader";
import * as aplayer from "tc-backend/web/audio/player";
import * as log from "tc-shared/log";
import {LogCategory} from "tc-shared/log";
import {tr} from "tc-shared/i18n/localize";
import {CodecType} from "tc-backend/web/codec/Codec";
import {VoiceConnection} from "tc-backend/web/voice/VoiceHandler";
import {BasicCodec} from "tc-backend/web/codec/BasicCodec";
import {createErrorModal} from "tc-shared/ui/elements/Modal";
import {CodecWrapperWorker} from "tc-backend/web/codec/CodecWrapperWorker";
class CacheEntry {
instance: BasicCodec;
owner: number;
last_access: number;
}
export function codec_supported(type: CodecType) {
return type == CodecType.OPUS_MUSIC || type == CodecType.OPUS_VOICE;
}
export class CodecPool {
codecIndex: number;
name: string;
type: CodecType;
entries: CacheEntry[] = [];
maxInstances: number = 2;
private _supported: boolean = true;
initialize(cached: number) {
/* test if we're able to use this codec */
const dummy_client_id = 0xFFEF;
this.ownCodec(dummy_client_id, _ => {}).then(codec => {
log.trace(LogCategory.VOICE, tr("Releasing codec instance (%o)"), codec);
this.releaseCodec(dummy_client_id);
}).catch(error => {
if(this._supported) {
log.warn(LogCategory.VOICE, tr("Disabling codec support for "), this.name);
createErrorModal(tr("Could not load codec driver"), tr("Could not load or initialize codec ") + this.name + "
" +
"Error: " + JSON.stringify(error) + "
").open();
log.error(LogCategory.VOICE, tr("Failed to initialize the opus codec. Error: %o"), error);
} else {
log.debug(LogCategory.VOICE, tr("Failed to initialize already disabled codec. Error: %o"), error);
}
this._supported = false;
});
}
supported() { return this._supported; }
ownCodec?(clientId: number, callback_encoded: (buffer: Uint8Array) => any, create: boolean = true) : Promise {
return new Promise((resolve, reject) => {
if(!this._supported) {
reject(tr("unsupported codec!"));
return;
}
let free_slot = 0;
for(let index = 0; index < this.entries.length; index++) {
if(this.entries[index].owner == clientId) {
this.entries[index].last_access = Date.now();
if(this.entries[index].instance.initialized())
resolve(this.entries[index].instance);
else {
this.entries[index].instance.initialise().then((flag) => {
//TODO test success flag
this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject);
}).catch(reject);
}
return;
} else if(this.entries[index].owner == 0) {
free_slot = index;
}
}
if(!create) {
resolve(undefined);
return;
}
if(free_slot == 0){
free_slot = this.entries.length;
let entry = new CacheEntry();
entry.instance = new CodecWrapperWorker(this.type);
this.entries.push(entry);
}
this.entries[free_slot].owner = clientId;
this.entries[free_slot].last_access = new Date().getTime();
this.entries[free_slot].instance.on_encoded_data = callback_encoded;
if(this.entries[free_slot].instance.initialized())
this.entries[free_slot].instance.reset();
else {
this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject);
return;
}
resolve(this.entries[free_slot].instance);
});
}
releaseCodec(clientId: number) {
for(let index = 0; index < this.entries.length; index++)
if(this.entries[index].owner == clientId) this.entries[index].owner = 0;
}
constructor(index: number, name: string, type: CodecType){
this.codecIndex = index;
this.name = name;
this.type = type;
this._supported = this.type !== undefined && codec_supported(this.type);
}
}
export let codecPool: CodecPool[];
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
priority: 10,
function: async () => {
aplayer.on_ready(() => {
log.info(LogCategory.VOICE, tr("Initializing voice handler after AudioController has been initialized!"));
codecPool = [
new CodecPool(0, tr("Speex Narrowband"), CodecType.SPEEX_NARROWBAND),
new CodecPool(1, tr("Speex Wideband"), CodecType.SPEEX_WIDEBAND),
new CodecPool(2, tr("Speex Ultra Wideband"), CodecType.SPEEX_ULTRA_WIDEBAND),
new CodecPool(3, tr("CELT Mono"), CodecType.CELT_MONO),
new CodecPool(4, tr("Opus Voice"), CodecType.OPUS_VOICE),
new CodecPool(5, tr("Opus Music"), CodecType.OPUS_MUSIC)
];
codecPool[4].initialize(2);
codecPool[5].initialize(2);
});
},
name: "registering codec initialisation"
});