mirror of
https://github.com/androidx/media.git
synced 2025-05-04 22:20:47 +08:00
Merge branch 'dev-v2' into release-v2
This commit is contained in:
commit
d979469659
34
README.md
34
README.md
@ -30,17 +30,39 @@ repositories {
|
||||
}
|
||||
```
|
||||
|
||||
Next, include the following in your module's `build.gradle` file:
|
||||
Next add a gradle compile dependency to the `build.gradle` file of your app
|
||||
module. The following will add a dependency to the full ExoPlayer library:
|
||||
|
||||
```gradle
|
||||
compile 'com.google.android.exoplayer:exoplayer:rX.X.X'
|
||||
compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
|
||||
```
|
||||
|
||||
where `rX.X.X` is the your preferred version. For the latest version, see the
|
||||
project's [Releases][]. For more details, see the project on [Bintray][].
|
||||
where `r2.X.X` is your preferred version. Alternatively, you can depend on only
|
||||
the library modules that you actually need. For example the following will add
|
||||
dependencies on the Core, DASH and UI library modules, as might be required for
|
||||
an app that plays DASH content:
|
||||
|
||||
[Releases]: https://github.com/google/ExoPlayer/releases
|
||||
[Bintray]: https://bintray.com/google/exoplayer/exoplayer/view
|
||||
```gradle
|
||||
compile 'com.google.android.exoplayer:exoplayer-core:r2.X.X'
|
||||
compile 'com.google.android.exoplayer:exoplayer-dash:r2.X.X'
|
||||
compile 'com.google.android.exoplayer:exoplayer-ui:r2.X.X'
|
||||
```
|
||||
|
||||
The available modules are listed below. Adding a dependency to the full
|
||||
ExoPlayer library is equivalent to adding dependencies on all of the modules
|
||||
individually.
|
||||
|
||||
* `exoplayer-core`: Core functionality (required).
|
||||
* `exoplayer-dash`: Support for DASH content.
|
||||
* `exoplayer-hls`: Support for HLS content.
|
||||
* `exoplayer-smoothstreaming`: Support for SmoothStreaming content.
|
||||
* `exoplayer-ui`: UI components and resources for use with ExoPlayer.
|
||||
|
||||
For more details, see the project on [Bintray][]. For information about the
|
||||
latest versions, see the [Release notes][].
|
||||
|
||||
[Bintray]: https://bintray.com/google/exoplayer
|
||||
[Release notes]: https://github.com/google/ExoPlayer/blob/release-v2/RELEASENOTES.md
|
||||
|
||||
## Developing ExoPlayer ##
|
||||
|
||||
|
237
RELEASENOTES.md
237
RELEASENOTES.md
@ -1,5 +1,38 @@
|
||||
# Release notes #
|
||||
|
||||
### r2.4.0 ###
|
||||
|
||||
* New modular library structure. You can read more about depending on individual
|
||||
library modules
|
||||
[here](https://medium.com/google-exoplayer/exoplayers-new-modular-structure-a916c0874907).
|
||||
* Variable speed playback support on API level 16+. You can read more about
|
||||
changing the playback speed
|
||||
[here](https://medium.com/google-exoplayer/variable-speed-playback-with-exoplayer-e6e6a71e0343)
|
||||
([#26](https://github.com/google/ExoPlayer/issues/26)).
|
||||
* New time bar view, including support for displaying ad break markers.
|
||||
* Support DVB subtitles in MPEG-TS and MKV.
|
||||
* Support adaptive playback for audio only DASH, HLS and SmoothStreaming
|
||||
([#1975](https://github.com/google/ExoPlayer/issues/1975)).
|
||||
* Support for setting extractor flags on DefaultExtractorsFactory
|
||||
([#2657](https://github.com/google/ExoPlayer/issues/2657)).
|
||||
* Support injecting custom renderers into SimpleExoPlayer using a new
|
||||
RenderersFactory interface.
|
||||
* Correctly set ExoPlayer's internal thread priority to `THREAD_PRIORITY_AUDIO`.
|
||||
* TX3G: Support styling and positioning.
|
||||
* FLV:
|
||||
* Support MP3 in FLV.
|
||||
* Skip unhandled metadata rather than failing
|
||||
([#2634](https://github.com/google/ExoPlayer/issues/2634)).
|
||||
* Fix potential OutOfMemory errors.
|
||||
* ID3: Better handle malformed ID3 data
|
||||
([#2604](https://github.com/google/ExoPlayer/issues/2604),
|
||||
[#2663](https://github.com/google/ExoPlayer/issues/2663)).
|
||||
* FFmpeg extension: Fixed build instructions
|
||||
([#2561](https://github.com/google/ExoPlayer/issues/2561)).
|
||||
* VP9 extension: Reduced binary size.
|
||||
* FLAC extension: Enabled 64 bit targets.
|
||||
* Misc bugfixes.
|
||||
|
||||
### r2.3.1 ###
|
||||
|
||||
* Fix NPE enabling WebVTT subtitles in DASH streams
|
||||
@ -15,32 +48,31 @@
|
||||
rendering. You can read more about the GVR extension
|
||||
[here](https://medium.com/google-exoplayer/spatial-audio-with-exoplayer-and-gvr-cecb00e9da5f#.xdjebjd7g).
|
||||
* DASH improvements:
|
||||
* Support embedded CEA-608 closed captions
|
||||
([#2362](https://github.com/google/ExoPlayer/issues/2362)).
|
||||
* Support embedded EMSG events
|
||||
([#2176](https://github.com/google/ExoPlayer/issues/2176)).
|
||||
* Support mspr:pro manifest element
|
||||
([#2386](https://github.com/google/ExoPlayer/issues/2386)).
|
||||
* Correct handling of empty segment indices at the start of live events
|
||||
([#1865](https://github.com/google/ExoPlayer/issues/1865)).
|
||||
* Support embedded CEA-608 closed captions
|
||||
([#2362](https://github.com/google/ExoPlayer/issues/2362)).
|
||||
* Support embedded EMSG events
|
||||
([#2176](https://github.com/google/ExoPlayer/issues/2176)).
|
||||
* Support mspr:pro manifest element
|
||||
([#2386](https://github.com/google/ExoPlayer/issues/2386)).
|
||||
* Correct handling of empty segment indices at the start of live events
|
||||
([#1865](https://github.com/google/ExoPlayer/issues/1865)).
|
||||
* HLS improvements:
|
||||
* Respect initial track selection
|
||||
([#2353](https://github.com/google/ExoPlayer/issues/2353)).
|
||||
* Reduced frequency of media playlist requests when playback position is
|
||||
close to the live edge
|
||||
([#2548](https://github.com/google/ExoPlayer/issues/2548)).
|
||||
* Exposed the master playlist through ExoPlayer.getCurrentManifest()
|
||||
([#2537](https://github.com/google/ExoPlayer/issues/2537)).
|
||||
* Support CLOSED-CAPTIONS #EXT-X-MEDIA type
|
||||
([#341](https://github.com/google/ExoPlayer/issues/341)).
|
||||
* Fixed handling of negative values in #EXT-X-SUPPORT
|
||||
([#2495](https://github.com/google/ExoPlayer/issues/2495)).
|
||||
* Fixed potential endless buffering state for streams with WebVTT subtitles
|
||||
([#2424](https://github.com/google/ExoPlayer/issues/2424)).
|
||||
* Respect initial track selection
|
||||
([#2353](https://github.com/google/ExoPlayer/issues/2353)).
|
||||
* Reduced frequency of media playlist requests when playback position is close
|
||||
to the live edge ([#2548](https://github.com/google/ExoPlayer/issues/2548)).
|
||||
* Exposed the master playlist through ExoPlayer.getCurrentManifest()
|
||||
([#2537](https://github.com/google/ExoPlayer/issues/2537)).
|
||||
* Support CLOSED-CAPTIONS #EXT-X-MEDIA type
|
||||
([#341](https://github.com/google/ExoPlayer/issues/341)).
|
||||
* Fixed handling of negative values in #EXT-X-SUPPORT
|
||||
([#2495](https://github.com/google/ExoPlayer/issues/2495)).
|
||||
* Fixed potential endless buffering state for streams with WebVTT subtitles
|
||||
([#2424](https://github.com/google/ExoPlayer/issues/2424)).
|
||||
* MPEG-TS improvements:
|
||||
* Support for multiple programs.
|
||||
* Support for multiple closed captions and caption service descriptors
|
||||
([#2161](https://github.com/google/ExoPlayer/issues/2161)).
|
||||
* Support for multiple programs.
|
||||
* Support for multiple closed captions and caption service descriptors
|
||||
([#2161](https://github.com/google/ExoPlayer/issues/2161)).
|
||||
* MP3: Add `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING` extractor option to enable
|
||||
constant bitrate seeking in MP3 files that would otherwise be unseekable
|
||||
([#2445](https://github.com/google/ExoPlayer/issues/2445)).
|
||||
@ -135,15 +167,15 @@
|
||||
* HLS: Support for seeking in live streams
|
||||
([#87](https://github.com/google/ExoPlayer/issues/87)).
|
||||
* HLS: Improved support:
|
||||
* Support for EXT-X-PROGRAM-DATE-TIME
|
||||
([#747](https://github.com/google/ExoPlayer/issues/747)).
|
||||
* Improved handling of sample timestamps and their alignment across variants
|
||||
and renditions.
|
||||
* Fix issue that could cause playbacks to get stuck in an endless initial
|
||||
buffering state.
|
||||
* Correctly propagate BehindLiveWindowException instead of
|
||||
IndexOutOfBoundsException exception
|
||||
([#1695](https://github.com/google/ExoPlayer/issues/1695)).
|
||||
* Support for EXT-X-PROGRAM-DATE-TIME
|
||||
([#747](https://github.com/google/ExoPlayer/issues/747)).
|
||||
* Improved handling of sample timestamps and their alignment across variants
|
||||
and renditions.
|
||||
* Fix issue that could cause playbacks to get stuck in an endless initial
|
||||
buffering state.
|
||||
* Correctly propagate BehindLiveWindowException instead of
|
||||
IndexOutOfBoundsException exception
|
||||
([#1695](https://github.com/google/ExoPlayer/issues/1695)).
|
||||
* MP3/MP4: Support for ID3 metadata, including embedded album art
|
||||
([#979](https://github.com/google/ExoPlayer/issues/979)).
|
||||
* Improved customization of UI components. You can read about customization of
|
||||
@ -153,19 +185,19 @@
|
||||
MediaPeriod transitions.
|
||||
* EIA608: Support for caption styling and positioning.
|
||||
* MPEG-TS: Improved support:
|
||||
* Support injection of custom TS payload readers.
|
||||
* Support injection of custom section payload readers.
|
||||
* Support SCTE-35 splice information messages.
|
||||
* Support multiple table sections in a single PSI section.
|
||||
* Fix NullPointerException when an unsupported stream type is encountered
|
||||
([#2149](https://github.com/google/ExoPlayer/issues/2149)).
|
||||
* Avoid failure when expected ID3 header not found
|
||||
([#1966](https://github.com/google/ExoPlayer/issues/1966)).
|
||||
* Support injection of custom TS payload readers.
|
||||
* Support injection of custom section payload readers.
|
||||
* Support SCTE-35 splice information messages.
|
||||
* Support multiple table sections in a single PSI section.
|
||||
* Fix NullPointerException when an unsupported stream type is encountered
|
||||
([#2149](https://github.com/google/ExoPlayer/issues/2149)).
|
||||
* Avoid failure when expected ID3 header not found
|
||||
([#1966](https://github.com/google/ExoPlayer/issues/1966)).
|
||||
* Improvements to the upstream cache package.
|
||||
* Support caching of media segments for DASH, HLS and SmoothStreaming. Note
|
||||
that caching of manifest and playlist files is still not supported in the
|
||||
(normal) case where the corresponding responses are compressed.
|
||||
* Support caching for ExtractorMediaSource based playbacks.
|
||||
* Support caching of media segments for DASH, HLS and SmoothStreaming. Note
|
||||
that caching of manifest and playlist files is still not supported in the
|
||||
(normal) case where the corresponding responses are compressed.
|
||||
* Support caching for ExtractorMediaSource based playbacks.
|
||||
* Improved flexibility of SimpleExoPlayer
|
||||
([#2102](https://github.com/google/ExoPlayer/issues/2102)).
|
||||
* Fix issue where only the audio of a video would play due to capability
|
||||
@ -237,63 +269,62 @@ some of the motivations behind ExoPlayer 2.x
|
||||
structure and class names have also been sanitized. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-new-package-and-class-names-ef8e1d9ba96f#.lv8sd4nez).
|
||||
* Key architectural changes:
|
||||
* Late binding between rendering and media source components. Allows the
|
||||
same rendering components to be re-used from one playback to another.
|
||||
Enables features such as gapless playback through playlists and DASH
|
||||
multi-period support.
|
||||
* Improved track selection design. More details can be found
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-track-selection-2b62ff712cc9#.n00zo76b6).
|
||||
* LoadControl now used to control buffering and loading across all playback
|
||||
types.
|
||||
* Media source components given additional structure. A new MediaSource
|
||||
class has been introduced. MediaSources expose Timelines that describe the
|
||||
media they expose, and can consist of multiple MediaPeriods. This enables
|
||||
features such as seeking in live playbacks and DASH multi-period support.
|
||||
* Responsibility for loading the initial DASH/SmoothStreaming/HLS manifest
|
||||
is promoted to the corresponding MediaSource components and is no longer
|
||||
the application's responsibility.
|
||||
* Higher level abstractions such as SimpleExoPlayer have been added to the
|
||||
library. These make the library easier to use for common use cases. The
|
||||
demo app is halved in size as a result, whilst at the same time gaining
|
||||
more functionality. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-improved-demo-app-d97171aaaaa1).
|
||||
* Enhanced library support for implementing audio extensions. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-new-audio-features-cfb26c2883a#.ua75vu4s3).
|
||||
* Format and MediaFormat are replaced by a single Format class.
|
||||
* Late binding between rendering and media source components. Allows the same
|
||||
rendering components to be re-used from one playback to another. Enables
|
||||
features such as gapless playback through playlists and DASH multi-period
|
||||
support.
|
||||
* Improved track selection design. More details can be found
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-track-selection-2b62ff712cc9#.n00zo76b6).
|
||||
* LoadControl now used to control buffering and loading across all playback
|
||||
types.
|
||||
* Media source components given additional structure. A new MediaSource class
|
||||
has been introduced. MediaSources expose Timelines that describe the media
|
||||
they expose, and can consist of multiple MediaPeriods. This enables features
|
||||
such as seeking in live playbacks and DASH multi-period support.
|
||||
* Responsibility for loading the initial DASH/SmoothStreaming/HLS manifest is
|
||||
promoted to the corresponding MediaSource components and is no longer the
|
||||
application's responsibility.
|
||||
* Higher level abstractions such as SimpleExoPlayer have been added to the
|
||||
library. These make the library easier to use for common use cases. The demo
|
||||
app is halved in size as a result, whilst at the same time gaining more
|
||||
functionality. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-improved-demo-app-d97171aaaaa1).
|
||||
* Enhanced library support for implementing audio extensions. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-new-audio-features-cfb26c2883a#.ua75vu4s3).
|
||||
* Format and MediaFormat are replaced by a single Format class.
|
||||
* Key new features:
|
||||
* Playlist support. Includes support for gapless playback between playlist
|
||||
items and consistent application of LoadControl and TrackSelector policies
|
||||
when transitioning between items
|
||||
([#1270](https://github.com/google/ExoPlayer/issues/1270)).
|
||||
* Seeking in live playbacks for DASH and SmoothStreaming
|
||||
([#291](https://github.com/google/ExoPlayer/issues/291)).
|
||||
* DASH multi-period support
|
||||
([#557](https://github.com/google/ExoPlayer/issues/557)).
|
||||
* MediaSource composition allows MediaSources to be concatenated into a
|
||||
playlist, merged and looped. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-mediasource-composition-6c285fcbca1f#.zfha8qupz).
|
||||
* Looping support (see above)
|
||||
([#490](https://github.com/google/ExoPlayer/issues/490)).
|
||||
* Ability to query information about all tracks in a piece of media
|
||||
(including those not supported by the device)
|
||||
([#1121](https://github.com/google/ExoPlayer/issues/1121)).
|
||||
* Improved player controls.
|
||||
* Support for PSSH in fMP4 moof atoms
|
||||
([#1143](https://github.com/google/ExoPlayer/issues/1143)).
|
||||
* Support for Opus in Ogg
|
||||
([#1447](https://github.com/google/ExoPlayer/issues/1447)).
|
||||
* CacheDataSource support for standalone media file playbacks (mp3, mp4
|
||||
etc).
|
||||
* FFMPEG extension (for audio only).
|
||||
* Playlist support. Includes support for gapless playback between playlist
|
||||
items and consistent application of LoadControl and TrackSelector policies
|
||||
when transitioning between items
|
||||
([#1270](https://github.com/google/ExoPlayer/issues/1270)).
|
||||
* Seeking in live playbacks for DASH and SmoothStreaming
|
||||
([#291](https://github.com/google/ExoPlayer/issues/291)).
|
||||
* DASH multi-period support
|
||||
([#557](https://github.com/google/ExoPlayer/issues/557)).
|
||||
* MediaSource composition allows MediaSources to be concatenated into a
|
||||
playlist, merged and looped. Read more
|
||||
[here](https://medium.com/google-exoplayer/exoplayer-2-x-mediasource-composition-6c285fcbca1f#.zfha8qupz).
|
||||
* Looping support (see above)
|
||||
([#490](https://github.com/google/ExoPlayer/issues/490)).
|
||||
* Ability to query information about all tracks in a piece of media (including
|
||||
those not supported by the device)
|
||||
([#1121](https://github.com/google/ExoPlayer/issues/1121)).
|
||||
* Improved player controls.
|
||||
* Support for PSSH in fMP4 moof atoms
|
||||
([#1143](https://github.com/google/ExoPlayer/issues/1143)).
|
||||
* Support for Opus in Ogg
|
||||
([#1447](https://github.com/google/ExoPlayer/issues/1447)).
|
||||
* CacheDataSource support for standalone media file playbacks (mp3, mp4 etc).
|
||||
* FFMPEG extension (for audio only).
|
||||
* Key bug fixes:
|
||||
* Removed unnecessary secondary requests when playing standalone media
|
||||
files ([#1041](https://github.com/google/ExoPlayer/issues/1041)).
|
||||
* Fixed playback of video only (i.e. no audio) live streams
|
||||
([#758](https://github.com/google/ExoPlayer/issues/758)).
|
||||
* Fixed silent failure when media buffer is too small
|
||||
([#583](https://github.com/google/ExoPlayer/issues/583)).
|
||||
* Suppressed "Sending message to a Handler on a dead thread" warnings
|
||||
([#426](https://github.com/google/ExoPlayer/issues/426)).
|
||||
* Removed unnecessary secondary requests when playing standalone media files
|
||||
([#1041](https://github.com/google/ExoPlayer/issues/1041)).
|
||||
* Fixed playback of video only (i.e. no audio) live streams
|
||||
([#758](https://github.com/google/ExoPlayer/issues/758)).
|
||||
* Fixed silent failure when media buffer is too small
|
||||
([#583](https://github.com/google/ExoPlayer/issues/583)).
|
||||
* Suppressed "Sending message to a Handler on a dead thread" warnings
|
||||
([#426](https://github.com/google/ExoPlayer/issues/426)).
|
||||
|
||||
# Legacy release notes #
|
||||
|
||||
@ -304,6 +335,12 @@ in all V2 releases. This cannot be assumed for changes in r1.5.12 and later,
|
||||
however it can be assumed that all such changes are included in the most recent
|
||||
V2 release.
|
||||
|
||||
### r1.5.16 ###
|
||||
|
||||
* VP9 extension: Reduced binary size.
|
||||
* FLAC extension: Enabled 64 bit targets and fixed proguard config.
|
||||
* Misc bugfixes.
|
||||
|
||||
### r1.5.15 ###
|
||||
|
||||
* SmoothStreaming: Fixed handling of start_time placeholder
|
||||
|
31
build.gradle
31
build.gradle
@ -19,8 +19,15 @@ buildscript {
|
||||
classpath 'com.android.tools.build:gradle:2.3.0'
|
||||
classpath 'com.novoda:bintray-release:0.4.0'
|
||||
}
|
||||
// Workaround for the following test coverage issue. Remove when fixed:
|
||||
// https://code.google.com/p/android/issues/detail?id=226070
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force 'org.jacoco:org.jacoco.report:0.7.4.201502262128'
|
||||
force 'org.jacoco:org.jacoco.core:0.7.4.201502262128'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
@ -30,16 +37,26 @@ allprojects {
|
||||
// components provided by the library may be of use on older devices.
|
||||
// However, please note that the core media playback functionality
|
||||
// provided by the library requires API level 16 or greater.
|
||||
minSdkVersion=9
|
||||
compileSdkVersion=25
|
||||
targetSdkVersion=25
|
||||
buildToolsVersion='25'
|
||||
minSdkVersion = 9
|
||||
compileSdkVersion = 25
|
||||
targetSdkVersion = 25
|
||||
buildToolsVersion = '25'
|
||||
testSupportLibraryVersion = '0.5'
|
||||
supportLibraryVersion = '25.3.1'
|
||||
dexmakerVersion = '1.2'
|
||||
mockitoVersion = '1.9.5'
|
||||
releaseRepoName = getBintrayRepo()
|
||||
releaseUserOrg = 'google'
|
||||
releaseGroupId = 'com.google.android.exoplayer'
|
||||
releaseVersion = 'r2.3.1'
|
||||
releaseVersion = 'r2.4.0'
|
||||
releaseWebsite = 'https://github.com/google/ExoPlayer'
|
||||
}
|
||||
if (it.hasProperty('externalBuildDir')) {
|
||||
if (!new File(externalBuildDir).isAbsolute()) {
|
||||
externalBuildDir = new File(rootDir, externalBuildDir)
|
||||
}
|
||||
buildDir = "${externalBuildDir}/${project.name}"
|
||||
}
|
||||
}
|
||||
|
||||
def getBintrayRepo() {
|
||||
@ -47,3 +64,5 @@ def getBintrayRepo() {
|
||||
property('publicRepo').toBoolean()
|
||||
return publicRepo ? 'exoplayer' : 'exoplayer-test'
|
||||
}
|
||||
|
||||
apply from: 'javadoc_combined.gradle'
|
||||
|
@ -45,7 +45,11 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
compile project(':library-dash')
|
||||
compile project(':library-hls')
|
||||
compile project(':library-smoothstreaming')
|
||||
compile project(':library-ui')
|
||||
withExtensionsCompile project(path: ':extension-ffmpeg')
|
||||
withExtensionsCompile project(path: ':extension-flac')
|
||||
withExtensionsCompile project(path: ':extension-opus')
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.exoplayer2.demo"
|
||||
android:versionCode="2301"
|
||||
android:versionName="2.3.1">
|
||||
android:versionCode="2400"
|
||||
android:versionName="2.4.0">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
@ -416,13 +416,16 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Audio -> Video",
|
||||
"name": "Audio -> Video -> Audio",
|
||||
"playlist": [
|
||||
{
|
||||
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4"
|
||||
},
|
||||
{
|
||||
"uri": "http://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
|
||||
},
|
||||
{
|
||||
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.demo;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility methods for demo application.
|
||||
*/
|
||||
/*package*/ final class DemoUtil {
|
||||
|
||||
/**
|
||||
* Builds a track name for display.
|
||||
*
|
||||
* @param format {@link Format} of the track.
|
||||
* @return a generated name specific to the track.
|
||||
*/
|
||||
public static String buildTrackName(Format format) {
|
||||
String trackName;
|
||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(
|
||||
buildResolutionString(format), buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
} else if (MimeTypes.isAudio(format.sampleMimeType)) {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(joinWithSeparator(
|
||||
buildLanguageString(format), buildAudioPropertyString(format)),
|
||||
buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
} else {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format),
|
||||
buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
}
|
||||
return trackName.length() == 0 ? "unknown" : trackName;
|
||||
}
|
||||
|
||||
private static String buildResolutionString(Format format) {
|
||||
return format.width == Format.NO_VALUE || format.height == Format.NO_VALUE
|
||||
? "" : format.width + "x" + format.height;
|
||||
}
|
||||
|
||||
private static String buildAudioPropertyString(Format format) {
|
||||
return format.channelCount == Format.NO_VALUE || format.sampleRate == Format.NO_VALUE
|
||||
? "" : format.channelCount + "ch, " + format.sampleRate + "Hz";
|
||||
}
|
||||
|
||||
private static String buildLanguageString(Format format) {
|
||||
return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? ""
|
||||
: format.language;
|
||||
}
|
||||
|
||||
private static String buildBitrateString(Format format) {
|
||||
return format.bitrate == Format.NO_VALUE ? ""
|
||||
: String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f);
|
||||
}
|
||||
|
||||
private static String joinWithSeparator(String first, String second) {
|
||||
return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second);
|
||||
}
|
||||
|
||||
private static String buildTrackIdString(Format format) {
|
||||
return format.id == null ? "" : ("id:" + format.id);
|
||||
}
|
||||
|
||||
private static String buildSampleMimeTypeString(Format format) {
|
||||
return format.sampleMimeType == null ? "" : format.sampleMimeType;
|
||||
}
|
||||
|
||||
private DemoUtil() {}
|
||||
}
|
@ -22,6 +22,7 @@ import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
@ -99,6 +100,12 @@ import java.util.Locale;
|
||||
Log.d(TAG, "positionDiscontinuity");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
Log.d(TAG, "playbackParameters " + String.format(
|
||||
"[speed=%.2f, pitch=%.2f]", playbackParameters.speed, playbackParameters.pitch));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
int periodCount = timeline.getPeriodCount();
|
||||
@ -274,7 +281,7 @@ import java.util.Locale;
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame(Surface surface) {
|
||||
// Do nothing.
|
||||
Log.d(TAG, "renderedFirstFrame [" + surface + "]");
|
||||
}
|
||||
|
||||
// DefaultDrmSessionManager.EventListener
|
||||
|
@ -21,6 +21,7 @@ import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
@ -30,10 +31,11 @@ import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
||||
@ -111,6 +113,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
private TrackSelectionHelper trackSelectionHelper;
|
||||
private DebugTextViewHelper debugViewHelper;
|
||||
private boolean needRetrySource;
|
||||
private TrackGroupArray lastSeenTrackGroupArray;
|
||||
|
||||
private boolean shouldAutoPlay;
|
||||
private int resumeWindow;
|
||||
@ -183,8 +186,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||
int[] grantResults) {
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
initializePlayer();
|
||||
} else {
|
||||
@ -250,17 +253,21 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
}
|
||||
}
|
||||
|
||||
@SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode =
|
||||
@DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode =
|
||||
((DemoApplication) getApplication()).useExtensionRenderers()
|
||||
? (preferExtensionDecoders ? SimpleExoPlayer.EXTENSION_RENDERER_MODE_PREFER
|
||||
: SimpleExoPlayer.EXTENSION_RENDERER_MODE_ON)
|
||||
: SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF;
|
||||
? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
||||
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
|
||||
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;
|
||||
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this,
|
||||
drmSessionManager, extensionRendererMode);
|
||||
|
||||
TrackSelection.Factory videoTrackSelectionFactory =
|
||||
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory);
|
||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(),
|
||||
drmSessionManager, extensionRendererMode);
|
||||
lastSeenTrackGroupArray = null;
|
||||
|
||||
player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
|
||||
player.addListener(this);
|
||||
|
||||
eventLogger = new EventLogger(trackSelector);
|
||||
@ -427,6 +434,11 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
// Do nothing.
|
||||
@ -472,18 +484,22 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
updateButtonVisibilities();
|
||||
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
|
||||
if (mappedTrackInfo != null) {
|
||||
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_VIDEO)
|
||||
== MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
|
||||
showToast(R.string.error_unsupported_video);
|
||||
}
|
||||
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_AUDIO)
|
||||
== MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
|
||||
showToast(R.string.error_unsupported_audio);
|
||||
if (trackGroups != lastSeenTrackGroupArray) {
|
||||
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
|
||||
if (mappedTrackInfo != null) {
|
||||
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_VIDEO)
|
||||
== MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
|
||||
showToast(R.string.error_unsupported_video);
|
||||
}
|
||||
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_AUDIO)
|
||||
== MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
|
||||
showToast(R.string.error_unsupported_audio);
|
||||
}
|
||||
}
|
||||
lastSeenTrackGroupArray = trackGroups;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,13 +21,11 @@ import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
@ -37,9 +35,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedT
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.SelectionOverride;
|
||||
import com.google.android.exoplayer2.trackselection.RandomTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Helper class for displaying track selection dialogs.
|
||||
@ -157,7 +153,7 @@ import java.util.Locale;
|
||||
CheckedTextView trackView = (CheckedTextView) inflater.inflate(
|
||||
trackViewLayoutId, root, false);
|
||||
trackView.setBackgroundResource(selectableItemBackgroundResourceId);
|
||||
trackView.setText(buildTrackName(group.getFormat(trackIndex)));
|
||||
trackView.setText(DemoUtil.buildTrackName(group.getFormat(trackIndex)));
|
||||
if (trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)
|
||||
== RendererCapabilities.FORMAT_HANDLED) {
|
||||
trackView.setFocusable(true);
|
||||
@ -296,57 +292,4 @@ import java.util.Locale;
|
||||
return tracks;
|
||||
}
|
||||
|
||||
// Track name construction.
|
||||
|
||||
private static String buildTrackName(Format format) {
|
||||
String trackName;
|
||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(
|
||||
buildResolutionString(format), buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
} else if (MimeTypes.isAudio(format.sampleMimeType)) {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(joinWithSeparator(
|
||||
buildLanguageString(format), buildAudioPropertyString(format)),
|
||||
buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
} else {
|
||||
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format),
|
||||
buildBitrateString(format)), buildTrackIdString(format)),
|
||||
buildSampleMimeTypeString(format));
|
||||
}
|
||||
return trackName.length() == 0 ? "unknown" : trackName;
|
||||
}
|
||||
|
||||
private static String buildResolutionString(Format format) {
|
||||
return format.width == Format.NO_VALUE || format.height == Format.NO_VALUE
|
||||
? "" : format.width + "x" + format.height;
|
||||
}
|
||||
|
||||
private static String buildAudioPropertyString(Format format) {
|
||||
return format.channelCount == Format.NO_VALUE || format.sampleRate == Format.NO_VALUE
|
||||
? "" : format.channelCount + "ch, " + format.sampleRate + "Hz";
|
||||
}
|
||||
|
||||
private static String buildLanguageString(Format format) {
|
||||
return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? ""
|
||||
: format.language;
|
||||
}
|
||||
|
||||
private static String buildBitrateString(Format format) {
|
||||
return format.bitrate == Format.NO_VALUE ? ""
|
||||
: String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f);
|
||||
}
|
||||
|
||||
private static String joinWithSeparator(String first, String second) {
|
||||
return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second);
|
||||
}
|
||||
|
||||
private static String buildTrackIdString(Format format) {
|
||||
return format.id == null ? "" : ("id:" + format.id);
|
||||
}
|
||||
|
||||
private static String buildSampleMimeTypeString(Format format) {
|
||||
return format.sampleMimeType == null ? "" : format.sampleMimeType;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,13 +29,18 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
compile files('libs/cronet_api.jar')
|
||||
compile files('libs/cronet_impl_common_java.jar')
|
||||
compile files('libs/cronet_impl_native_java.jar')
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.9.5'
|
||||
androidTestCompile project(':library')
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
||||
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
|
||||
androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'Cronet extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
@ -72,7 +72,7 @@ public final class CronetDataSourceFactory extends BaseFactory {
|
||||
protected CronetDataSource createDataSourceInternal(HttpDataSource.RequestProperties
|
||||
defaultRequestProperties) {
|
||||
return new CronetDataSource(cronetEngine, executor, contentTypePredicate, transferListener,
|
||||
connectTimeoutMs, readTimeoutMs, resetTimeoutOnRedirects, null, defaultRequestProperties);
|
||||
connectTimeoutMs, readTimeoutMs, resetTimeoutOnRedirects, defaultRequestProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,21 +31,18 @@ FFMPEG_EXT_PATH="${EXOPLAYER_ROOT}/extensions/ffmpeg/src/main"
|
||||
NDK_PATH="<path to Android NDK>"
|
||||
```
|
||||
|
||||
* Fetch and build FFmpeg. For example, to fetch and build for armv7a:
|
||||
* Set up host platform ("darwin-x86_64" for Mac OS X):
|
||||
|
||||
```
|
||||
cd "${FFMPEG_EXT_PATH}/jni" && \
|
||||
git clone git://source.ffmpeg.org/ffmpeg ffmpeg && cd ffmpeg && \
|
||||
./configure \
|
||||
--libdir=android-libs/armeabi-v7a \
|
||||
--arch=arm \
|
||||
--cpu=armv7-a \
|
||||
--cross-prefix="${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-" \
|
||||
HOST_PLATFORM="linux-x86_64"
|
||||
```
|
||||
|
||||
* Fetch and build FFmpeg. For example, to fetch and build for armeabi-v7a,
|
||||
arm64-v8a and x86 on Linux x86_64:
|
||||
|
||||
```
|
||||
COMMON_OPTIONS="\
|
||||
--target-os=android \
|
||||
--sysroot="${NDK_PATH}/platforms/android-9/arch-arm/" \
|
||||
--extra-cflags="-march=armv7-a -mfloat-abi=softfp" \
|
||||
--extra-ldflags="-Wl,--fix-cortex-a8" \
|
||||
--extra-ldexeflags=-pie \
|
||||
--disable-static \
|
||||
--enable-shared \
|
||||
--disable-doc \
|
||||
@ -57,22 +54,56 @@ git clone git://source.ffmpeg.org/ffmpeg ffmpeg && cd ffmpeg && \
|
||||
--disable-postproc \
|
||||
--disable-avfilter \
|
||||
--disable-symver \
|
||||
--disable-swresample \
|
||||
--enable-avresample \
|
||||
--enable-decoder=vorbis \
|
||||
--enable-decoder=opus \
|
||||
--enable-decoder=flac \
|
||||
--enable-decoder=alac \
|
||||
" && \
|
||||
cd "${FFMPEG_EXT_PATH}/jni" && \
|
||||
git clone git://source.ffmpeg.org/ffmpeg ffmpeg && cd ffmpeg && \
|
||||
./configure \
|
||||
--libdir=android-libs/armeabi-v7a \
|
||||
--arch=arm \
|
||||
--cpu=armv7-a \
|
||||
--cross-prefix="${NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/${HOST_PLATFORM}/bin/arm-linux-androideabi-" \
|
||||
--sysroot="${NDK_PATH}/platforms/android-9/arch-arm/" \
|
||||
--extra-cflags="-march=armv7-a -mfloat-abi=softfp" \
|
||||
--extra-ldflags="-Wl,--fix-cortex-a8" \
|
||||
--extra-ldexeflags=-pie \
|
||||
${COMMON_OPTIONS} \
|
||||
&& \
|
||||
make -j4 && \
|
||||
make install-libs
|
||||
make -j4 && make install-libs && \
|
||||
make clean && ./configure \
|
||||
--libdir=android-libs/arm64-v8a \
|
||||
--arch=aarch64 \
|
||||
--cpu=armv8-a \
|
||||
--cross-prefix="${NDK_PATH}/toolchains/aarch64-linux-android-4.9/prebuilt/${HOST_PLATFORM}/bin/aarch64-linux-android-" \
|
||||
--sysroot="${NDK_PATH}/platforms/android-21/arch-arm64/" \
|
||||
--extra-ldexeflags=-pie \
|
||||
${COMMON_OPTIONS} \
|
||||
&& \
|
||||
make -j4 && make install-libs && \
|
||||
make clean && ./configure \
|
||||
--libdir=android-libs/x86 \
|
||||
--arch=x86 \
|
||||
--cpu=i686 \
|
||||
--cross-prefix="${NDK_PATH}/toolchains/x86-4.9/prebuilt/${HOST_PLATFORM}/bin/i686-linux-android-" \
|
||||
--sysroot="${NDK_PATH}/platforms/android-9/arch-x86/" \
|
||||
--extra-ldexeflags=-pie \
|
||||
--disable-asm \
|
||||
${COMMON_OPTIONS} \
|
||||
&& \
|
||||
make -j4 && make install-libs && \
|
||||
make clean
|
||||
```
|
||||
|
||||
* Build the JNI native libraries. Repeat this step for any other architectures
|
||||
you need to support.
|
||||
* Build the JNI native libraries, setting `APP_ABI` to include the architectures
|
||||
built in the previous step. For example:
|
||||
|
||||
```
|
||||
cd "${FFMPEG_EXT_PATH}"/jni && \
|
||||
${NDK_PATH}/ndk-build APP_ABI=armeabi-v7a -j4
|
||||
${NDK_PATH}/ndk-build APP_ABI="armeabi-v7a arm64-v8a x86" -j4
|
||||
```
|
||||
|
||||
* In your project, you can add a dependency on the extension by using a rule
|
||||
|
@ -30,5 +30,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'FFmpeg extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
@ -31,15 +31,10 @@ LOCAL_MODULE := libavresample
|
||||
LOCAL_SRC_FILES := ffmpeg/android-libs/$(TARGET_ARCH_ABI)/$(LOCAL_MODULE).so
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libswresample
|
||||
LOCAL_SRC_FILES := ffmpeg/android-libs/$(TARGET_ARCH_ABI)/$(LOCAL_MODULE).so
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ffmpeg
|
||||
LOCAL_SRC_FILES := ffmpeg_jni.cc
|
||||
LOCAL_C_INCLUDES := ffmpeg
|
||||
LOCAL_SHARED_LIBRARIES := libavcodec libavresample libavutil libswresample
|
||||
LOCAL_SHARED_LIBRARIES := libavcodec libavresample libavutil
|
||||
LOCAL_LDLIBS := -Lffmpeg/android-libs/$(TARGET_ARCH_ABI) -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
@ -30,7 +30,11 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
androidTestCompile project(':testutils')
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'FLAC extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
@ -22,6 +22,7 @@ import android.test.InstrumentationTestCase;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
@ -102,6 +103,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
// Do nothing.
|
||||
|
@ -24,10 +24,15 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
compile 'com.google.vr:sdk-audio:1.30.0'
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'GVR extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
||||
ext {
|
||||
releaseArtifact = 'extension-gvr'
|
||||
releaseDescription = 'Google VR extension for ExoPlayer.'
|
||||
|
@ -152,14 +152,19 @@ public final class GvrAudioProcessor implements AudioProcessor {
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
gvrAudioSurround.flush();
|
||||
if (gvrAudioSurround != null) {
|
||||
gvrAudioSurround.flush();
|
||||
}
|
||||
inputEnded = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void release() {
|
||||
buffer = null;
|
||||
public synchronized void reset() {
|
||||
maybeReleaseGvrAudioSurround();
|
||||
inputEnded = false;
|
||||
buffer = null;
|
||||
sampleRateHz = Format.NO_VALUE;
|
||||
channelCount = Format.NO_VALUE;
|
||||
}
|
||||
|
||||
private void maybeReleaseGvrAudioSurround() {
|
||||
|
@ -29,12 +29,17 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
compile('com.squareup.okhttp3:okhttp:3.6.0') {
|
||||
exclude group: 'org.json'
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'OkHttp extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
||||
ext {
|
||||
releaseArtifact = 'extension-okhttp'
|
||||
releaseDescription = 'OkHttp extension for ExoPlayer.'
|
||||
|
@ -30,5 +30,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'Opus extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
@ -22,6 +22,7 @@ import android.test.InstrumentationTestCase;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
@ -102,6 +103,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
// Do nothing.
|
||||
|
@ -161,7 +161,7 @@ import java.util.List;
|
||||
cryptoInfo.key, cryptoInfo.iv, cryptoInfo.numSubSamples,
|
||||
cryptoInfo.numBytesOfClearData, cryptoInfo.numBytesOfEncryptedData)
|
||||
: opusDecode(nativeDecoderContext, inputBuffer.timeUs, inputData, inputData.limit(),
|
||||
outputBuffer, SAMPLE_RATE);
|
||||
outputBuffer);
|
||||
if (result < 0) {
|
||||
if (result == DRM_ERROR) {
|
||||
String message = "Drm error: " + opusGetErrorMessage(nativeDecoderContext);
|
||||
@ -210,7 +210,7 @@ import java.util.List;
|
||||
private native long opusInit(int sampleRate, int channelCount, int numStreams, int numCoupled,
|
||||
int gain, byte[] streamMap);
|
||||
private native int opusDecode(long decoder, long timeUs, ByteBuffer inputBuffer, int inputSize,
|
||||
SimpleOutputBuffer outputBuffer, int sampleRate);
|
||||
SimpleOutputBuffer outputBuffer);
|
||||
private native int opusSecureDecode(long decoder, long timeUs, ByteBuffer inputBuffer,
|
||||
int inputSize, SimpleOutputBuffer outputBuffer, int sampleRate,
|
||||
ExoMediaCrypto mediaCrypto, int inputMode, byte[] key, byte[] iv,
|
||||
|
@ -59,6 +59,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
}
|
||||
|
||||
static const int kBytesPerSample = 2; // opus fixed point uses 16 bit samples.
|
||||
static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
|
||||
static int channelCount;
|
||||
static int errorCode;
|
||||
|
||||
@ -92,16 +93,14 @@ DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount,
|
||||
}
|
||||
|
||||
DECODER_FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs,
|
||||
jobject jInputBuffer, jint inputSize, jobject jOutputBuffer,
|
||||
jint sampleRate) {
|
||||
jobject jInputBuffer, jint inputSize, jobject jOutputBuffer) {
|
||||
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||
const uint8_t* inputBuffer =
|
||||
reinterpret_cast<const uint8_t*>(
|
||||
env->GetDirectBufferAddress(jInputBuffer));
|
||||
|
||||
const int32_t inputSampleCount =
|
||||
opus_packet_get_nb_samples(inputBuffer, inputSize, sampleRate);
|
||||
const jint outputSize = inputSampleCount * kBytesPerSample * channelCount;
|
||||
const jint outputSize =
|
||||
kMaxOpusOutputPacketSizeSamples * kBytesPerSample * channelCount;
|
||||
|
||||
env->CallObjectMethod(jOutputBuffer, outputBufferInit, jTimeUs, outputSize);
|
||||
const jobject jOutputBufferData = env->CallObjectMethod(jOutputBuffer,
|
||||
@ -110,7 +109,7 @@ DECODER_FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs,
|
||||
int16_t* outputBufferData = reinterpret_cast<int16_t*>(
|
||||
env->GetDirectBufferAddress(jOutputBufferData));
|
||||
int sampleCount = opus_multistream_decode(decoder, inputBuffer, inputSize,
|
||||
outputBufferData, outputSize, 0);
|
||||
outputBufferData, kMaxOpusOutputPacketSizeSamples, 0);
|
||||
// record error code
|
||||
errorCode = (sampleCount < 0) ? sampleCount : 0;
|
||||
return (sampleCount < 0) ? sampleCount
|
||||
|
@ -49,7 +49,7 @@ git clone https://chromium.googlesource.com/libyuv/libyuv libyuv
|
||||
cd "${VP9_EXT_PATH}/jni/libvpx" && \
|
||||
git checkout tags/v1.6.1 -b v1.6.1 && \
|
||||
cd "${VP9_EXT_PATH}/jni/libyuv" && \
|
||||
git checkout e2611a73
|
||||
git checkout 996a2bbd
|
||||
```
|
||||
|
||||
* Run a script that generates necessary configuration files for libvpx:
|
||||
|
@ -30,6 +30,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
compile project(':library-core')
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'VP9 extension'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
@ -23,6 +23,7 @@ import android.util.Log;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
@ -134,6 +135,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||
// Do nothing.
|
||||
|
@ -87,8 +87,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
|
||||
private boolean inputStreamEnded;
|
||||
private boolean outputStreamEnded;
|
||||
private int lastReportedWidth;
|
||||
private int lastReportedHeight;
|
||||
private int reportedWidth;
|
||||
private int reportedHeight;
|
||||
|
||||
private long droppedFrameAccumulationStartTimeMs;
|
||||
private int droppedFrames;
|
||||
@ -147,10 +147,10 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
||||
joiningDeadlineMs = -1;
|
||||
clearLastReportedVideoSize();
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
clearReportedVideoSize();
|
||||
formatHolder = new FormatHolder();
|
||||
flagsOnlyBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
outputMode = VpxDecoder.OUTPUT_MODE_NONE;
|
||||
}
|
||||
@ -185,44 +185,40 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
if (isRendererAvailable()) {
|
||||
drmSession = pendingDrmSession;
|
||||
ExoMediaCrypto mediaCrypto = null;
|
||||
if (drmSession != null) {
|
||||
int drmSessionState = drmSession.getState();
|
||||
if (drmSessionState == DrmSession.STATE_ERROR) {
|
||||
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
|
||||
} else if (drmSessionState == DrmSession.STATE_OPENED
|
||||
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
|
||||
mediaCrypto = drmSession.getMediaCrypto();
|
||||
} else {
|
||||
// The drm session isn't open yet.
|
||||
return;
|
||||
}
|
||||
// We have a format.
|
||||
drmSession = pendingDrmSession;
|
||||
ExoMediaCrypto mediaCrypto = null;
|
||||
if (drmSession != null) {
|
||||
int drmSessionState = drmSession.getState();
|
||||
if (drmSessionState == DrmSession.STATE_ERROR) {
|
||||
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
|
||||
} else if (drmSessionState == DrmSession.STATE_OPENED
|
||||
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
|
||||
mediaCrypto = drmSession.getMediaCrypto();
|
||||
} else {
|
||||
// The drm session isn't open yet.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (decoder == null) {
|
||||
// If we don't have a decoder yet, we need to instantiate one.
|
||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||
TraceUtil.beginSection("createVpxDecoder");
|
||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
|
||||
mediaCrypto);
|
||||
decoder.setOutputMode(outputMode);
|
||||
TraceUtil.endSection();
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
decoderCounters.decoderInitCount++;
|
||||
}
|
||||
TraceUtil.beginSection("drainAndFeed");
|
||||
while (drainOutputBuffer(positionUs)) {}
|
||||
while (feedInputBuffer()) {}
|
||||
}
|
||||
try {
|
||||
if (decoder == null) {
|
||||
// If we don't have a decoder yet, we need to instantiate one.
|
||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||
TraceUtil.beginSection("createVpxDecoder");
|
||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto);
|
||||
decoder.setOutputMode(outputMode);
|
||||
TraceUtil.endSection();
|
||||
} catch (VpxDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||
decoderCounters.decoderInitCount++;
|
||||
}
|
||||
} else {
|
||||
skipToKeyframeBefore(positionUs);
|
||||
TraceUtil.beginSection("drainAndFeed");
|
||||
while (drainOutputBuffer(positionUs)) {}
|
||||
while (feedInputBuffer()) {}
|
||||
TraceUtil.endSection();
|
||||
} catch (VpxDecoderException e) {
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
decoderCounters.ensureUpdated();
|
||||
}
|
||||
@ -257,27 +253,26 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||
// and that's also late. Else we'll render what we have.
|
||||
if ((joiningDeadlineMs != -1 && outputBuffer.timeUs < positionUs - 30000)
|
||||
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
&& nextOutputBuffer.timeUs < positionUs)) {
|
||||
decoderCounters.droppedOutputBufferCount++;
|
||||
droppedFrames++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
||||
consecutiveDroppedFrameCount,
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrames == maxDroppedFramesToNotify) {
|
||||
maybeNotifyDroppedFrames();
|
||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||
if (outputBuffer.timeUs <= positionUs) {
|
||||
skipBuffer();
|
||||
return true;
|
||||
}
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
final long nextOutputBufferTimeUs =
|
||||
nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
? nextOutputBuffer.timeUs : C.TIME_UNSET;
|
||||
if (shouldDropOutputBuffer(
|
||||
outputBuffer.timeUs, nextOutputBufferTimeUs, positionUs, joiningDeadlineMs)) {
|
||||
dropBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have not rendered any frame so far (either initially or immediately following a seek),
|
||||
// render one frame irrespective of the state or current position.
|
||||
// If we have yet to render a frame to the current output (either initially or immediately
|
||||
// following a seek), render one irrespective of the state or current position.
|
||||
if (!renderedFirstFrame
|
||||
|| (getState() == STATE_STARTED && outputBuffer.timeUs <= positionUs + 30000)) {
|
||||
renderBuffer();
|
||||
@ -285,27 +280,63 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the current frame should be dropped.
|
||||
*
|
||||
* @param outputBufferTimeUs The timestamp of the current output buffer.
|
||||
* @param nextOutputBufferTimeUs The timestamp of the next output buffer or
|
||||
* {@link TIME_UNSET} if the next output buffer is unavailable.
|
||||
* @param positionUs The current playback position.
|
||||
* @param joiningDeadlineMs The joining deadline.
|
||||
* @return Returns whether to drop the current output buffer.
|
||||
*/
|
||||
protected boolean shouldDropOutputBuffer(long outputBufferTimeUs, long nextOutputBufferTimeUs,
|
||||
long positionUs, long joiningDeadlineMs) {
|
||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||
// and that's also late. Else we'll render what we have.
|
||||
return (joiningDeadlineMs != C.TIME_UNSET && outputBufferTimeUs < positionUs - 30000)
|
||||
|| (nextOutputBufferTimeUs != C.TIME_UNSET && nextOutputBufferTimeUs < positionUs);
|
||||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||
renderRgbFrame(outputBuffer, scaleToFit);
|
||||
if (!renderedFirstFrame) {
|
||||
renderedFirstFrame = true;
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
outputBuffer.release();
|
||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
||||
// The renderer will release the buffer.
|
||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||
if (!renderedFirstFrame) {
|
||||
renderedFirstFrame = true;
|
||||
eventDispatcher.renderedFirstFrame(null);
|
||||
}
|
||||
int bufferMode = outputBuffer.mode;
|
||||
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
||||
boolean renderYuv = bufferMode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null;
|
||||
if (!renderRgb && !renderYuv) {
|
||||
dropBuffer();
|
||||
} else {
|
||||
outputBuffer.release();
|
||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
if (renderRgb) {
|
||||
renderRgbFrame(outputBuffer, scaleToFit);
|
||||
outputBuffer.release();
|
||||
} else /* renderYuv */ {
|
||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||
// The renderer will release the buffer.
|
||||
}
|
||||
outputBuffer = null;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
}
|
||||
|
||||
private void dropBuffer() {
|
||||
decoderCounters.droppedOutputBufferCount++;
|
||||
droppedFrames++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
decoderCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(
|
||||
consecutiveDroppedFrameCount, decoderCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrames == maxDroppedFramesToNotify) {
|
||||
maybeNotifyDroppedFrames();
|
||||
}
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
}
|
||||
|
||||
private void skipBuffer() {
|
||||
decoderCounters.skippedOutputBufferCount++;
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
}
|
||||
|
||||
@ -406,11 +437,11 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
return false;
|
||||
}
|
||||
if (format != null && (isSourceReady() || outputBuffer != null)
|
||||
&& (renderedFirstFrame || !isRendererAvailable())) {
|
||||
&& (renderedFirstFrame || outputMode == VpxDecoder.OUTPUT_MODE_NONE)) {
|
||||
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
||||
joiningDeadlineMs = -1;
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
return true;
|
||||
} else if (joiningDeadlineMs == -1) {
|
||||
} else if (joiningDeadlineMs == C.TIME_UNSET) {
|
||||
// Not joining.
|
||||
return false;
|
||||
} else if (SystemClock.elapsedRealtime() < joiningDeadlineMs) {
|
||||
@ -418,7 +449,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
return true;
|
||||
} else {
|
||||
// The joining deadline has been exceeded. Give up and clear the deadline.
|
||||
joiningDeadlineMs = -1;
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -433,24 +464,27 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
protected void onPositionReset(long positionUs, boolean joining) {
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
renderedFirstFrame = false;
|
||||
clearRenderedFirstFrame();
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (decoder != null) {
|
||||
flushDecoder();
|
||||
}
|
||||
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : -1;
|
||||
if (joining) {
|
||||
setJoiningDeadlineMs();
|
||||
} else {
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStarted() {
|
||||
droppedFrames = 0;
|
||||
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopped() {
|
||||
joiningDeadlineMs = -1;
|
||||
maybeNotifyDroppedFrames();
|
||||
}
|
||||
|
||||
@ -460,7 +494,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
outputBuffer = null;
|
||||
format = null;
|
||||
waitingForKeys = false;
|
||||
clearLastReportedVideoSize();
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
try {
|
||||
releaseDecoder();
|
||||
} finally {
|
||||
@ -537,47 +572,78 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
||||
private void setOutput(Surface surface, VpxOutputBufferRenderer outputBufferRenderer) {
|
||||
// At most one output may be non-null. Both may be null if the output is being cleared.
|
||||
Assertions.checkState(surface == null || outputBufferRenderer == null);
|
||||
// Clear state so that we always call the event listener with the video size and when a frame
|
||||
// is rendered, even if the output hasn't changed.
|
||||
renderedFirstFrame = false;
|
||||
clearLastReportedVideoSize();
|
||||
// We only need to update the decoder if the output has changed.
|
||||
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
|
||||
// The output has changed.
|
||||
this.surface = surface;
|
||||
this.outputBufferRenderer = outputBufferRenderer;
|
||||
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
|
||||
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
|
||||
updateDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDecoder() {
|
||||
if (decoder != null) {
|
||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
releaseDecoder();
|
||||
if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
if (decoder != null) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
}
|
||||
// If we know the video size, report it again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
// We haven't rendered to the new output yet.
|
||||
clearRenderedFirstFrame();
|
||||
if (getState() == STATE_STARTED) {
|
||||
setJoiningDeadlineMs();
|
||||
}
|
||||
} else {
|
||||
decoder.setOutputMode(outputMode);
|
||||
// The output has been removed. We leave the outputMode of the underlying decoder unchanged
|
||||
// in anticipation that a subsequent output will likely be of the same type.
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
}
|
||||
} else if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
// The output is unchanged and non-null. If we know the video size and/or have already
|
||||
// rendered to the output, report these again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
maybeRenotifyRenderedFirstFrame();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRendererAvailable() {
|
||||
return surface != null || outputBufferRenderer != null;
|
||||
private void setJoiningDeadlineMs() {
|
||||
joiningDeadlineMs = allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private void clearLastReportedVideoSize() {
|
||||
lastReportedWidth = Format.NO_VALUE;
|
||||
lastReportedHeight = Format.NO_VALUE;
|
||||
private void clearRenderedFirstFrame() {
|
||||
renderedFirstFrame = false;
|
||||
}
|
||||
|
||||
private void maybeNotifyRenderedFirstFrame() {
|
||||
if (!renderedFirstFrame) {
|
||||
renderedFirstFrame = true;
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeRenotifyRenderedFirstFrame() {
|
||||
if (renderedFirstFrame) {
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearReportedVideoSize() {
|
||||
reportedWidth = Format.NO_VALUE;
|
||||
reportedHeight = Format.NO_VALUE;
|
||||
}
|
||||
|
||||
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
||||
if (lastReportedWidth != width || lastReportedHeight != height) {
|
||||
lastReportedWidth = width;
|
||||
lastReportedHeight = height;
|
||||
if (reportedWidth != width || reportedHeight != height) {
|
||||
reportedWidth = width;
|
||||
reportedHeight = height;
|
||||
eventDispatcher.videoSizeChanged(width, height, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeRenotifyVideoSizeChanged() {
|
||||
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
|
||||
eventDispatcher.videoSizeChanged(reportedWidth, reportedHeight, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyDroppedFrames() {
|
||||
if (droppedFrames > 0) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
|
@ -21,6 +21,7 @@ LIBYUV_ROOT := $(WORKING_DIR)/libyuv
|
||||
|
||||
# build libyuv_static.a
|
||||
LOCAL_PATH := $(WORKING_DIR)
|
||||
LIBYUV_DISABLE_JPEG := "yes"
|
||||
include $(LIBYUV_ROOT)/Android.mk
|
||||
|
||||
# build libvpx.so
|
||||
|
@ -68,7 +68,7 @@ limit=$((${#arch[@]} - 1))
|
||||
# everything else will be removed.
|
||||
allowed_files="libvpx_srcs.txt vpx_config.c vpx_config.h vpx_scale_rtcd.h"
|
||||
allowed_files+=" vp8_rtcd.h vp9_rtcd.h vpx_version.h vpx_config.asm"
|
||||
allowed_files+=" vpx_dsp_rtcd.h"
|
||||
allowed_files+=" vpx_dsp_rtcd.h libvpx.ver"
|
||||
|
||||
remove_trailing_whitespace() {
|
||||
perl -pi -e 's/\s\+$//' "$@"
|
||||
|
@ -37,9 +37,7 @@ LOCAL_SRC_FILES += $(addprefix libvpx/, $(filter-out vpx_config.c, \
|
||||
# include assembly files if they exist
|
||||
# "%.asm.[sS]" covers neon assembly and "%.asm" covers x86 assembly
|
||||
LOCAL_SRC_FILES += $(addprefix libvpx/, \
|
||||
$(filter %.asm.s %.asm, $(libvpx_codec_srcs)))
|
||||
LOCAL_SRC_FILES += $(addprefix libvpx/, \
|
||||
$(filter %.asm.S %.asm, $(libvpx_codec_srcs)))
|
||||
$(filter %.asm.s %.asm.S %.asm, $(libvpx_codec_srcs)))
|
||||
|
||||
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
||||
# append .neon to *_neon.c and *.[sS]
|
||||
@ -48,10 +46,8 @@ LOCAL_SRC_FILES := $(subst .s,.s.neon,$(LOCAL_SRC_FILES))
|
||||
LOCAL_SRC_FILES := $(subst .S,.S.neon,$(LOCAL_SRC_FILES))
|
||||
endif
|
||||
|
||||
# remove duplicates
|
||||
LOCAL_SRC_FILES := $(sort $(LOCAL_SRC_FILES))
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/libvpx \
|
||||
$(LOCAL_PATH)/libvpx/vpx
|
||||
|
||||
LOCAL_LDFLAGS := -Wl,--version-script=$(CONFIG_DIR)/libvpx.ver
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
@ -20,8 +20,9 @@
|
||||
#include <android/log.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#include "libyuv.h" // NOLINT
|
||||
|
68
javadoc_combined.gradle
Normal file
68
javadoc_combined.gradle
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2017 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.
|
||||
class CombinedJavadocPlugin implements Plugin<Project> {
|
||||
|
||||
static final String TASK_NAME = "generateCombinedJavadoc"
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
project.gradle.projectsEvaluated {
|
||||
Set<Project> libraryModules = getLibraryModules(project)
|
||||
if (!libraryModules.isEmpty()) {
|
||||
String sdkDirectory = getSdkDirectory(libraryModules)
|
||||
project.task(TASK_NAME, type: Javadoc) {
|
||||
description = "Generates combined Javadoc."
|
||||
title = "ExoPlayer library"
|
||||
source = libraryModules.generateJavadoc.source
|
||||
classpath = project.files(libraryModules.generateJavadoc.classpath)
|
||||
destinationDir = project.file("$project.buildDir/docs/javadoc")
|
||||
options {
|
||||
links "http://docs.oracle.com/javase/7/docs/api/"
|
||||
linksOffline "https://developer.android.com/reference",
|
||||
"${sdkDirectory}/docs/reference"
|
||||
encoding = "UTF-8"
|
||||
}
|
||||
exclude "**/BuildConfig.java"
|
||||
exclude "**/R.java"
|
||||
destinationDir project.file("$project.buildDir/docs/javadoc")
|
||||
doLast {
|
||||
libraryModules.each { libraryModule ->
|
||||
project.copy {
|
||||
from "${libraryModule.projectDir}/src/main/javadoc"
|
||||
into "${project.buildDir}/docs/javadoc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns Android library modules that declare a generateJavadoc task.
|
||||
private Set<Project> getLibraryModules(Project project) {
|
||||
project.subprojects.findAll {
|
||||
it.plugins.findPlugin("com.android.library") &&
|
||||
it.tasks.findByName("generateJavadoc")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the Android SDK directory given a set of Android library modules.
|
||||
private String getSdkDirectory(Set<Project> libraryModules) {
|
||||
// We can retrieve the Android SDK directory from any module.
|
||||
return libraryModules.iterator().next().android.sdkDirectory
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
apply plugin: CombinedJavadocPlugin
|
40
javadoc_library.gradle
Normal file
40
javadoc_library.gradle
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2017 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.
|
||||
android.libraryVariants.all { variant ->
|
||||
def name = variant.buildType.name
|
||||
if (!name.equals("release")) {
|
||||
return; // Skip non-release builds.
|
||||
}
|
||||
task("generateJavadoc", type: Javadoc) {
|
||||
description = "Generates Javadoc for the ${javadocTitle}."
|
||||
title = "ExoPlayer ${javadocTitle}"
|
||||
source = variant.javaCompile.source
|
||||
classpath = files(variant.javaCompile.classpath.files,
|
||||
project.android.getBootClasspath())
|
||||
options {
|
||||
links "http://docs.oracle.com/javase/7/docs/api/"
|
||||
linksOffline "https://developer.android.com/reference",
|
||||
"${android.sdkDirectory}/docs/reference"
|
||||
encoding = "UTF-8"
|
||||
}
|
||||
exclude "**/BuildConfig.java"
|
||||
exclude "**/R.java"
|
||||
doLast {
|
||||
copy {
|
||||
from "src/main/javadoc"
|
||||
into "$buildDir/docs/javadoc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
library/all/build.gradle
Normal file
38
library/all/build.gradle
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2016 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.
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion project.ext.compileSdkVersion
|
||||
buildToolsVersion project.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion project.ext.minSdkVersion
|
||||
targetSdkVersion project.ext.targetSdkVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library-core')
|
||||
compile project(':library-dash')
|
||||
compile project(':library-hls')
|
||||
compile project(':library-smoothstreaming')
|
||||
compile project(':library-ui')
|
||||
}
|
||||
|
||||
ext {
|
||||
releaseArtifact = 'exoplayer'
|
||||
releaseDescription = 'The ExoPlayer library (all modules).'
|
||||
}
|
||||
apply from: '../../publish.gradle'
|
@ -1,87 +0,0 @@
|
||||
// Copyright (C) 2016 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.
|
||||
import com.android.builder.core.BuilderConstants
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion project.ext.compileSdkVersion
|
||||
buildToolsVersion project.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion project.ext.minSdkVersion
|
||||
targetSdkVersion project.ext.targetSdkVersion
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
// Re-enable test coverage when the following issue is fixed:
|
||||
// https://code.google.com/p/android/issues/detail?id=226070
|
||||
// debug {
|
||||
// testCoverageEnabled = true
|
||||
// }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidTest {
|
||||
java.srcDirs += "../testutils/src/main/java/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-annotations:25.2.0'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.9.5'
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
def name = variant.buildType.name
|
||||
if (name.equals(BuilderConstants.DEBUG)) {
|
||||
return; // Skip debug builds.
|
||||
}
|
||||
def task = project.tasks.create "jar${name.capitalize()}", Jar
|
||||
task.dependsOn variant.javaCompile
|
||||
task.from variant.javaCompile.destinationDir
|
||||
artifacts.add('archives', task);
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) {
|
||||
title = "ExoPlayer library"
|
||||
description "Generates Javadoc for $variant.name."
|
||||
source = variant.javaCompile.source
|
||||
classpath = files(variant.javaCompile.classpath.files, project.android.getBootClasspath())
|
||||
options {
|
||||
links "http://docs.oracle.com/javase/7/docs/api/"
|
||||
linksOffline "https://developer.android.com/reference","${android.sdkDirectory}/docs/reference"
|
||||
encoding = 'UTF-8'
|
||||
}
|
||||
exclude '**/BuildConfig.java'
|
||||
exclude '**/R.java'
|
||||
doLast {
|
||||
copy {
|
||||
from "src/main/javadoc"
|
||||
into "$buildDir/docs/javadoc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
releaseArtifact = 'exoplayer'
|
||||
releaseDescription = 'The ExoPlayer library.'
|
||||
}
|
||||
apply from: '../publish.gradle'
|
54
library/core/build.gradle
Normal file
54
library/core/build.gradle
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (C) 2016 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.
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion project.ext.compileSdkVersion
|
||||
buildToolsVersion project.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion project.ext.minSdkVersion
|
||||
targetSdkVersion project.ext.targetSdkVersion
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidTest {
|
||||
java.srcDirs += "../../testutils/src/main/java/"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
testCoverageEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-annotations:' + supportLibraryVersion
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
|
||||
androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion
|
||||
}
|
||||
|
||||
ext {
|
||||
javadocTitle = 'Core module'
|
||||
}
|
||||
apply from: '../../javadoc_library.gradle'
|
||||
|
||||
ext {
|
||||
releaseArtifact = 'exoplayer-core'
|
||||
releaseDescription = 'The ExoPlayer library core module.'
|
||||
}
|
||||
apply from: '../../publish.gradle'
|
@ -16,7 +16,7 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer2.test">
|
||||
package="com.google.android.exoplayer2.core.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="24"/>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer2.test"
|
||||
android:targetPackage="com.google.android.exoplayer2.core.test"
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
tools:replace="android:targetPackage"/>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user