南宁市做网站百度的网站网址
ExoPlayer和大部分播放器一样,默认采用Audio Master的同步方式,其视频同步原因在MediaCodecVideoRender.java的processOutputBuffer()函数中,下面结合函数详细看下音画同步的原理。
protected boolean processOutputBuffer(long positionUs,long elapsedRealtimeUs,@Nullable MediaCodecAdapter codec,@Nullable ByteBuffer buffer,int bufferIndex,int bufferFlags,int sampleCount,long bufferPresentationTimeUs,boolean isDecodeOnlyBuffer,boolean isLastBuffer,Format format)throws ExoPlaybackException {//Video不支持BypassAssertions.checkNotNull(codec); // Can not render video without codecif (initialPositionUs == C.TIME_UNSET) {initialPositionUs = positionUs;}if (bufferPresentationTimeUs != lastBufferPresentationTimeUs) {frameReleaseHelper.onNextFrame(bufferPresentationTimeUs);this.lastBufferPresentationTimeUs = bufferPresentationTimeUs;}//outputStreamOffsetUs通常为0long outputStreamOffsetUs = getOutputStreamOffsetUs();long presentationTimeUs = bufferPresentationTimeUs - outputStreamOffsetUs;//isDecodeOnlyBuffer直接跳过if (isDecodeOnlyBuffer && !isLastBuffer) {skipOutputBuffer(codec, bufferIndex, presentationTimeUs);return true;}// Note: Use of double rather than float is intentional for accuracy in the calculations below.double playbackSpeed = getPlaybackSpeed();boolean isStarted = getState() == STATE_STARTED;long elapsedRealtimeNowUs = SystemClock.elapsedRealtime() * 1000;// Calculate how early we are. In other words, the realtime duration that needs to elapse whilst// the renderer is started before the frame should be rendered. A negative value means that// we're already late.//bufferPresentationTimeUs是当前帧的pts,positionUs是当前的播放位置,两者相减再除以播 //放速度即可获得该帧需要等待的时间long earlyUs = (long) ((bufferPresentationTimeUs - positionUs) / playbackSpeed);if (isStarted) {// Account for the elapsed time since the start of this iteration of the rendering loop.//这里把循环间隔的时间也计算在内了,毕竟一帧视频的duration就30ms左右earlyUs -= elapsedRealtimeNowUs - elapsedRealtimeUs;}if (surface == dummySurface) {// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.if (isBufferLate(earlyUs)) {skipOutputBuffer(codec, bufferIndex, presentationTimeUs);updateVideoFrameProcessingOffsetCounters(earlyUs);return true;}return false;}//这里是判断强制渲染的逻辑long elapsedSinceLastRenderUs = elapsedRealtimeNowUs - lastRenderRealtimeUs;boolean shouldRenderFirstFrame =!renderedFirstFrameAfterEnable? (isStarted || mayRenderFirstFrameAfterEnableIfNotStarted): !renderedFirstFrameAfterReset;// Don't force output until we joined and the position reached the current stream.boolean forceRenderOutputBuffer =joiningDeadlineMs == C.TIME_UNSET&& positionUs >= outputStreamOffsetUs&& (shouldRenderFirstFrame|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));if (forceRenderOutputBuffer) {long releaseTimeNs = System.nanoTime();notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format);if (Util.SDK_INT >= 21) {renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs);} else {renderOutputBuffer(codec, bufferIndex, presentationTimeUs);}updateVideoFrameProcessingOffsetCounters(earlyUs);return true;}if (!isStarted || positionUs == initialPositionUs) {return false;}// Compute the buffer's desired release time in nanoseconds.//这一帧应该显示的系统时间long systemTimeNs = System.nanoTime();long unadjustedFrameReleaseTimeNs = systemTimeNs + (earlyUs * 1000);// Apply a timestamp adjustment, if there is one.//这里做了微调,主要是将Android显示系统的VSYNC考虑在内long adjustedReleaseTimeNs = frameReleaseHelper.adjustReleaseTime(unadjustedFrameReleaseTimeNs);earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;//这里判断当前帧是否来晚了,这里涉及到函数isBufferLate和isBufferVeryLate,阈值分别是 //30ms和500ms,isBufferLate只是丢当前帧,isBufferVeryLate就需要丢到下一个I帧为止boolean treatDroppedBuffersAsSkipped = joiningDeadlineMs != C.TIME_UNSET;if (shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs, isLastBuffer)&& maybeDropBuffersToKeyframe(positionUs, treatDroppedBuffersAsSkipped)) {return false;} else if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs, isLastBuffer)) {if (treatDroppedBuffersAsSkipped) {skipOutputBuffer(codec, bufferIndex, presentationTimeUs);} else {dropOutputBuffer(codec, bufferIndex, presentationTimeUs);}updateVideoFrameProcessingOffsetCounters(earlyUs);return true;}//这里是显示逻辑if (Util.SDK_INT >= 21) {// Let the underlying framework time the release.//SDK版本大于等于21时,提前50ms renderOutputBufferif (earlyUs < 50000) {notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs);updateVideoFrameProcessingOffsetCounters(earlyUs);return true;}} else {// We need to time the release ourselves.//SDK版本小于21时,如果earlyUs小于11ms就直接显示,如果大于11ms小于30ms就sleep 10ms再 //显示,如果大于30ms就直接返回等下一次loop,谷歌也在注释里说了这些值定的比较随意,应该是 //调试出的经验值吧if (earlyUs < 30000) {if (earlyUs > 11000) {// We're a little too early to render the frame. Sleep until the frame can be rendered.// Note: The 11ms threshold was chosen fairly arbitrarily.try {// Subtracting 10000 rather than 11000 ensures the sleep time will be at least 1ms.Thread.sleep((earlyUs - 10000) / 1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);renderOutputBuffer(codec, bufferIndex, presentationTimeUs);updateVideoFrameProcessingOffsetCounters(earlyUs);return true;}}// We're either not playing, or it's not time to render the frame yet.return false;}