@Override protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities, TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) throws ExoPlaybackException { Assertions.checkState(rendererCapabilities[VIDEO_RENDERER_INDEX].getTrackType() == C.TRACK_TYPE_VIDEO); Assertions.checkState(rendererCapabilities[AUDIO_RENDERER_INDEX].getTrackType() == C.TRACK_TYPE_AUDIO); Assertions.checkState(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].length == 1); Assertions.checkState(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].length == 1); TrackSelection[] selections = new TrackSelection[rendererCapabilities.length]; selections[VIDEO_RENDERER_INDEX] = new RandomTrackSelection( rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0), getVideoTrackIndices(rendererTrackGroupArrays[VIDEO_RENDERER_INDEX].get(0), rendererFormatSupports[VIDEO_RENDERER_INDEX][0], videoFormatIds, canIncludeAdditionalVideoFormats), 0 /* seed */); selections[AUDIO_RENDERER_INDEX] = new FixedTrackSelection( rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0), getTrackIndex(rendererTrackGroupArrays[AUDIO_RENDERER_INDEX].get(0), audioFormatId)); includedAdditionalVideoFormats = selections[VIDEO_RENDERER_INDEX].length() > videoFormatIds.length; return selections; }
@Override public void onPlayerError(ExoPlaybackException error) { switch (error.type) { case TYPE_RENDERER: // error occurred in a Renderer LogHelper.e(LOG_TAG, "An error occurred. Type RENDERER: " + error.getRendererException().toString()); break; case TYPE_SOURCE: // error occurred loading data from a MediaSource. LogHelper.e(LOG_TAG, "An error occurred. Type SOURCE: " + error.getSourceException().toString()); break; case TYPE_UNEXPECTED: // error was an unexpected RuntimeException. LogHelper.e(LOG_TAG, "An error occurred. Type UNEXPECTED: " + error.getUnexpectedException().toString()); break; default: LogHelper.w(LOG_TAG, "An error occurred. Type OTHER ERROR."); break; } }
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveTrackSelectionFactory, boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException { TrackSelection selection = null; if (adaptiveTrackSelectionFactory != null) { selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, orientationMayChange, adaptiveTrackSelectionFactory); } if (selection == null) { selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange, exceedConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary); } return selection; }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveVideoTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Finds the renderer to which the provided {@link TrackGroup} should be associated. * <p> * A {@link TrackGroup} is associated to a renderer that reports * {@link RendererCapabilities#FORMAT_HANDLED} support for one or more of the tracks in the group, * or {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} if no such renderer exists, or * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} if again no such renderer exists. In * the case that two or more renderers report the same level of support, the renderer with the * lowest index is associated. * <p> * If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the * tracks in the group, then {@code renderers.length} is returned to indicate that no association * was made. * * @param rendererCapabilities The {@link RendererCapabilities} of the renderers. * @param group The {@link TrackGroup} whose associated renderer is to be found. * @return The index of the associated renderer, or {@code renderers.length} if no * association was made. * @throws ExoPlaybackException If an error occurs finding a renderer. */ private static int findRenderer(RendererCapabilities[] rendererCapabilities, TrackGroup group) throws ExoPlaybackException { int bestRendererIndex = rendererCapabilities.length; int bestFormatSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE; for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) { RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { int formatSupportLevel = rendererCapability.supportsFormat(group.getFormat(trackIndex)) & RendererCapabilities.FORMAT_SUPPORT_MASK; if (formatSupportLevel > bestFormatSupportLevel) { bestRendererIndex = rendererIndex; bestFormatSupportLevel = formatSupportLevel; if (bestFormatSupportLevel == RendererCapabilities.FORMAT_HANDLED) { // We can't do better. return bestRendererIndex; } } } } return bestRendererIndex; }
@Override protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) throws ExoPlaybackException { boolean passthrough = passthroughMediaFormat != null; String mimeType = passthrough ? passthroughMediaFormat.getString(MediaFormat.KEY_MIME) : MimeTypes.AUDIO_RAW; MediaFormat format = passthrough ? passthroughMediaFormat : outputFormat; int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); int[] channelMap; if (codecNeedsDiscardChannelsWorkaround && channelCount == 6 && this.channelCount < 6) { channelMap = new int[this.channelCount]; for (int i = 0; i < this.channelCount; i++) { channelMap[i] = i; } } else { channelMap = null; } try { audioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding, 0, channelMap); } catch (AudioTrack.ConfigurationException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } }
@Override public void onPlayerError(ExoPlaybackException error) { int errorMessageResourceId = R.string.error_stream_unknown; switch (error.type) { case ExoPlaybackException.TYPE_SOURCE: Timber.e(error,"exo player error TYPE_SOURCE"); errorMessageResourceId = R.string.error_stream_io; break; case ExoPlaybackException.TYPE_RENDERER: Timber.e(error,"exo player error TYPE_RENDERER"); errorMessageResourceId = R.string.error_stream_unsupported; break; case ExoPlaybackException.TYPE_UNEXPECTED: Timber.e(error,"exo player error TYPE_UNEXPECTED"); break; } listener.onVideoError(errorMessageResourceId); }
protected TrackSelection selectVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, int maxVideoBitrate, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory, boolean exceedConstraintsIfNecessary, boolean exceedRendererCapabilitiesIfNecessary) throws ExoPlaybackException { TrackSelection selection = null; if (adaptiveVideoTrackSelectionFactory != null) { selection = selectAdaptiveVideoTrack(rendererCapabilities, groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory); } if (selection == null) { selection = selectFixedVideoTrack(groups, formatSupport, maxVideoWidth, maxVideoHeight, maxVideoBitrate, viewportWidth, viewportHeight, orientationMayChange, exceedConstraintsIfNecessary, exceedRendererCapabilitiesIfNecessary); } return selection; }
@Override public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (format == null) { readFormat(); } maybeInitCodec(); if (codec != null) { TraceUtil.beginSection("drainAndFeed"); while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} while (feedInputBuffer()) {} TraceUtil.endSection(); } else if (format != null) { skipToKeyframeBefore(positionUs); } decoderCounters.ensureUpdated(); }
@Override public void handleMessage(int messageType, Object message) throws ExoPlaybackException { switch (messageType) { case C.MSG_SET_VOLUME: audioTrack.setVolume((Float) message); break; case C.MSG_SET_PLAYBACK_PARAMS: audioTrack.setPlaybackParams((PlaybackParams) message); break; case C.MSG_SET_STREAM_TYPE: @C.StreamType int streamType = (Integer) message; audioTrack.setStreamType(streamType); break; default: super.handleMessage(messageType, message); break; } }
private void setSurface(Surface surface) throws ExoPlaybackException { // We only need to update the codec if the surface has changed. if (this.surface != surface) { this.surface = surface; int state = getState(); if (state == STATE_ENABLED || state == STATE_STARTED) { MediaCodec codec = getCodec(); if (Util.SDK_INT >= 23 && codec != null && surface != null) { setOutputSurfaceV23(codec, surface); } else { releaseCodec(); maybeInitCodec(); } } } // Clear state so that we always call the event listener with the video size and when a frame // is rendered, even if the surface hasn't changed. clearRenderedFirstFrame(); clearLastReportedVideoSize(); }
private static TrackSelection selectAdaptiveVideoTrack(RendererCapabilities rendererCapabilities, TrackGroupArray groups, int[][] formatSupport, int maxVideoWidth, int maxVideoHeight, boolean allowNonSeamlessAdaptiveness, boolean allowMixedMimeAdaptiveness, int viewportWidth, int viewportHeight, boolean orientationMayChange, TrackSelection.Factory adaptiveVideoTrackSelectionFactory) throws ExoPlaybackException { int requiredAdaptiveSupport = allowNonSeamlessAdaptiveness ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) : RendererCapabilities.ADAPTIVE_SEAMLESS; boolean allowMixedMimeTypes = allowMixedMimeAdaptiveness && (rendererCapabilities.supportsMixedMimeTypeAdaptation() & requiredAdaptiveSupport) != 0; for (int i = 0; i < groups.length; i++) { TrackGroup group = groups.get(i); int[] adaptiveTracks = getAdaptiveTracksForGroup(group, formatSupport[i], allowMixedMimeTypes, requiredAdaptiveSupport, maxVideoWidth, maxVideoHeight, viewportWidth, viewportHeight, orientationMayChange); if (adaptiveTracks.length > 0) { return adaptiveVideoTrackSelectionFactory.createTrackSelection(group, adaptiveTracks); } } return null; }
/** * Finds the renderer to which the provided {@link TrackGroup} should be associated. * <p> * A {@link TrackGroup} is associated to a renderer that reports * {@link RendererCapabilities#FORMAT_HANDLED} support for one or more of the tracks in the group, * or {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES} if no such renderer exists, or * {@link RendererCapabilities#FORMAT_UNSUPPORTED_SUBTYPE} if again no such renderer exists. In * the case that two or more renderers report the same level of support, the renderer with the * lowest index is associated. * <p> * If all renderers report {@link RendererCapabilities#FORMAT_UNSUPPORTED_TYPE} for all of the * tracks in the group, then {@code renderers.length} is returned to indicate that no association * was made. * * @param rendererCapabilities The {@link RendererCapabilities} of the renderers. * @param group The {@link TrackGroup} whose associated renderer is to be found. * @return The index of the associated renderer, or {@code renderers.length} if no * association was made. * @throws ExoPlaybackException If an error occurs finding a renderer. */ private static int findRenderer(RendererCapabilities[] rendererCapabilities, TrackGroup group) throws ExoPlaybackException { int bestRendererIndex = rendererCapabilities.length; int bestSupportLevel = RendererCapabilities.FORMAT_UNSUPPORTED_TYPE; for (int rendererIndex = 0; rendererIndex < rendererCapabilities.length; rendererIndex++) { RendererCapabilities rendererCapability = rendererCapabilities[rendererIndex]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { int trackSupportLevel = rendererCapability.supportsFormat(group.getFormat(trackIndex)); if (trackSupportLevel > bestSupportLevel) { bestRendererIndex = rendererIndex; bestSupportLevel = trackSupportLevel; if (bestSupportLevel == RendererCapabilities.FORMAT_HANDLED) { // We can't do better. return bestRendererIndex; } } } } return bestRendererIndex; }
@Override public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (!inputStreamEnded && pendingMetadata == null) { buffer.clear(); int result = readSource(formatHolder, buffer); if (result == C.RESULT_BUFFER_READ) { if (buffer.isEndOfStream()) { inputStreamEnded = true; } else { pendingMetadataTimestamp = buffer.timeUs; try { buffer.flip(); ByteBuffer bufferData = buffer.data; pendingMetadata = metadataDecoder.decode(bufferData.array(), bufferData.limit()); } catch (MetadataDecoderException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } } } } if (pendingMetadata != null && pendingMetadataTimestamp <= positionUs) { invokeRenderer(pendingMetadata); pendingMetadata = null; } }
@Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { errorString = getResources().getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { errorString = getResources().getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { errorString = getResources().getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { errorString = getResources().getString(R.string.error_instantiating_decoder, decoderInitializationException.decoderName); } } } if (errorString != null) { eventEmitter.error(errorString, e); } playerNeedsSource = true; }
@Override public void onPlayerError(ExoPlaybackException error) { if (playingAd) { for (int i = 0; i < adCallbacks.size(); i++) { adCallbacks.get(i).onError(); } } }
/*** * 是否TYPE_SOURCE 异常 * * @param e 异常 * @return boolean boolean */ public static boolean isBehindLiveWindow(@NonNull ExoPlaybackException e) { if (e.type != ExoPlaybackException.TYPE_SOURCE) { return false; } Throwable cause = e.getSourceException(); while (cause != null) { if (cause instanceof BehindLiveWindowException) { return true; } cause = cause.getCause(); } return false; }
public void onPlayerError(ExoPlaybackException e) { Log.e(TAG, "onPlayerError:" + e.getMessage()); updateResumePosition(); if (VideoPlayUtils.isBehindLiveWindow(e)) { clearResumePosition(); startVideo(); } else { getPlayerViewListener().showErrorStateView(View.VISIBLE); if (videoInfoListener != null) { videoInfoListener.onPlayerError(e); } } }
@Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof DecoderInitializationException) { // Special case for decoder initialization failures. DecoderInitializationException decoderInitializationException = (DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof DecoderQueryException) { errorString = getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { errorString = getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { errorString = getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { errorString = getString(R.string.error_instantiating_decoder, decoderInitializationException.decoderName); } } } if (errorString != null) { showToast(errorString); } playerNeedsSource = true; if (isBehindLiveWindow(e)) { clearResumePosition(); initializePlayer(); } else { updateResumePosition(); updateButtonVisibilities(); showControls(); } }
private static boolean isBehindLiveWindow(ExoPlaybackException e) { if (e.type != ExoPlaybackException.TYPE_SOURCE) { return false; } Throwable cause = e.getSourceException(); while (cause != null) { if (cause instanceof BehindLiveWindowException) { return true; } cause = cause.getCause(); } return false; }
@Override protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackException { super.onInputFormatChanged(newFormat); // Ensure timestamps of buffers queued after this format change are never inserted into the // queue of expected output timestamps before those of buffers that have already been queued. minimumInsertIndex = startIndex + queueSize; }
/** * Called when an error occurs. The playback state will transition to {@link ExoPlayer#STATE_IDLE} * immediately after this method is called. The player instance can still be used, and * {@link #release()} must still be called on the player should it no longer be required. * * @param e The error. */ @Override public void onPlayerError(ExoPlaybackException e) { MediaError error = null; if (e == null) { error = new MediaError(MediaError.ERROR_UNKNOWN); } else { if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { error = new MediaError(MediaError.EXO_ERROR_QUERYING_DECODERS); } else if (decoderInitializationException.secureDecoderRequired) { error = new MediaError(MediaError.EXO_ERROR_NO_SECURE_DECODER); } else { error = new MediaError(MediaError.EXO_ERROR_NO_DECODER); } } else { error = new MediaError(MediaError.EXO_ERROR_INSTANTIATING_DECODER); } } } } if (error == null) { error = new MediaError(MediaError.ERROR_UNKNOWN); } notifyOnError(error); PlayerLog.d(TAG, "onPlayerError " + error.toString()); }
@Override protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { streamFormat = formats[0]; if (decoder != null) { decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM; } else { decoder = decoderFactory.createDecoder(streamFormat); } }
@Override public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (!inputStreamEnded && pendingMetadataCount < MAX_PENDING_METADATA_COUNT) { buffer.clear(); int result = readSource(formatHolder, buffer, false); if (result == C.RESULT_BUFFER_READ) { if (buffer.isEndOfStream()) { inputStreamEnded = true; } else if (buffer.isDecodeOnly()) { // Do nothing. Note this assumes that all metadata buffers can be decoded independently. // If we ever need to support a metadata format where this is not the case, we'll need to // pass the buffer to the decoder and discard the output. } else { buffer.subsampleOffsetUs = formatHolder.format.subsampleOffsetUs; buffer.flip(); try { int index = (pendingMetadataIndex + pendingMetadataCount) % MAX_PENDING_METADATA_COUNT; pendingMetadata[index] = decoder.decode(buffer); pendingMetadataTimestamps[index] = buffer.timeUs; pendingMetadataCount++; } catch (MetadataDecoderException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } } } } if (pendingMetadataCount > 0 && pendingMetadataTimestamps[pendingMetadataIndex] <= positionUs) { invokeRenderer(pendingMetadata[pendingMetadataIndex]); pendingMetadata[pendingMetadataIndex] = null; pendingMetadataIndex = (pendingMetadataIndex + 1) % MAX_PENDING_METADATA_COUNT; pendingMetadataCount--; } }
@Override public final int supportsFormat(Format format) throws ExoPlaybackException { try { return supportsFormat(mediaCodecSelector, format); } catch (DecoderQueryException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } }
@Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { inputStreamEnded = false; outputStreamEnded = false; if (codec != null) { flushCodec(); } }
protected void flushCodec() throws ExoPlaybackException { codecHotswapDeadlineMs = C.TIME_UNSET; inputIndex = C.INDEX_UNSET; outputIndex = C.INDEX_UNSET; waitingForFirstSyncFrame = true; waitingForKeys = false; shouldSkipOutputBuffer = false; decodeOnlyPresentationTimestamps.clear(); codecNeedsAdaptationWorkaroundBuffer = false; shouldSkipAdaptationWorkaroundOutputBuffer = false; if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { releaseCodec(); maybeInitCodec(); } else if (codecReinitializationState != REINITIALIZATION_STATE_NONE) { // We're already waiting to release and re-initialize the codec. Since we're now flushing, // there's no need to wait any longer. releaseCodec(); maybeInitCodec(); } else { // We can flush and re-use the existing decoder. codec.flush(); codecReceivedBuffers = false; } if (codecReconfigured && format != null) { // Any reconfiguration data that we send shortly before the flush may be discarded. We // avoid this issue by sending reconfiguration data following every flush. codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; } }
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException { if (drmSession == null) { return false; } @DrmSession.State int drmSessionState = drmSession.getState(); if (drmSessionState == DrmSession.STATE_ERROR) { throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); } return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS && (bufferEncrypted || !playClearSamplesWithoutKeys); }
/** * Called when a new format is read from the upstream {@link MediaPeriod}. * * @param newFormat The new format. * @throws ExoPlaybackException If an error occurs reinitializing the {@link MediaCodec}. */ protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackException { Format oldFormat = format; format = newFormat; boolean drmInitDataChanged = !Util.areEqual(format.drmInitData, oldFormat == null ? null : oldFormat.drmInitData); if (drmInitDataChanged) { if (format.drmInitData != null) { if (drmSessionManager == null) { throw ExoPlaybackException.createForRenderer( new IllegalStateException("Media requires a DrmSessionManager"), getIndex()); } pendingDrmSession = drmSessionManager.acquireSession(Looper.myLooper(), format.drmInitData); if (pendingDrmSession == drmSession) { drmSessionManager.releaseSession(pendingDrmSession); } } else { pendingDrmSession = null; } } if (pendingDrmSession == drmSession && codec != null && canReconfigureCodec(codec, codecIsAdaptive, oldFormat, format)) { codecReconfigured = true; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecNeedsAdaptationWorkaroundBuffer = codecNeedsAdaptationWorkaround && format.width == oldFormat.width && format.height == oldFormat.height; } else { if (codecReceivedBuffers) { // Signal end of stream and wait for any final output buffers before re-initialization. codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM; } else { // There aren't any final output buffers, so perform re-initialization immediately. releaseCodec(); maybeInitCodec(); } } }
/** * Processes a new output format. */ private void processOutputFormat() throws ExoPlaybackException { MediaFormat format = codec.getOutputFormat(); if (codecNeedsAdaptationWorkaround && format.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT && format.getInteger(MediaFormat.KEY_HEIGHT) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT) { // We assume this format changed event was caused by the adaptation workaround. shouldSkipAdaptationWorkaroundOutputBuffer = true; return; } if (codecNeedsMonoChannelCountWorkaround) { format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); } onOutputFormatChanged(codec, format); }
/** * Processes an end of stream signal. * * @throws ExoPlaybackException If an error occurs processing the signal. */ private void processEndOfStream() throws ExoPlaybackException { if (codecReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM) { // We're waiting to re-initialize the codec, and have now processed all final buffers. releaseCodec(); maybeInitCodec(); } else { outputStreamEnded = true; renderToEndOfStream(); } }
private void processEndOfStream() throws ExoPlaybackException { outputStreamEnded = true; try { audioTrack.playToEndOfStream(); } catch (AudioTrack.WriteException e) { throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); } }
private void flushDecoder() throws ExoPlaybackException { waitingForKeys = false; if (decoderReinitializationState != REINITIALIZATION_STATE_NONE) { releaseDecoder(); maybeInitDecoder(); } else { inputBuffer = null; if (outputBuffer != null) { outputBuffer.release(); outputBuffer = null; } decoder.flush(); decoderReceivedBuffers = false; } }
@Override protected void onEnabled(boolean joining) throws ExoPlaybackException { decoderCounters = new DecoderCounters(); eventDispatcher.enabled(decoderCounters); int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId; if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) { audioTrack.enableTunnelingV21(tunnelingAudioSessionId); } else { audioTrack.disableTunneling(); } }
@Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { audioTrack.reset(); currentPositionUs = positionUs; allowPositionDiscontinuity = true; inputStreamEnded = false; outputStreamEnded = false; if (decoder != null) { flushDecoder(); } }
private void maybeInitDecoder() throws ExoPlaybackException { if (decoder != null) { return; } drmSession = pendingDrmSession; ExoMediaCrypto mediaCrypto = null; if (drmSession != null) { @DrmSession.State int drmSessionState = drmSession.getState(); if (drmSessionState == DrmSession.STATE_ERROR) { throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); } else if (drmSessionState == DrmSession.STATE_OPENED || drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) { mediaCrypto = drmSession.getMediaCrypto(); } else { // The drm session isn't open yet. return; } } try { long codecInitializingTimestamp = SystemClock.elapsedRealtime(); TraceUtil.beginSection("createAudioDecoder"); decoder = createDecoder(inputFormat, mediaCrypto); TraceUtil.endSection(); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, codecInitializedTimestamp - codecInitializingTimestamp); decoderCounters.decoderInitCount++; } catch (AudioDecoderException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } }
private void onInputFormatChanged(Format newFormat) throws ExoPlaybackException { Format oldFormat = inputFormat; inputFormat = newFormat; boolean drmInitDataChanged = !Util.areEqual(inputFormat.drmInitData, oldFormat == null ? null : oldFormat.drmInitData); if (drmInitDataChanged) { if (inputFormat.drmInitData != null) { if (drmSessionManager == null) { throw ExoPlaybackException.createForRenderer( new IllegalStateException("Media requires a DrmSessionManager"), getIndex()); } pendingDrmSession = drmSessionManager.acquireSession(Looper.myLooper(), inputFormat.drmInitData); if (pendingDrmSession == drmSession) { drmSessionManager.releaseSession(pendingDrmSession); } } else { pendingDrmSession = null; } } if (decoderReceivedBuffers) { // Signal end of stream and wait for any final output buffers before re-initialization. decoderReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM; } else { // There aren't any final output buffers, so release the decoder immediately. releaseDecoder(); maybeInitDecoder(); audioTrackNeedsConfigure = true; } eventDispatcher.inputFormatChanged(newFormat); }