Search in sources :

Example 1 with DecoderDiscardReasons

use of androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons in project media by androidx.

the class MediaCodecAudioRenderer method canReuseCodec.

@Override
protected DecoderReuseEvaluation canReuseCodec(MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
    DecoderReuseEvaluation evaluation = codecInfo.canReuseCodec(oldFormat, newFormat);
    @DecoderDiscardReasons int discardReasons = evaluation.discardReasons;
    if (getCodecMaxInputSize(codecInfo, newFormat) > codecMaxInputSize) {
        discardReasons |= DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED;
    }
    return new DecoderReuseEvaluation(codecInfo.name, oldFormat, newFormat, discardReasons != 0 ? REUSE_RESULT_NO : evaluation.result, discardReasons);
}
Also used : DecoderReuseEvaluation(androidx.media3.exoplayer.DecoderReuseEvaluation) DecoderDiscardReasons(androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons) SuppressLint(android.annotation.SuppressLint)

Example 2 with DecoderDiscardReasons

use of androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons in project media by androidx.

the class MediaCodecVideoRenderer method canReuseCodec.

@Override
protected DecoderReuseEvaluation canReuseCodec(MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
    DecoderReuseEvaluation evaluation = codecInfo.canReuseCodec(oldFormat, newFormat);
    @DecoderDiscardReasons int discardReasons = evaluation.discardReasons;
    if (newFormat.width > codecMaxValues.width || newFormat.height > codecMaxValues.height) {
        discardReasons |= DISCARD_REASON_VIDEO_MAX_RESOLUTION_EXCEEDED;
    }
    if (getMaxInputSize(codecInfo, newFormat) > codecMaxValues.inputSize) {
        discardReasons |= DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED;
    }
    return new DecoderReuseEvaluation(codecInfo.name, oldFormat, newFormat, discardReasons != 0 ? REUSE_RESULT_NO : evaluation.result, discardReasons);
}
Also used : DecoderReuseEvaluation(androidx.media3.exoplayer.DecoderReuseEvaluation) DecoderDiscardReasons(androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons) SuppressLint(android.annotation.SuppressLint) Point(android.graphics.Point)

Example 3 with DecoderDiscardReasons

use of androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons in project media by androidx.

the class MediaCodecInfo method canReuseCodec.

/**
 * Evaluates whether it's possible to reuse an instance of this decoder that's currently decoding
 * {@code oldFormat} to decode {@code newFormat} instead.
 *
 * <p>For adaptation to succeed, the codec must also be configured with maximum values that are
 * compatible with the new format.
 *
 * @param oldFormat The format being decoded.
 * @param newFormat The new format.
 * @return The result of the evaluation.
 */
public DecoderReuseEvaluation canReuseCodec(Format oldFormat, Format newFormat) {
    @DecoderDiscardReasons int discardReasons = 0;
    if (!Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType)) {
        discardReasons |= DISCARD_REASON_MIME_TYPE_CHANGED;
    }
    if (isVideo) {
        if (oldFormat.rotationDegrees != newFormat.rotationDegrees) {
            discardReasons |= DISCARD_REASON_VIDEO_ROTATION_CHANGED;
        }
        if (!adaptive && (oldFormat.width != newFormat.width || oldFormat.height != newFormat.height)) {
            discardReasons |= DISCARD_REASON_VIDEO_RESOLUTION_CHANGED;
        }
        if (!Util.areEqual(oldFormat.colorInfo, newFormat.colorInfo)) {
            discardReasons |= DISCARD_REASON_VIDEO_COLOR_INFO_CHANGED;
        }
        if (needsAdaptationReconfigureWorkaround(name) && !oldFormat.initializationDataEquals(newFormat)) {
            discardReasons |= DISCARD_REASON_WORKAROUND;
        }
        if (discardReasons == 0) {
            return new DecoderReuseEvaluation(name, oldFormat, newFormat, oldFormat.initializationDataEquals(newFormat) ? REUSE_RESULT_YES_WITHOUT_RECONFIGURATION : REUSE_RESULT_YES_WITH_RECONFIGURATION, /* discardReasons= */
            0);
        }
    } else {
        if (oldFormat.channelCount != newFormat.channelCount) {
            discardReasons |= DISCARD_REASON_AUDIO_CHANNEL_COUNT_CHANGED;
        }
        if (oldFormat.sampleRate != newFormat.sampleRate) {
            discardReasons |= DISCARD_REASON_AUDIO_SAMPLE_RATE_CHANGED;
        }
        if (oldFormat.pcmEncoding != newFormat.pcmEncoding) {
            discardReasons |= DISCARD_REASON_AUDIO_ENCODING_CHANGED;
        }
        // without reconfiguration or flushing.
        if (discardReasons == 0 && MimeTypes.AUDIO_AAC.equals(mimeType)) {
            @Nullable Pair<Integer, Integer> oldCodecProfileLevel = MediaCodecUtil.getCodecProfileAndLevel(oldFormat);
            @Nullable Pair<Integer, Integer> newCodecProfileLevel = MediaCodecUtil.getCodecProfileAndLevel(newFormat);
            if (oldCodecProfileLevel != null && newCodecProfileLevel != null) {
                int oldProfile = oldCodecProfileLevel.first;
                int newProfile = newCodecProfileLevel.first;
                if (oldProfile == CodecProfileLevel.AACObjectXHE && newProfile == CodecProfileLevel.AACObjectXHE) {
                    return new DecoderReuseEvaluation(name, oldFormat, newFormat, REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, /* discardReasons= */
                    0);
                }
            }
        }
        if (!oldFormat.initializationDataEquals(newFormat)) {
            discardReasons |= DISCARD_REASON_INITIALIZATION_DATA_CHANGED;
        }
        if (needsAdaptationFlushWorkaround(mimeType)) {
            discardReasons |= DISCARD_REASON_WORKAROUND;
        }
        if (discardReasons == 0) {
            return new DecoderReuseEvaluation(name, oldFormat, newFormat, REUSE_RESULT_YES_WITH_FLUSH, /* discardReasons= */
            0);
        }
    }
    return new DecoderReuseEvaluation(name, oldFormat, newFormat, REUSE_RESULT_NO, discardReasons);
}
Also used : DecoderReuseEvaluation(androidx.media3.exoplayer.DecoderReuseEvaluation) DecoderDiscardReasons(androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons) Point(android.graphics.Point) Nullable(androidx.annotation.Nullable)

Example 4 with DecoderDiscardReasons

use of androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons in project media by androidx.

the class MediaCodecRenderer method onInputFormatChanged.

/**
 * Called when a new {@link Format} is read from the upstream {@link MediaPeriod}.
 *
 * @param formatHolder A {@link FormatHolder} that holds the new {@link Format}.
 * @throws ExoPlaybackException If an error occurs re-initializing the {@link MediaCodec}.
 * @return The result of the evaluation to determine whether the existing decoder instance can be
 *     reused for the new format, or {@code null} if the renderer did not have a decoder.
 */
@CallSuper
@Nullable
protected DecoderReuseEvaluation onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException {
    waitingForFirstSampleInFormat = true;
    Format newFormat = checkNotNull(formatHolder.format);
    if (newFormat.sampleMimeType == null) {
        throw createRendererException(new IllegalArgumentException(), newFormat, PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
    }
    setSourceDrmSession(formatHolder.drmSession);
    inputFormat = newFormat;
    if (bypassEnabled) {
        bypassDrainAndReinitialize = true;
        // Need to drain batch buffer first.
        return null;
    }
    if (codec == null) {
        availableCodecInfos = null;
        maybeInitCodecOrBypass();
        return null;
    }
    // We have an existing codec that we may need to reconfigure, re-initialize, or release to
    // switch to bypass. If the existing codec instance is kept then its operating rate and DRM
    // session may need to be updated.
    // Copy the current codec and codecInfo to local variables so they remain accessible if the
    // member variables are updated during the logic below.
    MediaCodecAdapter codec = this.codec;
    MediaCodecInfo codecInfo = this.codecInfo;
    Format oldFormat = codecInputFormat;
    if (drmNeedsCodecReinitialization(codecInfo, newFormat, codecDrmSession, sourceDrmSession)) {
        drainAndReinitializeCodec();
        return new DecoderReuseEvaluation(codecInfo.name, oldFormat, newFormat, REUSE_RESULT_NO, DISCARD_REASON_DRM_SESSION_CHANGED);
    }
    boolean drainAndUpdateCodecDrmSession = sourceDrmSession != codecDrmSession;
    Assertions.checkState(!drainAndUpdateCodecDrmSession || Util.SDK_INT >= 23);
    DecoderReuseEvaluation evaluation = canReuseCodec(codecInfo, oldFormat, newFormat);
    @DecoderDiscardReasons int overridingDiscardReasons = 0;
    switch(evaluation.result) {
        case REUSE_RESULT_NO:
            drainAndReinitializeCodec();
            break;
        case REUSE_RESULT_YES_WITH_FLUSH:
            if (!updateCodecOperatingRate(newFormat)) {
                overridingDiscardReasons |= DISCARD_REASON_OPERATING_RATE_CHANGED;
            } else {
                codecInputFormat = newFormat;
                if (drainAndUpdateCodecDrmSession) {
                    if (!drainAndUpdateCodecDrmSessionV23()) {
                        overridingDiscardReasons |= DISCARD_REASON_WORKAROUND;
                    }
                } else if (!drainAndFlushCodec()) {
                    overridingDiscardReasons |= DISCARD_REASON_WORKAROUND;
                }
            }
            break;
        case REUSE_RESULT_YES_WITH_RECONFIGURATION:
            if (!updateCodecOperatingRate(newFormat)) {
                overridingDiscardReasons |= DISCARD_REASON_OPERATING_RATE_CHANGED;
            } else {
                codecReconfigured = true;
                codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
                codecNeedsAdaptationWorkaroundBuffer = codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_ALWAYS || (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION && newFormat.width == oldFormat.width && newFormat.height == oldFormat.height);
                codecInputFormat = newFormat;
                if (drainAndUpdateCodecDrmSession && !drainAndUpdateCodecDrmSessionV23()) {
                    overridingDiscardReasons |= DISCARD_REASON_WORKAROUND;
                }
            }
            break;
        case REUSE_RESULT_YES_WITHOUT_RECONFIGURATION:
            if (!updateCodecOperatingRate(newFormat)) {
                overridingDiscardReasons |= DISCARD_REASON_OPERATING_RATE_CHANGED;
            } else {
                codecInputFormat = newFormat;
                if (drainAndUpdateCodecDrmSession && !drainAndUpdateCodecDrmSessionV23()) {
                    overridingDiscardReasons |= DISCARD_REASON_WORKAROUND;
                }
            }
            break;
        default:
            // Never happens.
            throw new IllegalStateException();
    }
    if (evaluation.result != REUSE_RESULT_NO && (this.codec != codec || codecDrainAction == DRAIN_ACTION_REINITIALIZE)) {
        // The reasons are indicated by overridingDiscardReasons.
        return new DecoderReuseEvaluation(codecInfo.name, oldFormat, newFormat, REUSE_RESULT_NO, overridingDiscardReasons);
    }
    return evaluation;
}
Also used : Format(androidx.media3.common.Format) MediaFormat(android.media.MediaFormat) DecoderReuseEvaluation(androidx.media3.exoplayer.DecoderReuseEvaluation) DecoderDiscardReasons(androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons) CallSuper(androidx.annotation.CallSuper) Nullable(androidx.annotation.Nullable)

Aggregations

DecoderReuseEvaluation (androidx.media3.exoplayer.DecoderReuseEvaluation)4 DecoderDiscardReasons (androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons)4 SuppressLint (android.annotation.SuppressLint)2 Point (android.graphics.Point)2 Nullable (androidx.annotation.Nullable)2 MediaFormat (android.media.MediaFormat)1 CallSuper (androidx.annotation.CallSuper)1 Format (androidx.media3.common.Format)1