better handling of input format change for non-adaptive codecs

* this fixes a bug when switching from HE-AAC 22050Hz to AAC 44100Hz (the AudioTrack was not reset and we were trying to send a bad number of bytes, triggering a "AudioTrack.write() called with invalid size" error)
* this also improves quality switches, making it almost seamless
This commit is contained in:
Martin Bonnin 2014-12-23 13:47:50 +01:00
parent 69c7cb09c8
commit e8a8c49a97

View File

@ -135,6 +135,25 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/ */
private static final int RECONFIGURATION_STATE_QUEUE_PENDING = 2; private static final int RECONFIGURATION_STATE_QUEUE_PENDING = 2;
/**
* No reinit is needed
*/
private static final int REINIT_STATE_NONE = 0;
/**
* The input format has just changed. Signal an end of stream to the codec so that we can
* retrieve the very last decoded samples from the previous format.
*/
private static final int REINIT_STATE_SIGNAL_END_OF_STREAM = 1;
/**
* The end of stream has been sent, wait for the codec to acknowledge it.
*/
private static final int REINIT_STATE_WAIT_END_OF_STREAM = 2;
/**
* The last sample has been processed, we can safely reinit now.
*/
private static final int REINIT_STATE_DO_REINIT_NOW = 3;
public final CodecCounters codecCounters; public final CodecCounters codecCounters;
private final DrmSessionManager drmSessionManager; private final DrmSessionManager drmSessionManager;
@ -159,6 +178,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private boolean openedDrmSession; private boolean openedDrmSession;
private boolean codecReconfigured; private boolean codecReconfigured;
private int codecReconfigurationState; private int codecReconfigurationState;
private int codecReinitState;
private int trackIndex; private int trackIndex;
private int sourceState; private int sourceState;
@ -166,6 +186,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private boolean outputStreamEnded; private boolean outputStreamEnded;
private boolean waitingForKeys; private boolean waitingForKeys;
private boolean waitingForFirstSyncFrame; private boolean waitingForFirstSyncFrame;
private boolean hasQueuedOneInputBuffer;
private long currentPositionUs; private long currentPositionUs;
/** /**
@ -194,6 +215,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
formatHolder = new MediaFormatHolder(); formatHolder = new MediaFormatHolder();
decodeOnlyPresentationTimestamps = new ArrayList<Long>(); decodeOnlyPresentationTimestamps = new ArrayList<Long>();
outputBufferInfo = new MediaCodec.BufferInfo(); outputBufferInfo = new MediaCodec.BufferInfo();
codecReinitState = REINIT_STATE_NONE;
} }
@Override @Override
@ -302,6 +324,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
inputIndex = -1; inputIndex = -1;
outputIndex = -1; outputIndex = -1;
waitingForFirstSyncFrame = true; waitingForFirstSyncFrame = true;
hasQueuedOneInputBuffer = false;
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
} }
@ -346,6 +369,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codecReconfigured = false; codecReconfigured = false;
codecIsAdaptive = false; codecIsAdaptive = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitState = REINIT_STATE_NONE;
codecCounters.codecReleaseCount++; codecCounters.codecReleaseCount++;
try { try {
codec.stop(); codec.stop();
@ -493,6 +517,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
if (inputStreamEnded) { if (inputStreamEnded) {
return false; return false;
} }
if (codecReinitState == REINIT_STATE_DO_REINIT_NOW) {
releaseCodec();
maybeInitCodec();
return false;
}
if (inputIndex < 0) { if (inputIndex < 0) {
inputIndex = codec.dequeueInputBuffer(0); inputIndex = codec.dequeueInputBuffer(0);
if (inputIndex < 0) { if (inputIndex < 0) {
@ -502,6 +533,16 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
sampleHolder.data.clear(); sampleHolder.data.clear();
} }
if (codecReinitState == REINIT_STATE_SIGNAL_END_OF_STREAM) {
codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
inputIndex = -1;
codecReinitState = REINIT_STATE_WAIT_END_OF_STREAM;
return false;
} else if (codecReinitState != REINIT_STATE_NONE) {
// we are still waiting for the last samples to be output
return false;
}
int result; int result;
if (waitingForKeys) { if (waitingForKeys) {
// We've already read an encrypted sample into sampleHolder, and are waiting for keys. // We've already read an encrypted sample into sampleHolder, and are waiting for keys.
@ -591,6 +632,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codec.queueInputBuffer(inputIndex, 0 , bufferSize, presentationTimeUs, 0); codec.queueInputBuffer(inputIndex, 0 , bufferSize, presentationTimeUs, 0);
} }
inputIndex = -1; inputIndex = -1;
hasQueuedOneInputBuffer = true;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
} catch (CryptoException e) { } catch (CryptoException e) {
notifyCryptoError(e); notifyCryptoError(e);
@ -644,8 +686,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codecReconfigured = true; codecReconfigured = true;
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
} else { } else {
releaseCodec(); if (!hasQueuedOneInputBuffer) {
maybeInitCodec(); // no need to signal end of stream if nothing has been queued, we can just reinit asap
codecReinitState = REINIT_STATE_DO_REINIT_NOW;
} else {
codecReinitState = REINIT_STATE_SIGNAL_END_OF_STREAM;
}
} }
} }
@ -733,7 +779,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputStreamEnded = true; if (codecReinitState == REINIT_STATE_WAIT_END_OF_STREAM) {
codecReinitState = REINIT_STATE_DO_REINIT_NOW;
} else {
outputStreamEnded = true;
}
return false; return false;
} }