Make sure Muxer is always accessed from playback thread
This thread will become the Transformer internal thread. PiperOrigin-RevId: 489168435
This commit is contained in:
parent
7b874e2dee
commit
f658fe79b1
@ -24,6 +24,7 @@ import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MIN_BUFFER_MS
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
@ -112,6 +113,10 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
||||
player.release();
|
||||
}
|
||||
|
||||
public Looper getPlaybackLooper() {
|
||||
return player.getPlaybackLooper();
|
||||
}
|
||||
|
||||
private static final class RenderersFactoryImpl implements RenderersFactory {
|
||||
|
||||
private final TransformerMediaClock mediaClock;
|
||||
|
@ -32,6 +32,7 @@ import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.Consumer;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -260,11 +261,34 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return trackTypeToSampleCount.get(trackType, /* valueIfKeyNotFound= */ 0);
|
||||
}
|
||||
|
||||
/** Returns the duration of the longest track in milliseconds. */
|
||||
/**
|
||||
* Returns the duration of the longest track in milliseconds, or {@link C#TIME_UNSET} if there is
|
||||
* no track.
|
||||
*/
|
||||
public long getDurationMs() {
|
||||
if (trackTypeToTimeUs.size() == 0) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
return Util.usToMs(maxValue(trackTypeToTimeUs));
|
||||
}
|
||||
|
||||
/** Returns the current size in bytes of the output, or {@link C#LENGTH_UNSET} if unavailable. */
|
||||
public long getCurrentOutputSizeBytes() {
|
||||
long fileSize = C.LENGTH_UNSET;
|
||||
|
||||
if (outputPath != null) {
|
||||
fileSize = new File(outputPath).length();
|
||||
} else if (outputParcelFileDescriptor != null) {
|
||||
fileSize = outputParcelFileDescriptor.getStatSize();
|
||||
}
|
||||
|
||||
if (fileSize <= 0) {
|
||||
fileSize = C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the muxer can write a sample of the given track type.
|
||||
*
|
||||
|
@ -38,13 +38,15 @@ import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.audio.AudioProcessor;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/* package */ final class TransformerInternal {
|
||||
|
||||
@ -56,8 +58,6 @@ import java.util.List;
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
@Nullable private final String outputPath;
|
||||
@Nullable private final ParcelFileDescriptor outputParcelFileDescriptor;
|
||||
private final TransformationRequest transformationRequest;
|
||||
private final ImmutableList<AudioProcessor> audioProcessors;
|
||||
private final ImmutableList<Effect> videoEffects;
|
||||
@ -66,14 +66,18 @@ import java.util.List;
|
||||
private final FrameProcessor.Factory frameProcessorFactory;
|
||||
private final Listener listener;
|
||||
private final DebugViewProvider debugViewProvider;
|
||||
private final Clock clock;
|
||||
private final Handler handler;
|
||||
private final ExoPlayerAssetLoader exoPlayerAssetLoader;
|
||||
private final MuxerWrapper muxerWrapper;
|
||||
private final List<SamplePipeline> samplePipelines;
|
||||
private final ConditionVariable releasingMuxerConditionVariable;
|
||||
|
||||
private @Transformer.ProgressState int progressState;
|
||||
private long durationMs;
|
||||
private boolean released;
|
||||
private volatile @MonotonicNonNull TransformationResult transformationResult;
|
||||
private volatile @MonotonicNonNull TransformationException releaseMuxerException;
|
||||
|
||||
public TransformerInternal(
|
||||
Context context,
|
||||
@ -95,8 +99,6 @@ import java.util.List;
|
||||
DebugViewProvider debugViewProvider,
|
||||
Clock clock) {
|
||||
this.context = context;
|
||||
this.outputPath = outputPath;
|
||||
this.outputParcelFileDescriptor = outputParcelFileDescriptor;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.audioProcessors = audioProcessors;
|
||||
this.videoEffects = videoEffects;
|
||||
@ -105,6 +107,7 @@ import java.util.List;
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
this.listener = listener;
|
||||
this.debugViewProvider = debugViewProvider;
|
||||
this.clock = clock;
|
||||
handler = Util.createHandlerForCurrentLooper();
|
||||
AssetLoaderListener assetLoaderListener = new AssetLoaderListener(mediaItem, fallbackListener);
|
||||
muxerWrapper =
|
||||
@ -123,6 +126,7 @@ import java.util.List;
|
||||
assetLoaderListener,
|
||||
clock);
|
||||
samplePipelines = new ArrayList<>(/* initialCapacity= */ 2);
|
||||
releasingMuxerConditionVariable = new ConditionVariable();
|
||||
progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
|
||||
}
|
||||
|
||||
@ -153,12 +157,33 @@ import java.util.List;
|
||||
samplePipelines.clear();
|
||||
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
|
||||
released = true;
|
||||
HandlerWrapper playbackHandler =
|
||||
clock.createHandler(exoPlayerAssetLoader.getPlaybackLooper(), /* callback= */ null);
|
||||
playbackHandler.post(
|
||||
() -> {
|
||||
transformationResult =
|
||||
new TransformationResult.Builder()
|
||||
.setDurationMs(checkNotNull(muxerWrapper).getDurationMs())
|
||||
.setAverageAudioBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO))
|
||||
.setAverageVideoBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO))
|
||||
.setVideoFrameCount(muxerWrapper.getTrackSampleCount(C.TRACK_TYPE_VIDEO))
|
||||
.setFileSizeBytes(muxerWrapper.getCurrentOutputSizeBytes())
|
||||
.build();
|
||||
try {
|
||||
muxerWrapper.release(forCancellation);
|
||||
} catch (Muxer.MuxerException e) {
|
||||
releaseMuxerException =
|
||||
TransformationException.createForMuxer(
|
||||
e, TransformationException.ERROR_CODE_MUXING_FAILED);
|
||||
} finally {
|
||||
releasingMuxerConditionVariable.open();
|
||||
}
|
||||
});
|
||||
clock.onThreadBlocked();
|
||||
releasingMuxerConditionVariable.blockUninterruptible();
|
||||
exoPlayerAssetLoader.release();
|
||||
try {
|
||||
muxerWrapper.release(forCancellation);
|
||||
} catch (Muxer.MuxerException e) {
|
||||
throw TransformationException.createForMuxer(
|
||||
e, TransformationException.ERROR_CODE_MUXING_FAILED);
|
||||
if (releaseMuxerException != null) {
|
||||
throw releaseMuxerException;
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,26 +198,6 @@ import java.util.List;
|
||||
return positionMsSum / samplePipelines.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size in bytes of the current/latest output file, or {@link C#LENGTH_UNSET}
|
||||
* if unavailable.
|
||||
*/
|
||||
private long getCurrentOutputFileCurrentSizeBytes() {
|
||||
long fileSize = C.LENGTH_UNSET;
|
||||
|
||||
if (outputPath != null) {
|
||||
fileSize = new File(outputPath).length();
|
||||
} else if (outputParcelFileDescriptor != null) {
|
||||
fileSize = outputParcelFileDescriptor.getStatSize();
|
||||
}
|
||||
|
||||
if (fileSize <= 0) {
|
||||
fileSize = C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
private class AssetLoaderListener implements ExoPlayerAssetLoader.Listener {
|
||||
|
||||
private final MediaItem mediaItem;
|
||||
@ -405,17 +410,7 @@ import java.util.List;
|
||||
if (exception != null) {
|
||||
listener.onTransformationError(exception);
|
||||
} else {
|
||||
TransformationResult result =
|
||||
new TransformationResult.Builder()
|
||||
.setDurationMs(checkNotNull(muxerWrapper).getDurationMs())
|
||||
.setAverageAudioBitrate(
|
||||
muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO))
|
||||
.setAverageVideoBitrate(
|
||||
muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO))
|
||||
.setVideoFrameCount(muxerWrapper.getTrackSampleCount(C.TRACK_TYPE_VIDEO))
|
||||
.setFileSizeBytes(getCurrentOutputFileCurrentSizeBytes())
|
||||
.build();
|
||||
listener.onTransformationCompleted(result);
|
||||
listener.onTransformationCompleted(checkNotNull(transformationResult));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user