diff --git a/core/decoders/h264.js b/core/decoders/h264.js index a508b6741..180106a39 100644 --- a/core/decoders/h264.js +++ b/core/decoders/h264.js @@ -196,9 +196,9 @@ export class H264Context { } if (parser.profileIdc !== null) { - self._profileIdc = parser.profileIdc; - self._constraintSet = parser.constraintSet; - self._levelIdc = parser.levelIdc; + this._profileIdc = parser.profileIdc; + this._constraintSet = parser.constraintSet; + this._levelIdc = parser.levelIdc; } if (this._decoder === null || this._decoder.state !== 'configured') { @@ -206,12 +206,12 @@ export class H264Context { Log.Warn("Missing key frame. Can't decode until one arrives"); continue; } - if (self._profileIdc === null) { + if (this._profileIdc === null) { Log.Warn('Cannot config decoder. Have not received SPS and PPS yet.'); continue; } - this._configureDecoder(self._profileIdc, self._constraintSet, - self._levelIdc); + this._configureDecoder(this._profileIdc, this._constraintSet, + this._levelIdc); } result = this._preparePendingFrame(timestamp); diff --git a/core/display.js b/core/display.js index a7bb2d6c1..98f31d8c3 100644 --- a/core/display.js +++ b/core/display.js @@ -16,6 +16,7 @@ export default class Display { this._renderQ = []; // queue drawing actions for in-order rendering this._flushPromise = null; + this._pendingFrames = []; // video frames awaiting decoder output // the full frame buffer (logical canvas) size this._fbWidth = 0; @@ -479,6 +480,22 @@ export default class Display { } } + _drawVideoFrame(pendingFrame, rect) { + let frame = pendingFrame.frame; + if (!frame) { + return; + } + if (frame.codedWidth < rect.width || frame.codedHeight < rect.height) { + Log.Warn("Decoded video frame does not cover its full rectangle area. Expecting at least " + + rect.width + "x" + rect.height + " but got " + + frame.codedWidth + "x" + frame.codedHeight); + } + this.drawImage(frame, + 0, 0, rect.width, rect.height, + rect.x, rect.y, rect.width, rect.height); + frame.close(); + } + _renderQPush(action) { this._renderQ.push(action); if (this._renderQ.length === 1) { @@ -501,7 +518,21 @@ export default class Display { const a = this._renderQ[0]; switch (a.type) { case 'flip': - this.flip(true); + if (this._pendingFrames.length > 0) { + // Wait for all pending video frames to be + // decoded before flipping, so they are + // visible on screen. + let display = this; + let frames = this._pendingFrames; + this._pendingFrames = []; + Promise.all(frames.map(f => f.promise)).then(() => { + display.flip(true); + display._scanRenderQ(); + }); + ready = false; + } else { + this.flip(true); + } break; case 'copy': this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true); @@ -533,32 +564,20 @@ export default class Display { } break; case 'frame': - if (a.frame.ready) { - // The encoded frame may be larger than the rect due to - // limitations of the encoder, so we need to crop the - // frame. - let frame = a.frame.frame; - if (frame.codedWidth < a.width || frame.codedHeight < a.height) { - Log.Warn("Decoded video frame does not cover its full rectangle area. Expecting at least " + - a.width + "x" + a.height + " but got " + - frame.codedWidth + "x" + frame.codedHeight); - } - const sx = 0; - const sy = 0; - const sw = a.width; - const sh = a.height; - const dx = a.x; - const dy = a.y; - const dw = sw; - const dh = sh; - this.drawImage(frame, sx, sy, sw, sh, dx, dy, dw, dh); - frame.close(); - } else { + if (!a.frame.ready) { + // Don't block the queue — the video decoder + // pipeline needs continued input to produce + // output. Register a callback to draw later, + // and let the queue keep feeding the decoder. let display = this; - a.frame.promise.then(() => { - display._scanRenderQ(); + let pendingFrame = a.frame; + let rect = { x: a.x, y: a.y, width: a.width, height: a.height }; + pendingFrame.promise.then(() => { + display._drawVideoFrame(pendingFrame, rect); }); - ready = false; + this._pendingFrames.push(pendingFrame); + } else { + this._drawVideoFrame(a.frame, a); } break; }