view release on metacpan or search on metacpan
lib/App/MHFS.pm view on Meta::CPAN
_ENOTTY => 25, #constant for Linux?
};
# x86_64 numbers
require 'syscall.ph';
my $TFD_CLOEXEC = 0x80000;
my $TFD_NONBLOCK = 0x800;
sub new {
my ($class, $evp) = @_;
my $timerfd = syscall(SYS_timerfd_create(), _clock_MONOTONIC, $TFD_NONBLOCK | $TFD_CLOEXEC);
$timerfd != -1 or die("failed to create timerfd: $!");
my $timerhandle = IO::Handle->new_from_fd($timerfd, "r");
$timerhandle or die("failed to turn timerfd into a file handle");
my %self = ('timerfd' => $timerfd, 'timerhandle' => $timerhandle);
bless \%self, $class;
$evp->set($self{'timerhandle'}, \%self, POLLIN);
$self{'evp'} = $evp;
return \%self;
}
sub packitimerspec {
my ($times) = @_;
my $it_interval_sec = int($times->{'it_interval'});
my $it_interval_nsec = floor(($times->{'it_interval'} - $it_interval_sec) * 1000000000);
my $it_value_sec = int($times->{'it_value'});
my $it_value_nsec = floor(($times->{'it_value'} - $it_value_sec) * 1000000000);
#say "packing $it_interval_sec, $it_interval_nsec, $it_value_sec, $it_value_nsec";
return pack 'qqqq', $it_interval_sec, $it_interval_nsec, $it_value_sec, $it_value_nsec;
}
sub settime_linux {
my ($self, $start, $interval) = @_;
# assume start 0 is supposed to run immediately not try to cancel a timer
$start = ($start > 0.000000001) ? $start : 0.000000001;
my $new_value = packitimerspec({'it_interval' => $interval, 'it_value' => $start});
my $settime_success = syscall(SYS_timerfd_settime(), $self->{'timerfd'}, 0, $new_value,0);
($settime_success == 0) or die("timerfd_settime failed: $!");
}
sub onReadReady {
my ($self) = @_;
my $nread;
my $buf;
while($nread = sysread($self->{'timerhandle'}, $buf, 8)) {
if($nread < 8) {
say "timer hit, ignoring $nread bytes";
next;
}
my $expirations = unpack 'Q', $buf;
say "Linux::Timer there were $expirations expirations";
}
if(! defined $nread) {
if( ! $!{EAGAIN}) {
say "sysread failed with $!";
}
}
$self->{'evp'}->check_timers;
return 1;
};
1;
} # package
}; # eval
}; # BEGIN
# You must provide event handlers for the events you are listening for
# return undef to have them removed from poll's structures
package MHFS::EventLoop::Poll::Base {
lib/App/MHFS.pm view on Meta::CPAN
sub _decode_status {
my ($rc) = @_;
print "$rc: normal exit with code ". WEXITSTATUS($rc)."\n" if WIFEXITED( $rc);
print "$rc: terminated with signal ".WTERMSIG( $rc)."\n" if WIFSIGNALED($rc);
print "$rc: stopped with signal ". WSTOPSIG( $rc)."\n" if WIFSTOPPED( $rc);
}
sub new {
my ($class) = @_;
my %self = ('poll' => IO::Poll->new(), 'fh_map' => {}, 'timers' => [], 'children' => {}, 'deadchildren' => []);
bless \%self, $class;
$SIG{CHLD} = sub {
while((my $child = waitpid(-1, WNOHANG)) > 0) {
my ($wstatus, $exitcode) = ($?, $?>> 8);
if(defined $self{'children'}{$child}) {
say "PID $child reaped (func) $exitcode";
push @{$self{'deadchildren'}}, [$self{'children'}{$child}, $child, $wstatus];
$self{'children'}{$child} = undef;
}
lib/App/MHFS.pm view on Meta::CPAN
return $self->{'poll'}->mask($handle);
}
sub remove {
my ($self, $handle) = @_;
$self->{'poll'}->remove($handle);
$self->{'fh_map'}{$handle} = undef;
}
sub _insert_timer {
my ($self, $timer) = @_;
my $i;
for($i = 0; defined($self->{'timers'}[$i]) && ($timer->{'desired'} >= $self->{'timers'}[$i]{'desired'}); $i++) { }
splice @{$self->{'timers'}}, $i, 0, ($timer);
return $i;
}
# all times are relative, is 0 is set as the interval, it will be run every main loop iteration
# return undef in the callback to delete the timer
sub add_timer {
my ($self, $start, $interval, $callback, $id) = @_;
my $current_time = clock_gettime(CLOCK_MONOTONIC);
my $desired = $current_time + $start;
my $timer = { 'desired' => $desired, 'interval' => $interval, 'callback' => $callback };
$timer->{'id'} = $id if(defined $id);
return _insert_timer($self, $timer);
}
sub remove_timer_by_id {
my ($self, $id) = @_;
my $lastindex = scalar(@{$self->{'timers'}}) - 1;
for my $i (0 .. $lastindex) {
next if(! defined $self->{'timers'}[$i]{'id'});
if($self->{'timers'}[$i]{'id'} == $id) {
#say "Removing timer with id: $id";
splice(@{$self->{'timers'}}, $i, 1);
return;
}
}
say "unable to remove timer $id, not found";
}
sub requeue_timers {
my ($self, $timers, $current_time) = @_;
foreach my $timer (@$timers) {
$timer->{'desired'} = $current_time + $timer->{'interval'};
_insert_timer($self, $timer);
}
}
sub check_timers {
my ($self) = @_;
my @requeue_timers;
my $timerhit = 0;
my $current_time = clock_gettime(CLOCK_MONOTONIC);
while(my $timer = shift (@{$self->{'timers'}}) ) {
if($current_time >= $timer->{'desired'}) {
$timerhit = 1;
if(defined $timer->{'callback'}->($timer, $current_time, $self)) { # callback may change interval
push @requeue_timers, $timer;
}
}
else {
unshift @{$self->{'timers'}}, $timer;
last;
}
}
$self->requeue_timers(\@requeue_timers, $current_time);
}
sub do_poll {
my ($self, $loop_interval, $poll) = @_;
my $pollret = $poll->poll($loop_interval);
if($pollret > 0){
foreach my $handle ($poll->handles()) {
my $revents = $poll->events($handle);
my $obj = $self->{'fh_map'}{$handle};
if($revents & POLLIN) {
lib/App/MHFS.pm view on Meta::CPAN
$self->run_dead_children_callbacks;
}
sub run {
my ($self, $loop_interval) = @_;
my $default_lp_interval = $loop_interval // -1;
my $poll = $self->{'poll'};
for(;;)
{
check_timers($self);
print "do_poll $$";
if($self->{'timers'}) {
say " timers " . scalar(@{$self->{'timers'}}) . ' handles ' . scalar($self->{'poll'}->handles());
}
else {
print "\n";
}
# we don't need to expire until a timer is expiring
if(@{$self->{'timers'}}) {
$loop_interval = $self->{'timers'}[0]{'desired'} - clock_gettime(CLOCK_MONOTONIC);
}
else {
$loop_interval = $default_lp_interval;
}
do_poll($self, $loop_interval, $poll);
}
}
1;
}
package MHFS::EventLoop::Poll::Linux {
use strict; use warnings;
use feature 'say';
use parent -norequire, 'MHFS::EventLoop::Poll::Base';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{'evp_timer'} = MHFS::EventLoop::Poll::Linux::Timer->new($self);
return $self;
};
sub add_timer {
my ($self, $start) = @_;
shift @_;
if($self->SUPER::add_timer(@_) == 0) {
say __PACKAGE__.": add_timer, updating linux timer to $start";
$self->{'evp_timer'}->settime_linux($start, 0);
}
};
sub requeue_timers {
my $self = shift @_;
$self->SUPER::requeue_timers(@_);
my ($timers, $current_time) = @_;
if(@{$self->{'timers'}}) {
my $start = $self->{'timers'}[0]{'desired'} - $current_time;
say __PACKAGE__.": requeue_timers, updating linux timer to $start";
$self->{'evp_timer'}->settime_linux($start, 0);
}
};
sub run {
my ($self, $loop_interval) = @_;
$loop_interval //= -1;
my $poll = $self->{'poll'};
for(;;)
{
print __PACKAGE__.": do_poll LINUX_X86_64 $$";
if($self->{'timers'}) {
say " timers " . scalar(@{$self->{'timers'}}) . ' handles ' . scalar($self->{'poll'}->handles());
}
else {
print "\n";
}
$self->SUPER::do_poll($loop_interval, $poll);
}
};
1;
}
lib/App/MHFS.pm view on Meta::CPAN
}
$self{'fs'} = $fs;
# load the plugins
foreach my $pluginname (@{$plugins}) {
next if(defined $settings->{$pluginname}{'enabled'} && (!$settings->{$pluginname}{'enabled'}));
my $plugin = $pluginname->new($settings, \%self);
next if(! $plugin);
foreach my $timer (@{$plugin->{'timers'}}) {
say __PACKAGE__.': adding '.ref($plugin).' timer';
$self{'evp'}->add_timer(@{$timer});
}
if(my $func = $plugin->{'uploader'}) {
say __PACKAGE__.': adding '. ref($plugin) .' uploader';
push (@{$self{'uploaders'}}, $func);
}
foreach my $route (@{$plugin->{'routes'}}) {
say __PACKAGE__.': adding ' . ref($plugin) . ' route ' . $route->[0];
push @{$self{'routes'}}, $route;
}
$plugin->{'server'} = \%self;
lib/App/MHFS.pm view on Meta::CPAN
$cb->($self->{fulfill}, $self->{reject});
return $self;
}
sub throw {
return MHFS::Promise::FakeException->new(@_);
}
sub handleResolved {
my ($self, $deferred) = @_;
$self->{evp}->add_timer(0, 0, sub {
my $success = $self->{state} == MHFS_PROMISE_SUCCESS;
my $value = $self->{end_value};
if($success && $deferred->{onFulfilled}) {
$value = $deferred->{onFulfilled}($value);
if(ref($value) eq 'MHFS::Promise::FakeException') {
$success = 0;
$value = $$value;
}
} elsif(!$success && $deferred->{onRejected}) {
$value = $deferred->{onRejected}->($value);
lib/App/MHFS.pm view on Meta::CPAN
sub new {
my ($class, $client) = @_;
my %self = ( 'client' => $client);
bless \%self, $class;
weaken($self{'client'}); #don't allow Request to keep client alive
$self{'on_read_ready'} = \&want_request_line;
$self{'outheaders'}{'X-MHFS-CONN-ID'} = $client->{'outheaders'}{'X-MHFS-CONN-ID'};
$self{'rl'} = 0;
# we want the request
$client->SetEvents(POLLIN | MHFS::EventLoop::Poll->ALWAYSMASK );
$self{'recvrequesttimerid'} = $client->AddClientCloseTimer($client->{'server'}{'settings'}{'recvrequestimeout'}, $client->{'CONN-ID'}, 1);
return \%self;
}
# on ready ready handlers
sub want_request_line {
my ($self) = @_;
my $ipos = index($self->{'client'}{'inbuf'}, "\r\n");
if($ipos != -1) {
if(substr($self->{'client'}{'inbuf'}, 0, $ipos+2, '') =~ /^(([^\s]+)\s+([^\s]+)\s+(?:HTTP\/1\.([0-1])))\r\n/) {
lib/App/MHFS.pm view on Meta::CPAN
}
# remove the final \r\n
substr($self->{'client'}{'inbuf'}, 0, 2, '');
if((defined $self->{'header'}{'Range'}) && ($self->{'header'}{'Range'} =~ /^bytes=([0-9]+)\-([0-9]*)$/)) {
$self->{'header'}{'_RangeStart'} = $1;
$self->{'header'}{'_RangeEnd'} = ($2 ne '') ? $2 : undef;
}
$self->{'on_read_ready'} = undef;
$self->{'client'}->SetEvents(MHFS::EventLoop::Poll->ALWAYSMASK );
$self->{'client'}->KillClientCloseTimer($self->{'recvrequesttimerid'});
$self->{'recvrequesttimerid'} = undef;
# finally handle the request
foreach my $route (@{$self->{'client'}{'server'}{'routes'}}) {
if($self->{'path'}{'unsafecollapse'} eq $route->[0]) {
$route->[1]($self);
return 1;
}
else {
# wildcard ending
next if(index($route->[0], '*', length($route->[0])-1) == -1);
lib/App/MHFS.pm view on Meta::CPAN
my ($class, $sock, $server, $serverhostinfo, $ip) = @_;
$sock->blocking(0);
my %self = ('sock' => $sock, 'server' => $server, 'time' => clock_gettime(CLOCK_MONOTONIC), 'inbuf' => '', 'serverhostname' => $serverhostinfo->{'hostname'}, 'absurl' => $serverhostinfo->{'absurl'}, 'ip' => $ip, 'X-MHFS-PROXY-KEY' => $serverh...
$self{'CONN-ID'} = int($self{'time'} * rand()); # insecure uid
$self{'outheaders'}{'X-MHFS-CONN-ID'} = sprintf("%X", $self{'CONN-ID'});
bless \%self, $class;
$self{'request'} = MHFS::HTTP::Server::Client::Request->new(\%self);
return \%self;
}
# add a connection timeout timer
sub AddClientCloseTimer {
my ($self, $timelength, $id, $is_requesttimeout) = @_;
weaken($self); #don't allow this timer to keep the client object alive
my $server = $self->{'server'};
say "CCT | add timer: $id";
$server->{'evp'}->add_timer($timelength, 0, sub {
if(! defined $self) {
say "CCT | $id self undef";
return undef;
}
# Commented out as with connection reuse on, Apache 2.4.10 seems sometimes
# pass 408 on to the next client.
#if($is_requesttimeout) {
# say "CCT | \$timelength ($timelength) exceeded, sending 408";
# $self->{request}->Send408;
# CT_WRITE($self);
lib/App/MHFS.pm view on Meta::CPAN
$server->{'evp'}->remove($self->{'sock'});
say "poll has " . scalar ( $server->{'evp'}{'poll'}->handles) . " handles";
return undef;
}, $id);
return $id;
}
sub KillClientCloseTimer {
my ($self, $id) = @_;
my $server = $self->{'server'};
say "CCT | removing timer: $id";
$server->{'evp'}->remove_timer_by_id($id);
}
sub SetEvents {
my ($self, $events) = @_;
$self->{'server'}{'evp'}->set($self->{'sock'}, $self, $events);
}
use constant {
RECV_SIZE => 65536,
CT_YIELD => 1,
lib/App/MHFS.pm view on Meta::CPAN
goto &onWriteReady;
#return onWriteReady($self);
}
#else {
elsif(defined $self->{'request'}{'on_read_ready'}) {
#say "do_on_data: goto onReadReady inbuf " . length($self->{'inbuf'});
goto &onReadReady;
#return onReadReady($self);
}
else {
say "do_on_data: response and on_read_ready not defined, response by timer or poll?";
}
}
return $res;
}
sub onReadReady {
goto &CT_READ;
my ($self) = @_;
my $tempdata;
lib/App/MHFS.pm view on Meta::CPAN
say "Connection close header set closing conn";
say "-------------------------------------------------";
return undef;
}
$client->{'request'} = MHFS::HTTP::Server::Client::Request->new($client);
# handle possible existing read data
goto &do_on_data;
}
}
else {
say "response not defined, probably set later by a timer or poll";
}
return 1;
}
sub _TSRReturnPrint {
my ($sentthiscall) = @_;
if($sentthiscall > 0) {
say "wrote $sentthiscall bytes";
}
}
lib/App/MHFS.pm view on Meta::CPAN
# Try to send the buf if set
if(length($dataitem->{'buf'})) {
my $sret = TrySendItem($csock, \$dataitem->{'buf'});
# critical conn error
if(! defined($sret)) {
_TSRReturnPrint($sentthiscall);
return undef;
}
if($sret) {
$sentthiscall += $sret;
# if we sent data, kill the send timer
if(defined $client->{'sendresponsetimerid'}) {
$client->KillClientCloseTimer($client->{'sendresponsetimerid'});
$client->{'sendresponsetimerid'} = undef;
}
}
# not all data sent, add timer
if(length($dataitem->{'buf'}) > 0) {
$client->{'sendresponsetimerid'} //= $client->AddClientCloseTimer($client->{'server'}{'settings'}{'sendresponsetimeout'}, $client->{'CONN-ID'});
_TSRReturnPrint($sentthiscall);
return '';
}
#we sent the full buf
}
# read more data
my $newdata;
if(defined $dataitem->{'fh'}) {
lib/App/MHFS.pm view on Meta::CPAN
$self->{'routes'} = [
['/music', $musicpageroute],
['/music_dl', $musicdlroute],
['/music_resources', $musicresourcesroute],
['/music_art', sub {
my ($request) = @_;
return $self->SendArt($request);
}]
];
$self->{'timers'} = [
# update the library at start and periodically
[0, 300, sub {
my ($timer, $current_time, $evp) = @_;
say "$pstart library timer";
UpdateLibrariesAsync($self, $evp, sub {
say "$pstart library timer done";
});
return 1;
}],
];
return $self;
}
1;
}
lib/App/MHFS.pm view on Meta::CPAN
['/torrent/tracker', sub {
my ($request) = @_;
$self->announce($request);
}],
['/torrent/create', sub {
my ($request) = @_;
$self->createTorrent($request);
}],
];
$self->{'timers'} = [
# once an hour evict peers that left the swarm ungracefully
[0, 3600, sub {
my ($timer, $current_time, $evp) = @_;
say __PACKAGE__.": evict peers timer";
foreach my $infohash (keys %{$self->{'torrents'}}) {
foreach my $peer (keys %{$self->{'torrents'}{$infohash}}) {
my $peerdata = $self->{'torrents'}{$infohash}{$peer};
if(($current_time - $peerdata->{'last_announce'}) > ($self->{'announce_interval'}+60)) {
$self->removeTorrentPeer($infohash, $peer, " timeout");
}
}
}
return 1;
}],
lib/App/MHFS.pm view on Meta::CPAN
#$request->Send404;
#return undef;
if($fmt eq 'hls') {
$video{'on_exists'} = \&video_hls_write_master_playlist;
}
# deprecated
$video{'pid'} = ASYNC(\&shellcmd_unlock, \@cmd, $video{'out_filepath'});
# our file isn't ready yet, so create a timer to check the progress and act
weaken($request); # the only one who should be keeping $request alive is the client
$request->{'client'}{'server'}{'evp'}->add_timer(0, 0, sub {
if(! defined $request) {
say "\$request undef, ignoring CB";
return undef;
}
# test if its ready to send
while(1) {
my $filename = $video{'out_filepath'};
if(! -e $filename) {
last;
}
my $minsize = $videoformats->{$fmt}{'minsize'};
if(defined($minsize) && ((-s $filename) < $minsize)) {
last;
}
if(defined $video{'on_exists'}) {
last if (! $video{'on_exists'}->($settings, \%video));
}
say "get_video_timer is destructing";
$request->SendLocalFile($filename);
return undef;
}
# 404, if we didn't send yet the process is not running
if(pid_running($video{'pid'})) {
return 1;
}
say "pid not running: " . $video{'pid'} . " get_video_timer done with 404";
$request->Send404;
return undef;
});
say "get_video: added timer " . $video{'out_filepath'};
});
}
else {
say "out_fmt: " . $video{'out_fmt'};
$request->Send404;
return undef;
}
return 1;
}
share/public_html/static/hls.js view on Meta::CPAN
_this._boundTick = _this.tick.bind(_this);
return _this;
}
/**
* @override
*/
TaskLoop.prototype.onHandlerDestroying = function onHandlerDestroying() {
// clear all timers before unregistering from event bus
this.clearNextTick();
this.clearInterval();
};
/**
* @returns {boolean}
*/
TaskLoop.prototype.hasInterval = function hasInterval() {
share/public_html/static/hls.js view on Meta::CPAN
*/
TaskLoop.prototype.tick = function tick() {
this._tickCallCount++;
if (this._tickCallCount === 1) {
this.doTick();
// re-entrant call to tick from previous doTick call stack
// -> schedule a call on the next main loop iteration to process this task processing request
if (this._tickCallCount > 1) {
// make sure only one timer exists at any time at max
this.clearNextTick();
this._tickTimer = setTimeout(this._boundTick, 0);
}
this._tickCallCount = 0;
}
};
/**
* For subclass to implement task logic
* @abstract
share/public_html/static/hls.js view on Meta::CPAN
var partial = this.fragmentTracker.getPartialFragment(currentTime);
if (partial) {
// Try to skip over the buffer hole caused by a partial fragment
// This method isn't limited by the size of the gap between buffered ranges
this._trySkipBufferHole(partial);
}
if (bufferInfo.len > jumpThreshold && stalledDuration > config.highBufferWatchdogPeriod * 1000) {
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
// We only try to jump the hole if it's under the configured size
// Reset stalled so to rearm watchdog timer
this.stalled = null;
this._tryNudgeBuffer();
}
};
/**
* Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
* @param bufferLen - The playhead distance from the end of the current buffer segment.
* @private
*/
share/public_html/static/hls.js view on Meta::CPAN
level_controller__inherits(LevelController, _EventHandler);
function LevelController(hls) {
level_controller__classCallCheck(this, LevelController);
var _this = level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MANIFEST_LOADED, events["a" /* default */].LEVEL_LOADED, events["a" /* default */].AUDIO_TRACK_SWITCHED, events["a" /* default *...
_this.canload = false;
_this.currentLevelIndex = null;
_this.manualLevelIndex = -1;
_this.timer = null;
return _this;
}
LevelController.prototype.onHandlerDestroying = function onHandlerDestroying() {
this.clearTimer();
this.manualLevelIndex = -1;
};
LevelController.prototype.clearTimer = function clearTimer() {
if (this.timer !== null) {
clearTimeout(this.timer);
this.timer = null;
}
};
LevelController.prototype.startLoad = function startLoad() {
var levels = this._levels;
this.canload = true;
this.levelRetryCount = 0;
// clean up live level details to force reload them, and reset load errors
if (levels) {
levels.forEach(function (level) {
level.loadError = 0;
var levelDetails = level.details;
if (levelDetails && levelDetails.live) {
level.details = undefined;
}
});
}
// speed up live playlist refresh if timer exists
if (this.timer !== null) {
this.loadLevel();
}
};
LevelController.prototype.stopLoad = function stopLoad() {
this.canload = false;
};
LevelController.prototype.onManifestLoaded = function onManifestLoaded(data) {
var levels = [];
share/public_html/static/hls.js view on Meta::CPAN
reason: 'no level with compatible codecs found in manifest'
});
}
};
LevelController.prototype.setLevelInternal = function setLevelInternal(newLevel) {
var levels = this._levels;
var hls = this.hls;
// check if level idx is valid
if (newLevel >= 0 && newLevel < levels.length) {
// stopping live reloading timer if any
this.clearTimer();
if (this.currentLevelIndex !== newLevel) {
logger["b" /* logger */].log('switching to level ' + newLevel);
this.currentLevelIndex = newLevel;
var levelProperties = levels[newLevel];
levelProperties.level = newLevel;
hls.trigger(events["a" /* default */].LEVEL_SWITCHING, levelProperties);
}
var level = levels[newLevel];
var levelDetails = level.details;
share/public_html/static/hls.js view on Meta::CPAN
nextLevel = void 0;
level.loadError++;
level.fragmentError = fragmentError;
if (levelError) {
if (this.levelRetryCount + 1 <= config.levelLoadingMaxRetry) {
// exponential backoff capped to max retry timeout
delay = Math.min(Math.pow(2, this.levelRetryCount) * config.levelLoadingRetryDelay, config.levelLoadingMaxRetryTimeout);
// Schedule level reload
this.timer = setTimeout(function () {
return _this2.loadLevel();
}, delay);
// boolean used to inform stream controller not to switch back to IDLE on non fatal error
errorEvent.levelRetry = true;
this.levelRetryCount++;
logger["b" /* logger */].warn('level controller, ' + errorDetails + ', retry in ' + delay + ' ms, current retry count is ' + this.levelRetryCount);
} else {
logger["b" /* logger */].error('level controller, cannot recover from ' + errorDetails + ' error');
this.currentLevelIndex = null;
// stopping live reloading timer if any
this.clearTimer();
// switch error to fatal
errorEvent.fatal = true;
return;
}
}
// Try any redundant streams if available for both errors: level and fragment
// If level.loadError reaches redundantLevels it means that we tried them all, no hope => let's switch down
if (levelError || fragmentError) {
share/public_html/static/hls.js view on Meta::CPAN
return;
}
var curLevel = this._levels[levelId];
// reset level load error counter on successful level loaded only if there is no issues with fragments
if (!curLevel.fragmentError) {
curLevel.loadError = 0;
this.levelRetryCount = 0;
}
var newDetails = data.details;
// if current playlist is a live playlist, arm a timer to reload it
if (newDetails.live) {
var targetdurationMs = 1000 * (newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration);
var reloadInterval = targetdurationMs,
curDetails = curLevel.details;
if (curDetails && newDetails.endSN === curDetails.endSN) {
// follow HLS Spec, If the client reloads a Playlist file and finds that it has not
// changed then it MUST wait for a period of one-half the target
// duration before retrying.
reloadInterval /= 2;
logger["b" /* logger */].log('same live playlist, reload twice faster');
}
// decrement reloadInterval with level loading delay
reloadInterval -= level_controller_performance.now() - data.stats.trequest;
// in any case, don't reload more than half of target duration
reloadInterval = Math.max(targetdurationMs / 2, Math.round(reloadInterval));
logger["b" /* logger */].log('live playlist, reload in ' + Math.round(reloadInterval) + ' ms');
this.timer = setTimeout(function () {
return _this3.loadLevel();
}, reloadInterval);
} else {
this.clearTimer();
}
};
LevelController.prototype.onAudioTrackSwitched = function onAudioTrackSwitched(data) {
var audioGroupId = this.hls.audioTracks[data.id].groupId;
share/public_html/static/hls.js view on Meta::CPAN
abr_controller__inherits(AbrController, _EventHandler);
function AbrController(hls) {
abr_controller__classCallCheck(this, AbrController);
var _this = abr_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FRAG_LOADING, events["a" /* default */].FRAG_LOADED, events["a" /* default */].FRAG_BUFFERED, events["a" /* default */].ERROR));
_this.lastLoadedFragLevel = 0;
_this._nextAutoLevel = -1;
_this.hls = hls;
_this.timer = null;
_this._bwEstimator = null;
_this.onCheck = _this._abandonRulesCheck.bind(_this);
return _this;
}
AbrController.prototype.destroy = function destroy() {
this.clearTimer();
event_handler.prototype.destroy.call(this);
};
AbrController.prototype.onFragLoading = function onFragLoading(data) {
var frag = data.frag;
if (frag.type === 'main') {
if (!this.timer) {
this.fragCurrent = frag;
this.timer = setInterval(this.onCheck, 100);
}
// lazy init of BwEstimator, rationale is that we use different params for Live/VoD
// so we need to wait for stream manifest / playlist type to instantiate it.
if (!this._bwEstimator) {
var hls = this.hls;
var config = hls.config;
var level = frag.level;
var isLive = hls.levels[level].details.live;
share/public_html/static/hls.js view on Meta::CPAN
var video = hls.media;
var frag = this.fragCurrent;
if (!frag) {
return;
}
var loader = frag.loader;
var minAutoLevel = hls.minAutoLevel;
// if loader has been destroyed or loading has been aborted, stop timer and return
if (!loader || loader.stats && loader.stats.aborted) {
logger["b" /* logger */].warn('frag loader destroy or aborted, disarm abandonRules');
this.clearTimer();
// reset forced auto level value so that next level will be selected
this._nextAutoLevel = -1;
return;
}
var stats = loader.stats;
/* only monitor frag retrieval time if
(video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */
share/public_html/static/hls.js view on Meta::CPAN
// only emergency switch down if it takes less time to load new fragment at lowest level instead
// of finishing loading current one ...
if (fragLevelNextLoadedDelay < fragLoadedDelay) {
logger["b" /* logger */].warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel + ':fragLoadedDelay[' + nextLoadLevel + ']<fragLoadedDelay[' + (frag.level - 1) + '];bufferStarvationDelay:' + fragLevelNextLoad...
// force next load level in auto mode
hls.nextLoadLevel = nextLoadLevel;
// update bw estimate for this fragment before cancelling load (this will help reducing the bw)
this._bwEstimator.sample(requestDelay, stats.loaded);
// abort fragment loading
loader.abort();
// stop abandon rules timer
this.clearTimer();
hls.trigger(events["a" /* default */].FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats });
}
}
}
}
};
AbrController.prototype.onFragLoaded = function onFragLoaded(data) {
var frag = data.frag;
share/public_html/static/hls.js view on Meta::CPAN
// if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration
if (frag.bitrateTest) {
this.bitrateTestDelay = fragLoadingProcessingMs / 1000;
} else {
this.bitrateTestDelay = 0;
}
}
};
AbrController.prototype.onError = function onError(data) {
// stop timer in case of frag loading error
switch (data.details) {
case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
this.clearTimer();
break;
default:
break;
}
};
AbrController.prototype.clearTimer = function clearTimer() {
clearInterval(this.timer);
this.timer = null;
};
// return next auto level
AbrController.prototype._findBestLevel = function _findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) {
for (var i = maxAutoLevel; i >= minAutoLevel; i--) {
var levelInfo = levels[i],
levelDetails = levelInfo.details,
avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration,
share/public_html/static/hls.js view on Meta::CPAN
function CapLevelController(hls) {
cap_level_controller__classCallCheck(this, CapLevelController);
var _this = cap_level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FPS_DROP_LEVEL_CAPPING, events["a" /* default */].MEDIA_ATTACHING, events["a" /* default */].MANIFEST_PARSED, events["a" /* ...
_this.autoLevelCapping = Number.POSITIVE_INFINITY;
_this.firstLevel = null;
_this.levels = [];
_this.media = null;
_this.restrictedLevels = [];
_this.timer = null;
return _this;
}
CapLevelController.prototype.destroy = function destroy() {
if (this.hls.config.capLevelToPlayerSize) {
this.media = null;
this._stopCapping();
}
};
share/public_html/static/hls.js view on Meta::CPAN
}
var validLevels = this.levels.filter(function (level, index) {
return CapLevelController.isLevelAllowed(index, _this2.restrictedLevels) && index <= capLevelIndex;
});
return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);
};
CapLevelController.prototype._startCapping = function _startCapping() {
if (this.timer) {
// Don't reset capping if started twice; this can happen if the manifest signals a video codec
return;
}
this.autoLevelCapping = Number.POSITIVE_INFINITY;
this.hls.firstLevel = this.getMaxLevel(this.firstLevel);
clearInterval(this.timer);
this.timer = setInterval(this.detectPlayerSize.bind(this), 1000);
this.detectPlayerSize();
};
CapLevelController.prototype._stopCapping = function _stopCapping() {
this.restrictedLevels = [];
this.firstLevel = null;
this.autoLevelCapping = Number.POSITIVE_INFINITY;
if (this.timer) {
this.timer = clearInterval(this.timer);
this.timer = null;
}
};
CapLevelController.isLevelAllowed = function isLevelAllowed(level) {
var restrictedLevels = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
return restrictedLevels.indexOf(level) === -1;
};
CapLevelController.getMaxLevelByMediaSize = function getMaxLevelByMediaSize(levels, width, height) {
share/public_html/static/hls.js view on Meta::CPAN
var fps_controller_FPSController = function (_EventHandler) {
fps_controller__inherits(FPSController, _EventHandler);
function FPSController(hls) {
fps_controller__classCallCheck(this, FPSController);
return fps_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHING));
}
FPSController.prototype.destroy = function destroy() {
if (this.timer) {
clearInterval(this.timer);
}
this.isVideoPlaybackQualityAvailable = false;
};
FPSController.prototype.onMediaAttaching = function onMediaAttaching(data) {
var config = this.hls.config;
if (config.capLevelOnFPSDrop) {
var video = this.video = data.media instanceof window.HTMLVideoElement ? data.media : null;
if (typeof video.getVideoPlaybackQuality === 'function') {
this.isVideoPlaybackQualityAvailable = true;
}
clearInterval(this.timer);
this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod);
}
};
FPSController.prototype.checkFPS = function checkFPS(video, decodedFrames, droppedFrames) {
var currentTime = fps_controller_performance.now();
if (decodedFrames) {
if (this.lastTime) {
var currentPeriod = currentTime - this.lastTime,
currentDropped = droppedFrames - this.lastDroppedFrames,
currentDecoded = decodedFrames - this.lastDecodedFrames,
share/public_html/static/hls.js view on Meta::CPAN
// check if current playlist is a live playlist
// and if we have already our reload interval setup
if (data.details.live && !this.hasInterval()) {
// if live playlist we will have to reload it periodically
// set reload period to playlist target duration
var updatePeriodMs = data.details.targetduration * 1000;
this.setInterval(updatePeriodMs);
}
if (!data.details.live && this.hasInterval()) {
// playlist is not live and timer is scheduled: cancel it
this.clearInterval();
}
};
/**
* Update the internal group ID to any audio-track we may have set manually
* or because of a failure-handling fallback.
*
* Quality-levels should update to that group ID in this case.
*
share/public_html/static/hls.js view on Meta::CPAN
* @param {number} newId
*/
AudioTrackController.prototype._updateTrack = function _updateTrack(newId) {
// check if level idx is valid
if (newId < 0 || newId >= this.tracks.length) {
return;
}
// stopping live reloading timer if any
this.clearInterval();
this.trackId = newId;
logger["b" /* logger */].log('trying to update audio-track ' + newId);
var audioTrack = this.tracks[newId];
this._loadTrackDetailsIfNeeded(audioTrack);
};
/**
* @private
*/
share/public_html/static/hls.js view on Meta::CPAN
// check if level idx is valid
if (newId < 0 || newId >= this.tracks.length) {
logger["b" /* logger */].warn('Invalid id passed to audio-track controller');
return;
}
var audioTrack = this.tracks[newId];
logger["b" /* logger */].log('Now switching to audio-track index ' + newId);
// stopping live reloading timer if any
this.clearInterval();
this.trackId = newId;
var url = audioTrack.url,
type = audioTrack.type,
id = audioTrack.id;
this.hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHING, { id: id, type: type, url: url });
this._loadTrackDetailsIfNeeded(audioTrack);
}
share/public_html/static/hls.js view on Meta::CPAN
this.fragCurrent = null;
this.state = audio_stream_controller_State.PAUSED;
this.waitingFragment = null;
// destroy useless demuxer when switching audio to main
if (!altAudio) {
if (this.demuxer) {
this.demuxer.destroy();
this.demuxer = null;
}
} else {
// switching to audio track, start timer if not already started
this.setInterval(100);
}
// should we switch tracks ?
if (altAudio) {
this.audioSwitch = true;
// main audio track are handled by stream-controller, just do something if switching to alt audio track
this.state = audio_stream_controller_State.IDLE;
}
this.tick();
share/public_html/static/hls.js view on Meta::CPAN
}
};
SubtitleTrackController.prototype.onSubtitleTrackLoaded = function onSubtitleTrackLoaded(data) {
var _this4 = this;
if (data.id < this.tracks.length) {
logger["b" /* logger */].log('subtitle track ' + data.id + ' loaded');
this.tracks[data.id].details = data.details;
// check if current playlist is a live playlist
if (data.details.live && !this.timer) {
// if live playlist we will have to reload it periodically
// set reload period to playlist target duration
this.timer = setInterval(function () {
_this4.onTick();
}, 1000 * data.details.targetduration, this);
}
if (!data.details.live && this.timer) {
// playlist is not live and timer is armed : stopping it
this._stopTimer();
}
}
};
/** get alternate subtitle tracks list from playlist **/
/**
* This method is responsible for validating the subtitle index and periodically reloading if live.
share/public_html/static/hls.js view on Meta::CPAN
var subtitleTrack = tracks[newId];
var details = subtitleTrack.details;
if (!details || details.live) {
// track not retrieved yet, or live playlist we need to (re)load it
logger["b" /* logger */].log('(re)loading playlist for subtitle track ' + newId);
hls.trigger(events["a" /* default */].SUBTITLE_TRACK_LOADING, { url: subtitleTrack.url, id: newId });
}
};
SubtitleTrackController.prototype._stopTimer = function _stopTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
};
/**
* Disables the old subtitleTrack and sets current mode on the next subtitleTrack.
* This operates on the DOM textTracks.
* A value of -1 will disable all subtitle tracks.
* @param newId - The id of the next track to enable
* @private
*/
share/public_html/static/music_inc/drflac.js view on Meta::CPAN
var Module = (() => {
var _scriptName = import.meta.url;
return (
async function(moduleArg = {}) {
var moduleRtn;
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});["_network_drflac_open_mem","_network_drflac_read_pcm_frames_f32_mem","_network_dr...
return moduleRtn;
}
);
})();
export default Module;
share/public_html/static/music_inc/music_inc_module.js view on Meta::CPAN
if(!tracks.length) return;
QueueTracks(tracks, after);
}
// remove played items from the audio queue
function AQ_clean() {
let toDelete = 0;
for(let i = 0; i < AudioQueue.length; i++) {
if(! AudioQueue[i].endTime) break;
// run and remove associated graphics timers
let timerdel = 0;
for(let j = 0; j < AudioQueue[i].timers.length; j++) {
if(AudioQueue[i].timers[j].time <= MainAudioContext.currentTime) {
console.log('aqid: ' + AudioQueue[i].aqid + ' running timer at ' + MainAudioContext.currentTime);
AudioQueue[i].timers[j].func(AudioQueue[i].timers[j]);
timerdel++;
}
}
if(timerdel)AudioQueue[i].timers.splice(0, timerdel);
// remove if it has passed
if(AudioQueue[i].endTime <= MainAudioContext.currentTime) {
console.log('aqid: ' + AudioQueue[i].aqid + ' segment elapsed, removing');
toDelete++;
}
}
if(toDelete) {
// if the AQ is empty and there's a current track we fell behind
if((toDelete === AudioQueue.length) && (Tracks_QueueCurrent)) {
share/public_html/static/music_inc/music_inc_module.js view on Meta::CPAN
if(dCount) {
AudioQueue.splice(AudioQueue.length - dCount, dCount);
}
}
if(typeof abortablesleep === 'undefined') {
//const sleep = m => new Promise(r => setTimeout(r, m));
const abortablesleep = (ms, signal) => new Promise(function(resolve) {
const onTimerDone = function() {
resolve();
signal.removeEventListener('abort', stoptimer);
};
let timer = setTimeout(function() {
console.log('sleep done ' + ms);
onTimerDone();
}, ms);
const stoptimer = function() {
console.log('aborted sleep');
onTimerDone();
clearTimeout(timer);
};
signal.addEventListener('abort', stoptimer);
});
DeclareGlobalFunc('abortablesleep', abortablesleep);
}
let FAQ_MUTEX = new Mutex();
async function fillAudioQueue(time) {
// starting a fresh queue, render the text
if(Tracks_QueueCurrent) {
let track = Tracks_QueueCurrent;
let prevtext = track.prev ? track.prev.trackname : '';
share/public_html/static/music_inc/music_inc_module.js view on Meta::CPAN
console.log('Encountered error twice, advancing to next track');
// assume it's corrupted. force free it
await NWDRFLAC.close();
NWDRFLAC = null;
continue TRACKLOOP;
}
}
}
// Add to the audio queue
let aqItem = { 'buffer' : buffer, 'duration' : todec, 'aqid' : AQID, 'skiptime' : (dectime / NWDRFLAC.sampleRate), 'track' : track, 'playbackinfo' : {}, 'timers' : []};
// At start and end track update the GUI
let isEnd = ((dectime+todec) === NWDRFLAC.totalPCMFrameCount);
if(isStart || isEnd) {
aqItem.func = function(startTime, endTime) {
if(isStart) {
console.log('aqid: ' + aqItem.aqid + ' start timer at ' + startTime + ' currentTime ' + MainAudioContext.currentTime);
aqItem.timers.push({'time': startTime, 'func': function() {
seekbar.min = 0;
seekbar.max = track.duration;
SetEndtimeText(track.duration);
SetPlayText(track.trackname);
let prevtext = track.prev ? track.prev.trackname : '';
SetPrevText(prevtext);
let nexttext = track.next ? track.next.trackname : '';
SetNextText(nexttext);
}});
isStart = false;
}
if(isEnd) {
console.log('aqid: ' + aqItem.aqid + ' end timer at ' + endTime + ' currentTime ' + MainAudioContext.currentTime);
aqItem.timers.push({'time': endTime, 'func': function(){
let curTime = 0;
SetEndtimeText(0);
SetCurtimeText(curTime);
SetSeekbarValue(curTime);
SetPrevText(track.trackname);
SetPlayText('');
SetNextText('');
}});
}
}
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
} ma_ios_session_category_option;
typedef union
{
ma_int64 counter;
double counterD;
} ma_timer;
typedef union
{
wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
/*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
char alsa[256]; /* ALSA uses a name string for identification. */
char pulse[256]; /* PulseAudio uses a name string for identification. */
int jack; /* JACK always uses default devices. */
char coreaudio[256]; /* Core Audio uses a string for identification. */
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
} webaudio;
#endif
#ifdef MA_SUPPORT_NULL
struct
{
ma_thread deviceThread;
ma_event operationEvent;
ma_event operationCompletionEvent;
ma_uint32 operation;
ma_result operationResult;
ma_timer timer;
double priorRunTime;
ma_uint32 currentPeriodFramesRemainingPlayback;
ma_uint32 currentPeriodFramesRemainingCapture;
ma_uint64 lastProcessedFramePlayback;
ma_uint64 lastProcessedFrameCapture;
ma_bool32 isStarted;
} null_device;
#endif
};
};
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
}
/*******************************************************************************
Timing
*******************************************************************************/
#ifdef MA_WIN32
static LARGE_INTEGER g_ma_TimerFrequency = {{0}};
static void ma_timer_init(ma_timer* pTimer)
{
LARGE_INTEGER counter;
if (g_ma_TimerFrequency.QuadPart == 0) {
QueryPerformanceFrequency(&g_ma_TimerFrequency);
}
QueryPerformanceCounter(&counter);
pTimer->counter = counter.QuadPart;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
LARGE_INTEGER counter;
if (!QueryPerformanceCounter(&counter)) {
return 0;
}
return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
}
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
static ma_uint64 g_ma_TimerFrequency = 0;
static void ma_timer_init(ma_timer* pTimer)
{
mach_timebase_info_data_t baseTime;
mach_timebase_info(&baseTime);
g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
pTimer->counter = mach_absolute_time();
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter = mach_absolute_time();
ma_uint64 oldTimeCounter = pTimer->counter;
return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
}
#elif defined(MA_EMSCRIPTEN)
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
{
pTimer->counterD = emscripten_get_now();
}
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
}
#else
#if _POSIX_C_SOURCE >= 199309L
#if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
static void ma_timer_init(ma_timer* pTimer)
{
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
oldTimeCounter = pTimer->counter;
return (newTimeCounter - oldTimeCounter) / 1000000000.0;
}
#else
static void ma_timer_init(ma_timer* pTimer)
{
struct timeval newTime;
gettimeofday(&newTime, NULL);
pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timeval newTime;
gettimeofday(&newTime, NULL);
newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
oldTimeCounter = pTimer->counter;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
for (;;) { /* Keep the thread alive until the device is uninitialized. */
/* Wait for an operation to be requested. */
ma_event_wait(&pDevice->null_device.operationEvent);
/* At this point an event should have been triggered. */
/* Starting the device needs to put the thread into a loop. */
if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
/* Reset the timer just in case. */
ma_timer_init(&pDevice->null_device.timer);
/* Keep looping until an operation has been requested. */
while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
ma_sleep(10); /* Don't hog the CPU. */
}
/* Getting here means a suspend or kill operation has been requested. */
c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
continue;
}
/* Suspending the device means we need to stop the timer and just continue the loop. */
if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
/* We need to add the current run time to the prior run time, then reset the timer. */
pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
ma_timer_init(&pDevice->null_device.timer);
/* We're done. */
c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
continue;
}
/* Killing the device means we need to get out of this loop so that this thread can terminate. */
if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
ma_uint32 internalSampleRate;
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
internalSampleRate = pDevice->capture.internalSampleRate;
} else {
internalSampleRate = pDevice->playback.internalSampleRate;
}
return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}
static ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pID0 != NULL);
MA_ASSERT(pID1 != NULL);
(void)pContext;
return pID0->nullbackend == pID1->nullbackend;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
pDevice->playback.internalFormat = pConfig->playback.format;
pDevice->playback.internalChannels = pConfig->playback.channels;
ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, ma_min(pConfig->playback.channels, MA_MAX_CHANNELS));
pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
pDevice->playback.internalPeriods = pConfig->periods;
}
/*
In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the
first period is "written" to it, and then stopped in ma_device_stop__null().
*/
result = ma_event_init(&pDevice->null_device.operationEvent);
if (result != MA_SUCCESS) {
return result;
}
result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
if (result != MA_SUCCESS) {
return result;
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
config.format = format;
config.channels = channels;
config.sampleRateIn = sampleRateIn;
config.sampleRateOut = sampleRateOut;
config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
config.lpfNyquistFactor = 1;
return config;
}
static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
{
/*
So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will
be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
*/
ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */
ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
pResampler->inTimeFrac =
(oldRateTimeWhole * newSampleRateOut) +
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
}
if (result != MA_SUCCESS) {
return result;
}
pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
/* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
return MA_SUCCESS;
}
MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
{
ma_result result;
if (pResampler == NULL) {
return MA_INVALID_ARGS;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
the be started and/or stopped at a specific time. This can be done with the following functions:
```c
ma_sound_set_start_time_in_pcm_frames()
ma_sound_set_start_time_in_milliseconds()
ma_sound_set_stop_time_in_pcm_frames()
ma_sound_set_stop_time_in_milliseconds()
```
The start/stop time needs to be specified based on the absolute timer which is controlled by the
engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
before anything will play:
```c
ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
ma_sound_start(&sound);
```
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
*/
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
/*
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
input frames.
*/
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
/*
Resets the resampler's timer and clears it's internal cache.
*/
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
/**************************************************************************************************************************************************************
Channel Conversion
**************************************************************************************************************************************************************/
typedef enum
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
} ma_aaudio_input_preset;
typedef union
{
ma_int64 counter;
double counterD;
} ma_timer;
typedef union
{
wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
/*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
char alsa[256]; /* ALSA uses a name string for identification. */
char pulse[256]; /* PulseAudio uses a name string for identification. */
int jack; /* JACK always uses default devices. */
char coreaudio[256]; /* Core Audio uses a string for identification. */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
#endif
#ifdef MA_SUPPORT_NULL
struct
{
ma_thread deviceThread;
ma_event operationEvent;
ma_event operationCompletionEvent;
ma_semaphore operationSemaphore;
ma_uint32 operation;
ma_result operationResult;
ma_timer timer;
double priorRunTime;
ma_uint32 currentPeriodFramesRemainingPlayback;
ma_uint32 currentPeriodFramesRemainingCapture;
ma_uint64 lastProcessedFramePlayback;
ma_uint64 lastProcessedFrameCapture;
MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
} null_device;
#endif
};
};
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/*******************************************************************************
Timing
*******************************************************************************/
#ifdef MA_WIN32
static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
void ma_timer_init(ma_timer* pTimer)
{
LARGE_INTEGER counter;
if (g_ma_TimerFrequency.QuadPart == 0) {
QueryPerformanceFrequency(&g_ma_TimerFrequency);
}
QueryPerformanceCounter(&counter);
pTimer->counter = counter.QuadPart;
}
double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
LARGE_INTEGER counter;
if (!QueryPerformanceCounter(&counter)) {
return 0;
}
return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
}
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
static ma_uint64 g_ma_TimerFrequency = 0;
static void ma_timer_init(ma_timer* pTimer)
{
mach_timebase_info_data_t baseTime;
mach_timebase_info(&baseTime);
g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
pTimer->counter = mach_absolute_time();
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter = mach_absolute_time();
ma_uint64 oldTimeCounter = pTimer->counter;
return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
}
#elif defined(MA_EMSCRIPTEN)
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
{
pTimer->counterD = emscripten_get_now();
}
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
}
#else
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
#if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
static void ma_timer_init(ma_timer* pTimer)
{
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timespec newTime;
clock_gettime(MA_CLOCK_ID, &newTime);
newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
oldTimeCounter = pTimer->counter;
return (newTimeCounter - oldTimeCounter) / 1000000000.0;
}
#else
static void ma_timer_init(ma_timer* pTimer)
{
struct timeval newTime;
gettimeofday(&newTime, NULL);
pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
}
static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
{
ma_uint64 newTimeCounter;
ma_uint64 oldTimeCounter;
struct timeval newTime;
gettimeofday(&newTime, NULL);
newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
oldTimeCounter = pTimer->counter;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
ma_uint32 operation;
/* Wait for an operation to be requested. */
ma_event_wait(&pDevice->null_device.operationEvent);
/* At this point an event should have been triggered. */
operation = pDevice->null_device.operation;
/* Starting the device needs to put the thread into a loop. */
if (operation == MA_DEVICE_OP_START__NULL) {
/* Reset the timer just in case. */
ma_timer_init(&pDevice->null_device.timer);
/* Getting here means a suspend or kill operation has been requested. */
pDevice->null_device.operationResult = MA_SUCCESS;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
continue;
}
/* Suspending the device means we need to stop the timer and just continue the loop. */
if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
/* We need to add the current run time to the prior run time, then reset the timer. */
pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
ma_timer_init(&pDevice->null_device.timer);
/* We're done. */
pDevice->null_device.operationResult = MA_SUCCESS;
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
continue;
}
/* Killing the device means we need to get out of this loop so that this thread can terminate. */
if (operation == MA_DEVICE_OP_KILL__NULL) {
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
ma_uint32 internalSampleRate;
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
internalSampleRate = pDevice->capture.internalSampleRate;
} else {
internalSampleRate = pDevice->playback.internalSampleRate;
}
return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}
static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
ma_bool32 cbResult = MA_TRUE;
MA_ASSERT(pContext != NULL);
MA_ASSERT(callback != NULL);
/* Playback. */
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
}
pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
}
/*
In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the
first period is "written" to it, and then stopped in ma_device_stop__null().
*/
result = ma_event_init(&pDevice->null_device.operationEvent);
if (result != MA_SUCCESS) {
return result;
}
result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
if (result != MA_SUCCESS) {
return result;
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
/* Fast path. No gain calculation required. */
ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
/* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
if (pGainer->t == (ma_uint32)-1) {
pGainer->t = pGainer->config.smoothTimeInFrames;
}
} else {
/* Slow path. Need to interpolate the gain for each channel individually. */
/* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
if (pFramesOut != NULL && pFramesIn != NULL) {
float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
float d = 1.0f / pGainer->config.smoothTimeInFrames;
ma_uint32 channelCount = pGainer->config.channels;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a);
}
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
if (a > 1) {
a = 1;
}
}
}
pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
#if 0 /* Reference implementation. */
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
/* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
if (pFramesOut != NULL && pFramesIn != NULL) {
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel);
}
}
/* Move interpolation time forward, but don't go beyond our smoothing time. */
pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
}
#endif
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
typedef struct
{
size_t sizeInBytes;
size_t x0Offset;
size_t x1Offset;
size_t lpfOffset;
} ma_linear_resampler_heap_layout;
static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
{
/*
So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will
be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
*/
ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */
ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
pResampler->inTimeFrac =
(oldRateTimeWhole * newSampleRateOut) +
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
}
if (result != MA_SUCCESS) {
return result;
}
pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
/* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
return MA_SUCCESS;
}
static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
{
MA_ASSERT(pHeapLayout != NULL);
MA_ZERO_OBJECT(pHeapLayout);
share/public_html/static/music_worklet_inprogress/player/mhfsplayer.js view on Meta::CPAN
willLock.then(() => this._locked = false);
let willUnlock = this._locking.then(() => unlockNext);
this._locking = this._locking.then(() => willLock);
return willUnlock;
}
}
const abortablesleep = (ms, signal) => new Promise(function(resolve) {
const onTimerDone = function() {
resolve();
signal.removeEventListener('abort', stoptimer);
};
let timer = setTimeout(function() {
//console.log('sleep done ' + ms);
onTimerDone();
}, ms);
const stoptimer = function() {
console.log('aborted sleep');
onTimerDone();
clearTimeout(timer);
};
signal.addEventListener('abort', stoptimer);
});
const abortablesleep_status = async function (ms, signal) {
await abortablesleep(ms, signal);
if(signal.aborted) {
return false;
}
return true;
}