Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
e4b0c207
authored
Apr 08, 2022
by
olly
Committed by
Ian Baker
Apr 26, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Expect PresentationTime Discontinuity During Stream Transitions
PiperOrigin-RevId: 440378974
parent
1fbaa5c0
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
131 additions
and
3 deletions
library/common/src/main/java/com/google/android/exoplayer2/C.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java
library/decoder/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java
library/decoder/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java
testdata/src/test/assets/audiosinkdumps/mka/bear-flac-16bit.mka.audiosink.dump
testdata/src/test/assets/audiosinkdumps/mka/bear-flac-24bit.mka.audiosink.dump
library/common/src/main/java/com/google/android/exoplayer2/C.java
View file @
e4b0c207
...
@@ -580,8 +580,9 @@ public final class C {
...
@@ -580,8 +580,9 @@ public final class C {
/**
/**
* Flags which can apply to a buffer containing a media sample. Possible flag values are {@link
* Flags which can apply to a buffer containing a media sample. Possible flag values are {@link
* #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_LAST_SAMPLE},
* #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_FIRST_SAMPLE},
* {@link #BUFFER_FLAG_ENCRYPTED} and {@link #BUFFER_FLAG_DECODE_ONLY}.
* {@link #BUFFER_FLAG_LAST_SAMPLE}, {@link #BUFFER_FLAG_ENCRYPTED} and {@link
* #BUFFER_FLAG_DECODE_ONLY}.
*/
*/
@Documented
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
...
@@ -591,6 +592,7 @@ public final class C {
...
@@ -591,6 +592,7 @@ public final class C {
value
=
{
value
=
{
BUFFER_FLAG_KEY_FRAME
,
BUFFER_FLAG_KEY_FRAME
,
BUFFER_FLAG_END_OF_STREAM
,
BUFFER_FLAG_END_OF_STREAM
,
BUFFER_FLAG_FIRST_SAMPLE
,
BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA
,
BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA
,
BUFFER_FLAG_LAST_SAMPLE
,
BUFFER_FLAG_LAST_SAMPLE
,
BUFFER_FLAG_ENCRYPTED
,
BUFFER_FLAG_ENCRYPTED
,
...
@@ -601,6 +603,8 @@ public final class C {
...
@@ -601,6 +603,8 @@ public final class C {
public
static
final
int
BUFFER_FLAG_KEY_FRAME
=
MediaCodec
.
BUFFER_FLAG_KEY_FRAME
;
public
static
final
int
BUFFER_FLAG_KEY_FRAME
=
MediaCodec
.
BUFFER_FLAG_KEY_FRAME
;
/** Flag for empty buffers that signal that the end of the stream was reached. */
/** Flag for empty buffers that signal that the end of the stream was reached. */
public
static
final
int
BUFFER_FLAG_END_OF_STREAM
=
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
;
public
static
final
int
BUFFER_FLAG_END_OF_STREAM
=
MediaCodec
.
BUFFER_FLAG_END_OF_STREAM
;
/** Indicates that a buffer is known to contain the first media sample of the stream. */
public
static
final
int
BUFFER_FLAG_FIRST_SAMPLE
=
1
<<
27
;
// 0x08000000
/** Indicates that a buffer has supplemental data. */
/** Indicates that a buffer has supplemental data. */
public
static
final
int
BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA
=
1
<<
28
;
// 0x10000000
public
static
final
int
BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA
=
1
<<
28
;
// 0x10000000
/** Indicates that a buffer is known to contain the last media sample of the stream. */
/** Indicates that a buffer is known to contain the last media sample of the stream. */
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java
View file @
e4b0c207
...
@@ -126,6 +126,7 @@ public abstract class DecoderAudioRenderer<
...
@@ -126,6 +126,7 @@ public abstract class DecoderAudioRenderer<
private
int
encoderPadding
;
private
int
encoderPadding
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
private
boolean
experimentalKeepAudioTrackOnSeek
;
private
boolean
firstStreamSampleRead
;
@Nullable
private
T
decoder
;
@Nullable
private
T
decoder
;
...
@@ -385,6 +386,9 @@ public abstract class DecoderAudioRenderer<
...
@@ -385,6 +386,9 @@ public abstract class DecoderAudioRenderer<
decoderCounters
.
skippedOutputBufferCount
+=
outputBuffer
.
skippedOutputBufferCount
;
decoderCounters
.
skippedOutputBufferCount
+=
outputBuffer
.
skippedOutputBufferCount
;
audioSink
.
handleDiscontinuity
();
audioSink
.
handleDiscontinuity
();
}
}
if
(
outputBuffer
.
isFirstSample
())
{
audioSink
.
handleDiscontinuity
();
}
}
}
if
(
outputBuffer
.
isEndOfStream
())
{
if
(
outputBuffer
.
isEndOfStream
())
{
...
@@ -466,6 +470,10 @@ public abstract class DecoderAudioRenderer<
...
@@ -466,6 +470,10 @@ public abstract class DecoderAudioRenderer<
inputBuffer
=
null
;
inputBuffer
=
null
;
return
false
;
return
false
;
}
}
if
(!
firstStreamSampleRead
)
{
firstStreamSampleRead
=
true
;
inputBuffer
.
addFlag
(
C
.
BUFFER_FLAG_FIRST_SAMPLE
);
}
inputBuffer
.
flip
();
inputBuffer
.
flip
();
inputBuffer
.
format
=
inputFormat
;
inputBuffer
.
format
=
inputFormat
;
onQueueInputBuffer
(
inputBuffer
);
onQueueInputBuffer
(
inputBuffer
);
...
@@ -584,6 +592,13 @@ public abstract class DecoderAudioRenderer<
...
@@ -584,6 +592,13 @@ public abstract class DecoderAudioRenderer<
}
}
@Override
@Override
protected
void
onStreamChanged
(
Format
[]
formats
,
long
startPositionUs
,
long
offsetUs
)
throws
ExoPlaybackException
{
super
.
onStreamChanged
(
formats
,
startPositionUs
,
offsetUs
);
firstStreamSampleRead
=
false
;
}
@Override
public
void
handleMessage
(
@MessageType
int
messageType
,
@Nullable
Object
message
)
public
void
handleMessage
(
@MessageType
int
messageType
,
@Nullable
Object
message
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
switch
(
messageType
)
{
switch
(
messageType
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
e4b0c207
...
@@ -878,7 +878,6 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -878,7 +878,6 @@ public final class DefaultAudioSink implements AudioSink {
@Override
@Override
public
void
handleDiscontinuity
()
{
public
void
handleDiscontinuity
()
{
// Force resynchronization after a skipped buffer.
startMediaTimeUsNeedsSync
=
true
;
startMediaTimeUsNeedsSync
=
true
;
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java
View file @
e4b0c207
...
@@ -21,7 +21,12 @@ import static com.google.android.exoplayer2.RendererCapabilities.DECODER_SUPPORT
...
@@ -21,7 +21,12 @@ import static com.google.android.exoplayer2.RendererCapabilities.DECODER_SUPPORT
import
static
com
.
google
.
android
.
exoplayer2
.
RendererCapabilities
.
TUNNELING_NOT_SUPPORTED
;
import
static
com
.
google
.
android
.
exoplayer2
.
RendererCapabilities
.
TUNNELING_NOT_SUPPORTED
;
import
static
com
.
google
.
android
.
exoplayer2
.
RendererCapabilities
.
TUNNELING_SUPPORTED
;
import
static
com
.
google
.
android
.
exoplayer2
.
RendererCapabilities
.
TUNNELING_SUPPORTED
;
import
static
com
.
google
.
android
.
exoplayer2
.
testutil
.
FakeSampleStream
.
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
;
import
static
com
.
google
.
android
.
exoplayer2
.
testutil
.
FakeSampleStream
.
FakeSampleStreamItem
.
END_OF_STREAM_ITEM
;
import
static
com
.
google
.
android
.
exoplayer2
.
testutil
.
FakeSampleStream
.
FakeSampleStreamItem
.
oneByteSample
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyInt
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyLong
;
import
static
org
.
mockito
.
Mockito
.
inOrder
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
static
org
.
mockito
.
Mockito
.
when
;
...
@@ -46,6 +51,7 @@ import com.google.common.collect.ImmutableList;
...
@@ -46,6 +51,7 @@ import com.google.common.collect.ImmutableList;
import
org.junit.Before
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
import
org.mockito.InOrder
;
import
org.mockito.Mock
;
import
org.mockito.Mock
;
import
org.mockito.MockitoAnnotations
;
import
org.mockito.MockitoAnnotations
;
import
org.robolectric.annotation.Config
;
import
org.robolectric.annotation.Config
;
...
@@ -139,6 +145,100 @@ public class DecoderAudioRendererTest {
...
@@ -139,6 +145,100 @@ public class DecoderAudioRendererTest {
verify
(
mockAudioSink
,
times
(
1
)).
reset
();
verify
(
mockAudioSink
,
times
(
1
)).
reset
();
}
}
@Test
public
void
firstSampleOfStreamSignalsDiscontinuityToAudioSink
()
throws
Exception
{
when
(
mockAudioSink
.
handleBuffer
(
any
(),
anyLong
(),
anyInt
())).
thenReturn
(
true
);
when
(
mockAudioSink
.
isEnded
()).
thenReturn
(
true
);
InOrder
inOrderAudioSink
=
inOrder
(
mockAudioSink
);
FakeSampleStream
fakeSampleStream
=
new
FakeSampleStream
(
new
DefaultAllocator
(
/* trimOnReset= */
true
,
/* individualAllocationSize= */
1024
),
/* mediaSourceEventDispatcher= */
null
,
DrmSessionManager
.
DRM_UNSUPPORTED
,
new
DrmSessionEventListener
.
EventDispatcher
(),
FORMAT
,
ImmutableList
.
of
(
oneByteSample
(
/* timeUs= */
0
,
C
.
BUFFER_FLAG_KEY_FRAME
),
oneByteSample
(
/* timeUs= */
1_000
),
END_OF_STREAM_ITEM
));
fakeSampleStream
.
writeData
(
/* startPositionUs= */
0
);
audioRenderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
FORMAT
},
fakeSampleStream
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
true
,
/* startPositionUs= */
0
,
/* offsetUs= */
0
);
audioRenderer
.
setCurrentStreamFinal
();
while
(!
audioRenderer
.
isEnded
())
{
audioRenderer
.
render
(
/* positionUs= */
0
,
/* elapsedRealtimeUs= */
0
);
}
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
1
)).
handleDiscontinuity
();
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
2
)).
handleBuffer
(
any
(),
anyLong
(),
anyInt
());
}
@Test
public
void
firstSampleOfReplacementStreamSignalsDiscontinuityToAudioSink
()
throws
Exception
{
when
(
mockAudioSink
.
handleBuffer
(
any
(),
anyLong
(),
anyInt
())).
thenReturn
(
true
);
when
(
mockAudioSink
.
isEnded
()).
thenReturn
(
true
);
InOrder
inOrderAudioSink
=
inOrder
(
mockAudioSink
);
FakeSampleStream
fakeSampleStream1
=
new
FakeSampleStream
(
new
DefaultAllocator
(
/* trimOnReset= */
true
,
/* individualAllocationSize= */
1024
),
/* mediaSourceEventDispatcher= */
null
,
DrmSessionManager
.
DRM_UNSUPPORTED
,
new
DrmSessionEventListener
.
EventDispatcher
(),
FORMAT
,
ImmutableList
.
of
(
oneByteSample
(
/* timeUs= */
0
,
C
.
BUFFER_FLAG_KEY_FRAME
),
oneByteSample
(
/* timeUs= */
1_000
),
END_OF_STREAM_ITEM
));
fakeSampleStream1
.
writeData
(
/* startPositionUs= */
0
);
FakeSampleStream
fakeSampleStream2
=
new
FakeSampleStream
(
new
DefaultAllocator
(
/* trimOnReset= */
true
,
/* individualAllocationSize= */
1024
),
/* mediaSourceEventDispatcher= */
null
,
DrmSessionManager
.
DRM_UNSUPPORTED
,
new
DrmSessionEventListener
.
EventDispatcher
(),
FORMAT
,
ImmutableList
.
of
(
oneByteSample
(
/* timeUs= */
1_000_000
,
C
.
BUFFER_FLAG_KEY_FRAME
),
oneByteSample
(
/* timeUs= */
1_001_000
),
END_OF_STREAM_ITEM
));
fakeSampleStream2
.
writeData
(
/* startPositionUs= */
0
);
audioRenderer
.
enable
(
RendererConfiguration
.
DEFAULT
,
new
Format
[]
{
FORMAT
},
fakeSampleStream1
,
/* positionUs= */
0
,
/* joining= */
false
,
/* mayRenderStartOfStream= */
true
,
/* startPositionUs= */
0
,
/* offsetUs= */
0
);
while
(!
audioRenderer
.
hasReadStreamToEnd
())
{
audioRenderer
.
render
(
/* positionUs= */
0
,
/* elapsedRealtimeUs= */
0
);
}
audioRenderer
.
replaceStream
(
new
Format
[]
{
FORMAT
},
fakeSampleStream2
,
/* startPositionUs= */
1_000_000
,
/* offsetUs= */
1_000_000
);
audioRenderer
.
setCurrentStreamFinal
();
while
(!
audioRenderer
.
isEnded
())
{
audioRenderer
.
render
(
/* positionUs= */
0
,
/* elapsedRealtimeUs= */
0
);
}
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
1
)).
handleDiscontinuity
();
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
2
)).
handleBuffer
(
any
(),
anyLong
(),
anyInt
());
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
1
)).
handleDiscontinuity
();
inOrderAudioSink
.
verify
(
mockAudioSink
,
times
(
2
)).
handleBuffer
(
any
(),
anyLong
(),
anyInt
());
}
private
static
final
class
FakeDecoder
private
static
final
class
FakeDecoder
extends
SimpleDecoder
<
DecoderInputBuffer
,
SimpleDecoderOutputBuffer
,
DecoderException
>
{
extends
SimpleDecoder
<
DecoderInputBuffer
,
SimpleDecoderOutputBuffer
,
DecoderException
>
{
...
...
library/decoder/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java
View file @
e4b0c207
...
@@ -32,6 +32,11 @@ public abstract class Buffer {
...
@@ -32,6 +32,11 @@ public abstract class Buffer {
return
getFlag
(
C
.
BUFFER_FLAG_DECODE_ONLY
);
return
getFlag
(
C
.
BUFFER_FLAG_DECODE_ONLY
);
}
}
/** Returns whether the {@link C#BUFFER_FLAG_FIRST_SAMPLE} flag is set. */
public
final
boolean
isFirstSample
()
{
return
getFlag
(
C
.
BUFFER_FLAG_FIRST_SAMPLE
);
}
/** Returns whether the {@link C#BUFFER_FLAG_END_OF_STREAM} flag is set. */
/** Returns whether the {@link C#BUFFER_FLAG_END_OF_STREAM} flag is set. */
public
final
boolean
isEndOfStream
()
{
public
final
boolean
isEndOfStream
()
{
return
getFlag
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
return
getFlag
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
...
...
library/decoder/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java
View file @
e4b0c207
...
@@ -233,6 +233,9 @@ public abstract class SimpleDecoder<
...
@@ -233,6 +233,9 @@ public abstract class SimpleDecoder<
if
(
inputBuffer
.
isDecodeOnly
())
{
if
(
inputBuffer
.
isDecodeOnly
())
{
outputBuffer
.
addFlag
(
C
.
BUFFER_FLAG_DECODE_ONLY
);
outputBuffer
.
addFlag
(
C
.
BUFFER_FLAG_DECODE_ONLY
);
}
}
if
(
inputBuffer
.
isFirstSample
())
{
outputBuffer
.
addFlag
(
C
.
BUFFER_FLAG_FIRST_SAMPLE
);
}
@Nullable
E
exception
;
@Nullable
E
exception
;
try
{
try
{
exception
=
decode
(
inputBuffer
,
outputBuffer
,
resetDecoder
);
exception
=
decode
(
inputBuffer
,
outputBuffer
,
resetDecoder
);
...
...
testdata/src/test/assets/audiosinkdumps/mka/bear-flac-16bit.mka.audiosink.dump
View file @
e4b0c207
discontinuity:
config:
config:
pcmEncoding = 2
pcmEncoding = 2
channelCount = 2
channelCount = 2
...
...
testdata/src/test/assets/audiosinkdumps/mka/bear-flac-24bit.mka.audiosink.dump
View file @
e4b0c207
discontinuity:
config:
config:
pcmEncoding = 536870912
pcmEncoding = 536870912
channelCount = 2
channelCount = 2
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment