Skip to content

Commit

Permalink
Add lots of debug logging, limit render fps of events
Browse files Browse the repository at this point in the history
  • Loading branch information
JustAMan committed Mar 23, 2020
1 parent 5430cc9 commit 4e42502
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 21 deletions.
31 changes: 31 additions & 0 deletions src/SubtitleOctopus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@ double libassjs_find_next_event_start(double tm) {
return closest / 1000.0;
}

int _is_event_complex(ASS_Event *event) {
// event is complex if it's animated in any way,
// either by having non-empty Effect or
// by having tags (enclosed in '{}' in Text)
if (event->Effect && event->Effect[0] != '\0') return 1;

int escaped = 0;
for (char *p = event->Text; *p != '\0'; p++) {
switch (*p) {
case '\\':
escaped = !escaped;
break;
case '{':
if (escaped) return 1;
break;
}
}

return 0;
}

void libassjs_find_event_stop_times(double tm, double *eventFinish, double *emptyFinish) {
if (!track || track->n_events == 0) {
*eventFinish = *emptyFinish = -1;
Expand All @@ -112,6 +133,7 @@ void libassjs_find_event_stop_times(double tm, double *eventFinish, double *empt
long long now = (long long)(tm * 1000);

long long minFinish = -1, maxFinish = -1, minStart = -1;
int current_animated = 0;

for (int i = 0; i < track->n_events; i++, cur++) {
long long start = cur->Start;
Expand All @@ -124,12 +146,21 @@ void libassjs_find_event_stop_times(double tm, double *eventFinish, double *empt
if (finish > maxFinish) {
maxFinish = finish;
}
if (!current_animated && _is_event_complex(cur)) current_animated = 1;
}
} else if (start < minStart || minStart == -1) {
minStart = start;
}
}

if (current_animated) {
printf("libass: detected animated event, forcing finish times to be +5ms from %f\n", tm);
// what plays now is animated, so consider this event border to be
// after 5ms from now, so it's properly redrawn afterwards
*eventFinish = *emptyFinish = tm + 0.005;
return;
}

if (minFinish != -1) {
// some event is going on, so we need to re-draw either when it stops
// or when some other event starts
Expand Down
87 changes: 66 additions & 21 deletions src/subtitles-octopus.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ var SubtitlesOctopus = function (options) {
self.oneshotState = {
eventStart: null,
eventOver: false,
iteration: 0
iteration: 0,
renderRequested: false,
requestNextTimestamp: -1
}

self.hasAlphaBug = false;
Expand Down Expand Up @@ -175,6 +177,7 @@ var SubtitlesOctopus = function (options) {
self.video = video;
if (self.video) {
if (self.renderAhead > 0) {
console.debug('starting oneshot render because new video detected');
window.requestAnimationFrame(oneshotRender);
tryRequestOneshot();
}
Expand Down Expand Up @@ -266,8 +269,9 @@ var SubtitlesOctopus = function (options) {
return removed;
}

function tryRequestOneshot(currentTime) {
function tryRequestOneshot(currentTime, postIfBusy) {
if (!self.renderAhead || self.renderAhead <= 0) return;
if (self.oneshotState.renderRequested && !postIfBusy) return;

if (typeof currentTime === 'undefined') {
if (!self.video) return;
Expand All @@ -277,31 +281,45 @@ var SubtitlesOctopus = function (options) {
var size = 0;
for (var i = 0, len = self.renderedItems.length; i < len; i++) {
var item = self.renderedItems[i];
if ((item.eventStart < 0 || currentTime >= item.eventStart) &&
(item.emptyFinish < 0 || currentTime < item.emptyFinish)) {
if (item.emptyFinish < 0) {
console.info('oneshot already reached end-of-events');
return;
}
if (currentTime >= item.eventStart && currentTime < item.emptyFinish) {
// an event for requested time already exists
console.debug('not requesting a render for ' + currentTime +
' as event already covering it exists (start=' +
item.eventStart + ', empty=' + item.emptyFinish + ')');
return;
}
size += item.size;
}

if (size <= self.renderAhead) {
self.worker.postMessage({
target: 'oneshot-render',
lastRendered: currentTime - 0.001,
renderNow: false,
iteration: self.oneshotState.iteration
});
lastRendered = currentTime - 0.001;
console.info('requesting new frame because current prerender size is less than limit (start=' + lastRendered + ')');
if (!self.oneshotState.renderRequested) {
self.oneshotState.renderRequested = true;
self.worker.postMessage({
target: 'oneshot-render',
lastRendered: lastRendered,
renderNow: false,
iteration: self.oneshotState.iteration
});
} else {
console.info('worker busy, requesting to seek');
self.oneshotState.requestNextTimestamp = lastRendered;
}
} else {
console.debug('not requesting new frame yet as prerender size is over limit');
}
}

function _renderSubtitleEvent(event, currentTime) {
var eventOver = event.eventFinish < currentTime;
if (self.oneshotState.eventStart == event.eventStart && self.oneshotState.eventOver == eventOver) return;
self.oneshotState = {
eventStart: event.eventStart,
eventOver: eventOver
};
self.oneshotState.eventStart = event.eventStart;
self.oneshotState.eventOver = eventOver;

var beforeDrawTime = performance.now();
self.ctx.clearRect(0, 0, self.canvas.width, self.canvas.height);
Expand All @@ -325,25 +343,34 @@ var SubtitlesOctopus = function (options) {
if (!self.video) return;

var currentTime = self.video.currentTime + self.timeOffset;
var finishTime = -1;
var finishTime = -1, eventShown = false;
for (var i = 0, len = self.renderedItems.length; i < len; i++) {
var item = self.renderedItems[i];
if (item.eventStart <= currentTime && (item.emptyFinish < 0 || item.emptyFinish >= currentTime)) {
if (!eventShown && item.eventStart <= currentTime && (item.emptyFinish < 0 || item.emptyFinish >= currentTime)) {
_renderSubtitleEvent(item, currentTime);
eventShown = true;
}
if (item.emptyFinish > finishTime) {
finishTime = item.emptyFinish;
break;
}
}

if (_cleanPastRendered(currentTime) && finishTime >= 0) {
tryRequestOneshot(finishTime);
console.debug('some prerendered frame retired, requesting new');
if (eventShown) {
tryRequestOneshot(finishTime);
} else {
tryRequestOneshot(currentTime, true);
}
}
}

function resetRenderAheadCache() {
console.debug('resetting prerender cache');
self.renderedItems = [];
self.oneshotState.eventStart = null;
self.oneshotState.iteration++;
self.oneshotState.renderRequested = false;
tryRequestOneshot();
}

Expand Down Expand Up @@ -473,9 +500,14 @@ var SubtitlesOctopus = function (options) {
}
case 'oneshot-result': {
if (data.iteration != self.oneshotState.iteration) {
// stale render, ignore
console.debug('received stale prerender, ignoring');
return;
}

console.info('oneshot received (start=' +
data.eventStart + ', empty=' + data.emptyFinish +
'), render: ' + Math.round(data.spentTime) + ' ms');
self.oneshotState.renderRequested = false;
var items = [];
var size = 0;
for (var i = 0, len = data.canvases.length; i < len; i++) {
Expand All @@ -489,6 +521,10 @@ var SubtitlesOctopus = function (options) {
});
size += item.buffer.byteLength;
}
if (data.emptyFinish > 0 && data.emptyFinish - data.eventStart < 1.0 / self.targetFps) {
data.emptyFinish = data.eventStart + 1.0 / self.targetFps;
data.eventFinish = data.emptyFinish;
}
self.renderedItems.push({
eventStart: data.eventStart,
eventFinish: data.eventFinish,
Expand All @@ -498,9 +534,18 @@ var SubtitlesOctopus = function (options) {
items: items,
size: size
});
if (data.emptyFinish >= 0) {
// there's some more event to render, try doing so

if (self.oneshotState.requestNextTimestamp >= 0) {
console.debug("requesting out of order event at " + self.oneshotState.requestNextTimestamp);
tryRequestOneshot(self.oneshotState.requestNextTimestamp);
self.oneshotState.requestNextTimestamp = -1;
} else if (data.eventStart < 0) {
console.info('oneshot received "end of frames" event');
} else if (data.emptyFinish >= 0) {
console.debug("there's some more event to render, try requesting next event");
tryRequestOneshot(data.emptyFinish);
} else {
console.info('there are no more events to prerender');
}
break;
}
Expand Down

0 comments on commit 4e42502

Please sign in to comment.