mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add an option to force a silent audio track.
PiperOrigin-RevId: 492154544
This commit is contained in:
parent
c5dc5aeb11
commit
0993479efe
7 changed files with 744 additions and 5 deletions
|
|
@ -0,0 +1,467 @@
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
maxInputSize = 36722
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
frameRate = 29.970028
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
format 1:
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
channelCount = 2
|
||||||
|
sampleRate = 44100
|
||||||
|
pcmEncoding = 2
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 0
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 23220
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 46440
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 69660
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 92880
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 116100
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 139320
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 162540
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 185760
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 208980
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 232200
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 255420
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 278640
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 301860
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 325080
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 348300
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 371520
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 394740
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 417960
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 441180
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 464400
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 487620
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 510840
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -770308242
|
||||||
|
size = 36692
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 0
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -732087136
|
||||||
|
size = 5312
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 66733
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 534059
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 557279
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 580499
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 468156717
|
||||||
|
size = 599
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 33366
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1150349584
|
||||||
|
size = 7735
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 200200
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 603719
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 626939
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 650159
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 673379
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 696599
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 719819
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1443582006
|
||||||
|
size = 987
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 133466
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -310585145
|
||||||
|
size = 673
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 100100
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 807460688
|
||||||
|
size = 523
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 166833
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1936487090
|
||||||
|
size = 6061
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 333666
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 743039
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 766259
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 789479
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 812699
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 835919
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -32297181
|
||||||
|
size = 992
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 266933
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1529616406
|
||||||
|
size = 623
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 233566
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1949198785
|
||||||
|
size = 421
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 300300
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -147880287
|
||||||
|
size = 4899
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 433766
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 859139
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 882359
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 905579
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 928799
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 952019
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1369083472
|
||||||
|
size = 568
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 400400
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 965782073
|
||||||
|
size = 620
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 367033
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -261176150
|
||||||
|
size = 5450
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 567233
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 975239
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = 1742602241
|
||||||
|
size = 4096
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 998459
|
||||||
|
sample:
|
||||||
|
trackIndex = 1
|
||||||
|
dataHashCode = -1029274849
|
||||||
|
size = 409
|
||||||
|
isKeyFrame = true
|
||||||
|
presentationTimeUs = 1021679
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1830836678
|
||||||
|
size = 1051
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 500500
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1767407540
|
||||||
|
size = 874
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 467133
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 918440283
|
||||||
|
size = 781
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 533866
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1408463661
|
||||||
|
size = 4725
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 700700
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1569455924
|
||||||
|
size = 1022
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 633966
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1723778407
|
||||||
|
size = 790
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 600600
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1578275472
|
||||||
|
size = 610
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 667333
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1989768395
|
||||||
|
size = 2751
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 834166
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1215674502
|
||||||
|
size = 745
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 767433
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -814473606
|
||||||
|
size = 621
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 734066
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 498370894
|
||||||
|
size = 505
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 800800
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1051506468
|
||||||
|
size = 1268
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 967633
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -1025604144
|
||||||
|
size = 880
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 900900
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = -913586520
|
||||||
|
size = 530
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 867533
|
||||||
|
sample:
|
||||||
|
trackIndex = 0
|
||||||
|
dataHashCode = 1340459242
|
||||||
|
size = 568
|
||||||
|
isKeyFrame = false
|
||||||
|
presentationTimeUs = 934266
|
||||||
|
released = true
|
||||||
|
|
@ -41,6 +41,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
private static final int DEFAULT_ENCODER_BITRATE = 128 * 1024;
|
private static final int DEFAULT_ENCODER_BITRATE = 128 * 1024;
|
||||||
|
|
||||||
|
@Nullable private final SilentAudioGenerator silentAudioGenerator;
|
||||||
private final DecoderInputBuffer inputBuffer;
|
private final DecoderInputBuffer inputBuffer;
|
||||||
private final AudioProcessingPipeline audioProcessingPipeline;
|
private final AudioProcessingPipeline audioProcessingPipeline;
|
||||||
private final Codec encoder;
|
private final Codec encoder;
|
||||||
|
|
@ -52,12 +53,14 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
private long nextEncoderInputBufferTimeUs;
|
private long nextEncoderInputBufferTimeUs;
|
||||||
private long encoderBufferDurationRemainder;
|
private long encoderBufferDurationRemainder;
|
||||||
|
|
||||||
|
// TODO(b/260618558): Move silent audio generation upstream of this component.
|
||||||
public AudioTranscodingSamplePipeline(
|
public AudioTranscodingSamplePipeline(
|
||||||
Format inputFormat,
|
Format inputFormat,
|
||||||
long streamStartPositionUs,
|
long streamStartPositionUs,
|
||||||
long streamOffsetUs,
|
long streamOffsetUs,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
ImmutableList<AudioProcessor> audioProcessors,
|
ImmutableList<AudioProcessor> audioProcessors,
|
||||||
|
long forceSilentAudioDurationUs,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
MuxerWrapper muxerWrapper,
|
MuxerWrapper muxerWrapper,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
|
|
@ -71,6 +74,16 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
muxerWrapper,
|
muxerWrapper,
|
||||||
listener);
|
listener);
|
||||||
|
|
||||||
|
if (forceSilentAudioDurationUs != C.TIME_UNSET) {
|
||||||
|
silentAudioGenerator =
|
||||||
|
new SilentAudioGenerator(
|
||||||
|
forceSilentAudioDurationUs,
|
||||||
|
inputFormat.sampleRate,
|
||||||
|
Util.getPcmFrameSize(C.ENCODING_PCM_16BIT, inputFormat.channelCount));
|
||||||
|
} else {
|
||||||
|
silentAudioGenerator = null;
|
||||||
|
}
|
||||||
|
|
||||||
inputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
inputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
encoderInputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
encoderInputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
encoderOutputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
encoderOutputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
|
|
@ -160,11 +173,17 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean processDataUpToMuxer() throws TransformationException {
|
protected boolean processDataUpToMuxer() throws TransformationException {
|
||||||
if (audioProcessingPipeline.isOperational()) {
|
if (!audioProcessingPipeline.isOperational()) {
|
||||||
return feedEncoderFromProcessingPipeline() || feedProcessingPipelineFromInput();
|
return silentAudioGenerator == null ? feedEncoderFromInput() : feedEncoderFromSilence();
|
||||||
} else {
|
|
||||||
return feedEncoderFromInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (feedEncoderFromProcessingPipeline()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return silentAudioGenerator == null
|
||||||
|
? feedProcessingPipelineFromInput()
|
||||||
|
: feedProcessingPipelineFromSilence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -268,6 +287,45 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to pass silent audio to the encoder.
|
||||||
|
*
|
||||||
|
* @return Whether it may be possible to feed more data immediately by calling this method again.
|
||||||
|
*/
|
||||||
|
private boolean feedEncoderFromSilence() throws TransformationException {
|
||||||
|
checkNotNull(silentAudioGenerator);
|
||||||
|
if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (silentAudioGenerator.isEnded()) {
|
||||||
|
queueEndOfStreamToEncoder();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer silence = silentAudioGenerator.getBuffer();
|
||||||
|
feedEncoder(silence);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to feed silent audio to the {@link AudioProcessingPipeline}.
|
||||||
|
*
|
||||||
|
* @return Whether it may be possible to feed more data immediately by calling this method again.
|
||||||
|
*/
|
||||||
|
private boolean feedProcessingPipelineFromSilence() {
|
||||||
|
checkNotNull(silentAudioGenerator);
|
||||||
|
if (silentAudioGenerator.isEnded()) {
|
||||||
|
audioProcessingPipeline.queueEndOfStream();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
checkState(!audioProcessingPipeline.isEnded());
|
||||||
|
|
||||||
|
ByteBuffer silence = silentAudioGenerator.getBuffer();
|
||||||
|
audioProcessingPipeline.queueInput(silence);
|
||||||
|
return !silence.hasRemaining();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feeds as much data as possible between the current position and limit of the specified {@link
|
* Feeds as much data as possible between the current position and limit of the specified {@link
|
||||||
* ByteBuffer} to the encoder, and advances its position by the number of bytes fed.
|
* ByteBuffer} to the encoder, and advances its position by the number of bytes fed.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
/* package */ final class SilentAudioGenerator {
|
||||||
|
private static final int DEFAULT_BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
private final ByteBuffer internalBuffer;
|
||||||
|
|
||||||
|
private long remainingBytesToOutput;
|
||||||
|
|
||||||
|
public SilentAudioGenerator(long totalDurationUs, long sampleRate, int frameSize) {
|
||||||
|
remainingBytesToOutput = (sampleRate * frameSize * totalDurationUs) / 1_000_000L;
|
||||||
|
internalBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE).order(ByteOrder.nativeOrder());
|
||||||
|
internalBuffer.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer getBuffer() {
|
||||||
|
if (!internalBuffer.hasRemaining()) {
|
||||||
|
// "next" buffer.
|
||||||
|
internalBuffer.clear();
|
||||||
|
if (remainingBytesToOutput < internalBuffer.capacity()) {
|
||||||
|
internalBuffer.limit((int) remainingBytesToOutput);
|
||||||
|
}
|
||||||
|
// Only reduce remaining bytes when we "generate" a new one.
|
||||||
|
remainingBytesToOutput -= internalBuffer.remaining();
|
||||||
|
}
|
||||||
|
return internalBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnded() {
|
||||||
|
return !internalBuffer.hasRemaining() && remainingBytesToOutput == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.effect.GlEffect;
|
import androidx.media3.effect.GlEffect;
|
||||||
import androidx.media3.effect.GlEffectsFrameProcessor;
|
import androidx.media3.effect.GlEffectsFrameProcessor;
|
||||||
import androidx.media3.effect.GlMatrixTransformation;
|
import androidx.media3.effect.GlMatrixTransformation;
|
||||||
|
import androidx.media3.exoplayer.audio.SonicAudioProcessor;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||||
|
|
@ -88,6 +89,7 @@ public final class Transformer {
|
||||||
private ImmutableList<Effect> videoEffects;
|
private ImmutableList<Effect> videoEffects;
|
||||||
private boolean removeAudio;
|
private boolean removeAudio;
|
||||||
private boolean removeVideo;
|
private boolean removeVideo;
|
||||||
|
private boolean forceSilentAudio;
|
||||||
private ListenerSet<Transformer.Listener> listeners;
|
private ListenerSet<Transformer.Listener> listeners;
|
||||||
private MediaSource.@MonotonicNonNull Factory mediaSourceFactory;
|
private MediaSource.@MonotonicNonNull Factory mediaSourceFactory;
|
||||||
private Codec.DecoderFactory decoderFactory;
|
private Codec.DecoderFactory decoderFactory;
|
||||||
|
|
@ -126,6 +128,7 @@ public final class Transformer {
|
||||||
this.videoEffects = transformer.videoEffects;
|
this.videoEffects = transformer.videoEffects;
|
||||||
this.removeAudio = transformer.removeAudio;
|
this.removeAudio = transformer.removeAudio;
|
||||||
this.removeVideo = transformer.removeVideo;
|
this.removeVideo = transformer.removeVideo;
|
||||||
|
this.forceSilentAudio = transformer.forceSilentAudio;
|
||||||
this.listeners = transformer.listeners;
|
this.listeners = transformer.listeners;
|
||||||
this.mediaSourceFactory = transformer.mediaSourceFactory;
|
this.mediaSourceFactory = transformer.mediaSourceFactory;
|
||||||
this.decoderFactory = transformer.decoderFactory;
|
this.decoderFactory = transformer.decoderFactory;
|
||||||
|
|
@ -416,6 +419,33 @@ public final class Transformer {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to force silent audio for the output file, ignoring any existing audio.
|
||||||
|
*
|
||||||
|
* <p>This method is experimental and may be removed or changed without warning.
|
||||||
|
*
|
||||||
|
* <p>Audio properties/format:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Duration will match duration of the input media.
|
||||||
|
* <li>Sample mime type will match {@link TransformationRequest#audioMimeType}, or {@link
|
||||||
|
* MimeTypes#AUDIO_AAC} if {@code null}.
|
||||||
|
* <li>Sample rate will be 44100hz. This can be modified by passing a {@link
|
||||||
|
* SonicAudioProcessor} to {@link #setAudioProcessors(List)}, using {@link
|
||||||
|
* SonicAudioProcessor#setOutputSampleRateHz(int)}.
|
||||||
|
* <li>Channel count will be 2. This can be modified by implementing a custom {@link
|
||||||
|
* AudioProcessor} and passing it to {@link #setAudioProcessors(List)}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param forceSilentAudio Whether to output silent audio for the output file.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder experimentalSetForceSilentAudio(boolean forceSilentAudio) {
|
||||||
|
this.forceSilentAudio = forceSilentAudio;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@link Transformer} instance.
|
* Builds a {@link Transformer} instance.
|
||||||
*
|
*
|
||||||
|
|
@ -445,6 +475,7 @@ public final class Transformer {
|
||||||
videoEffects,
|
videoEffects,
|
||||||
removeAudio,
|
removeAudio,
|
||||||
removeVideo,
|
removeVideo,
|
||||||
|
forceSilentAudio,
|
||||||
listeners,
|
listeners,
|
||||||
mediaSourceFactory,
|
mediaSourceFactory,
|
||||||
decoderFactory,
|
decoderFactory,
|
||||||
|
|
@ -557,6 +588,7 @@ public final class Transformer {
|
||||||
private final ImmutableList<Effect> videoEffects;
|
private final ImmutableList<Effect> videoEffects;
|
||||||
private final boolean removeAudio;
|
private final boolean removeAudio;
|
||||||
private final boolean removeVideo;
|
private final boolean removeVideo;
|
||||||
|
private final boolean forceSilentAudio;
|
||||||
private final ListenerSet<Transformer.Listener> listeners;
|
private final ListenerSet<Transformer.Listener> listeners;
|
||||||
private final MediaSource.Factory mediaSourceFactory;
|
private final MediaSource.Factory mediaSourceFactory;
|
||||||
private final FrameProcessor.Factory frameProcessorFactory;
|
private final FrameProcessor.Factory frameProcessorFactory;
|
||||||
|
|
@ -574,7 +606,8 @@ public final class Transformer {
|
||||||
ImmutableList<Effect> videoEffects,
|
ImmutableList<Effect> videoEffects,
|
||||||
boolean removeAudio,
|
boolean removeAudio,
|
||||||
boolean removeVideo,
|
boolean removeVideo,
|
||||||
ListenerSet<Transformer.Listener> listeners,
|
boolean forceSilentAudio,
|
||||||
|
ListenerSet<Listener> listeners,
|
||||||
MediaSource.Factory mediaSourceFactory,
|
MediaSource.Factory mediaSourceFactory,
|
||||||
Codec.DecoderFactory decoderFactory,
|
Codec.DecoderFactory decoderFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
|
|
@ -583,6 +616,10 @@ public final class Transformer {
|
||||||
Looper looper,
|
Looper looper,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Clock clock) {
|
Clock clock) {
|
||||||
|
if (forceSilentAudio) {
|
||||||
|
removeAudio = true;
|
||||||
|
}
|
||||||
|
checkState(!removeVideo || !forceSilentAudio, "Silent only audio track needs a video track.");
|
||||||
checkState(!removeAudio || !removeVideo, "Audio and video cannot both be removed.");
|
checkState(!removeAudio || !removeVideo, "Audio and video cannot both be removed.");
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.transformationRequest = transformationRequest;
|
this.transformationRequest = transformationRequest;
|
||||||
|
|
@ -590,6 +627,7 @@ public final class Transformer {
|
||||||
this.videoEffects = videoEffects;
|
this.videoEffects = videoEffects;
|
||||||
this.removeAudio = removeAudio;
|
this.removeAudio = removeAudio;
|
||||||
this.removeVideo = removeVideo;
|
this.removeVideo = removeVideo;
|
||||||
|
this.forceSilentAudio = forceSilentAudio;
|
||||||
this.listeners = listeners;
|
this.listeners = listeners;
|
||||||
this.mediaSourceFactory = mediaSourceFactory;
|
this.mediaSourceFactory = mediaSourceFactory;
|
||||||
this.decoderFactory = decoderFactory;
|
this.decoderFactory = decoderFactory;
|
||||||
|
|
@ -728,6 +766,7 @@ public final class Transformer {
|
||||||
videoEffects,
|
videoEffects,
|
||||||
removeAudio,
|
removeAudio,
|
||||||
removeVideo,
|
removeVideo,
|
||||||
|
forceSilentAudio,
|
||||||
mediaSourceFactory,
|
mediaSourceFactory,
|
||||||
decoderFactory,
|
decoderFactory,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
private final TransformationRequest transformationRequest;
|
private final TransformationRequest transformationRequest;
|
||||||
private final ImmutableList<AudioProcessor> audioProcessors;
|
private final ImmutableList<AudioProcessor> audioProcessors;
|
||||||
private final ImmutableList<Effect> videoEffects;
|
private final ImmutableList<Effect> videoEffects;
|
||||||
|
private final boolean forceSilentAudio;
|
||||||
private final Codec.DecoderFactory decoderFactory;
|
private final Codec.DecoderFactory decoderFactory;
|
||||||
private final Codec.EncoderFactory encoderFactory;
|
private final Codec.EncoderFactory encoderFactory;
|
||||||
private final FrameProcessor.Factory frameProcessorFactory;
|
private final FrameProcessor.Factory frameProcessorFactory;
|
||||||
|
|
@ -114,6 +115,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
@Nullable private DecoderInputBuffer pendingInputBuffer;
|
@Nullable private DecoderInputBuffer pendingInputBuffer;
|
||||||
private boolean isDrainingPipelines;
|
private boolean isDrainingPipelines;
|
||||||
|
private int silentSamplePipelineIndex;
|
||||||
private @Transformer.ProgressState int progressState;
|
private @Transformer.ProgressState int progressState;
|
||||||
private long progressPositionMs;
|
private long progressPositionMs;
|
||||||
private long durationUs;
|
private long durationUs;
|
||||||
|
|
@ -131,6 +133,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
ImmutableList<Effect> videoEffects,
|
ImmutableList<Effect> videoEffects,
|
||||||
boolean removeAudio,
|
boolean removeAudio,
|
||||||
boolean removeVideo,
|
boolean removeVideo,
|
||||||
|
boolean forceSilentAudio,
|
||||||
MediaSource.Factory mediaSourceFactory,
|
MediaSource.Factory mediaSourceFactory,
|
||||||
Codec.DecoderFactory decoderFactory,
|
Codec.DecoderFactory decoderFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
|
|
@ -145,6 +148,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
this.transformationRequest = transformationRequest;
|
this.transformationRequest = transformationRequest;
|
||||||
this.audioProcessors = audioProcessors;
|
this.audioProcessors = audioProcessors;
|
||||||
this.videoEffects = videoEffects;
|
this.videoEffects = videoEffects;
|
||||||
|
this.forceSilentAudio = forceSilentAudio;
|
||||||
this.decoderFactory = decoderFactory;
|
this.decoderFactory = decoderFactory;
|
||||||
this.encoderFactory = encoderFactory;
|
this.encoderFactory = encoderFactory;
|
||||||
this.frameProcessorFactory = frameProcessorFactory;
|
this.frameProcessorFactory = frameProcessorFactory;
|
||||||
|
|
@ -168,6 +172,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
componentListener,
|
componentListener,
|
||||||
clock);
|
clock);
|
||||||
samplePipelines = new ArrayList<>();
|
samplePipelines = new ArrayList<>();
|
||||||
|
silentSamplePipelineIndex = C.INDEX_UNSET;
|
||||||
dequeueBufferConditionVariable = new ConditionVariable();
|
dequeueBufferConditionVariable = new ConditionVariable();
|
||||||
muxerWrapper =
|
muxerWrapper =
|
||||||
new MuxerWrapper(
|
new MuxerWrapper(
|
||||||
|
|
@ -258,6 +263,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
while (samplePipeline.processData()) {}
|
while (samplePipeline.processData()) {}
|
||||||
pendingInputBuffer = samplePipeline.dequeueInputBuffer();
|
pendingInputBuffer = samplePipeline.dequeueInputBuffer();
|
||||||
dequeueBufferConditionVariable.open();
|
dequeueBufferConditionVariable.open();
|
||||||
|
|
||||||
|
if (forceSilentAudio) {
|
||||||
|
while (samplePipelines.get(silentSamplePipelineIndex).processData()) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueInputInternal(int samplePipelineIndex) throws TransformationException {
|
private void queueInputInternal(int samplePipelineIndex) throws TransformationException {
|
||||||
|
|
@ -391,6 +400,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
trackRegistered = true;
|
trackRegistered = true;
|
||||||
muxerWrapper.registerTrack();
|
muxerWrapper.registerTrack();
|
||||||
fallbackListener.registerTrack();
|
fallbackListener.registerTrack();
|
||||||
|
|
||||||
|
if (forceSilentAudio) {
|
||||||
|
muxerWrapper.registerTrack();
|
||||||
|
fallbackListener.registerTrack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -410,6 +424,23 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
int samplePipelineIndex = tracksAddedCount;
|
int samplePipelineIndex = tracksAddedCount;
|
||||||
tracksAddedCount++;
|
tracksAddedCount++;
|
||||||
|
|
||||||
|
if (forceSilentAudio) {
|
||||||
|
Format silentAudioFormat =
|
||||||
|
new Format.Builder()
|
||||||
|
.setSampleMimeType(MimeTypes.AUDIO_AAC)
|
||||||
|
.setSampleRate(44100)
|
||||||
|
.setChannelCount(2)
|
||||||
|
.build();
|
||||||
|
SamplePipeline audioSamplePipeline =
|
||||||
|
getSamplePipeline(silentAudioFormat, streamStartPositionUs, streamOffsetUs);
|
||||||
|
internalHandler
|
||||||
|
.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, audioSamplePipeline)
|
||||||
|
.sendToTarget();
|
||||||
|
silentSamplePipelineIndex = tracksAddedCount;
|
||||||
|
tracksAddedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
return new SamplePipelineInput(samplePipelineIndex, samplePipeline.expectsDecodedData());
|
return new SamplePipelineInput(samplePipelineIndex, samplePipeline.expectsDecodedData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,6 +490,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
streamOffsetUs,
|
streamOffsetUs,
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
audioProcessors,
|
audioProcessors,
|
||||||
|
forceSilentAudio ? durationUs : C.TIME_UNSET,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
muxerWrapper,
|
muxerWrapper,
|
||||||
/* listener= */ this,
|
/* listener= */ this,
|
||||||
|
|
@ -509,6 +541,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
if (!audioProcessors.isEmpty()) {
|
if (!audioProcessors.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (forceSilentAudio) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit tests for {@link SilentAudioGenerator}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SilentAudioGeneratorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void numberOfBytesProduced_isCorrect() {
|
||||||
|
SilentAudioGenerator generator =
|
||||||
|
new SilentAudioGenerator(
|
||||||
|
/* totalDurationUs= */ 3_000_000, /* sampleRate= */ 88_200, /* frameSize= */ 12);
|
||||||
|
int bytesOutput = 0;
|
||||||
|
while (!generator.isEnded()) {
|
||||||
|
ByteBuffer output = generator.getBuffer();
|
||||||
|
bytesOutput += output.remaining();
|
||||||
|
// "Consume" buffer.
|
||||||
|
output.position(output.limit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 88_200 * 12 * 3s = 3175200
|
||||||
|
assertThat(bytesOutput).isEqualTo(3_175_200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lastBufferProduced_isCorrectSize() {
|
||||||
|
SilentAudioGenerator generator =
|
||||||
|
new SilentAudioGenerator(
|
||||||
|
/* totalDurationUs= */ 1_000_000, /* sampleRate= */ 44_100, /* frameSize= */ 4);
|
||||||
|
|
||||||
|
int currentBufferSize = 0;
|
||||||
|
while (!generator.isEnded()) {
|
||||||
|
ByteBuffer output = generator.getBuffer();
|
||||||
|
currentBufferSize = output.remaining();
|
||||||
|
// "Consume" buffer.
|
||||||
|
output.position(output.limit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last buffer is smaller and only outputs the 'leftover' bytes.
|
||||||
|
// (44_100 * 4) % 4096 = 272
|
||||||
|
assertThat(currentBufferSize).isEqualTo(272);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void totalBytesLowerThanDefaultBufferSize_smallBufferProduced() {
|
||||||
|
SilentAudioGenerator generator =
|
||||||
|
new SilentAudioGenerator(
|
||||||
|
/* totalDurationUs= */ 5_000, /* sampleRate= */ 48_000, /* frameSize= */ 4);
|
||||||
|
// 5_000 * 48_000 * 4 / 1_000_000 = 960
|
||||||
|
assertThat(generator.getBuffer().remaining()).isEqualTo(960);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -265,6 +265,21 @@ public final class TransformerEndToEndTest {
|
||||||
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
|
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startTransformation_silentAudio_completesSuccessfully() throws Exception {
|
||||||
|
Transformer transformer =
|
||||||
|
createTransformerBuilder(/* enableFallback= */ false)
|
||||||
|
.experimentalSetForceSilentAudio(true)
|
||||||
|
.build();
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||||
|
|
||||||
|
transformer.startTransformation(mediaItem, outputPath);
|
||||||
|
TransformerTestRunner.runUntilCompleted(transformer);
|
||||||
|
|
||||||
|
DumpFileAsserts.assertOutput(
|
||||||
|
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".silentaudio"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception {
|
public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception {
|
||||||
Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
|
Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue