Use silence audio in RawAssetLoader tests

This is avoid dependency on `MediaExtractor` whose
behaviour is not consistent across different API versions.

PiperOrigin-RevId: 627330485
This commit is contained in:
sheenachhabra 2024-04-23 03:35:34 -07:00 committed by Copybara-Service
parent 4091303c7e
commit b813bd3976

View File

@ -15,9 +15,6 @@
*/ */
package androidx.media3.transformer; package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.test.utils.TestUtil.buildAssetUri;
import static androidx.media3.test.utils.TestUtil.retrieveTrackFormat;
import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING;
import static androidx.media3.transformer.AndroidTestUtil.createOpenGlObjects; import static androidx.media3.transformer.AndroidTestUtil.createOpenGlObjects;
import static androidx.media3.transformer.AndroidTestUtil.generateTextureFromBitmap; import static androidx.media3.transformer.AndroidTestUtil.generateTextureFromBitmap;
@ -25,8 +22,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri; import android.net.Uri;
import android.opengl.EGLContext; import android.opengl.EGLContext;
import android.os.Looper; import android.os.Looper;
@ -38,7 +33,9 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.OnInputFrameProcessedListener; import androidx.media3.common.OnInputFrameProcessedListener;
import androidx.media3.common.VideoFrameProcessingException; import androidx.media3.common.VideoFrameProcessingException;
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader; import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.effect.DefaultGlObjectsProvider; import androidx.media3.effect.DefaultGlObjectsProvider;
import androidx.media3.effect.DefaultVideoFrameProcessor; import androidx.media3.effect.DefaultVideoFrameProcessor;
@ -48,7 +45,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@ -61,6 +57,14 @@ import org.junit.runner.RunWith;
public class RawAssetLoaderAndroidTest { public class RawAssetLoaderAndroidTest {
@Rule public final TestName testName = new TestName(); @Rule public final TestName testName = new TestName();
private static final Format AUDIO_FORMAT =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_RAW)
.setSampleRate(44_100)
.setChannelCount(1)
.setPcmEncoding(C.ENCODING_PCM_16BIT)
.build();
private final Context context = ApplicationProvider.getApplicationContext(); private final Context context = ApplicationProvider.getApplicationContext();
private String testId; private String testId;
@ -72,25 +76,25 @@ public class RawAssetLoaderAndroidTest {
@Test @Test
public void audioTranscoding_withRawAudio_completesWithCorrectDuration() throws Exception { public void audioTranscoding_withRawAudio_completesWithCorrectDuration() throws Exception {
String rawAudioUri = "media/wav/sample.wav";
Format rawAudioFormat =
retrieveTrackFormat(context, buildAssetUri(rawAudioUri).toString(), C.TRACK_TYPE_AUDIO);
SettableFuture<RawAssetLoader> rawAssetLoaderFuture = SettableFuture.create(); SettableFuture<RawAssetLoader> rawAssetLoaderFuture = SettableFuture.create();
Transformer transformer = Transformer transformer =
new Transformer.Builder(context) new Transformer.Builder(context)
.setAssetLoaderFactory( .setAssetLoaderFactory(
new TestRawAssetLoaderFactory( new TestRawAssetLoaderFactory(
rawAudioFormat, /* videoFormat= */ null, rawAssetLoaderFuture)) AUDIO_FORMAT, /* videoFormat= */ null, rawAssetLoaderFuture))
.build(); .build();
long mediaDurationUs = C.MICROS_PER_SECOND;
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)).setDurationUs(1_000_000).build(); new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY))
.setDurationUs(mediaDurationUs)
.build();
ListenableFuture<ExportResult> exportCompletionFuture = ListenableFuture<ExportResult> exportCompletionFuture =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.runAsync(testId, editedMediaItem); .runAsync(testId, editedMediaItem);
RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get(); RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get();
feedRawAudioDataToAssetLoader(rawAssetLoader, rawAudioUri); feedRawAudioDataToAssetLoader(rawAssetLoader, AUDIO_FORMAT, mediaDurationUs);
ExportResult exportResult = exportCompletionFuture.get(); ExportResult exportResult = exportCompletionFuture.get();
// The durationMs is the timestamp of the last sample and not the total duration. // The durationMs is the timestamp of the last sample and not the total duration.
@ -120,9 +124,10 @@ public class RawAssetLoaderAndroidTest {
/* audioFormat= */ null, videoFormat, rawAssetLoaderFuture)) /* audioFormat= */ null, videoFormat, rawAssetLoaderFuture))
.setVideoFrameProcessorFactory(videoFrameProcessorFactory) .setVideoFrameProcessorFactory(videoFrameProcessorFactory)
.build(); .build();
long mediaDurationUs = C.MICROS_PER_SECOND;
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)) new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY))
.setDurationUs(C.MICROS_PER_SECOND) .setDurationUs(mediaDurationUs)
.build(); .build();
ListenableFuture<ExportResult> exportCompletionFuture = ListenableFuture<ExportResult> exportCompletionFuture =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
@ -132,7 +137,7 @@ public class RawAssetLoaderAndroidTest {
RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get(); RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get();
int firstTextureId = generateTextureFromBitmap(bitmap); int firstTextureId = generateTextureFromBitmap(bitmap);
int secondTextureId = generateTextureFromBitmap(bitmap); int secondTextureId = generateTextureFromBitmap(bitmap);
long lastSampleTimestampUs = C.MICROS_PER_SECOND / 2; long lastSampleTimestampUs = mediaDurationUs / 2;
while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {} while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {}
while (!rawAssetLoader.queueInputTexture(secondTextureId, lastSampleTimestampUs)) {} while (!rawAssetLoader.queueInputTexture(secondTextureId, lastSampleTimestampUs)) {}
rawAssetLoader.signalEndOfVideoInput(); rawAssetLoader.signalEndOfVideoInput();
@ -165,9 +170,10 @@ public class RawAssetLoaderAndroidTest {
.setVideoFrameProcessorFactory(videoFrameProcessorFactory) .setVideoFrameProcessorFactory(videoFrameProcessorFactory)
.build(); .build();
ImmutableList<Effect> videoEffects = ImmutableList.of(Presentation.createForHeight(480)); ImmutableList<Effect> videoEffects = ImmutableList.of(Presentation.createForHeight(480));
long mediaDurationUs = C.MICROS_PER_SECOND;
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)) new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY))
.setDurationUs(C.MICROS_PER_SECOND) .setDurationUs(mediaDurationUs)
.setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects)) .setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects))
.build(); .build();
ListenableFuture<ExportResult> exportCompletionFuture = ListenableFuture<ExportResult> exportCompletionFuture =
@ -178,7 +184,7 @@ public class RawAssetLoaderAndroidTest {
RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get(); RawAssetLoader rawAssetLoader = rawAssetLoaderFuture.get();
int firstTextureId = generateTextureFromBitmap(bitmap); int firstTextureId = generateTextureFromBitmap(bitmap);
int secondTextureId = generateTextureFromBitmap(bitmap); int secondTextureId = generateTextureFromBitmap(bitmap);
long lastSampleTimestampUs = C.MICROS_PER_SECOND / 2; long lastSampleTimestampUs = mediaDurationUs / 2;
while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {} while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {}
while (!rawAssetLoader.queueInputTexture(secondTextureId, lastSampleTimestampUs)) {} while (!rawAssetLoader.queueInputTexture(secondTextureId, lastSampleTimestampUs)) {}
rawAssetLoader.signalEndOfVideoInput(); rawAssetLoader.signalEndOfVideoInput();
@ -193,9 +199,6 @@ public class RawAssetLoaderAndroidTest {
@Test @Test
public void audioAndVideoTranscoding_withRawData_completesWithCorrectFrameCountAndDuration() public void audioAndVideoTranscoding_withRawData_completesWithCorrectFrameCountAndDuration()
throws Exception { throws Exception {
String rawAudioUri = "media/wav/sample.wav";
Format audioFormat =
retrieveTrackFormat(context, buildAssetUri(rawAudioUri).toString(), C.TRACK_TYPE_AUDIO);
Bitmap bitmap = Bitmap bitmap =
new DataSourceBitmapLoader(context).loadBitmap(Uri.parse(PNG_ASSET_URI_STRING)).get(); new DataSourceBitmapLoader(context).loadBitmap(Uri.parse(PNG_ASSET_URI_STRING)).get();
DefaultVideoFrameProcessor.Factory videoFrameProcessorFactory = DefaultVideoFrameProcessor.Factory videoFrameProcessorFactory =
@ -208,12 +211,13 @@ public class RawAssetLoaderAndroidTest {
Transformer transformer = Transformer transformer =
new Transformer.Builder(context) new Transformer.Builder(context)
.setAssetLoaderFactory( .setAssetLoaderFactory(
new TestRawAssetLoaderFactory(audioFormat, videoFormat, rawAssetLoaderFuture)) new TestRawAssetLoaderFactory(AUDIO_FORMAT, videoFormat, rawAssetLoaderFuture))
.setVideoFrameProcessorFactory(videoFrameProcessorFactory) .setVideoFrameProcessorFactory(videoFrameProcessorFactory)
.build(); .build();
long mediaDurationUs = C.MICROS_PER_SECOND;
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY)) new EditedMediaItem.Builder(MediaItem.fromUri(Uri.EMPTY))
.setDurationUs(C.MICROS_PER_SECOND) .setDurationUs(mediaDurationUs)
.build(); .build();
ListenableFuture<ExportResult> exportCompletionFuture = ListenableFuture<ExportResult> exportCompletionFuture =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
@ -226,19 +230,13 @@ public class RawAssetLoaderAndroidTest {
// Feed audio and video data in parallel so that export is not blocked waiting for all the // Feed audio and video data in parallel so that export is not blocked waiting for all the
// tracks. // tracks.
new Thread( new Thread(
() -> { () -> // Queue raw audio data.
// Queue raw audio data. feedRawAudioDataToAssetLoader(rawAssetLoader, AUDIO_FORMAT, mediaDurationUs))
try {
feedRawAudioDataToAssetLoader(rawAssetLoader, rawAudioUri);
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.start(); .start();
// Queue raw video data. // Queue raw video data.
while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {} while (!rawAssetLoader.queueInputTexture(firstTextureId, /* presentationTimeUs= */ 0)) {}
while (!rawAssetLoader.queueInputTexture( while (!rawAssetLoader.queueInputTexture(
secondTextureId, /* presentationTimeUs= */ C.MICROS_PER_SECOND / 2)) {} secondTextureId, /* presentationTimeUs= */ mediaDurationUs / 2)) {}
rawAssetLoader.signalEndOfVideoInput(); rawAssetLoader.signalEndOfVideoInput();
ExportResult exportResult = exportCompletionFuture.get(); ExportResult exportResult = exportCompletionFuture.get();
@ -251,32 +249,27 @@ public class RawAssetLoaderAndroidTest {
assertThat(exportResult.durationMs).isAtMost(1025); assertThat(exportResult.durationMs).isAtMost(1025);
} }
private void feedRawAudioDataToAssetLoader(RawAssetLoader rawAssetLoader, String audioAssetUri) private void feedRawAudioDataToAssetLoader(
throws IOException { RawAssetLoader rawAssetLoader, Format rawAudioFormat, long durationUs) {
// TODO: b/270695884 - Use media3 extractor to extract the samples. AudioFormat audioFormat = new AudioFormat(rawAudioFormat);
MediaExtractor extractor = new MediaExtractor(); SilentAudioGenerator silentAudioGenerator = new SilentAudioGenerator(audioFormat);
extractor.setDataSource(context.getResources().getAssets().openFd(audioAssetUri)); silentAudioGenerator.addSilence(durationUs);
int bytesWritten = 0;
// The audio only file should have only one track. while (silentAudioGenerator.hasRemaining()) {
MediaFormat audioFormat = extractor.getTrackFormat(0); ByteBuffer byteBuffer = silentAudioGenerator.getBuffer();
checkState(MimeTypes.isAudio(audioFormat.getString(MediaFormat.KEY_MIME))); int byteBufferSize = byteBuffer.remaining();
extractor.selectTrack(0); while (!rawAssetLoader.queueAudioData(
int maxSampleSize = 34_000; byteBuffer,
do { /* presentationTimeUs= */ Util.sampleCountToDurationUs(
long samplePresentationTimeUs = extractor.getSampleTime(); bytesWritten / audioFormat.bytesPerFrame, audioFormat.sampleRate),
ByteBuffer sampleBuffer = ByteBuffer.allocateDirect(maxSampleSize); /* isLast= */ false)) {}
if (extractor.readSampleData(sampleBuffer, /* offset= */ 0) == -1) { bytesWritten += byteBufferSize;
break; }
} while (!rawAssetLoader.queueAudioData(
while (true) { ByteBuffer.allocate(0),
if (rawAssetLoader.queueAudioData( /* presentationTimeUs= */ Util.sampleCountToDurationUs(
sampleBuffer, samplePresentationTimeUs, /* isLast= */ false)) { bytesWritten / audioFormat.bytesPerFrame, audioFormat.sampleRate),
break; /* isLast= */ true)) {}
}
}
} while (extractor.advance());
extractor.release();
checkState(rawAssetLoader.queueAudioData(ByteBuffer.allocate(0), 0, /* isLast= */ true));
} }
private static final class TestRawAssetLoaderFactory implements AssetLoader.Factory { private static final class TestRawAssetLoaderFactory implements AssetLoader.Factory {