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
1c4ea26f
authored
Jan 21, 2019
by
Oliver Woodman
Committed by
GitHub
Jan 21, 2019
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #5388 from google/dev-v2-r2.9.4
r2.9.4
parents
71f72c59
200c877d
Hide whitespace changes
Inline
Side-by-side
Showing
72 changed files
with
1030 additions
and
317 deletions
README.md
RELEASENOTES.md
constants.gradle
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java
demos/cast/src/main/res/layout/cast_context_error_message_layout.xml
demos/cast/src/main/res/values/strings.xml
extensions/cast/build.gradle
extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java
extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc
extensions/gvr/build.gradle
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakeAd.java
extensions/rtmp/README.md
library/core/src/main/java/com/google/android/exoplayer2/C.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/Format.java
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java
library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/DeferredMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java
library/core/src/main/java/com/google/android/exoplayer2/util/AtomicFile.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/test/assets/mp4/sample.mp4.0.dump
library/core/src/test/assets/mp4/sample.mp4.1.dump
library/core/src/test/assets/mp4/sample.mp4.2.dump
library/core/src/test/assets/mp4/sample.mp4.3.dump
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntryTest.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java
library/smoothstreaming/src/test/assets/sample_ismc_1
library/smoothstreaming/src/test/assets/sample_ismc_2
library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java
README.md
View file @
1c4ea26f
...
...
@@ -27,6 +27,8 @@ repository and depend on the modules locally.
### From JCenter ###
#### 1. Add repositories ####
The easiest way to get started using ExoPlayer is to add it as a gradle
dependency. You need to make sure you have the Google and JCenter repositories
included in the
`build.gradle`
file in the root of your project:
...
...
@@ -38,6 +40,8 @@ repositories {
}
```
#### 2. Add ExoPlayer module dependencies ####
Next add a dependency in the
`build.gradle`
file of your app module. The
following will add a dependency to the full library:
...
...
@@ -45,15 +49,7 @@ following will add a dependency to the full library:
implementation
'com.google.android.exoplayer:exoplayer:2.X.X'
```
where
`2.X.X`
is your preferred version. If not enabled already, you also need
to turn on Java 8 support in all
`build.gradle`
files depending on ExoPlayer, by
adding the following to the
`android`
section:
```
gradle
compileOptions
{
targetCompatibility
JavaVersion
.
VERSION_1_8
}
```
where
`2.X.X`
is your preferred version.
As an alternative to the full library, you can depend on only the library
modules that you actually need. For example the following will add dependencies
...
...
@@ -87,6 +83,32 @@ JCenter can be found on [Bintray][].
[
extensions directory
]:
https://github.com/google/ExoPlayer/tree/release-v2/extensions/
[
Bintray
]:
https://bintray.com/google/exoplayer
#### 3. Turn on Java 8 support ####
If not enabled already, you also need to turn on Java 8 support in all
`build.gradle`
files depending on ExoPlayer, by adding the following to the
`android`
section:
```
gradle
compileOptions
{
targetCompatibility
JavaVersion
.
VERSION_1_8
}
```
Note that if you want to use Java 8 features in your own code, the following
additional options need to be set:
```
gradle
// For Java compilers:
compileOptions
{
sourceCompatibility
JavaVersion
.
VERSION_1_8
}
// For Kotlin compilers:
kotlinOptions
{
jvmTarget
=
JavaVersion
.
VERSION_1_8
}
```
### Locally ###
Cloning the repository and depending on the modules locally is required when
...
...
RELEASENOTES.md
View file @
1c4ea26f
# Release notes #
### 2.9.4 ###
*
IMA extension: Clear ads loader listeners on release
(
[
#4114
](
https://github.com/google/ExoPlayer/issues/4114
)
).
*
SmoothStreaming: Fix support for subtitles in DRM protected streams
(
[
#5378
](
https://github.com/google/ExoPlayer/issues/5378
)
).
*
FFmpeg extension: Treat invalid data errors as non-fatal to match the behavior
of MediaCodec (
[
#5293
](
https://github.com/google/ExoPlayer/issues/5293
)
).
*
GVR extension: upgrade GVR SDK dependency to 1.190.0.
*
Associate fatal player errors of type SOURCE with the loading source in
`AnalyticsListener.EventTime`
(
[
#5407
](
https://github.com/google/ExoPlayer/issues/5407
)
).
*
Add
`startPositionUs`
to
`MediaSource.createPeriod`
. This fixes an issue where
using lazy preparation in
`ConcatenatingMediaSource`
with an
`ExtractorMediaSource`
overrides initial seek positions
(
[
#5350
](
https://github.com/google/ExoPlayer/issues/5350
)
).
*
Add subtext to the
`MediaDescriptionAdapter`
of the
`PlayerNotificationManager`
.
*
Add workaround for video quality problems with Amlogic decoders
(
[
#5003
](
https://github.com/google/ExoPlayer/issues/5003
)
).
*
Fix issue where sending callbacks for playlist changes may cause problems
because of parallel player access
(
[
#5240
](
https://github.com/google/ExoPlayer/issues/5240
)
).
*
Fix issue with reusing a
`ClippingMediaSource`
with an inner
`ExtractorMediaSource`
and a non-zero start position
(
[
#5351
](
https://github.com/google/ExoPlayer/issues/5351
)
).
*
Fix issue where uneven track durations in MP4 streams can cause OOM problems
(
[
#3670
](
https://github.com/google/ExoPlayer/issues/3670
)
).
### 2.9.3 ###
*
Captions: Support PNG subtitles in SMPTE-TT
...
...
constants.gradle
View file @
1c4ea26f
...
...
@@ -13,8 +13,8 @@
// limitations under the License.
project
.
ext
{
// ExoPlayer version and version code.
releaseVersion
=
'2.9.
3
'
releaseVersionCode
=
200900
3
releaseVersion
=
'2.9.
4
'
releaseVersionCode
=
200900
4
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided
...
...
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java
View file @
1c4ea26f
...
...
@@ -41,6 +41,7 @@ import com.google.android.exoplayer2.ui.PlayerControlView;
import
com.google.android.exoplayer2.ui.PlayerView
;
import
com.google.android.gms.cast.framework.CastButtonFactory
;
import
com.google.android.gms.cast.framework.CastContext
;
import
com.google.android.gms.dynamite.DynamiteModule
;
/**
* An activity that plays video using {@link SimpleExoPlayer} and {@link CastPlayer}.
...
...
@@ -61,7 +62,20 @@ public class MainActivity extends AppCompatActivity implements OnClickListener,
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
// Getting the cast context later than onStart can cause device discovery not to take place.
castContext
=
CastContext
.
getSharedInstance
(
this
);
try
{
castContext
=
CastContext
.
getSharedInstance
(
this
);
}
catch
(
RuntimeException
e
)
{
Throwable
cause
=
e
.
getCause
();
while
(
cause
!=
null
)
{
if
(
cause
instanceof
DynamiteModule
.
LoadingException
)
{
setContentView
(
R
.
layout
.
cast_context_error_message_layout
);
return
;
}
cause
=
cause
.
getCause
();
}
// Unknown error. We propagate it.
throw
e
;
}
setContentView
(
R
.
layout
.
main_activity
);
...
...
@@ -91,6 +105,10 @@ public class MainActivity extends AppCompatActivity implements OnClickListener,
@Override
public
void
onResume
()
{
super
.
onResume
();
if
(
castContext
==
null
)
{
// There is no Cast context to work with. Do nothing.
return
;
}
playerManager
=
PlayerManager
.
createPlayerManager
(
/* queuePositionListener= */
this
,
...
...
@@ -104,6 +122,10 @@ public class MainActivity extends AppCompatActivity implements OnClickListener,
@Override
public
void
onPause
()
{
super
.
onPause
();
if
(
castContext
==
null
)
{
// Nothing to release.
return
;
}
mediaQueueListAdapter
.
notifyItemRangeRemoved
(
0
,
mediaQueueListAdapter
.
getItemCount
());
mediaQueueList
.
setAdapter
(
null
);
playerManager
.
release
();
...
...
demos/cast/src/main/res/layout/cast_context_error_message_layout.xml
0 → 100644
View file @
1c4ea26f
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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.
-->
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<TextView
android:id=
"@+id/textView"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:layout_weight=
"1"
android:textSize=
"20sp"
android:gravity=
"center"
android:text=
"@string/cast_context_error"
/>
</LinearLayout>
demos/cast/src/main/res/values/strings.xml
View file @
1c4ea26f
...
...
@@ -22,4 +22,6 @@
<string
name=
"sample_list_dialog_title"
>
Add samples
</string>
<string
name=
"cast_context_error"
>
Failed to get Cast context. Try updating Google Play Services and restart the app.
</string>
</resources>
extensions/cast/build.gradle
View file @
1c4ea26f
...
...
@@ -31,7 +31,9 @@ android {
}
dependencies
{
api
'com.google.android.gms:play-services-cast-framework:16.0.3'
api
'com.google.android.gms:play-services-cast-framework:16.1.2'
compileOnly
'org.checkerframework:checker-qual:'
+
checkerframeworkVersion
compileOnly
'org.checkerframework:checker-compat-qual:'
+
checkerframeworkVersion
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-ui'
)
testImplementation
project
(
modulePrefix
+
'testutils'
)
...
...
extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegDecoder.java
View file @
1c4ea26f
...
...
@@ -37,6 +37,10 @@ import java.util.List;
private
static
final
int
OUTPUT_BUFFER_SIZE_16BIT
=
65536
;
private
static
final
int
OUTPUT_BUFFER_SIZE_32BIT
=
OUTPUT_BUFFER_SIZE_16BIT
*
2
;
// Error codes matching ffmpeg_jni.cc.
private
static
final
int
DECODER_ERROR_INVALID_DATA
=
-
1
;
private
static
final
int
DECODER_ERROR_OTHER
=
-
2
;
private
final
String
codecName
;
private
final
@Nullable
byte
[]
extraData
;
private
final
@C
.
Encoding
int
encoding
;
...
...
@@ -106,8 +110,14 @@ import java.util.List;
int
inputSize
=
inputData
.
limit
();
ByteBuffer
outputData
=
outputBuffer
.
init
(
inputBuffer
.
timeUs
,
outputBufferSize
);
int
result
=
ffmpegDecode
(
nativeContext
,
inputData
,
inputSize
,
outputData
,
outputBufferSize
);
if
(
result
<
0
)
{
return
new
FfmpegDecoderException
(
"Error decoding (see logcat). Code: "
+
result
);
if
(
result
==
DECODER_ERROR_INVALID_DATA
)
{
// Treat invalid data errors as non-fatal to match the behavior of MediaCodec. No output will
// be produced for this buffer, so mark it as decode-only to ensure that the audio sink's
// position is reset when more audio is produced.
outputBuffer
.
setFlags
(
C
.
BUFFER_FLAG_DECODE_ONLY
);
return
null
;
}
else
if
(
result
==
DECODER_ERROR_OTHER
)
{
return
new
FfmpegDecoderException
(
"Error decoding (see logcat)."
);
}
if
(!
hasOutputFormat
)
{
channelCount
=
ffmpegGetChannelCount
(
nativeContext
);
...
...
extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc
View file @
1c4ea26f
...
...
@@ -63,6 +63,10 @@ static const AVSampleFormat OUTPUT_FORMAT_PCM_16BIT = AV_SAMPLE_FMT_S16;
// Output format corresponding to AudioFormat.ENCODING_PCM_FLOAT.
static
const
AVSampleFormat
OUTPUT_FORMAT_PCM_FLOAT
=
AV_SAMPLE_FMT_FLT
;
// Error codes matching FfmpegDecoder.java.
static
const
int
DECODER_ERROR_INVALID_DATA
=
-
1
;
static
const
int
DECODER_ERROR_OTHER
=
-
2
;
/**
* Returns the AVCodec with the specified name, or NULL if it is not available.
*/
...
...
@@ -79,7 +83,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
/**
* Decodes the packet into the output buffer, returning the number of bytes
* written, or a negative value in the case of an error.
* written, or a negative
DECODER_ERROR constant
value in the case of an error.
*/
int
decodePacket
(
AVCodecContext
*
context
,
AVPacket
*
packet
,
uint8_t
*
outputBuffer
,
int
outputSize
);
...
...
@@ -238,6 +242,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
context
->
channels
=
rawChannelCount
;
context
->
channel_layout
=
av_get_default_channel_layout
(
rawChannelCount
);
}
context
->
err_recognition
=
AV_EF_IGNORE_ERR
;
int
result
=
avcodec_open2
(
context
,
codec
,
NULL
);
if
(
result
<
0
)
{
logError
(
"avcodec_open2"
,
result
);
...
...
@@ -254,7 +259,8 @@ int decodePacket(AVCodecContext *context, AVPacket *packet,
result
=
avcodec_send_packet
(
context
,
packet
);
if
(
result
)
{
logError
(
"avcodec_send_packet"
,
result
);
return
result
;
return
result
==
AVERROR_INVALIDDATA
?
DECODER_ERROR_INVALID_DATA
:
DECODER_ERROR_OTHER
;
}
// Dequeue output data until it runs out.
...
...
extensions/gvr/build.gradle
View file @
1c4ea26f
...
...
@@ -32,7 +32,8 @@ android {
dependencies
{
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
implementation
'com.google.vr:sdk-audio:1.80.0'
api
'com.google.vr:sdk-base:1.190.0'
compileOnly
'org.checkerframework:checker-qual:'
+
checkerframeworkVersion
}
ext
{
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
1c4ea26f
...
...
@@ -597,6 +597,8 @@ public final class ImaAdsLoader
adsManager
.
destroy
();
adsManager
=
null
;
}
adsLoader
.
removeAdsLoadedListener
(
/* adsLoadedListener= */
this
);
adsLoader
.
removeAdErrorListener
(
/* adErrorListener= */
this
);
imaPausedContent
=
false
;
imaAdState
=
IMA_AD_STATE_NONE
;
pendingAdLoadError
=
null
;
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java
View file @
1c4ea26f
...
...
@@ -97,8 +97,8 @@ public final class ImaAdsMediaSource extends BaseMediaSource implements SourceIn
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
return
adsMediaSource
.
createPeriod
(
id
,
allocator
);
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
return
adsMediaSource
.
createPeriod
(
id
,
allocator
,
startPositionUs
);
}
@Override
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakeAd.java
View file @
1c4ea26f
...
...
@@ -64,14 +64,17 @@ import java.util.Set;
};
}
@Override
public
int
getVastMediaWidth
()
{
throw
new
UnsupportedOperationException
();
}
@Override
public
int
getVastMediaHeight
()
{
throw
new
UnsupportedOperationException
();
}
@Override
public
int
getVastMediaBitrate
()
{
throw
new
UnsupportedOperationException
();
}
...
...
extensions/rtmp/README.md
View file @
1c4ea26f
...
...
@@ -39,7 +39,7 @@ either instantiated and injected from application code, or obtained from
instances of
`DataSource.Factory`
that are instantiated and injected from
application code.
`DefaultDataSource`
will automatically use
uses
the RTMP extension whenever it's
`DefaultDataSource`
will automatically use the RTMP extension whenever it's
available. Hence if your application is using
`DefaultDataSource`
or
`DefaultDataSourceFactory`
, adding support for RTMP streams is as simple as
adding a dependency to the RTMP extension as described above. No changes to your
...
...
library/core/src/main/java/com/google/android/exoplayer2/C.java
View file @
1c4ea26f
...
...
@@ -460,8 +460,8 @@ public final class C {
/**
* 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_
ENCRYPTED} and
* {@link #BUFFER_FLAG_DECODE_ONLY}.
* #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_
LAST_SAMPLE},
* {@link #BUFFER_FLAG_
ENCRYPTED} and {@link #BUFFER_FLAG_
DECODE_ONLY}.
*/
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
...
...
@@ -470,6 +470,7 @@ public final class C {
value
=
{
BUFFER_FLAG_KEY_FRAME
,
BUFFER_FLAG_END_OF_STREAM
,
BUFFER_FLAG_LAST_SAMPLE
,
BUFFER_FLAG_ENCRYPTED
,
BUFFER_FLAG_DECODE_ONLY
})
...
...
@@ -482,6 +483,8 @@ public final class C {
* 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
;
/** Indicates that a buffer is known to contain the last media sample of the stream. */
public
static
final
int
BUFFER_FLAG_LAST_SAMPLE
=
1
<<
29
;
// 0x20000000
/** Indicates that a buffer is (at least partially) encrypted. */
public
static
final
int
BUFFER_FLAG_ENCRYPTED
=
1
<<
30
;
// 0x40000000
/** Indicates that a buffer should be decoded but not rendered. */
...
...
@@ -896,6 +899,26 @@ public final class C {
*/
public
static
final
int
COLOR_RANGE_FULL
=
MediaFormat
.
COLOR_RANGE_FULL
;
/** Video projection types. */
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
Format
.
NO_VALUE
,
PROJECTION_RECTANGULAR
,
PROJECTION_EQUIRECTANGULAR
,
PROJECTION_CUBEMAP
,
PROJECTION_MESH
})
public
@interface
Projection
{}
/** Conventional rectangular projection. */
public
static
final
int
PROJECTION_RECTANGULAR
=
0
;
/** Equirectangular spherical projection. */
public
static
final
int
PROJECTION_EQUIRECTANGULAR
=
1
;
/** Cube map projection. */
public
static
final
int
PROJECTION_CUBEMAP
=
2
;
/** 3-D mesh projection. */
public
static
final
int
PROJECTION_MESH
=
3
;
/**
* Priority for media playback.
*
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
1c4ea26f
...
...
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public
static
final
String
VERSION
=
"2.9.
3
"
;
public
static
final
String
VERSION
=
"2.9.
4
"
;
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.9.
3
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.9.
4
"
;
/**
* The version of the library expressed as an integer, for example 1002003.
...
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
int
VERSION_INT
=
200900
3
;
public
static
final
int
VERSION_INT
=
200900
4
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/Format.java
View file @
1c4ea26f
...
...
@@ -1181,6 +1181,37 @@ public final class Format implements Parcelable {
metadata
);
}
public
Format
copyWithFrameRate
(
float
frameRate
)
{
return
new
Format
(
id
,
label
,
containerMimeType
,
sampleMimeType
,
codecs
,
bitrate
,
maxInputSize
,
width
,
height
,
frameRate
,
rotationDegrees
,
pixelWidthHeightRatio
,
projectionData
,
stereoMode
,
colorInfo
,
channelCount
,
sampleRate
,
pcmEncoding
,
encoderDelay
,
encoderPadding
,
selectionFlags
,
language
,
accessibilityChannel
,
subsampleOffsetUs
,
initializationData
,
drmInitData
,
metadata
);
}
public
Format
copyWithDrmInitData
(
@Nullable
DrmInitData
drmInitData
)
{
return
new
Format
(
id
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java
View file @
1c4ea26f
...
...
@@ -79,7 +79,7 @@ import com.google.android.exoplayer2.util.Log;
this
.
info
=
info
;
sampleStreams
=
new
SampleStream
[
rendererCapabilities
.
length
];
mayRetainStreamFlags
=
new
boolean
[
rendererCapabilities
.
length
];
MediaPeriod
mediaPeriod
=
mediaSource
.
createPeriod
(
info
.
id
,
allocator
);
MediaPeriod
mediaPeriod
=
mediaSource
.
createPeriod
(
info
.
id
,
allocator
,
info
.
startPositionUs
);
if
(
info
.
id
.
endPositionUs
!=
C
.
TIME_END_OF_SOURCE
)
{
mediaPeriod
=
new
ClippingMediaPeriod
(
...
...
library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
View file @
1c4ea26f
...
...
@@ -94,25 +94,25 @@ public class SimpleExoPlayer extends BasePlayer
private
final
AudioFocusManager
audioFocusManager
;
private
Format
videoFormat
;
private
Format
audioFormat
;
@Nullable
private
Format
videoFormat
;
@Nullable
private
Format
audioFormat
;
private
Surface
surface
;
@Nullable
private
Surface
surface
;
private
boolean
ownsSurface
;
private
@C
.
VideoScalingMode
int
videoScalingMode
;
private
SurfaceHolder
surfaceHolder
;
private
TextureView
textureView
;
@Nullable
private
SurfaceHolder
surfaceHolder
;
@Nullable
private
TextureView
textureView
;
private
int
surfaceWidth
;
private
int
surfaceHeight
;
private
DecoderCounters
videoDecoderCounters
;
private
DecoderCounters
audioDecoderCounters
;
@Nullable
private
DecoderCounters
videoDecoderCounters
;
@Nullable
private
DecoderCounters
audioDecoderCounters
;
private
int
audioSessionId
;
private
AudioAttributes
audioAttributes
;
private
float
audioVolume
;
private
MediaSource
mediaSource
;
@Nullable
private
MediaSource
mediaSource
;
private
List
<
Cue
>
currentCues
;
private
VideoFrameMetadataListener
videoFrameMetadataListener
;
private
CameraMotionListener
cameraMotionListener
;
@Nullable
private
VideoFrameMetadataListener
videoFrameMetadataListener
;
@Nullable
private
CameraMotionListener
cameraMotionListener
;
private
boolean
hasNotifiedFullWrongThreadWarning
;
/**
...
...
@@ -558,30 +558,26 @@ public class SimpleExoPlayer extends BasePlayer
setPlaybackParameters
(
playbackParameters
);
}
/**
* Returns the video format currently being played, or null if no video is being played.
*/
/** Returns the video format currently being played, or null if no video is being played. */
@Nullable
public
Format
getVideoFormat
()
{
return
videoFormat
;
}
/**
* Returns the audio format currently being played, or null if no audio is being played.
*/
/** Returns the audio format currently being played, or null if no audio is being played. */
@Nullable
public
Format
getAudioFormat
()
{
return
audioFormat
;
}
/**
* Returns {@link DecoderCounters} for video, or null if no video is being played.
*/
/** Returns {@link DecoderCounters} for video, or null if no video is being played. */
@Nullable
public
DecoderCounters
getVideoDecoderCounters
()
{
return
videoDecoderCounters
;
}
/**
* Returns {@link DecoderCounters} for audio, or null if no audio is being played.
*/
/** Returns {@link DecoderCounters} for audio, or null if no audio is being played. */
@Nullable
public
DecoderCounters
getAudioDecoderCounters
()
{
return
audioDecoderCounters
;
}
...
...
@@ -1048,7 +1044,8 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public
@Nullable
Object
getCurrentManifest
()
{
@Nullable
public
Object
getCurrentManifest
()
{
verifyApplicationThread
();
return
player
.
getCurrentManifest
();
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
View file @
1c4ea26f
...
...
@@ -488,7 +488,10 @@ public class AnalyticsCollector
@Override
public
final
void
onPlayerError
(
ExoPlaybackException
error
)
{
EventTime
eventTime
=
generatePlayingMediaPeriodEventTime
();
EventTime
eventTime
=
error
.
type
==
ExoPlaybackException
.
TYPE_SOURCE
?
generateLoadingMediaPeriodEventTime
()
:
generatePlayingMediaPeriodEventTime
();
for
(
AnalyticsListener
listener
:
listeners
)
{
listener
.
onPlayerError
(
eventTime
,
error
);
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
View file @
1c4ea26f
...
...
@@ -366,7 +366,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
if
(
outputBuffer
==
null
)
{
return
false
;
}
decoderCounters
.
skippedOutputBufferCount
+=
outputBuffer
.
skippedOutputBufferCount
;
if
(
outputBuffer
.
skippedOutputBufferCount
>
0
)
{
decoderCounters
.
skippedOutputBufferCount
+=
outputBuffer
.
skippedOutputBufferCount
;
audioSink
.
handleDiscontinuity
();
}
}
if
(
outputBuffer
.
isEndOfStream
())
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
View file @
1c4ea26f
...
...
@@ -191,7 +191,11 @@ public final class MatroskaExtractor implements Extractor {
private
static
final
int
ID_CUE_CLUSTER_POSITION
=
0xF1
;
private
static
final
int
ID_LANGUAGE
=
0x22B59C
;
private
static
final
int
ID_PROJECTION
=
0x7670
;
private
static
final
int
ID_PROJECTION_TYPE
=
0x7671
;
private
static
final
int
ID_PROJECTION_PRIVATE
=
0x7672
;
private
static
final
int
ID_PROJECTION_POSE_YAW
=
0x7673
;
private
static
final
int
ID_PROJECTION_POSE_PITCH
=
0x7674
;
private
static
final
int
ID_PROJECTION_POSE_ROLL
=
0x7675
;
private
static
final
int
ID_STEREO_MODE
=
0x53B8
;
private
static
final
int
ID_COLOUR
=
0x55B0
;
private
static
final
int
ID_COLOUR_RANGE
=
0x55B9
;
...
...
@@ -760,6 +764,24 @@ public final class MatroskaExtractor implements Extractor {
case
ID_MAX_FALL:
currentTrack
.
maxFrameAverageLuminance
=
(
int
)
value
;
break
;
case
ID_PROJECTION_TYPE:
switch
((
int
)
value
)
{
case
0
:
currentTrack
.
projectionType
=
C
.
PROJECTION_RECTANGULAR
;
break
;
case
1
:
currentTrack
.
projectionType
=
C
.
PROJECTION_EQUIRECTANGULAR
;
break
;
case
2
:
currentTrack
.
projectionType
=
C
.
PROJECTION_CUBEMAP
;
break
;
case
3
:
currentTrack
.
projectionType
=
C
.
PROJECTION_MESH
;
break
;
default
:
break
;
}
break
;
default
:
break
;
}
...
...
@@ -803,6 +825,15 @@ public final class MatroskaExtractor implements Extractor {
case
ID_LUMNINANCE_MIN:
currentTrack
.
minMasteringLuminance
=
(
float
)
value
;
break
;
case
ID_PROJECTION_POSE_YAW:
currentTrack
.
projectionPoseYaw
=
(
float
)
value
;
break
;
case
ID_PROJECTION_POSE_PITCH:
currentTrack
.
projectionPosePitch
=
(
float
)
value
;
break
;
case
ID_PROJECTION_POSE_ROLL:
currentTrack
.
projectionPoseRoll
=
(
float
)
value
;
break
;
default
:
break
;
}
...
...
@@ -1465,6 +1496,7 @@ public final class MatroskaExtractor implements Extractor {
case
ID_COLOUR_PRIMARIES:
case
ID_MAX_CLL:
case
ID_MAX_FALL:
case
ID_PROJECTION_TYPE:
return
TYPE_UNSIGNED_INT
;
case
ID_DOC_TYPE:
case
ID_NAME:
...
...
@@ -1491,6 +1523,9 @@ public final class MatroskaExtractor implements Extractor {
case
ID_WHITE_POINT_CHROMATICITY_Y:
case
ID_LUMNINANCE_MAX:
case
ID_LUMNINANCE_MIN:
case
ID_PROJECTION_POSE_YAW:
case
ID_PROJECTION_POSE_PITCH:
case
ID_PROJECTION_POSE_ROLL:
return
TYPE_FLOAT
;
default
:
return
TYPE_UNKNOWN
;
...
...
@@ -1631,6 +1666,10 @@ public final class MatroskaExtractor implements Extractor {
public
int
displayWidth
=
Format
.
NO_VALUE
;
public
int
displayHeight
=
Format
.
NO_VALUE
;
public
int
displayUnit
=
DISPLAY_UNIT_PIXELS
;
@C
.
Projection
public
int
projectionType
=
Format
.
NO_VALUE
;
public
float
projectionPoseYaw
=
0
f
;
public
float
projectionPosePitch
=
0
f
;
public
float
projectionPoseRoll
=
0
f
;
public
byte
[]
projectionData
=
null
;
@C
.
StereoMode
public
int
stereoMode
=
Format
.
NO_VALUE
;
...
...
@@ -1850,6 +1889,21 @@ public final class MatroskaExtractor implements Extractor {
}
else
if
(
"htc_video_rotA-270"
.
equals
(
name
))
{
rotationDegrees
=
270
;
}
if
(
projectionType
==
C
.
PROJECTION_RECTANGULAR
&&
Float
.
compare
(
projectionPoseYaw
,
0
f
)
==
0
&&
Float
.
compare
(
projectionPosePitch
,
0
f
)
==
0
)
{
// The range of projectionPoseRoll is [-180, 180].
if
(
Float
.
compare
(
projectionPoseRoll
,
0
f
)
==
0
)
{
rotationDegrees
=
0
;
}
else
if
(
Float
.
compare
(
projectionPosePitch
,
90
f
)
==
0
)
{
rotationDegrees
=
90
;
}
else
if
(
Float
.
compare
(
projectionPosePitch
,
-
180
f
)
==
0
||
Float
.
compare
(
projectionPosePitch
,
180
f
)
==
0
)
{
rotationDegrees
=
180
;
}
else
if
(
Float
.
compare
(
projectionPosePitch
,
-
90
f
)
==
0
)
{
rotationDegrees
=
270
;
}
}
format
=
Format
.
createVideoSampleFormat
(
Integer
.
toString
(
trackId
),
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
View file @
1c4ea26f
...
...
@@ -22,8 +22,8 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.List
;
@SuppressWarnings
(
"ConstantField"
)
/* package*/
abstract
class
Atom
{
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package
*/
abstract
class
Atom
{
/**
* Size of an atom header, in bytes.
...
...
@@ -130,6 +130,7 @@ import java.util.List;
public
static
final
int
TYPE_sawb
=
Util
.
getIntegerCodeForString
(
"sawb"
);
public
static
final
int
TYPE_udta
=
Util
.
getIntegerCodeForString
(
"udta"
);
public
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
public
static
final
int
TYPE_keys
=
Util
.
getIntegerCodeForString
(
"keys"
);
public
static
final
int
TYPE_ilst
=
Util
.
getIntegerCodeForString
(
"ilst"
);
public
static
final
int
TYPE_mean
=
Util
.
getIntegerCodeForString
(
"mean"
);
public
static
final
int
TYPE_name
=
Util
.
getIntegerCodeForString
(
"name"
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
1c4ea26f
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.mp4;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
MimeTypes
.
getMimeTypeFromMp4ObjectType
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
...
...
@@ -39,7 +40,7 @@ import java.util.Collections;
import
java.util.List
;
/** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */
@SuppressWarnings
(
"ConstantField"
)
@SuppressWarnings
(
{
"ConstantField"
,
"ConstantCaseForConstants"
}
)
/* package */
final
class
AtomParsers
{
private
static
final
String
TAG
=
"AtomParsers"
;
...
...
@@ -51,6 +52,7 @@ import java.util.List;
private
static
final
int
TYPE_subt
=
Util
.
getIntegerCodeForString
(
"subt"
);
private
static
final
int
TYPE_clcp
=
Util
.
getIntegerCodeForString
(
"clcp"
);
private
static
final
int
TYPE_meta
=
Util
.
getIntegerCodeForString
(
"meta"
);
private
static
final
int
TYPE_mdta
=
Util
.
getIntegerCodeForString
(
"mdta"
);
/**
* The threshold number of samples to trim from the start/end of an audio track when applying an
...
...
@@ -77,7 +79,7 @@ import java.util.List;
DrmInitData
drmInitData
,
boolean
ignoreEditLists
,
boolean
isQuickTime
)
throws
ParserException
{
Atom
.
ContainerAtom
mdia
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
);
int
trackType
=
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
);
int
trackType
=
getTrackTypeForHdlr
(
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
)
);
if
(
trackType
==
C
.
TRACK_TYPE_UNKNOWN
)
{
return
null
;
}
...
...
@@ -485,6 +487,7 @@ import java.util.List;
* @param isQuickTime True for QuickTime media. False otherwise.
* @return Parsed metadata, or null.
*/
@Nullable
public
static
Metadata
parseUdta
(
Atom
.
LeafAtom
udtaAtom
,
boolean
isQuickTime
)
{
if
(
isQuickTime
)
{
// Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and
...
...
@@ -499,14 +502,69 @@ import java.util.List;
int
atomType
=
udtaData
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_meta
)
{
udtaData
.
setPosition
(
atomPosition
);
return
parse
MetaAtom
(
udtaData
,
atomPosition
+
atomSize
);
return
parse
UdtaMeta
(
udtaData
,
atomPosition
+
atomSize
);
}
udtaData
.
s
kipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
udtaData
.
s
etPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
private
static
Metadata
parseMetaAtom
(
ParsableByteArray
meta
,
int
limit
)
{
/**
* Parses a metadata meta atom if it contains metadata with handler 'mdta'.
*
* @param meta The metadata atom to decode.
* @return Parsed metadata, or null.
*/
@Nullable
public
static
Metadata
parseMdtaFromMeta
(
Atom
.
ContainerAtom
meta
)
{
Atom
.
LeafAtom
hdlrAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
);
Atom
.
LeafAtom
keysAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_keys
);
Atom
.
LeafAtom
ilstAtom
=
meta
.
getLeafAtomOfType
(
Atom
.
TYPE_ilst
);
if
(
hdlrAtom
==
null
||
keysAtom
==
null
||
ilstAtom
==
null
||
AtomParsers
.
parseHdlr
(
hdlrAtom
.
data
)
!=
TYPE_mdta
)
{
// There isn't enough information to parse the metadata, or the handler type is unexpected.
return
null
;
}
// Parse metadata keys.
ParsableByteArray
keys
=
keysAtom
.
data
;
keys
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
);
int
entryCount
=
keys
.
readInt
();
String
[]
keyNames
=
new
String
[
entryCount
];
for
(
int
i
=
0
;
i
<
entryCount
;
i
++)
{
int
entrySize
=
keys
.
readInt
();
keys
.
skipBytes
(
4
);
// keyNamespace
int
keySize
=
entrySize
-
8
;
keyNames
[
i
]
=
keys
.
readString
(
keySize
);
}
// Parse metadata items.
ParsableByteArray
ilst
=
ilstAtom
.
data
;
ilst
.
setPosition
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
while
(
ilst
.
bytesLeft
()
>
Atom
.
HEADER_SIZE
)
{
int
atomPosition
=
ilst
.
getPosition
();
int
atomSize
=
ilst
.
readInt
();
int
keyIndex
=
ilst
.
readInt
()
-
1
;
if
(
keyIndex
>=
0
&&
keyIndex
<
keyNames
.
length
)
{
String
key
=
keyNames
[
keyIndex
];
Metadata
.
Entry
entry
=
MetadataUtil
.
parseMdtaMetadataEntryFromIlst
(
ilst
,
atomPosition
+
atomSize
,
key
);
if
(
entry
!=
null
)
{
entries
.
add
(
entry
);
}
}
else
{
Log
.
w
(
TAG
,
"Skipped metadata with unknown key index: "
+
keyIndex
);
}
ilst
.
setPosition
(
atomPosition
+
atomSize
);
}
return
entries
.
isEmpty
()
?
null
:
new
Metadata
(
entries
);
}
@Nullable
private
static
Metadata
parseUdtaMeta
(
ParsableByteArray
meta
,
int
limit
)
{
meta
.
skipBytes
(
Atom
.
FULL_HEADER_SIZE
);
while
(
meta
.
getPosition
()
<
limit
)
{
int
atomPosition
=
meta
.
getPosition
();
...
...
@@ -516,11 +574,12 @@ import java.util.List;
meta
.
setPosition
(
atomPosition
);
return
parseIlst
(
meta
,
atomPosition
+
atomSize
);
}
meta
.
s
kipBytes
(
atomSize
-
Atom
.
HEADER_SIZE
);
meta
.
s
etPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
@Nullable
private
static
Metadata
parseIlst
(
ParsableByteArray
ilst
,
int
limit
)
{
ilst
.
skipBytes
(
Atom
.
HEADER_SIZE
);
ArrayList
<
Metadata
.
Entry
>
entries
=
new
ArrayList
<>();
...
...
@@ -610,19 +669,22 @@ import java.util.List;
* Parses an hdlr atom.
*
* @param hdlr The hdlr atom to decode.
* @return The
track typ
e.
* @return The
handler valu
e.
*/
private
static
int
parseHdlr
(
ParsableByteArray
hdlr
)
{
hdlr
.
setPosition
(
Atom
.
FULL_HEADER_SIZE
+
4
);
int
trackType
=
hdlr
.
readInt
();
if
(
trackType
==
TYPE_soun
)
{
return
hdlr
.
readInt
();
}
/** Returns the track type for a given handler value. */
private
static
int
getTrackTypeForHdlr
(
int
hdlr
)
{
if
(
hdlr
==
TYPE_soun
)
{
return
C
.
TRACK_TYPE_AUDIO
;
}
else
if
(
trackType
==
TYPE_vide
)
{
}
else
if
(
hdlr
==
TYPE_vide
)
{
return
C
.
TRACK_TYPE_VIDEO
;
}
else
if
(
trackType
==
TYPE_text
||
trackType
==
TYPE_sbtl
||
trackType
==
TYPE_subt
||
trackType
==
TYPE_clcp
)
{
}
else
if
(
hdlr
==
TYPE_text
||
hdlr
==
TYPE_sbtl
||
hdlr
==
TYPE_subt
||
hdlr
==
TYPE_clcp
)
{
return
C
.
TRACK_TYPE_TEXT
;
}
else
if
(
trackType
==
TYPE_meta
)
{
}
else
if
(
hdlr
==
TYPE_meta
)
{
return
C
.
TRACK_TYPE_METADATA
;
}
else
{
return
C
.
TRACK_TYPE_UNKNOWN
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntry.java
0 → 100644
View file @
1c4ea26f
/*
* Copyright (C) 2019 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
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.Arrays
;
/**
* Stores extensible metadata with handler type 'mdta'. See also the QuickTime File Format
* Specification.
*/
public
final
class
MdtaMetadataEntry
implements
Metadata
.
Entry
{
/** The metadata key name. */
public
final
String
key
;
/** The payload. The interpretation of the value depends on {@link #typeIndicator}. */
public
final
byte
[]
value
;
/** The four byte locale indicator. */
public
final
int
localeIndicator
;
/** The four byte type indicator. */
public
final
int
typeIndicator
;
/** Creates a new metadata entry for the specified metadata key/value. */
public
MdtaMetadataEntry
(
String
key
,
byte
[]
value
,
int
localeIndicator
,
int
typeIndicator
)
{
this
.
key
=
key
;
this
.
value
=
value
;
this
.
localeIndicator
=
localeIndicator
;
this
.
typeIndicator
=
typeIndicator
;
}
private
MdtaMetadataEntry
(
Parcel
in
)
{
key
=
Util
.
castNonNull
(
in
.
readString
());
value
=
new
byte
[
in
.
readInt
()];
in
.
readByteArray
(
value
);
localeIndicator
=
in
.
readInt
();
typeIndicator
=
in
.
readInt
();
}
@Override
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
==
null
||
getClass
()
!=
obj
.
getClass
())
{
return
false
;
}
MdtaMetadataEntry
other
=
(
MdtaMetadataEntry
)
obj
;
return
key
.
equals
(
other
.
key
)
&&
Arrays
.
equals
(
value
,
other
.
value
)
&&
localeIndicator
==
other
.
localeIndicator
&&
typeIndicator
==
other
.
typeIndicator
;
}
@Override
public
int
hashCode
()
{
int
result
=
17
;
result
=
31
*
result
+
key
.
hashCode
();
result
=
31
*
result
+
Arrays
.
hashCode
(
value
);
result
=
31
*
result
+
localeIndicator
;
result
=
31
*
result
+
typeIndicator
;
return
result
;
}
@Override
public
String
toString
()
{
return
"mdta: key="
+
key
;
}
// Parcelable implementation.
@Override
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
dest
.
writeString
(
key
);
dest
.
writeInt
(
value
.
length
);
dest
.
writeByteArray
(
value
);
dest
.
writeInt
(
localeIndicator
);
dest
.
writeInt
(
typeIndicator
);
}
@Override
public
int
describeContents
()
{
return
0
;
}
public
static
final
Parcelable
.
Creator
<
MdtaMetadataEntry
>
CREATOR
=
new
Parcelable
.
Creator
<
MdtaMetadataEntry
>()
{
@Override
public
MdtaMetadataEntry
createFromParcel
(
Parcel
in
)
{
return
new
MdtaMetadataEntry
(
in
);
}
@Override
public
MdtaMetadataEntry
[]
newArray
(
int
size
)
{
return
new
MdtaMetadataEntry
[
size
];
}
};
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
View file @
1c4ea26f
...
...
@@ -16,6 +16,9 @@
package
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.GaplessInfoHolder
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
...
...
@@ -25,10 +28,9 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
/**
* Parses metadata items stored in ilst atoms.
*/
/** Utilities for handling metadata in MP4. */
/* package */
final
class
MetadataUtil
{
private
static
final
String
TAG
=
"MetadataUtil"
;
...
...
@@ -103,24 +105,73 @@ import com.google.android.exoplayer2.util.Util;
private
static
final
String
LANGUAGE_UNDEFINED
=
"und"
;
private
static
final
int
TYPE_TOP_BYTE_COPYRIGHT
=
0xA9
;
private
static
final
int
TYPE_TOP_BYTE_REPLACEMENT
=
0xFD
;
// Truncated value of \uFFFD.
private
static
final
String
MDTA_KEY_ANDROID_CAPTURE_FPS
=
"com.android.capture.fps"
;
private
static
final
int
MDTA_TYPE_INDICATOR_FLOAT
=
23
;
private
MetadataUtil
()
{}
/**
* Parses a single ilst element from a {@link ParsableByteArray}. The element is read starting
* from the current position of the {@link ParsableByteArray}, and the position is advanced by the
* size of the element. The position is advanced even if the element's type is unrecognized.
* Returns a {@link Format} that is the same as the input format but includes information from the
* specified sources of metadata.
*/
public
static
Format
getFormatWithMetadata
(
int
trackType
,
Format
format
,
@Nullable
Metadata
udtaMetadata
,
@Nullable
Metadata
mdtaMetadata
,
GaplessInfoHolder
gaplessInfoHolder
)
{
if
(
trackType
==
C
.
TRACK_TYPE_AUDIO
)
{
if
(
gaplessInfoHolder
.
hasGaplessInfo
())
{
format
=
format
.
copyWithGaplessInfo
(
gaplessInfoHolder
.
encoderDelay
,
gaplessInfoHolder
.
encoderPadding
);
}
// We assume all udta metadata is associated with the audio track.
if
(
udtaMetadata
!=
null
)
{
format
=
format
.
copyWithMetadata
(
udtaMetadata
);
}
}
else
if
(
trackType
==
C
.
TRACK_TYPE_VIDEO
&&
mdtaMetadata
!=
null
)
{
// Populate only metadata keys that are known to be specific to video.
for
(
int
i
=
0
;
i
<
mdtaMetadata
.
length
();
i
++)
{
Metadata
.
Entry
entry
=
mdtaMetadata
.
get
(
i
);
if
(
entry
instanceof
MdtaMetadataEntry
)
{
MdtaMetadataEntry
mdtaMetadataEntry
=
(
MdtaMetadataEntry
)
entry
;
if
(
MDTA_KEY_ANDROID_CAPTURE_FPS
.
equals
(
mdtaMetadataEntry
.
key
)
&&
mdtaMetadataEntry
.
typeIndicator
==
MDTA_TYPE_INDICATOR_FLOAT
)
{
try
{
float
fps
=
ByteBuffer
.
wrap
(
mdtaMetadataEntry
.
value
).
asFloatBuffer
().
get
();
format
=
format
.
copyWithFrameRate
(
fps
);
format
=
format
.
copyWithMetadata
(
new
Metadata
(
mdtaMetadataEntry
));
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
"Ignoring invalid framerate"
);
}
}
}
}
}
return
format
;
}
/**
* Parses a single userdata ilst element from a {@link ParsableByteArray}. The element is read
* starting from the current position of the {@link ParsableByteArray}, and the position is
* advanced by the size of the element. The position is advanced even if the element's type is
* unrecognized.
*
* @param ilst Holds the data to be parsed.
* @return The parsed element, or null if the element's type was not recognized.
*/
public
static
@Nullable
Metadata
.
Entry
parseIlstElement
(
ParsableByteArray
ilst
)
{
@Nullable
public
static
Metadata
.
Entry
parseIlstElement
(
ParsableByteArray
ilst
)
{
int
position
=
ilst
.
getPosition
();
int
endPosition
=
position
+
ilst
.
readInt
();
int
type
=
ilst
.
readInt
();
int
typeTopByte
=
(
type
>>
24
)
&
0xFF
;
try
{
if
(
typeTopByte
==
'\
u00A9
'
/* Copyright char */
||
typeTopByte
==
'\
uFFFD
'
/* Replacement char */
)
{
if
(
typeTopByte
==
TYPE_TOP_BYTE_COPYRIGHT
||
typeTopByte
==
TYPE_TOP_BYTE_REPLACEMENT
)
{
int
shortType
=
type
&
0x00FFFFFF
;
if
(
shortType
==
SHORT_TYPE_COMMENT
)
{
return
parseCommentAttribute
(
type
,
ilst
);
...
...
@@ -185,7 +236,36 @@ import com.google.android.exoplayer2.util.Util;
}
}
private
static
@Nullable
TextInformationFrame
parseTextAttribute
(
/**
* Parses an 'mdta' metadata entry starting at the current position in an ilst box.
*
* @param ilst The ilst box.
* @param endPosition The end position of the entry in the ilst box.
* @param key The mdta metadata entry key for the entry.
* @return The parsed element, or null if the entry wasn't recognized.
*/
@Nullable
public
static
MdtaMetadataEntry
parseMdtaMetadataEntryFromIlst
(
ParsableByteArray
ilst
,
int
endPosition
,
String
key
)
{
int
atomPosition
;
while
((
atomPosition
=
ilst
.
getPosition
())
<
endPosition
)
{
int
atomSize
=
ilst
.
readInt
();
int
atomType
=
ilst
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
int
typeIndicator
=
ilst
.
readInt
();
int
localeIndicator
=
ilst
.
readInt
();
int
dataSize
=
atomSize
-
16
;
byte
[]
value
=
new
byte
[
dataSize
];
ilst
.
readBytes
(
value
,
0
,
dataSize
);
return
new
MdtaMetadataEntry
(
key
,
value
,
localeIndicator
,
typeIndicator
);
}
ilst
.
setPosition
(
atomPosition
+
atomSize
);
}
return
null
;
}
@Nullable
private
static
TextInformationFrame
parseTextAttribute
(
int
type
,
String
id
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
...
@@ -198,7 +278,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
CommentFrame
parseCommentAttribute
(
int
type
,
ParsableByteArray
data
)
{
@Nullable
private
static
CommentFrame
parseCommentAttribute
(
int
type
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
...
@@ -210,7 +291,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
Id3Frame
parseUint8Attribute
(
@Nullable
private
static
Id3Frame
parseUint8Attribute
(
int
type
,
String
id
,
ParsableByteArray
data
,
...
...
@@ -229,7 +311,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
TextInformationFrame
parseIndexAndCountAttribute
(
@Nullable
private
static
TextInformationFrame
parseIndexAndCountAttribute
(
int
type
,
String
attributeName
,
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
...
...
@@ -249,8 +332,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
@Nullable
private
static
TextInformationFrame
parseStandardGenreAttribute
(
ParsableByteArray
data
)
{
int
genreCode
=
parseUint8AttributeValue
(
data
);
String
genreString
=
(
0
<
genreCode
&&
genreCode
<=
STANDARD_GENRES
.
length
)
?
STANDARD_GENRES
[
genreCode
-
1
]
:
null
;
...
...
@@ -261,7 +344,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
@Nullable
private
static
ApicFrame
parseCoverArt
(
ParsableByteArray
data
)
{
int
atomSize
=
data
.
readInt
();
int
atomType
=
data
.
readInt
();
if
(
atomType
==
Atom
.
TYPE_data
)
{
...
...
@@ -285,8 +369,8 @@ import com.google.android.exoplayer2.util.Util;
return
null
;
}
private
static
@Nullable
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
@Nullable
private
static
Id3Frame
parseInternalAttribute
(
ParsableByteArray
data
,
int
endPosition
)
{
String
domain
=
null
;
String
name
=
null
;
int
dataAtomPosition
=
-
1
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
1c4ea26f
...
...
@@ -75,7 +75,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
static
final
int
STATE_READING_ATOM_PAYLOAD
=
1
;
private
static
final
int
STATE_READING_SAMPLE
=
2
;
/
/ Brand stored in the ftyp atom for QuickTime media.
/
** Brand stored in the ftyp atom for QuickTime media. */
private
static
final
int
BRAND_QUICKTIME
=
Util
.
getIntegerCodeForString
(
"qt "
);
/**
...
...
@@ -377,15 +377,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
long
durationUs
=
C
.
TIME_UNSET
;
List
<
Mp4Track
>
tracks
=
new
ArrayList
<>();
Metadata
metadata
=
null
;
// Process metadata.
Metadata
udtaMetadata
=
null
;
GaplessInfoHolder
gaplessInfoHolder
=
new
GaplessInfoHolder
();
Atom
.
LeafAtom
udta
=
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_udta
);
if
(
udta
!=
null
)
{
m
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
m
etadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
m
etadata
);
udtaM
etadata
=
AtomParsers
.
parseUdta
(
udta
,
isQuickTime
);
if
(
udtaM
etadata
!=
null
)
{
gaplessInfoHolder
.
setFromMetadata
(
udtaM
etadata
);
}
}
Metadata
mdtaMetadata
=
null
;
Atom
.
ContainerAtom
meta
=
moov
.
getContainerAtomOfType
(
Atom
.
TYPE_meta
);
if
(
meta
!=
null
)
{
mdtaMetadata
=
AtomParsers
.
parseMdtaFromMeta
(
meta
);
}
boolean
ignoreEditLists
=
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
;
ArrayList
<
TrackSampleTable
>
trackSampleTables
=
...
...
@@ -401,15 +407,9 @@ public final class Mp4Extractor implements Extractor, SeekMap {
// Allow ten source samples per output sample, like the platform extractor.
int
maxInputSize
=
trackSampleTable
.
maximumSize
+
3
*
10
;
Format
format
=
track
.
format
.
copyWithMaxInputSize
(
maxInputSize
);
if
(
track
.
type
==
C
.
TRACK_TYPE_AUDIO
)
{
if
(
gaplessInfoHolder
.
hasGaplessInfo
())
{
format
=
format
.
copyWithGaplessInfo
(
gaplessInfoHolder
.
encoderDelay
,
gaplessInfoHolder
.
encoderPadding
);
}
if
(
metadata
!=
null
)
{
format
=
format
.
copyWithMetadata
(
metadata
);
}
}
format
=
MetadataUtil
.
getFormatWithMetadata
(
track
.
type
,
format
,
udtaMetadata
,
mdtaMetadata
,
gaplessInfoHolder
);
mp4Track
.
trackOutput
.
format
(
format
);
durationUs
=
...
...
@@ -716,24 +716,37 @@ public final class Mp4Extractor implements Extractor, SeekMap {
return
false
;
}
/**
* Returns whether the extractor should decode a leaf atom with type {@code atom}.
*/
/** Returns whether the extractor should decode a leaf atom with type {@code atom}. */
private
static
boolean
shouldParseLeafAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_hdlr
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_stss
||
atom
==
Atom
.
TYPE_ctts
||
atom
==
Atom
.
TYPE_elst
||
atom
==
Atom
.
TYPE_stsc
||
atom
==
Atom
.
TYPE_stsz
||
atom
==
Atom
.
TYPE_stz2
||
atom
==
Atom
.
TYPE_stco
||
atom
==
Atom
.
TYPE_co64
||
atom
==
Atom
.
TYPE_tkhd
||
atom
==
Atom
.
TYPE_ftyp
||
atom
==
Atom
.
TYPE_udta
;
return
atom
==
Atom
.
TYPE_mdhd
||
atom
==
Atom
.
TYPE_mvhd
||
atom
==
Atom
.
TYPE_hdlr
||
atom
==
Atom
.
TYPE_stsd
||
atom
==
Atom
.
TYPE_stts
||
atom
==
Atom
.
TYPE_stss
||
atom
==
Atom
.
TYPE_ctts
||
atom
==
Atom
.
TYPE_elst
||
atom
==
Atom
.
TYPE_stsc
||
atom
==
Atom
.
TYPE_stsz
||
atom
==
Atom
.
TYPE_stz2
||
atom
==
Atom
.
TYPE_stco
||
atom
==
Atom
.
TYPE_co64
||
atom
==
Atom
.
TYPE_tkhd
||
atom
==
Atom
.
TYPE_ftyp
||
atom
==
Atom
.
TYPE_udta
||
atom
==
Atom
.
TYPE_keys
||
atom
==
Atom
.
TYPE_ilst
;
}
/**
* Returns whether the extractor should decode a container atom with type {@code atom}.
*/
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
private
static
boolean
shouldParseContainerAtom
(
int
atom
)
{
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_trak
||
atom
==
Atom
.
TYPE_mdia
||
atom
==
Atom
.
TYPE_minf
||
atom
==
Atom
.
TYPE_stbl
||
atom
==
Atom
.
TYPE_edts
;
return
atom
==
Atom
.
TYPE_moov
||
atom
==
Atom
.
TYPE_trak
||
atom
==
Atom
.
TYPE_mdia
||
atom
==
Atom
.
TYPE_minf
||
atom
==
Atom
.
TYPE_stbl
||
atom
==
Atom
.
TYPE_edts
||
atom
==
Atom
.
TYPE_meta
;
}
private
static
final
class
Mp4Track
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java
View file @
1c4ea26f
...
...
@@ -27,9 +27,7 @@ import java.io.IOException;
*/
/* package */
final
class
Sniffer
{
/**
* The maximum number of bytes to peek when sniffing.
*/
/** The maximum number of bytes to peek when sniffing. */
private
static
final
int
SEARCH_LENGTH
=
4
*
1024
;
private
static
final
int
[]
COMPATIBLE_BRANDS
=
new
int
[]
{
...
...
@@ -109,15 +107,19 @@ import java.io.IOException;
headerSize
=
Atom
.
LONG_HEADER_SIZE
;
input
.
peekFully
(
buffer
.
data
,
Atom
.
HEADER_SIZE
,
Atom
.
LONG_HEADER_SIZE
-
Atom
.
HEADER_SIZE
);
buffer
.
setLimit
(
Atom
.
LONG_HEADER_SIZE
);
atomSize
=
buffer
.
read
UnsignedLongTo
Long
();
atomSize
=
buffer
.
readLong
();
}
else
if
(
atomSize
==
Atom
.
EXTENDS_TO_END_SIZE
)
{
// The atom extends to the end of the file.
long
e
ndPosition
=
input
.
getLength
();
if
(
e
ndPosition
!=
C
.
LENGTH_UNSET
)
{
atomSize
=
endPosition
-
input
.
get
Position
()
+
headerSize
;
long
fileE
ndPosition
=
input
.
getLength
();
if
(
fileE
ndPosition
!=
C
.
LENGTH_UNSET
)
{
atomSize
=
fileEndPosition
-
input
.
getPeek
Position
()
+
headerSize
;
}
}
if
(
inputLength
!=
C
.
LENGTH_UNSET
&&
bytesSearched
+
atomSize
>
inputLength
)
{
// The file is invalid because the atom extends past the end of the file.
return
false
;
}
if
(
atomSize
<
headerSize
)
{
// The file is invalid because the atom size is too small for its header.
return
false
;
...
...
@@ -125,6 +127,13 @@ import java.io.IOException;
bytesSearched
+=
headerSize
;
if
(
atomType
==
Atom
.
TYPE_moov
)
{
// We have seen the moov atom. We increase the search size to make sure we don't miss an
// mvex atom because the moov's size exceeds the search length.
bytesToSearch
+=
(
int
)
atomSize
;
if
(
inputLength
!=
C
.
LENGTH_UNSET
&&
bytesToSearch
>
inputLength
)
{
// Make sure we don't exceed the file size.
bytesToSearch
=
(
int
)
inputLength
;
}
// Check for an mvex atom inside the moov atom to identify whether the file is fragmented.
continue
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java
View file @
1c4ea26f
...
...
@@ -64,6 +64,9 @@ import com.google.android.exoplayer2.util.Util;
this
.
flags
=
flags
;
this
.
durationUs
=
durationUs
;
sampleCount
=
offsets
.
length
;
if
(
flags
.
length
>
0
)
{
flags
[
flags
.
length
-
1
]
|=
C
.
BUFFER_FLAG_LAST_SAMPLE
;
}
}
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
View file @
1c4ea26f
...
...
@@ -326,7 +326,9 @@ public final class MediaCodecUtil {
||
Util
.
MODEL
.
startsWith
(
"SM-G350"
)
||
Util
.
MODEL
.
startsWith
(
"SM-G386"
)
||
Util
.
MODEL
.
startsWith
(
"SM-T231"
)
||
Util
.
MODEL
.
startsWith
(
"SM-T530"
)))
{
||
Util
.
MODEL
.
startsWith
(
"SM-T530"
)
||
Util
.
MODEL
.
startsWith
(
"SCH-I535"
)
||
Util
.
MODEL
.
startsWith
(
"SPH-L710"
)))
{
return
false
;
}
if
(
"OMX.brcm.audio.mp3.decoder"
.
equals
(
name
)
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java
View file @
1c4ea26f
...
...
@@ -240,10 +240,10 @@ public final class ClippingMediaSource extends CompositeMediaSource<Void> {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
ClippingMediaPeriod
mediaPeriod
=
new
ClippingMediaPeriod
(
mediaSource
.
createPeriod
(
id
,
allocator
),
mediaSource
.
createPeriod
(
id
,
allocator
,
startPositionUs
),
enableInitialDiscontinuity
,
periodStartUs
,
periodEndUs
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
View file @
1c4ea26f
...
...
@@ -16,13 +16,12 @@
package
com
.
google
.
android
.
exoplayer2
.
source
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlayer
;
import
com.google.android.exoplayer2.PlayerMessage
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.ConcatenatingMediaSource.MediaSourceHolder
;
import
com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder
;
...
...
@@ -45,8 +44,7 @@ import java.util.Map;
* during playback. It is valid for the same {@link MediaSource} instance to be present more than
* once in the concatenation. Access to this class is thread-safe.
*/
public
class
ConcatenatingMediaSource
extends
CompositeMediaSource
<
MediaSourceHolder
>
implements
PlayerMessage
.
Target
{
public
class
ConcatenatingMediaSource
extends
CompositeMediaSource
<
MediaSourceHolder
>
{
private
static
final
int
MSG_ADD
=
0
;
private
static
final
int
MSG_REMOVE
=
1
;
...
...
@@ -68,8 +66,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Period
period
;
private
@Nullable
ExoPlayer
play
er
;
private
@Nullable
Handler
playerApplication
Handler
;
@Nullable
private
Handler
playbackThreadHandl
er
;
@Nullable
private
Handler
applicationThread
Handler
;
private
boolean
listenerNotificationScheduled
;
private
ShuffleOrder
shuffleOrder
;
private
int
windowCount
;
...
...
@@ -239,12 +237,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
mediaSourceHolders
.
add
(
new
MediaSourceHolder
(
mediaSource
));
}
mediaSourcesPublic
.
addAll
(
index
,
mediaSourceHolders
);
if
(
player
!=
null
&&
!
mediaSources
.
isEmpty
())
{
player
.
createMessage
(
this
)
.
setType
(
MSG_ADD
)
.
setPayload
(
new
MessageData
<>(
index
,
mediaSourceHolders
,
actionOnCompletion
))
.
send
();
if
(
playbackThreadHandler
!=
null
&&
!
mediaSources
.
isEmpty
())
{
playbackThreadHandler
.
obtainMessage
(
MSG_ADD
,
new
MessageData
<>(
index
,
mediaSourceHolders
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
...
...
@@ -328,12 +324,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
return
;
}
if
(
player
!=
null
)
{
player
.
createMessage
(
this
)
.
setType
(
MSG_REMOVE
)
.
setPayload
(
new
MessageData
<>(
fromIndex
,
toIndex
,
actionOnCompletion
))
.
send
();
if
(
playbackThreadHandler
!=
null
)
{
playbackThreadHandler
.
obtainMessage
(
MSG_REMOVE
,
new
MessageData
<>(
fromIndex
,
toIndex
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
...
...
@@ -371,12 +365,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
return
;
}
mediaSourcesPublic
.
add
(
newIndex
,
mediaSourcesPublic
.
remove
(
currentIndex
));
if
(
player
!=
null
)
{
player
.
createMessage
(
this
)
.
setType
(
MSG_MOVE
)
.
setPayload
(
new
MessageData
<>(
currentIndex
,
newIndex
,
actionOnCompletion
))
.
send
();
if
(
playbackThreadHandler
!=
null
)
{
playbackThreadHandler
.
obtainMessage
(
MSG_MOVE
,
new
MessageData
<>(
currentIndex
,
newIndex
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
...
...
@@ -430,8 +422,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
*/
public
final
synchronized
void
setShuffleOrder
(
ShuffleOrder
shuffleOrder
,
@Nullable
Runnable
actionOnCompletion
)
{
ExoPlayer
player
=
this
.
play
er
;
if
(
player
!=
null
)
{
Handler
playbackThreadHandler
=
this
.
playbackThreadHandl
er
;
if
(
play
backThreadHandl
er
!=
null
)
{
int
size
=
getSize
();
if
(
shuffleOrder
.
getLength
()
!=
size
)
{
shuffleOrder
=
...
...
@@ -439,11 +431,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
.
cloneAndClear
()
.
cloneAndInsert
(
/* insertionIndex= */
0
,
/* insertionCount= */
size
);
}
player
.
createMessage
(
this
)
.
setType
(
MSG_SET_SHUFFLE_ORDER
)
.
setPayload
(
new
MessageData
<>(
/* index= */
0
,
shuffleOrder
,
actionOnCompletion
))
.
send
();
play
backThreadHandl
er
.
obtainMessage
(
MSG_SET_SHUFFLE_ORDER
,
new
MessageData
<>(
/* index= */
0
,
shuffleOrder
,
actionOnCompletion
))
.
send
ToTarget
();
}
else
{
this
.
shuffleOrder
=
shuffleOrder
.
getLength
()
>
0
?
shuffleOrder
.
cloneAndClear
()
:
shuffleOrder
;
...
...
@@ -465,8 +457,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
boolean
isTopLevelSource
,
@Nullable
TransferListener
mediaTransferListener
)
{
super
.
prepareSourceInternal
(
player
,
isTopLevelSource
,
mediaTransferListener
);
this
.
player
=
player
;
playerApplication
Handler
=
new
Handler
(
player
.
getApplicationLooper
());
playbackThreadHandler
=
new
Handler
(
/* callback= */
this
::
handleMessage
)
;
applicationThread
Handler
=
new
Handler
(
player
.
getApplicationLooper
());
if
(
mediaSourcesPublic
.
isEmpty
())
{
notifyListener
();
}
else
{
...
...
@@ -484,7 +476,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
@Override
public
final
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
final
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
Object
mediaSourceHolderUid
=
getMediaSourceHolderUid
(
id
.
periodUid
);
MediaSourceHolder
holder
=
mediaSourceByUid
.
get
(
mediaSourceHolderUid
);
if
(
holder
==
null
)
{
...
...
@@ -492,7 +485,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
holder
=
new
MediaSourceHolder
(
new
DummyMediaSource
());
holder
.
hasStartedPreparing
=
true
;
}
DeferredMediaPeriod
mediaPeriod
=
new
DeferredMediaPeriod
(
holder
.
mediaSource
,
id
,
allocator
);
DeferredMediaPeriod
mediaPeriod
=
new
DeferredMediaPeriod
(
holder
.
mediaSource
,
id
,
allocator
,
startPositionUs
);
mediaSourceByMediaPeriod
.
put
(
mediaPeriod
,
holder
);
holder
.
activeMediaPeriods
.
add
(
mediaPeriod
);
if
(!
holder
.
hasStartedPreparing
)
{
...
...
@@ -519,8 +513,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
super
.
releaseSourceInternal
();
mediaSourceHolders
.
clear
();
mediaSourceByUid
.
clear
();
player
=
null
;
playerApplication
Handler
=
null
;
play
backThreadHandl
er
=
null
;
applicationThread
Handler
=
null
;
shuffleOrder
=
shuffleOrder
.
cloneAndClear
();
windowCount
=
0
;
periodCount
=
0
;
...
...
@@ -556,24 +550,22 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
return
windowIndex
+
mediaSourceHolder
.
firstWindowIndexInChild
;
}
@Override
@SuppressWarnings
(
"unchecked"
)
public
final
void
handleMessage
(
int
messageType
,
@Nullable
Object
message
)
throws
ExoPlaybackException
{
if
(
player
==
null
)
{
private
boolean
handleMessage
(
Message
msg
)
{
if
(
playbackThreadHandler
==
null
)
{
// Stale event.
return
;
return
false
;
}
switch
(
m
essageType
)
{
switch
(
m
sg
.
what
)
{
case
MSG_ADD:
MessageData
<
Collection
<
MediaSourceHolder
>>
addMessage
=
(
MessageData
<
Collection
<
MediaSourceHolder
>>)
Util
.
castNonNull
(
m
essage
);
(
MessageData
<
Collection
<
MediaSourceHolder
>>)
Util
.
castNonNull
(
m
sg
.
obj
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
addMessage
.
index
,
addMessage
.
customData
.
size
());
addMediaSourcesInternal
(
addMessage
.
index
,
addMessage
.
customData
);
scheduleListenerNotification
(
addMessage
.
actionOnCompletion
);
break
;
case
MSG_REMOVE:
MessageData
<
Integer
>
removeMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
m
essage
);
MessageData
<
Integer
>
removeMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
m
sg
.
obj
);
int
fromIndex
=
removeMessage
.
index
;
int
toIndex
=
removeMessage
.
customData
;
if
(
fromIndex
==
0
&&
toIndex
==
shuffleOrder
.
getLength
())
{
...
...
@@ -587,7 +579,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
scheduleListenerNotification
(
removeMessage
.
actionOnCompletion
);
break
;
case
MSG_MOVE:
MessageData
<
Integer
>
moveMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
m
essage
);
MessageData
<
Integer
>
moveMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
m
sg
.
obj
);
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
moveMessage
.
index
,
moveMessage
.
index
+
1
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
moveMessage
.
customData
,
1
);
moveMediaSourceInternal
(
moveMessage
.
index
,
moveMessage
.
customData
);
...
...
@@ -595,7 +587,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
break
;
case
MSG_SET_SHUFFLE_ORDER:
MessageData
<
ShuffleOrder
>
shuffleOrderMessage
=
(
MessageData
<
ShuffleOrder
>)
Util
.
castNonNull
(
m
essage
);
(
MessageData
<
ShuffleOrder
>)
Util
.
castNonNull
(
m
sg
.
obj
);
shuffleOrder
=
shuffleOrderMessage
.
customData
;
scheduleListenerNotification
(
shuffleOrderMessage
.
actionOnCompletion
);
break
;
...
...
@@ -603,8 +595,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
notifyListener
();
break
;
case
MSG_ON_COMPLETION:
List
<
Runnable
>
actionsOnCompletion
=
(
List
<
Runnable
>)
Util
.
castNonNull
(
m
essage
);
Handler
handler
=
Assertions
.
checkNotNull
(
playerApplication
Handler
);
List
<
Runnable
>
actionsOnCompletion
=
(
List
<
Runnable
>)
Util
.
castNonNull
(
m
sg
.
obj
);
Handler
handler
=
Assertions
.
checkNotNull
(
applicationThread
Handler
);
for
(
int
i
=
0
;
i
<
actionsOnCompletion
.
size
();
i
++)
{
handler
.
post
(
actionsOnCompletion
.
get
(
i
));
}
...
...
@@ -612,11 +604,14 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
default
:
throw
new
IllegalStateException
();
}
return
true
;
}
private
void
scheduleListenerNotification
(
@Nullable
Runnable
actionOnCompletion
)
{
if
(!
listenerNotificationScheduled
)
{
Assertions
.
checkNotNull
(
player
).
createMessage
(
this
).
setType
(
MSG_NOTIFY_LISTENER
).
send
();
Assertions
.
checkNotNull
(
playbackThreadHandler
)
.
obtainMessage
(
MSG_NOTIFY_LISTENER
)
.
sendToTarget
();
listenerNotificationScheduled
=
true
;
}
if
(
actionOnCompletion
!=
null
)
{
...
...
@@ -636,11 +631,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
mediaSourceHolders
,
windowCount
,
periodCount
,
shuffleOrder
,
isAtomic
),
/* manifest= */
null
);
if
(!
actionsOnCompletion
.
isEmpty
())
{
Assertions
.
checkNotNull
(
player
)
.
createMessage
(
this
)
.
setType
(
MSG_ON_COMPLETION
)
.
setPayload
(
actionsOnCompletion
)
.
send
();
Assertions
.
checkNotNull
(
playbackThreadHandler
)
.
obtainMessage
(
MSG_ON_COMPLETION
,
actionsOnCompletion
)
.
sendToTarget
();
}
}
...
...
@@ -718,6 +711,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
// unlikely to be a problem as a non-zero default position usually only occurs for live
// playbacks and seeking to zero in a live window would cause BehindLiveWindowExceptions
// anyway.
timeline
.
getWindow
(
/* windowIndex= */
0
,
window
);
long
windowStartPositionUs
=
window
.
getDefaultPositionUs
();
if
(
deferredMediaPeriod
!=
null
)
{
long
periodPreparePositionUs
=
deferredMediaPeriod
.
getPreparePositionUs
();
...
...
@@ -1101,7 +1095,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
throw
new
UnsupportedOperationException
();
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/DeferredMediaPeriod.java
View file @
1c4ea26f
...
...
@@ -25,7 +25,7 @@ import java.io.IOException;
/**
* Media period that wraps a media source and defers calling its {@link
* MediaSource#createPeriod(MediaPeriodId, Allocator)} method until {@link
* MediaSource#createPeriod(MediaPeriodId, Allocator
, long
)} method until {@link
* #createPeriod(MediaPeriodId)} has been called. This is useful if you need to return a media
* period immediately but the media source that should create it is not yet prepared.
*/
...
...
@@ -60,11 +60,14 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
* @param mediaSource The media source to wrap.
* @param id The identifier used to create the deferred media period.
* @param allocator The allocator used to create the media period.
* @param preparePositionUs The expected start position, in microseconds.
*/
public
DeferredMediaPeriod
(
MediaSource
mediaSource
,
MediaPeriodId
id
,
Allocator
allocator
)
{
public
DeferredMediaPeriod
(
MediaSource
mediaSource
,
MediaPeriodId
id
,
Allocator
allocator
,
long
preparePositionUs
)
{
this
.
id
=
id
;
this
.
allocator
=
allocator
;
this
.
mediaSource
=
mediaSource
;
this
.
preparePositionUs
=
preparePositionUs
;
preparePositionOverrideUs
=
C
.
TIME_UNSET
;
}
...
...
@@ -86,28 +89,25 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
/**
* Overrides the default prepare position at which to prepare the media period. This value is only
* used if
the call to {@link MediaPeriod#prepare(Callback, long)} is being deferred
.
* used if
called before {@link #createPeriod(MediaPeriodId)}
.
*
* @param
defaultP
reparePositionUs The default prepare position to use, in microseconds.
* @param
p
reparePositionUs The default prepare position to use, in microseconds.
*/
public
void
overridePreparePositionUs
(
long
defaultP
reparePositionUs
)
{
preparePositionOverrideUs
=
defaultP
reparePositionUs
;
public
void
overridePreparePositionUs
(
long
p
reparePositionUs
)
{
preparePositionOverrideUs
=
p
reparePositionUs
;
}
/**
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator
)} on the wrapped source then
*
prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()}
* to release the period.
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator
, long)} on the wrapped source
*
then prepares it if {@link #prepare(Callback, long)} has been called. Call {@link
*
#releasePeriod()}
to release the period.
*
* @param id The identifier that should be used to create the media period from the media source.
*/
public
void
createPeriod
(
MediaPeriodId
id
)
{
mediaPeriod
=
mediaSource
.
createPeriod
(
id
,
allocator
);
long
preparePositionUs
=
getPreparePositionWithOverride
(
this
.
preparePositionUs
);
mediaPeriod
=
mediaSource
.
createPeriod
(
id
,
allocator
,
preparePositionUs
);
if
(
callback
!=
null
)
{
long
preparePositionUs
=
preparePositionOverrideUs
!=
C
.
TIME_UNSET
?
preparePositionOverrideUs
:
this
.
preparePositionUs
;
mediaPeriod
.
prepare
(
this
,
preparePositionUs
);
}
}
...
...
@@ -124,9 +124,8 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override
public
void
prepare
(
Callback
callback
,
long
preparePositionUs
)
{
this
.
callback
=
callback
;
this
.
preparePositionUs
=
preparePositionUs
;
if
(
mediaPeriod
!=
null
)
{
mediaPeriod
.
prepare
(
this
,
preparePositionUs
);
mediaPeriod
.
prepare
(
this
,
getPreparePositionWithOverride
(
this
.
preparePositionUs
)
);
}
}
...
...
@@ -217,4 +216,9 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
callback
.
onPrepared
(
this
);
}
private
long
getPreparePositionWithOverride
(
long
preparePositionUs
)
{
return
preparePositionOverrideUs
!=
C
.
TIME_UNSET
?
preparePositionOverrideUs
:
preparePositionUs
;
}
}
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
View file @
1c4ea26f
...
...
@@ -346,18 +346,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
}
else
if
(
isPendingReset
())
{
return
pendingResetPositionUs
;
}
long
largestQueuedTimestampUs
;
long
largestQueuedTimestampUs
=
C
.
TIME_UNSET
;
if
(
haveAudioVideoTracks
)
{
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
largestQueuedTimestampUs
=
Long
.
MAX_VALUE
;
int
trackCount
=
sampleQueues
.
length
;
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
if
(
trackIsAudioVideoFlags
[
i
])
{
if
(
trackIsAudioVideoFlags
[
i
]
&&
!
sampleQueues
[
i
].
isLastSampleQueued
()
)
{
largestQueuedTimestampUs
=
Math
.
min
(
largestQueuedTimestampUs
,
sampleQueues
[
i
].
getLargestQueuedTimestampUs
());
}
}
}
else
{
}
if
(
largestQueuedTimestampUs
==
C
.
TIME_UNSET
)
{
largestQueuedTimestampUs
=
getLargestQueuedTimestampUs
();
}
return
largestQueuedTimestampUs
==
Long
.
MIN_VALUE
?
lastSeekPositionUs
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java
View file @
1c4ea26f
...
...
@@ -370,7 +370,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
boolean
isTopLevelSource
,
@Nullable
TransferListener
mediaTransferListener
)
{
transferListener
=
mediaTransferListener
;
notifySourceInfoRefreshed
(
timelineDurationUs
,
/* isSeekable= */
fals
e
);
notifySourceInfoRefreshed
(
timelineDurationUs
,
timelineIsSeekabl
e
);
}
@Override
...
...
@@ -379,7 +379,7 @@ public final class ExtractorMediaSource extends BaseMediaSource
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
DataSource
dataSource
=
dataSourceFactory
.
createDataSource
();
if
(
transferListener
!=
null
)
{
dataSource
.
addTransferListener
(
transferListener
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
View file @
1c4ea26f
...
...
@@ -80,14 +80,15 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
if
(
loopCount
==
Integer
.
MAX_VALUE
)
{
return
childSource
.
createPeriod
(
id
,
allocator
);
return
childSource
.
createPeriod
(
id
,
allocator
,
startPositionUs
);
}
Object
childPeriodUid
=
LoopingTimeline
.
getChildPeriodUidFromConcatenatedUid
(
id
.
periodUid
);
MediaPeriodId
childMediaPeriodId
=
id
.
copyWithPeriodUid
(
childPeriodUid
);
childMediaPeriodIdToMediaPeriodId
.
put
(
childMediaPeriodId
,
id
);
MediaPeriod
mediaPeriod
=
childSource
.
createPeriod
(
childMediaPeriodId
,
allocator
);
MediaPeriod
mediaPeriod
=
childSource
.
createPeriod
(
childMediaPeriodId
,
allocator
,
startPositionUs
);
mediaPeriodToChildMediaPeriodId
.
put
(
mediaPeriod
,
childMediaPeriodId
);
return
mediaPeriod
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java
View file @
1c4ea26f
...
...
@@ -35,8 +35,8 @@ import java.io.IOException;
* on the {@link SourceInfoRefreshListener}s passed to {@link #prepareSource(ExoPlayer,
* boolean, SourceInfoRefreshListener, TransferListener)}.
* <li>To provide {@link MediaPeriod} instances for the periods in its timeline. MediaPeriods are
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator
)}, and provide a way for
* the player to load and read the media.
* obtained by calling {@link #createPeriod(MediaPeriodId, Allocator
, long)}, and provide a
*
way for
the player to load and read the media.
* </ul>
*
* All methods are called on the player's internal playback thread, as described in the {@link
...
...
@@ -274,9 +274,10 @@ public interface MediaSource {
*
* @param id The identifier of the period.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param startPositionUs The expected start position, in microseconds.
* @return A new {@link MediaPeriod}.
*/
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
);
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
);
/**
* Releases the period.
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java
View file @
1c4ea26f
...
...
@@ -124,13 +124,13 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
MediaPeriod
[]
periods
=
new
MediaPeriod
[
mediaSources
.
length
];
int
periodIndex
=
timelines
[
0
].
getIndexOfPeriod
(
id
.
periodUid
);
for
(
int
i
=
0
;
i
<
periods
.
length
;
i
++)
{
MediaPeriodId
childMediaPeriodId
=
id
.
copyWithPeriodUid
(
timelines
[
i
].
getUidOfPeriod
(
periodIndex
));
periods
[
i
]
=
mediaSources
[
i
].
createPeriod
(
childMediaPeriodId
,
allocator
);
periods
[
i
]
=
mediaSources
[
i
].
createPeriod
(
childMediaPeriodId
,
allocator
,
startPositionUs
);
}
return
new
MergingMediaPeriod
(
compositeSequenceableLoaderFactory
,
periods
);
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java
View file @
1c4ea26f
...
...
@@ -57,6 +57,7 @@ import com.google.android.exoplayer2.util.Util;
private
long
largestDiscardedTimestampUs
;
private
long
largestQueuedTimestampUs
;
private
boolean
isLastSampleQueued
;
private
boolean
upstreamKeyframeRequired
;
private
boolean
upstreamFormatRequired
;
private
Format
upstreamFormat
;
...
...
@@ -93,6 +94,7 @@ import com.google.android.exoplayer2.util.Util;
upstreamKeyframeRequired
=
true
;
largestDiscardedTimestampUs
=
Long
.
MIN_VALUE
;
largestQueuedTimestampUs
=
Long
.
MIN_VALUE
;
isLastSampleQueued
=
false
;
if
(
resetUpstreamFormat
)
{
upstreamFormat
=
null
;
upstreamFormatRequired
=
true
;
...
...
@@ -118,6 +120,7 @@ import com.google.android.exoplayer2.util.Util;
Assertions
.
checkArgument
(
0
<=
discardCount
&&
discardCount
<=
(
length
-
readPosition
));
length
-=
discardCount
;
largestQueuedTimestampUs
=
Math
.
max
(
largestDiscardedTimestampUs
,
getLargestTimestamp
(
length
));
isLastSampleQueued
=
discardCount
==
0
&&
isLastSampleQueued
;
if
(
length
==
0
)
{
return
0
;
}
else
{
...
...
@@ -186,6 +189,19 @@ import com.google.android.exoplayer2.util.Util;
return
largestQueuedTimestampUs
;
}
/**
* Returns whether the last sample of the stream has knowingly been queued. A return value of
* {@code false} means that the last sample had not been queued or that it's unknown whether the
* last sample has been queued.
*
* <p>Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
* considered as having been queued. Samples that were dequeued from the front of the queue are
* considered as having been queued.
*/
public
synchronized
boolean
isLastSampleQueued
()
{
return
isLastSampleQueued
;
}
/** Returns the timestamp of the first sample, or {@link Long#MIN_VALUE} if the queue is empty. */
public
synchronized
long
getFirstTimestampUs
()
{
return
length
==
0
?
Long
.
MIN_VALUE
:
timesUs
[
relativeFirstIndex
];
...
...
@@ -224,7 +240,7 @@ import com.google.android.exoplayer2.util.Util;
boolean
formatRequired
,
boolean
loadingFinished
,
Format
downstreamFormat
,
SampleExtrasHolder
extrasHolder
)
{
if
(!
hasNextSample
())
{
if
(
loadingFinished
)
{
if
(
loadingFinished
||
isLastSampleQueued
)
{
buffer
.
setFlags
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
return
C
.
RESULT_BUFFER_READ
;
}
else
if
(
upstreamFormat
!=
null
...
...
@@ -388,7 +404,9 @@ import com.google.android.exoplayer2.util.Util;
upstreamKeyframeRequired
=
false
;
}
Assertions
.
checkState
(!
upstreamFormatRequired
);
commitSampleTimestamp
(
timeUs
);
isLastSampleQueued
=
(
sampleFlags
&
C
.
BUFFER_FLAG_LAST_SAMPLE
)
!=
0
;
largestQueuedTimestampUs
=
Math
.
max
(
largestQueuedTimestampUs
,
timeUs
);
int
relativeEndIndex
=
getRelativeIndex
(
length
);
timesUs
[
relativeEndIndex
]
=
timeUs
;
...
...
@@ -439,10 +457,6 @@ import com.google.android.exoplayer2.util.Util;
}
}
public
synchronized
void
commitSampleTimestamp
(
long
timeUs
)
{
largestQueuedTimestampUs
=
Math
.
max
(
largestQueuedTimestampUs
,
timeUs
);
}
/**
* Attempts to discard samples from the end of the queue to allow samples starting from the
* specified timestamp to be spliced in. Samples will not be discarded prior to the read position.
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
View file @
1c4ea26f
...
...
@@ -224,6 +224,15 @@ public class SampleQueue implements TrackOutput {
return
metadataQueue
.
getLargestQueuedTimestampUs
();
}
/**
* Returns whether the last sample of the stream has knowingly been queued. A return value of
* {@code false} means that the last sample had not been queued or that it's unknown whether the
* last sample has been queued.
*/
public
boolean
isLastSampleQueued
()
{
return
metadataQueue
.
isLastSampleQueued
();
}
/** Returns the timestamp of the first sample, or {@link Long#MIN_VALUE} if the queue is empty. */
public
long
getFirstTimestampUs
()
{
return
metadataQueue
.
getFirstTimestampUs
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java
View file @
1c4ea26f
...
...
@@ -318,7 +318,7 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
return
new
SingleSampleMediaPeriod
(
dataSpec
,
dataSourceFactory
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
View file @
1c4ea26f
...
...
@@ -341,7 +341,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
if
(
adPlaybackState
.
adGroupCount
>
0
&&
id
.
isAd
())
{
int
adGroupIndex
=
id
.
adGroupIndex
;
int
adIndexInAdGroup
=
id
.
adIndexInAdGroup
;
...
...
@@ -360,7 +360,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
prepareChildSource
(
id
,
adMediaSource
);
}
MediaSource
mediaSource
=
adGroupMediaSources
[
adGroupIndex
][
adIndexInAdGroup
];
DeferredMediaPeriod
deferredMediaPeriod
=
new
DeferredMediaPeriod
(
mediaSource
,
id
,
allocator
);
DeferredMediaPeriod
deferredMediaPeriod
=
new
DeferredMediaPeriod
(
mediaSource
,
id
,
allocator
,
startPositionUs
);
deferredMediaPeriod
.
setPrepareErrorListener
(
new
AdPrepareErrorListener
(
adUri
,
adGroupIndex
,
adIndexInAdGroup
));
List
<
DeferredMediaPeriod
>
mediaPeriods
=
deferredMediaPeriodByAdMediaSource
.
get
(
mediaSource
);
...
...
@@ -376,7 +377,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
}
return
deferredMediaPeriod
;
}
else
{
DeferredMediaPeriod
mediaPeriod
=
new
DeferredMediaPeriod
(
contentMediaSource
,
id
,
allocator
);
DeferredMediaPeriod
mediaPeriod
=
new
DeferredMediaPeriod
(
contentMediaSource
,
id
,
allocator
,
startPositionUs
);
mediaPeriod
.
createPeriod
(
id
);
return
mediaPeriod
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSource.java
View file @
1c4ea26f
...
...
@@ -64,11 +64,11 @@ public interface DataSource {
long
open
(
DataSpec
dataSpec
)
throws
IOException
;
/**
* Reads up to {@code
l
ength} bytes of data and stores them into {@code buffer}, starting at
* Reads up to {@code
readL
ength} bytes of data and stores them into {@code buffer}, starting at
* index {@code offset}.
*
<p>
*
If {@code length} is zero then 0 is returned. Otherwise, if no data is available because th
e
* end of the opened range has been reached, then {@link C#RESULT_END_OF_INPUT} is returned.
*
*
<p>If {@code readLength} is zero then 0 is returned. Otherwise, if no data is available becaus
e
*
the
end of the opened range has been reached, then {@link C#RESULT_END_OF_INPUT} is returned.
* Otherwise, the call will block until at least one byte of data has been read and the number of
* bytes read is returned.
*
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSink.java
View file @
1c4ea26f
...
...
@@ -43,8 +43,8 @@ public final class CacheDataSink implements DataSink {
private
final
Cache
cache
;
private
final
long
maxCacheFileSize
;
private
final
int
bufferSize
;
private
final
boolean
syncFileDescriptor
;
private
boolean
syncFileDescriptor
;
private
DataSpec
dataSpec
;
private
File
file
;
private
OutputStream
outputStream
;
...
...
@@ -68,25 +68,12 @@ public final class CacheDataSink implements DataSink {
* Constructs a CacheDataSink using the {@link #DEFAULT_BUFFER_SIZE}.
*
* @param cache The cache into which data should be written.
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the sink is opened for
* a {@link DataSpec} whose size exceeds this value, then the data will be fragmented into
* multiple cache files.
*/
public
CacheDataSink
(
Cache
cache
,
long
maxCacheFileSize
)
{
this
(
cache
,
maxCacheFileSize
,
DEFAULT_BUFFER_SIZE
,
true
);
}
/**
* Constructs a CacheDataSink using the {@link #DEFAULT_BUFFER_SIZE}.
*
* @param cache The cache into which data should be written.
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the sink is opened for a
* {@link DataSpec} whose size exceeds this value, then the data will be fragmented into
* multiple cache files.
* @param syncFileDescriptor Whether file descriptors are sync'd when closing output streams.
*/
public
CacheDataSink
(
Cache
cache
,
long
maxCacheFileSize
,
boolean
syncFileDescriptor
)
{
this
(
cache
,
maxCacheFileSize
,
DEFAULT_BUFFER_SIZE
,
syncFileDescriptor
);
public
CacheDataSink
(
Cache
cache
,
long
maxCacheFileSize
)
{
this
(
cache
,
maxCacheFileSize
,
DEFAULT_BUFFER_SIZE
);
}
/**
...
...
@@ -98,23 +85,21 @@ public final class CacheDataSink implements DataSink {
* value disables buffering.
*/
public
CacheDataSink
(
Cache
cache
,
long
maxCacheFileSize
,
int
bufferSize
)
{
this
(
cache
,
maxCacheFileSize
,
bufferSize
,
true
);
this
.
cache
=
Assertions
.
checkNotNull
(
cache
);
this
.
maxCacheFileSize
=
maxCacheFileSize
;
this
.
bufferSize
=
bufferSize
;
syncFileDescriptor
=
true
;
}
/**
* @param cache The cache into which data should be written.
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the sink is opened for a
* {@link DataSpec} whose size exceeds this value, then the data will be fragmented into
* multiple cache files.
* @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
* value disables buffering.
* @param syncFileDescriptor Whether file descriptors are sync'd when closing output streams.
* Sets whether file descriptors are synced when closing output streams.
*
* <p>This method is experimental, and will be renamed or removed in a future release. It should
* only be called before the renderer is used.
*
* @param syncFileDescriptor Whether file descriptors are synced when closing output streams.
*/
public
CacheDataSink
(
Cache
cache
,
long
maxCacheFileSize
,
int
bufferSize
,
boolean
syncFileDescriptor
)
{
this
.
cache
=
Assertions
.
checkNotNull
(
cache
);
this
.
maxCacheFileSize
=
maxCacheFileSize
;
this
.
bufferSize
=
bufferSize
;
public
void
experimental_setSyncFileDescriptor
(
boolean
syncFileDescriptor
)
{
this
.
syncFileDescriptor
=
syncFileDescriptor
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/AtomicFile.java
View file @
1c4ea26f
...
...
@@ -29,7 +29,7 @@ import java.io.OutputStream;
* has successfully completed.
*
* <p>Atomic file guarantees file integrity by ensuring that a file has been completely written and
* sync
'
d to disk before removing its backup. As long as the backup file exists, the original file
* sync
e
d to disk before removing its backup. As long as the backup file exists, the original file
* is considered to be invalid (left over from a previous attempt to write the file).
*
* <p>Atomic file does not confer any file locking semantics. Do not use this class when the file
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
1c4ea26f
...
...
@@ -1087,6 +1087,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
throws
DecoderQueryException
{
int
maxWidth
=
format
.
width
;
int
maxHeight
=
format
.
height
;
if
(
codecNeedsMaxVideoSizeResetWorkaround
(
codecInfo
.
name
))
{
maxWidth
=
Math
.
max
(
maxWidth
,
1920
);
maxHeight
=
Math
.
max
(
maxHeight
,
1089
);
}
int
maxInputSize
=
getMaxInputSize
(
codecInfo
,
format
);
if
(
streamFormats
.
length
==
1
)
{
// The single entry in streamFormats must correspond to the format for which the codec is
...
...
@@ -1274,6 +1278,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return
"NVIDIA"
.
equals
(
Util
.
MANUFACTURER
);
}
/**
* Returns whether the codec is known to have problems with the configuration for interlaced
* content and needs minimum values for the maximum video size to force reset the configuration.
*
* <p>See https://github.com/google/ExoPlayer/issues/5003.
*
* @param name The name of the codec.
*/
private
static
boolean
codecNeedsMaxVideoSizeResetWorkaround
(
String
name
)
{
return
"OMX.amlogic.avc.decoder.awesome"
.
equals
(
name
)
&&
Util
.
SDK_INT
<=
25
;
}
/*
* TODO:
*
...
...
@@ -1322,7 +1338,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// https://github.com/google/ExoPlayer/issues/4315,
// https://github.com/google/ExoPlayer/issues/4419,
// https://github.com/google/ExoPlayer/issues/4460,
// https://github.com/google/ExoPlayer/issues/4468.
// https://github.com/google/ExoPlayer/issues/4468,
// https://github.com/google/ExoPlayer/issues/5312.
switch
(
Util
.
DEVICE
)
{
case
"1601"
:
case
"1713"
:
...
...
@@ -1378,6 +1395,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
case
"HWBLN-H"
:
case
"HWCAM-H"
:
case
"HWVNS-H"
:
case
"HWWAS-H"
:
case
"i9031"
:
case
"iball8735_9806"
:
case
"Infinix-X572"
:
...
...
library/core/src/test/assets/mp4/sample.mp4.0.dump
View file @
1c4ea26f
...
...
@@ -147,7 +147,7 @@ track 0:
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags =
0
flags =
536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
...
...
@@ -352,6 +352,6 @@ track 1:
data = length 229, hash FFF98DF0
sample 44:
time = 1065678
flags =
1
flags =
536870913
data = length 6, hash 31B22286
tracksEnded = true
library/core/src/test/assets/mp4/sample.mp4.1.dump
View file @
1c4ea26f
...
...
@@ -147,7 +147,7 @@ track 0:
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags =
0
flags =
536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
...
...
@@ -304,6 +304,6 @@ track 1:
data = length 229, hash FFF98DF0
sample 32:
time = 1065678
flags =
1
flags =
536870913
data = length 6, hash 31B22286
tracksEnded = true
library/core/src/test/assets/mp4/sample.mp4.2.dump
View file @
1c4ea26f
...
...
@@ -147,7 +147,7 @@ track 0:
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags =
0
flags =
536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
...
...
@@ -244,6 +244,6 @@ track 1:
data = length 229, hash FFF98DF0
sample 17:
time = 1065678
flags =
1
flags =
536870913
data = length 6, hash 31B22286
tracksEnded = true
library/core/src/test/assets/mp4/sample.mp4.3.dump
View file @
1c4ea26f
...
...
@@ -147,7 +147,7 @@ track 0:
data = length 530, hash C98BC6A8
sample 29:
time = 934266
flags =
0
flags =
536870912
data = length 568, hash 4FE5C8EA
track 1:
format:
...
...
@@ -184,6 +184,6 @@ track 1:
data = length 229, hash FFF98DF0
sample 2:
time = 1065678
flags =
1
flags =
536870913
data = length 6, hash 31B22286
tracksEnded = true
library/core/src/test/java/com/google/android/exoplayer2/extractor/mp4/MdtaMetadataEntryTest.java
0 → 100644
View file @
1c4ea26f
/*
* Copyright (C) 2019 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
com
.
google
.
android
.
exoplayer2
.
extractor
.
mp4
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
android.os.Parcel
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
/** Test for {@link MdtaMetadataEntry}. */
@RunWith
(
RobolectricTestRunner
.
class
)
public
final
class
MdtaMetadataEntryTest
{
@Test
public
void
testParcelable
()
{
MdtaMetadataEntry
mdtaMetadataEntryToParcel
=
new
MdtaMetadataEntry
(
"test"
,
new
byte
[]
{
1
,
2
},
3
,
4
);
Parcel
parcel
=
Parcel
.
obtain
();
mdtaMetadataEntryToParcel
.
writeToParcel
(
parcel
,
0
);
parcel
.
setDataPosition
(
0
);
MdtaMetadataEntry
mdtaMetadataEntryFromParcel
=
MdtaMetadataEntry
.
CREATOR
.
createFromParcel
(
parcel
);
assertThat
(
mdtaMetadataEntryFromParcel
).
isEqualTo
(
mdtaMetadataEntryToParcel
);
parcel
.
recycle
();
}
}
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
View file @
1c4ea26f
...
...
@@ -46,6 +46,7 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import
com.google.android.exoplayer2.upstream.LoaderErrorThrower
;
import
com.google.android.exoplayer2.upstream.TransferListener
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.IOException
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
...
...
@@ -452,13 +453,22 @@ import java.util.List;
if
(
adaptationSetSwitchingProperty
==
null
)
{
groupedAdaptationSetIndices
[
groupCount
++]
=
new
int
[]
{
i
};
}
else
{
String
[]
extraAdaptationSetIds
=
adaptationSetSwitchingProperty
.
value
.
split
(
","
);
String
[]
extraAdaptationSetIds
=
Util
.
split
(
adaptationSetSwitchingProperty
.
value
,
","
);
int
[]
adaptationSetIndices
=
new
int
[
1
+
extraAdaptationSetIds
.
length
];
adaptationSetIndices
[
0
]
=
i
;
int
outputIndex
=
1
;
for
(
int
j
=
0
;
j
<
extraAdaptationSetIds
.
length
;
j
++)
{
int
extraIndex
=
idToIndexMap
.
get
(
Integer
.
parseInt
(
extraAdaptationSetIds
[
j
]));
adaptationSetUsedFlags
[
extraIndex
]
=
true
;
adaptationSetIndices
[
1
+
j
]
=
extraIndex
;
int
extraIndex
=
idToIndexMap
.
get
(
Integer
.
parseInt
(
extraAdaptationSetIds
[
j
]),
/* valueIfKeyNotFound= */
-
1
);
if
(
extraIndex
!=
-
1
)
{
adaptationSetUsedFlags
[
extraIndex
]
=
true
;
adaptationSetIndices
[
outputIndex
]
=
extraIndex
;
outputIndex
++;
}
}
if
(
outputIndex
<
adaptationSetIndices
.
length
)
{
adaptationSetIndices
=
Arrays
.
copyOf
(
adaptationSetIndices
,
outputIndex
);
}
groupedAdaptationSetIndices
[
groupCount
++]
=
adaptationSetIndices
;
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
View file @
1c4ea26f
...
...
@@ -635,7 +635,8 @@ public final class DashMediaSource extends BaseMediaSource {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
periodId
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
periodId
,
Allocator
allocator
,
long
startPositionUs
)
{
int
periodIndex
=
(
Integer
)
periodId
.
periodUid
-
firstPeriodId
;
EventDispatcher
periodEventDispatcher
=
createEventDispatcher
(
periodId
,
manifest
.
getPeriod
(
periodIndex
).
startMs
);
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
1c4ea26f
...
...
@@ -457,10 +457,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
private
ArrayList
<
Representation
>
getRepresentations
()
{
List
<
AdaptationSet
>
manifestAdapationSets
=
manifest
.
getPeriod
(
periodIndex
).
adaptationSets
;
List
<
AdaptationSet
>
manifestAdap
t
ationSets
=
manifest
.
getPeriod
(
periodIndex
).
adaptationSets
;
ArrayList
<
Representation
>
representations
=
new
ArrayList
<>();
for
(
int
adaptationSetIndex
:
adaptationSetIndices
)
{
representations
.
addAll
(
manifestAdapationSets
.
get
(
adaptationSetIndex
).
representations
);
representations
.
addAll
(
manifestAdap
t
ationSets
.
get
(
adaptationSetIndex
).
representations
);
}
return
representations
;
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
1c4ea26f
...
...
@@ -412,7 +412,7 @@ public final class HlsMediaSource extends BaseMediaSource
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
EventDispatcher
eventDispatcher
=
createEventDispatcher
(
id
);
return
new
HlsMediaPeriod
(
extractorFactory
,
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
View file @
1c4ea26f
...
...
@@ -360,7 +360,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* initializationData= */
null
,
selectionFlags
,
language
);
if
(
uri
==
null
)
{
if
(
isMediaTagMuxed
(
variants
,
uri
)
)
{
muxedAudioFormat
=
format
;
}
else
{
audios
.
add
(
new
HlsMasterPlaylist
.
HlsUrl
(
uri
,
format
));
...
...
@@ -766,6 +766,20 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return
Pattern
.
compile
(
attribute
+
"=("
+
BOOLEAN_FALSE
+
"|"
+
BOOLEAN_TRUE
+
")"
);
}
private
static
boolean
isMediaTagMuxed
(
List
<
HlsMasterPlaylist
.
HlsUrl
>
variants
,
String
mediaTagUri
)
{
if
(
mediaTagUri
==
null
)
{
return
true
;
}
// The URI attribute is defined, but it may match the uri of a variant.
for
(
int
i
=
0
;
i
<
variants
.
size
();
i
++)
{
if
(
mediaTagUri
.
equals
(
variants
.
get
(
i
).
url
))
{
return
true
;
}
}
return
false
;
}
private
static
class
LineIterator
{
private
final
BufferedReader
reader
;
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
View file @
1c4ea26f
...
...
@@ -134,6 +134,17 @@ public class HlsMasterPlaylistParserTest {
+
"#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"{$codecs}\"\n"
+
"http://example.com/{$tricky}\n"
;
private
static
final
String
PLAYLIST_WITH_MULTIPLE_MUXED_MEDIA_TAGS
=
"#EXTM3U\n"
+
"#EXT-X-VERSION:3\n"
+
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"a\",NAME=\"audio_0\",DEFAULT=YES,URI=\"0/0.m3u8\"\n"
+
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"b\",NAME=\"audio_0\",DEFAULT=YES,URI=\"1/1.m3u8\"\n"
+
"#EXT-X-STREAM-INF:BANDWIDTH=140800,CODECS=\"mp4a.40.2\",AUDIO=\"a\"\n"
+
"0/0.m3u8\n"
+
"\n"
+
"#EXT-X-STREAM-INF:BANDWIDTH=281600,CODECS=\"mp4a.40.2\",AUDIO=\"b\"\n"
+
"1/1.m3u8\n"
;
@Test
public
void
testParseMasterPlaylist
()
throws
IOException
{
HlsMasterPlaylist
masterPlaylist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
PLAYLIST_SIMPLE
);
...
...
@@ -271,6 +282,14 @@ public class HlsMasterPlaylistParserTest {
assertThat
(
variant
.
url
).
isEqualTo
(
"http://example.com/This/{$nested}/reference/shouldnt/work"
);
}
@Test
public
void
testMultipleMuxedMediaTags
()
throws
IOException
{
HlsMasterPlaylist
playlistWithMultipleMuxedMediaTags
=
parseMasterPlaylist
(
PLAYLIST_URI
,
PLAYLIST_WITH_MULTIPLE_MUXED_MEDIA_TAGS
);
assertThat
(
playlistWithMultipleMuxedMediaTags
.
variants
).
hasSize
(
2
);
assertThat
(
playlistWithMultipleMuxedMediaTags
.
audios
).
isEmpty
();
}
private
static
HlsMasterPlaylist
parseMasterPlaylist
(
String
uri
,
String
playlistString
)
throws
IOException
{
Uri
playlistUri
=
Uri
.
parse
(
uri
);
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/DefaultSsChunkSource.java
View file @
1c4ea26f
...
...
@@ -61,14 +61,13 @@ public class DefaultSsChunkSource implements SsChunkSource {
SsManifest
manifest
,
int
elementIndex
,
TrackSelection
trackSelection
,
TrackEncryptionBox
[]
trackEncryptionBoxes
,
@Nullable
TransferListener
transferListener
)
{
DataSource
dataSource
=
dataSourceFactory
.
createDataSource
();
if
(
transferListener
!=
null
)
{
dataSource
.
addTransferListener
(
transferListener
);
}
return
new
DefaultSsChunkSource
(
manifestLoaderErrorThrower
,
manifest
,
elementIndex
,
trackSelection
,
dataSource
,
trackEncryptionBoxes
);
return
new
DefaultSsChunkSource
(
manifestLoaderErrorThrower
,
manifest
,
elementIndex
,
trackSelection
,
dataSource
);
}
}
...
...
@@ -90,15 +89,13 @@ public class DefaultSsChunkSource implements SsChunkSource {
* @param streamElementIndex The index of the stream element in the manifest.
* @param trackSelection The track selection.
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param trackEncryptionBoxes Track encryption boxes for the stream.
*/
public
DefaultSsChunkSource
(
LoaderErrorThrower
manifestLoaderErrorThrower
,
SsManifest
manifest
,
int
streamElementIndex
,
TrackSelection
trackSelection
,
DataSource
dataSource
,
TrackEncryptionBox
[]
trackEncryptionBoxes
)
{
DataSource
dataSource
)
{
this
.
manifestLoaderErrorThrower
=
manifestLoaderErrorThrower
;
this
.
manifest
=
manifest
;
this
.
streamElementIndex
=
streamElementIndex
;
...
...
@@ -110,6 +107,8 @@ public class DefaultSsChunkSource implements SsChunkSource {
for
(
int
i
=
0
;
i
<
extractorWrappers
.
length
;
i
++)
{
int
manifestTrackIndex
=
trackSelection
.
getIndexInTrackGroup
(
i
);
Format
format
=
streamElement
.
formats
[
manifestTrackIndex
];
TrackEncryptionBox
[]
trackEncryptionBoxes
=
format
.
drmInitData
!=
null
?
manifest
.
protectionElement
.
trackEncryptionBoxes
:
null
;
int
nalUnitLengthFieldLength
=
streamElement
.
type
==
C
.
TRACK_TYPE_VIDEO
?
4
:
0
;
Track
track
=
new
Track
(
manifestTrackIndex
,
streamElement
.
type
,
streamElement
.
timescale
,
C
.
TIME_UNSET
,
manifest
.
durationUs
,
format
,
Track
.
TRANSFORMATION_NONE
,
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsChunkSource.java
View file @
1c4ea26f
...
...
@@ -16,7 +16,6 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
smoothstreaming
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox
;
import
com.google.android.exoplayer2.source.chunk.ChunkSource
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest
;
import
com.google.android.exoplayer2.trackselection.TrackSelection
;
...
...
@@ -38,7 +37,6 @@ public interface SsChunkSource extends ChunkSource {
* @param manifest The initial manifest.
* @param streamElementIndex The index of the corresponding stream element in the manifest.
* @param trackSelection The track selection.
* @param trackEncryptionBoxes Track encryption boxes for the stream.
* @param transferListener The transfer listener which should be informed of any data transfers.
* May be null if no listener is available.
* @return The created {@link SsChunkSource}.
...
...
@@ -48,7 +46,6 @@ public interface SsChunkSource extends ChunkSource {
SsManifest
manifest
,
int
streamElementIndex
,
TrackSelection
trackSelection
,
TrackEncryptionBox
[]
trackEncryptionBoxes
,
@Nullable
TransferListener
transferListener
);
}
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java
View file @
1c4ea26f
...
...
@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.source.TrackGroup;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.source.chunk.ChunkSampleStream
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement
;
import
com.google.android.exoplayer2.trackselection.TrackSelection
;
import
com.google.android.exoplayer2.upstream.Allocator
;
import
com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy
;
...
...
@@ -44,8 +43,6 @@ import java.util.ArrayList;
/* package */
final
class
SsMediaPeriod
implements
MediaPeriod
,
SequenceableLoader
.
Callback
<
ChunkSampleStream
<
SsChunkSource
>>
{
private
static
final
int
INITIALIZATION_VECTOR_SIZE
=
8
;
private
final
SsChunkSource
.
Factory
chunkSourceFactory
;
private
final
@Nullable
TransferListener
transferListener
;
private
final
LoaderErrorThrower
manifestLoaderErrorThrower
;
...
...
@@ -53,7 +50,6 @@ import java.util.ArrayList;
private
final
EventDispatcher
eventDispatcher
;
private
final
Allocator
allocator
;
private
final
TrackGroupArray
trackGroups
;
private
final
TrackEncryptionBox
[]
trackEncryptionBoxes
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
@Nullable
Callback
callback
;
...
...
@@ -71,6 +67,7 @@ import java.util.ArrayList;
EventDispatcher
eventDispatcher
,
LoaderErrorThrower
manifestLoaderErrorThrower
,
Allocator
allocator
)
{
this
.
manifest
=
manifest
;
this
.
chunkSourceFactory
=
chunkSourceFactory
;
this
.
transferListener
=
transferListener
;
this
.
manifestLoaderErrorThrower
=
manifestLoaderErrorThrower
;
...
...
@@ -78,18 +75,7 @@ import java.util.ArrayList;
this
.
eventDispatcher
=
eventDispatcher
;
this
.
allocator
=
allocator
;
this
.
compositeSequenceableLoaderFactory
=
compositeSequenceableLoaderFactory
;
trackGroups
=
buildTrackGroups
(
manifest
);
ProtectionElement
protectionElement
=
manifest
.
protectionElement
;
if
(
protectionElement
!=
null
)
{
byte
[]
keyId
=
getProtectionElementKeyId
(
protectionElement
.
data
);
// We assume pattern encryption does not apply.
trackEncryptionBoxes
=
new
TrackEncryptionBox
[]
{
new
TrackEncryptionBox
(
true
,
null
,
INITIALIZATION_VECTOR_SIZE
,
keyId
,
0
,
0
,
null
)};
}
else
{
trackEncryptionBoxes
=
null
;
}
this
.
manifest
=
manifest
;
sampleStreams
=
newSampleStreamArray
(
0
);
compositeSequenceableLoader
=
compositeSequenceableLoaderFactory
.
createCompositeSequenceableLoader
(
sampleStreams
);
...
...
@@ -229,7 +215,6 @@ import java.util.ArrayList;
manifest
,
streamElementIndex
,
selection
,
trackEncryptionBoxes
,
transferListener
);
return
new
ChunkSampleStream
<>(
manifest
.
streamElements
[
streamElementIndex
].
type
,
...
...
@@ -277,5 +262,4 @@ import java.util.ArrayList;
data
[
firstPosition
]
=
data
[
secondPosition
];
data
[
secondPosition
]
=
temp
;
}
}
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java
View file @
1c4ea26f
...
...
@@ -533,7 +533,7 @@ public final class SsMediaSource extends BaseMediaSource
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
EventDispatcher
eventDispatcher
=
createEventDispatcher
(
id
);
SsMediaPeriod
period
=
new
SsMediaPeriod
(
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifest.java
View file @
1c4ea26f
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.smoothstreaming.manifest;
import
android.net.Uri
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox
;
import
com.google.android.exoplayer2.offline.FilterableManifest
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.util.Assertions
;
...
...
@@ -41,10 +42,12 @@ public class SsManifest implements FilterableManifest<SsManifest> {
public
final
UUID
uuid
;
public
final
byte
[]
data
;
public
final
TrackEncryptionBox
[]
trackEncryptionBoxes
;
public
ProtectionElement
(
UUID
uuid
,
byte
[]
data
)
{
public
ProtectionElement
(
UUID
uuid
,
byte
[]
data
,
TrackEncryptionBox
[]
trackEncryptionBoxes
)
{
this
.
uuid
=
uuid
;
this
.
data
=
data
;
this
.
trackEncryptionBoxes
=
trackEncryptionBoxes
;
}
}
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java
View file @
1c4ea26f
...
...
@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.ParserException;
import
com.google.android.exoplayer2.drm.DrmInitData
;
import
com.google.android.exoplayer2.drm.DrmInitData.SchemeData
;
import
com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil
;
import
com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
...
...
@@ -397,9 +398,10 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
public
static
final
String
TAG
=
"Protection"
;
public
static
final
String
TAG_PROTECTION_HEADER
=
"ProtectionHeader"
;
public
static
final
String
KEY_SYSTEM_ID
=
"SystemID"
;
private
static
final
int
INITIALIZATION_VECTOR_SIZE
=
8
;
private
boolean
inProtectionHeader
;
private
UUID
uuid
;
private
byte
[]
initData
;
...
...
@@ -439,7 +441,44 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
@Override
public
Object
build
()
{
return
new
ProtectionElement
(
uuid
,
PsshAtomUtil
.
buildPsshAtom
(
uuid
,
initData
));
return
new
ProtectionElement
(
uuid
,
PsshAtomUtil
.
buildPsshAtom
(
uuid
,
initData
),
buildTrackEncryptionBoxes
(
initData
));
}
private
static
TrackEncryptionBox
[]
buildTrackEncryptionBoxes
(
byte
[]
initData
)
{
return
new
TrackEncryptionBox
[]
{
new
TrackEncryptionBox
(
/* isEncrypted= */
true
,
/* schemeType= */
null
,
INITIALIZATION_VECTOR_SIZE
,
getProtectionElementKeyId
(
initData
),
/* defaultEncryptedBlocks= */
0
,
/* defaultClearBlocks= */
0
,
/* defaultInitializationVector= */
null
)
};
}
private
static
byte
[]
getProtectionElementKeyId
(
byte
[]
initData
)
{
StringBuilder
initDataStringBuilder
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
initData
.
length
;
i
+=
2
)
{
initDataStringBuilder
.
append
((
char
)
initData
[
i
]);
}
String
initDataString
=
initDataStringBuilder
.
toString
();
String
keyIdString
=
initDataString
.
substring
(
initDataString
.
indexOf
(
"<KID>"
)
+
5
,
initDataString
.
indexOf
(
"</KID>"
));
byte
[]
keyId
=
Base64
.
decode
(
keyIdString
,
Base64
.
DEFAULT
);
swap
(
keyId
,
0
,
3
);
swap
(
keyId
,
1
,
2
);
swap
(
keyId
,
4
,
5
);
swap
(
keyId
,
6
,
7
);
return
keyId
;
}
private
static
void
swap
(
byte
[]
data
,
int
firstPosition
,
int
secondPosition
)
{
byte
temp
=
data
[
firstPosition
];
data
[
firstPosition
]
=
data
[
secondPosition
];
data
[
secondPosition
]
=
temp
;
}
private
static
String
stripCurlyBraces
(
String
uuidString
)
{
...
...
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/offline/SsDownloadHelper.java
View file @
1c4ea26f
...
...
@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.source.TrackGroup;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsUtil
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
import
com.google.android.exoplayer2.util.Assertions
;
...
...
@@ -42,7 +43,7 @@ public final class SsDownloadHelper extends DownloadHelper {
private
@MonotonicNonNull
SsManifest
manifest
;
public
SsDownloadHelper
(
Uri
uri
,
DataSource
.
Factory
manifestDataSourceFactory
)
{
this
.
uri
=
uri
;
this
.
uri
=
SsUtil
.
fixManifestUri
(
uri
);
;
this
.
manifestDataSourceFactory
=
manifestDataSourceFactory
;
}
...
...
library/smoothstreaming/src/test/assets/sample_ismc_1
View file @
1c4ea26f
...
...
@@ -3,7 +3,7 @@
Duration=
"2300000000"
TimeScale=
"10000000"
>
<Protection>
<ProtectionHeader
SystemID=
"9A04F079-9840-4286-AB92-E65BE0885F95"
>
<!-- Base 64-Encoded data omitted for clarity -->
fgMAAAEAAQB0AzwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AQgBhAFUATQBPAEcAYwBzAGgAVQBDAEQAZAB3ADMANABZAGMAawBmAFoAQQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBnADcATgBhAFIARABJAEkATwA5ADAAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APABMAEEAXwBVAFIATAA+AGgAdAB0AHAAcwA6AC8ALwBUAC0ATwBOAEwASQBOAEUALgBEAFUATQBNAFkALQBTAEUAUgBWAEUAUgAvAEEAcgB0AGUAbQBpAHMATABpAGMAZQBuAHMAZQBTAGUAcgB2AGUAcgAvAFAAbABhAHkAUgBlAGEAZAB5AE0AYQBuAGEAZwBlAHIALgBhAHMAbQB4ADwALwBMAEEAXwBVAFIATAA+ADwAQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwAQwBJAEQAPgAxADcANQA4ADIANgA8AC8AQwBJAEQAPgA8AEkASQBTAF8ARABSAE0AXwBWAEUAUgBTAEkATwBOAD4ANwAuADEALgAxADUANgA1AC4ANAA8AC8ASQBJAFMAXwBEAFIATQBfAFYARQBSAFMASQBPAE4APgA8AC8AQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwALwBEAEEAVABBAD4APAAvAFcAUgBNAEgARQBBAEQARQBSAD4A
</ProtectionHeader>
</Protection>
...
...
library/smoothstreaming/src/test/assets/sample_ismc_2
View file @
1c4ea26f
...
...
@@ -3,7 +3,7 @@
Duration=
"2300000000"
TimeScale=
"10000000"
>
<Protection>
<ProtectionHeader
SystemID=
"{9A04F079-9840-4286-AB92-E65BE0885F95}"
>
<!-- Base 64-Encoded data omitted for clarity -->
fgMAAAEAAQB0AzwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AQgBhAFUATQBPAEcAYwBzAGgAVQBDAEQAZAB3ADMANABZAGMAawBmAFoAQQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBnADcATgBhAFIARABJAEkATwA5ADAAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APABMAEEAXwBVAFIATAA+AGgAdAB0AHAAcwA6AC8ALwBUAC0ATwBOAEwASQBOAEUALgBEAFUATQBNAFkALQBTAEUAUgBWAEUAUgAvAEEAcgB0AGUAbQBpAHMATABpAGMAZQBuAHMAZQBTAGUAcgB2AGUAcgAvAFAAbABhAHkAUgBlAGEAZAB5AE0AYQBuAGEAZwBlAHIALgBhAHMAbQB4ADwALwBMAEEAXwBVAFIATAA+ADwAQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwAQwBJAEQAPgAxADcANQA4ADIANgA8AC8AQwBJAEQAPgA8AEkASQBTAF8ARABSAE0AXwBWAEUAUgBTAEkATwBOAD4ANwAuADEALgAxADUANgA1AC4ANAA8AC8ASQBJAFMAXwBEAFIATQBfAFYARQBSAFMASQBPAE4APgA8AC8AQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwALwBEAEEAVABBAD4APAAvAFcAUgBNAEgARQBBAEQARQBSAD4A
</ProtectionHeader>
</Protection>
...
...
library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestTest.java
View file @
1c4ea26f
...
...
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.ProtectionElement
;
import
com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement
;
...
...
@@ -36,7 +37,7 @@ import org.robolectric.RobolectricTestRunner;
public
class
SsManifestTest
{
private
static
final
ProtectionElement
DUMMY_PROTECTION_ELEMENT
=
new
ProtectionElement
(
C
.
WIDEVINE_UUID
,
new
byte
[
]
{
0
,
1
,
2
}
);
new
ProtectionElement
(
C
.
WIDEVINE_UUID
,
new
byte
[
0
],
new
TrackEncryptionBox
[
0
]
);
@Test
public
void
testCopy
()
throws
Exception
{
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java
View file @
1c4ea26f
...
...
@@ -137,23 +137,40 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
/** Returns a string containing video debugging information. */
protected
String
getVideoString
()
{
Format
format
=
player
.
getVideoFormat
();
if
(
format
==
null
)
{
DecoderCounters
decoderCounters
=
player
.
getVideoDecoderCounters
();
if
(
format
==
null
||
decoderCounters
==
null
)
{
return
""
;
}
return
"\n"
+
format
.
sampleMimeType
+
"(id:"
+
format
.
id
+
" r:"
+
format
.
width
+
"x"
+
format
.
height
+
getPixelAspectRatioString
(
format
.
pixelWidthHeightRatio
)
+
getDecoderCountersBufferCountString
(
player
.
getVideoDecoderCounters
())
+
")"
;
return
"\n"
+
format
.
sampleMimeType
+
"(id:"
+
format
.
id
+
" r:"
+
format
.
width
+
"x"
+
format
.
height
+
getPixelAspectRatioString
(
format
.
pixelWidthHeightRatio
)
+
getDecoderCountersBufferCountString
(
decoderCounters
)
+
")"
;
}
/** Returns a string containing audio debugging information. */
protected
String
getAudioString
()
{
Format
format
=
player
.
getAudioFormat
();
if
(
format
==
null
)
{
DecoderCounters
decoderCounters
=
player
.
getAudioDecoderCounters
();
if
(
format
==
null
||
decoderCounters
==
null
)
{
return
""
;
}
return
"\n"
+
format
.
sampleMimeType
+
"(id:"
+
format
.
id
+
" hz:"
+
format
.
sampleRate
+
" ch:"
return
"\n"
+
format
.
sampleMimeType
+
"(id:"
+
format
.
id
+
" hz:"
+
format
.
sampleRate
+
" ch:"
+
format
.
channelCount
+
getDecoderCountersBufferCountString
(
player
.
getAudioDecoderCounters
())
+
")"
;
+
getDecoderCountersBufferCountString
(
decoderCounters
)
+
")"
;
}
private
static
String
getDecoderCountersBufferCountString
(
DecoderCounters
counters
)
{
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
View file @
1c4ea26f
...
...
@@ -126,6 +126,18 @@ public class PlayerNotificationManager {
String
getCurrentContentText
(
Player
player
);
/**
* Gets the content sub text for the current media item.
*
* <p>See {@link NotificationCompat.Builder#setSubText(CharSequence)}.
*
* @param player The {@link Player} for which a notification is being built.
*/
@Nullable
default
String
getCurrentSubText
(
Player
player
)
{
return
null
;
}
/**
* Gets the large icon for the current media item.
*
* <p>When a bitmap initially needs to be asynchronously loaded, a placeholder (or null) can be
...
...
@@ -832,6 +844,7 @@ public class PlayerNotificationManager {
// Set media specific notification properties from MediaDescriptionAdapter.
builder
.
setContentTitle
(
mediaDescriptionAdapter
.
getCurrentContentTitle
(
player
));
builder
.
setContentText
(
mediaDescriptionAdapter
.
getCurrentContentText
(
player
));
builder
.
setSubText
(
mediaDescriptionAdapter
.
getCurrentSubText
(
player
));
if
(
largeIcon
==
null
)
{
largeIcon
=
mediaDescriptionAdapter
.
getCurrentLargeIcon
(
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
1c4ea26f
...
...
@@ -679,8 +679,9 @@ public class PlayerView extends FrameLayout {
/**
* Sets whether the currently displayed video frame or media artwork is kept visible when the
* player is reset. A player reset is defined to mean the player being re-prepared with different
* media, {@link Player#stop(boolean)} being called with {@code reset=true}, or the player being
* replaced or cleared by calling {@link #setPlayer(Player)}.
* media, the player transitioning to unprepared media, {@link Player#stop(boolean)} being called
* with {@code reset=true}, or the player being replaced or cleared by calling {@link
* #setPlayer(Player)}.
*
* <p>If enabled, the currently displayed video frame or media artwork will be kept visible until
* the player set on the view has been successfully prepared with new media and loaded enough of
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
View file @
1c4ea26f
...
...
@@ -116,7 +116,7 @@ public class FakeMediaSource extends BaseMediaSource {
}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
)
{
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
assertThat
(
preparedSource
).
isTrue
();
assertThat
(
releasedSource
).
isFalse
();
int
periodIndex
=
timeline
.
getIndexOfPeriod
(
id
.
periodUid
);
...
...
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java
View file @
1c4ea26f
...
...
@@ -142,15 +142,28 @@ public class MediaSourceTestRunner {
}
/**
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator)} on the playback
* thread, asserting that a non-null {@link MediaPeriod} is returned.
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator, long)} with a zero
* start position on the playback thread, asserting that a non-null {@link MediaPeriod} is
* returned.
*
* @param periodId The id of the period to create.
* @return The created {@link MediaPeriod}.
*/
public
MediaPeriod
createPeriod
(
final
MediaPeriodId
periodId
)
{
return
createPeriod
(
periodId
,
/* startPositionUs= */
0
);
}
/**
* Calls {@link MediaSource#createPeriod(MediaSource.MediaPeriodId, Allocator, long)} on the
* playback thread, asserting that a non-null {@link MediaPeriod} is returned.
*
* @param periodId The id of the period to create.
* @return The created {@link MediaPeriod}.
*/
public
MediaPeriod
createPeriod
(
final
MediaPeriodId
periodId
,
long
startPositionUs
)
{
final
MediaPeriod
[]
holder
=
new
MediaPeriod
[
1
];
runOnPlaybackThread
(()
->
holder
[
0
]
=
mediaSource
.
createPeriod
(
periodId
,
allocator
));
runOnPlaybackThread
(
()
->
holder
[
0
]
=
mediaSource
.
createPeriod
(
periodId
,
allocator
,
startPositionUs
));
assertThat
(
holder
[
0
]).
isNotNull
();
return
holder
[
0
];
}
...
...
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