From 2ca14b025fc1bea3b5db6d9e23a259db02c4bf0b Mon Sep 17 00:00:00 2001 From: ojw28 Date: Tue, 7 Mar 2017 15:02:52 +0000 Subject: [PATCH 001/133] Update ISSUE_TEMPLATE --- ISSUE_TEMPLATE | 66 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/ISSUE_TEMPLATE b/ISSUE_TEMPLATE index 6e55f3dcd6..8c9263b8d1 100644 --- a/ISSUE_TEMPLATE +++ b/ISSUE_TEMPLATE @@ -1,19 +1,51 @@ -*** PLEASE DO NOT IGNORE THIS ISSUE TEMPLATE *** +*** ISSUES THAT IGNORE THIS TEMPLATE WILL BE CLOSED WITHOUT INVESTIGATION *** -Please search the existing issues before filing a new one, including issues that -are closed. When filing a new issue please include ALL of the following, unless -you're certain that they're not useful for the particular issue being reported. +Before filing an issue: +----------------------- -- A description of the issue. -- Steps describing how the issue can be reproduced, ideally in the ExoPlayer - demo app. -- A link to content that reproduces the issue. If you don't wish to post it - publicly, please submit the issue, then email the link to - dev.exoplayer@gmail.com including the issue number in the subject line. -- The version of ExoPlayer being used. -- The device(s) and version(s) of Android on which the issue can be reproduced, - and how easily it reproduces. If possible, please test on multiple devices and - Android versions. -- A bug report taken from the device just after the issue occurs, attached as a - file. A bug report can be captured using "adb bugreport". Output from "adb - logcat" or a log snippet is not sufficient. +- Search existing issues, including issues that are closed. If an existing issue + exists, please do not file a new one. + +- Consult our FAQs, supported devices and supported formats pages. These can be + found at https://google.github.io/ExoPlayer/. + +- Rule out issues in your own code. A good way to do this is to try and + reproduce the issue in the ExoPlayer demo app. + +- This issue tracker is intended for bugs, feature requests and ExoPlayer + specific questions. If you're asking a general Android development question, + please do so on Stack Overflow. + +When reporting a bug: +----------------------- + +Please fill out the sections below, leaving the section headers but replacing the +content. If you're unable to provide certain information, please explain why in the +relevant section. We may close issues without investigation if they do not include +sufficient information. + +### Issue description +Describe the issue in detail, including observed and expected behavior. + +### Reproduction steps +Describe how the issue can be reproduced, ideally using the ExoPlayer demo app. + +### Link to test content +Provide a link to media that reproduces the issue. If you don't wish to post it +publicly, please submit the issue, then email the link to +dev.exoplayer@gmail.com including the issue number in the subject line. + +### Version of ExoPlayer being used +Specify the absolute version number. Avoid using terms such as "latest". + +### Device(s) and version(s) of Android being used +Specify the devices and versions of Android on which the issue can be +reproduced, and how easily it reproduces. If possible, please test on multiple +devices and Android versions. + +### A full bug report captured from the device +Capture a full bug report using "adb bugreport". Output from "adb logcat" or a +log snippet is NOT sufficient. Please attach the captured bug report as a file. +If you don't wish to post it publicly, please submit the issue, then email the +bug report to dev.exoplayer@gmail.com including the issue number in the subject +line. From c50b570d463a2214e252ebca4b8840cd15d16768 Mon Sep 17 00:00:00 2001 From: ojw28 Date: Tue, 7 Mar 2017 15:03:01 +0000 Subject: [PATCH 002/133] Update ISSUE_TEMPLATE --- ISSUE_TEMPLATE | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ISSUE_TEMPLATE b/ISSUE_TEMPLATE index 8c9263b8d1..b1e6687374 100644 --- a/ISSUE_TEMPLATE +++ b/ISSUE_TEMPLATE @@ -5,13 +5,13 @@ Before filing an issue: - Search existing issues, including issues that are closed. If an existing issue exists, please do not file a new one. - + - Consult our FAQs, supported devices and supported formats pages. These can be found at https://google.github.io/ExoPlayer/. - + - Rule out issues in your own code. A good way to do this is to try and reproduce the issue in the ExoPlayer demo app. - + - This issue tracker is intended for bugs, feature requests and ExoPlayer specific questions. If you're asking a general Android development question, please do so on Stack Overflow. From aaaa23e5e4a6771b06067bd28d728781990bd454 Mon Sep 17 00:00:00 2001 From: ojw28 Date: Tue, 7 Mar 2017 15:11:51 +0000 Subject: [PATCH 003/133] Update ISSUE_TEMPLATE --- ISSUE_TEMPLATE | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/ISSUE_TEMPLATE b/ISSUE_TEMPLATE index b1e6687374..1b912312d1 100644 --- a/ISSUE_TEMPLATE +++ b/ISSUE_TEMPLATE @@ -2,27 +2,20 @@ Before filing an issue: ----------------------- - -- Search existing issues, including issues that are closed. If an existing issue - exists, please do not file a new one. - +- Search existing issues, including issues that are closed. - Consult our FAQs, supported devices and supported formats pages. These can be found at https://google.github.io/ExoPlayer/. - - Rule out issues in your own code. A good way to do this is to try and reproduce the issue in the ExoPlayer demo app. - - This issue tracker is intended for bugs, feature requests and ExoPlayer specific questions. If you're asking a general Android development question, please do so on Stack Overflow. When reporting a bug: ----------------------- - -Please fill out the sections below, leaving the section headers but replacing the -content. If you're unable to provide certain information, please explain why in the -relevant section. We may close issues without investigation if they do not include -sufficient information. +Fill out the sections below, leaving the headers but replacing the content. If +you're unable to provide certain information, please explain why in the relevant +section. We may close issues if they do not include sufficient information. ### Issue description Describe the issue in detail, including observed and expected behavior. From 3c447130a5aeb323fcf0c76816b02e2ae6777283 Mon Sep 17 00:00:00 2001 From: ojw28 Date: Fri, 17 Mar 2017 20:53:09 +0000 Subject: [PATCH 004/133] Update RELEASENOTES.md --- RELEASENOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f45cb9aff6..4f99f4175d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -6,8 +6,8 @@ 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 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 From 2966ea71f050347152ca0f8a52e4186e5a5892f9 Mon Sep 17 00:00:00 2001 From: ojw28 Date: Fri, 17 Mar 2017 20:55:45 +0000 Subject: [PATCH 005/133] Fix RELEASENOTES.md nesting. --- RELEASENOTES.md | 194 ++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4f99f4175d..1030cbdee4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -8,29 +8,29 @@ * 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 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)). @@ -125,15 +125,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 @@ -143,19 +143,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)). -* 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 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. * 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 @@ -227,62 +227,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 # From 569cec7fe81250db8a7aa49447c5b96ddd57d7a2 Mon Sep 17 00:00:00 2001 From: ojw28 Date: Fri, 17 Mar 2017 21:00:45 +0000 Subject: [PATCH 006/133] Cleanup RELEASENOTES.md --- RELEASENOTES.md | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1030cbdee4..2b0936e0d9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -17,8 +17,9 @@ * 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)). + * 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 @@ -30,7 +31,7 @@ * MPEG-TS improvements: * Support for multiple programs. * Support for multiple closed captions and caption service descriptors - ([#2161](https://github.com/google/ExoPlayer/issues/2161)). + ([#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)). @@ -151,7 +152,7 @@ ([#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. +* 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. @@ -227,25 +228,25 @@ 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. + * 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. + * 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 + 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). @@ -264,19 +265,20 @@ some of the motivations behind ExoPlayer 2.x [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) + * 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). + * 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)). + * 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 From 646f6a74c94e4ffb228ea018b61fff1eeb59e966 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 23 Mar 2017 17:44:47 +0000 Subject: [PATCH 007/133] Fix NullPointerException enabling WebVtt subtitles in DASH Issue: #2596 --- .../source/dash/DefaultDashChunkSource.java | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java index 7ccea8a2a6..a6e909ddac 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java @@ -180,23 +180,24 @@ public class DefaultDashChunkSource implements DashChunkSource { RepresentationHolder representationHolder = representationHolders[trackSelection.getSelectedIndex()]; - Representation selectedRepresentation = representationHolder.representation; - DashSegmentIndex segmentIndex = representationHolder.segmentIndex; - RangedUri pendingInitializationUri = null; - RangedUri pendingIndexUri = null; - if (representationHolder.extractorWrapper.getSampleFormats() == null) { - pendingInitializationUri = selectedRepresentation.getInitializationUri(); - } - if (segmentIndex == null) { - pendingIndexUri = selectedRepresentation.getIndexUri(); - } - if (pendingInitializationUri != null || pendingIndexUri != null) { - // We have initialization and/or index requests to make. - out.chunk = newInitializationChunk(representationHolder, dataSource, - trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), - trackSelection.getSelectionData(), pendingInitializationUri, pendingIndexUri); - return; + if (representationHolder.extractorWrapper != null) { + Representation selectedRepresentation = representationHolder.representation; + RangedUri pendingInitializationUri = null; + RangedUri pendingIndexUri = null; + if (representationHolder.extractorWrapper.getSampleFormats() == null) { + pendingInitializationUri = selectedRepresentation.getInitializationUri(); + } + if (representationHolder.segmentIndex == null) { + pendingIndexUri = selectedRepresentation.getIndexUri(); + } + if (pendingInitializationUri != null || pendingIndexUri != null) { + // We have initialization and/or index requests to make. + out.chunk = newInitializationChunk(representationHolder, dataSource, + trackSelection.getSelectedFormat(), trackSelection.getSelectionReason(), + trackSelection.getSelectionData(), pendingInitializationUri, pendingIndexUri); + return; + } } long nowUs = getNowUnixTimeUs(); From 23b5921b828be728a345bcf4088edcd736f52ade Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 23 Mar 2017 17:46:12 +0000 Subject: [PATCH 008/133] Fix typo on CEA-708 decoder Issue: #2595 --- .../com/google/android/exoplayer2/text/cea/Cea708Decoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java index 740fd17013..8fd70f7a67 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java @@ -483,7 +483,7 @@ public final class Cea708Decoder extends CeaDecoder { private void handleC2Command(int command) { // C2 Table doesn't contain any commands in CEA-708-B, but we do need to skip bytes - if (command <= 0x0F) { + if (command <= 0x07) { // Do nothing. } else if (command <= 0x0F) { serviceBlockPacket.skipBits(8); From c96f18f5ce4264752c39482a4b2a3b996663fc22 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 23 Mar 2017 17:46:43 +0000 Subject: [PATCH 009/133] Fix skipping to keyframe to use correct position Issue: #2575 --- .../main/java/com/google/android/exoplayer2/BaseRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index f65be3afcd..f6aae200dd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -311,7 +311,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { * @param timeUs The specified time. */ protected void skipToKeyframeBefore(long timeUs) { - stream.skipToKeyframeBefore(timeUs); + stream.skipToKeyframeBefore(timeUs - streamOffsetUs); } } From 6503f016e9b54b6818f04d364727f5d6d3407443 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 23 Mar 2017 17:59:20 +0000 Subject: [PATCH 010/133] Update release notes + bump versions --- RELEASENOTES.md | 9 +++++++++ build.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../google/android/exoplayer2/ExoPlayerLibraryInfo.java | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2b0936e0d9..a0c750660d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,14 @@ # Release notes # +### r2.3.1 ### + +* Fix NPE enabling WebVTT subtitles in DASH streams + ([#2596](https://github.com/google/ExoPlayer/issues/2596)). +* Fix skipping to keyframes when MediaCodecVideoRenderer is enabled but without + a Surface ([#2575](https://github.com/google/ExoPlayer/issues/2575)). +* Minor fix for CEA-708 decoder + ([#2595](https://github.com/google/ExoPlayer/issues/2595)). + ### r2.3.0 ### * GVR extension: Wraps the Google VR Audio SDK to provide spatial audio diff --git a/build.gradle b/build.gradle index f1901a1270..9883c04e54 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.3.0' + releaseVersion = 'r2.3.1' releaseWebsite = 'https://github.com/google/ExoPlayer' } } diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index a834c5df19..9a6e1a4d3a 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2301" + android:versionName="2.3.1"> diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 5ec7fac5dd..bee9904590 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo { /** * The version of the library, expressed as a string. */ - String VERSION = "2.3.0"; + String VERSION = "2.3.1"; /** * The version of the library, expressed as an integer. @@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo { * corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding * integer version 123045006 (123-045-006). */ - int VERSION_INT = 2003000; + int VERSION_INT = 2003001; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From dc80be0ca1c8c87ea4522f9efbb55dd346c836bd Mon Sep 17 00:00:00 2001 From: Andrew Orobator Date: Wed, 3 May 2017 10:49:36 -0400 Subject: [PATCH 011/133] Improved Documentation Added missing coma and fixed typo for EventListener#onTimelineChanged --- .../main/java/com/google/android/exoplayer2/ExoPlayer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index ab521e3733..82ec610be7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -113,8 +113,8 @@ public interface ExoPlayer { * Called when the timeline and/or manifest has been refreshed. *

* Note that if the timeline has changed then a position discontinuity may also have occurred. - * For example the current period index may have changed as a result of periods being added or - * removed from the timeline. The will not be reported via a separate call to + * For example, the current period index may have changed as a result of periods being added or + * removed from the timeline. This will not be reported via a separate call to * {@link #onPositionDiscontinuity()}. * * @param timeline The latest timeline. Never null, but may be empty. From 2e2b0085c1de98acfe054b3797de298a767566b1 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 27 Apr 2017 07:49:14 -0700 Subject: [PATCH 012/133] Prevent text tracks with no language being selected by default ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154421706 --- .../exoplayer2/trackselection/DefaultTrackSelector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 9db77fd7ad..941df66e4d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -867,7 +867,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } protected static boolean formatHasLanguage(Format format, String language) { - return TextUtils.equals(language, Util.normalizeLanguageCode(format.language)); + return language != null + && TextUtils.equals(language, Util.normalizeLanguageCode(format.language)); } // Viewport size util methods. From e90e2caec4fe94d7fbb706c7da827c9f0ad3a1c7 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 2 May 2017 06:38:57 -0700 Subject: [PATCH 013/133] Improve DefaultTimeBar color customization Add attributes for the scrubber handle color and unplayed color. If attributes are missing, derive defaults from the played color. Issue: #2740 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154825736 --- .../android/exoplayer2/ui/DefaultTimeBar.java | 50 +++++++++++++------ library/ui/src/main/res/values/attrs.xml | 2 + 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index d40da451a2..12f31f5da1 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -61,22 +61,21 @@ public class DefaultTimeBar extends View implements TimeBar { private static final int DEFAULT_INCREMENT_COUNT = 20; private static final int DEFAULT_BAR_HEIGHT = 4; private static final int DEFAULT_TOUCH_TARGET_HEIGHT = 26; - private static final int DEFAULT_PLAYED_COLOR = 0x33FFFFFF; - private static final int DEFAULT_BUFFERED_COLOR = 0xCCFFFFFF; + private static final int DEFAULT_PLAYED_COLOR = 0xFFFFFFFF; private static final int DEFAULT_AD_MARKER_COLOR = 0xB2FFFF00; private static final int DEFAULT_AD_MARKER_WIDTH = 4; private static final int DEFAULT_SCRUBBER_ENABLED_SIZE = 12; private static final int DEFAULT_SCRUBBER_DISABLED_SIZE = 0; private static final int DEFAULT_SCRUBBER_DRAGGED_SIZE = 16; - private static final int OPAQUE_COLOR = 0xFF000000; private final Rect seekBounds; private final Rect progressBar; private final Rect bufferedBar; private final Rect scrubberBar; - private final Paint progressPaint; - private final Paint bufferedPaint; + private final Paint playedPaint; private final Paint scrubberPaint; + private final Paint bufferedPaint; + private final Paint unplayedPaint; private final Paint adMarkerPaint; private final int barHeight; private final int touchTargetHeight; @@ -115,9 +114,10 @@ public class DefaultTimeBar extends View implements TimeBar { progressBar = new Rect(); bufferedBar = new Rect(); scrubberBar = new Rect(); - progressPaint = new Paint(); - bufferedPaint = new Paint(); + playedPaint = new Paint(); scrubberPaint = new Paint(); + bufferedPaint = new Paint(); + unplayedPaint = new Paint(); adMarkerPaint = new Paint(); // Calculate the dimensions and paints for drawn elements. @@ -147,13 +147,18 @@ public class DefaultTimeBar extends View implements TimeBar { scrubberDraggedSize = a.getDimensionPixelSize( R.styleable.DefaultTimeBar_scrubber_dragged_size, defaultScrubberDraggedSize); int playedColor = a.getInt(R.styleable.DefaultTimeBar_played_color, DEFAULT_PLAYED_COLOR); + int scrubberColor = a.getInt(R.styleable.DefaultTimeBar_scrubber_color, + getDefaultScrubberColor(playedColor)); int bufferedColor = a.getInt(R.styleable.DefaultTimeBar_buffered_color, - DEFAULT_BUFFERED_COLOR); + getDefaultBufferedColor(playedColor)); + int unplayedColor = a.getInt(R.styleable.DefaultTimeBar_unplayed_color, + getDefaultUnplayedColor(playedColor)); int adMarkerColor = a.getInt(R.styleable.DefaultTimeBar_ad_marker_color, DEFAULT_AD_MARKER_COLOR); - progressPaint.setColor(playedColor); - scrubberPaint.setColor(OPAQUE_COLOR | playedColor); + playedPaint.setColor(playedColor); + scrubberPaint.setColor(scrubberColor); bufferedPaint.setColor(bufferedColor); + unplayedPaint.setColor(unplayedColor); adMarkerPaint.setColor(adMarkerColor); } finally { a.recycle(); @@ -165,9 +170,10 @@ public class DefaultTimeBar extends View implements TimeBar { scrubberEnabledSize = defaultScrubberEnabledSize; scrubberDisabledSize = defaultScrubberDisabledSize; scrubberDraggedSize = defaultScrubberDraggedSize; - scrubberPaint.setColor(OPAQUE_COLOR | DEFAULT_PLAYED_COLOR); - progressPaint.setColor(DEFAULT_PLAYED_COLOR); - bufferedPaint.setColor(DEFAULT_BUFFERED_COLOR); + playedPaint.setColor(DEFAULT_PLAYED_COLOR); + scrubberPaint.setColor(getDefaultScrubberColor(DEFAULT_PLAYED_COLOR)); + bufferedPaint.setColor(getDefaultBufferedColor(DEFAULT_PLAYED_COLOR)); + unplayedPaint.setColor(getDefaultUnplayedColor(DEFAULT_PLAYED_COLOR)); adMarkerPaint.setColor(DEFAULT_AD_MARKER_COLOR); } formatBuilder = new StringBuilder(); @@ -502,21 +508,21 @@ public class DefaultTimeBar extends View implements TimeBar { int barTop = progressBar.centerY() - progressBarHeight / 2; int barBottom = barTop + progressBarHeight; if (duration <= 0) { - canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, progressPaint); + canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, unplayedPaint); return; } int bufferedLeft = bufferedBar.left; int bufferedRight = bufferedBar.right; int progressLeft = Math.max(Math.max(progressBar.left, bufferedRight), scrubberBar.right); if (progressLeft < progressBar.right) { - canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, progressPaint); + canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, unplayedPaint); } bufferedLeft = Math.max(bufferedLeft, scrubberBar.right); if (bufferedRight > bufferedLeft) { canvas.drawRect(bufferedLeft, barTop, bufferedRight, barBottom, bufferedPaint); } if (scrubberBar.width() > 0) { - canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, scrubberPaint); + canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint); } int adMarkerOffset = adMarkerWidth / 2; for (int i = 0; i < adBreakCount; i++) { @@ -577,4 +583,16 @@ public class DefaultTimeBar extends View implements TimeBar { return (int) (dps * displayMetrics.density + 0.5f); } + private static int getDefaultScrubberColor(int playedColor) { + return 0xFF000000 | playedColor; + } + + private static int getDefaultUnplayedColor(int playedColor) { + return 0x33000000 | (playedColor & 0x00FFFFFF); + } + + private static int getDefaultBufferedColor(int playedColor) { + return 0xCC000000 | (playedColor & 0x00FFFFFF); + } + } diff --git a/library/ui/src/main/res/values/attrs.xml b/library/ui/src/main/res/values/attrs.xml index 521e535ce3..d8340c21cd 100644 --- a/library/ui/src/main/res/values/attrs.xml +++ b/library/ui/src/main/res/values/attrs.xml @@ -68,7 +68,9 @@ + + From 60bf31ff24f492e8c0bbd798b76d3081e270a4ba Mon Sep 17 00:00:00 2001 From: falhassen Date: Tue, 2 May 2017 10:09:55 -0700 Subject: [PATCH 014/133] Use Looper.getMainLooper() in Handler constructors in ExoPlayer when needed. Looper.myLooper(), the default looper, may be null in background threads. This adds a fallback to use the main app looper. This will allow ExoPlayer instances to be built in background threads in Photos. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154845446 --- .../google/android/exoplayer2/ExoPlayer.java | 9 ++++-- .../android/exoplayer2/ExoPlayerFactory.java | 28 ++++++------------- .../android/exoplayer2/ExoPlayerImpl.java | 3 +- .../android/exoplayer2/SimpleExoPlayer.java | 7 +++-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 82ec610be7..18bf9eeb8c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2; +import android.os.Looper; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.metadata.MetadataRenderer; @@ -88,7 +90,9 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; * thread. The application's main thread is ideal. Accessing an instance from multiple threads is * discouraged, however if an application does wish to do this then it may do so provided that it * ensures accesses are synchronized. - *

  • Registered listeners are called on the thread that created the ExoPlayer instance.
  • + *
  • Registered listeners are called on the thread that created the ExoPlayer instance, unless + * the thread that created the ExoPlayer instance does not have a {@link Looper}. In that case, + * registered listeners will be called on the application's main thread.
  • *
  • An internal playback thread is responsible for playback. Injected player components such as * Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this * thread.
  • @@ -253,7 +257,8 @@ public interface ExoPlayer { /** * Register a listener to receive events from the player. The listener's methods will be called on - * the thread that was used to construct the player. + * the thread that was used to construct the player. However, if the thread used to construct the + * player does not have a {@link Looper}, then the listener will be called on the main thread. * * @param listener The listener to register. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java index 7aecd20d4e..97a310c3da 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2; import android.content.Context; -import android.os.Looper; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.trackselection.TrackSelector; @@ -29,8 +28,7 @@ public final class ExoPlayerFactory { private ExoPlayerFactory() {} /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param context A {@link Context}. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -45,8 +43,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. Available extension renderers are not used. + * Creates a {@link SimpleExoPlayer} instance. Available extension renderers are not used. * * @param context A {@link Context}. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -63,8 +60,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param context A {@link Context}. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -86,8 +82,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param context A {@link Context}. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -112,8 +107,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param context A {@link Context}. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -123,8 +117,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -135,8 +128,7 @@ public final class ExoPlayerFactory { } /** - * Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates a {@link SimpleExoPlayer} instance. * * @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -148,8 +140,7 @@ public final class ExoPlayerFactory { } /** - * Creates an {@link ExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates an {@link ExoPlayer} instance. * * @param renderers The {@link Renderer}s that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance. @@ -159,8 +150,7 @@ public final class ExoPlayerFactory { } /** - * Creates an {@link ExoPlayer} instance. Must be called from a thread that has an associated - * {@link Looper}. + * Creates an {@link ExoPlayer} instance. * * @param renderers The {@link Renderer}s that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 4131b97954..cb0958a3b1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -92,7 +92,8 @@ import java.util.concurrent.CopyOnWriteArraySet; trackGroups = TrackGroupArray.EMPTY; trackSelections = emptyTrackSelections; playbackParameters = PlaybackParameters.DEFAULT; - eventHandler = new Handler() { + Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper(); + eventHandler = new Handler(eventLooper) { @Override public void handleMessage(Message msg) { ExoPlayerImpl.this.handleEvent(msg); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 28ba8cf9d7..6094513913 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -20,6 +20,7 @@ import android.graphics.SurfaceTexture; import android.media.MediaCodec; import android.media.PlaybackParams; import android.os.Handler; +import android.os.Looper; import android.support.annotation.Nullable; import android.util.Log; import android.view.Surface; @@ -111,8 +112,10 @@ public class SimpleExoPlayer implements ExoPlayer { protected SimpleExoPlayer(RenderersFactory renderersFactory, TrackSelector trackSelector, LoadControl loadControl) { componentListener = new ComponentListener(); - renderers = renderersFactory.createRenderers(new Handler(), componentListener, - componentListener, componentListener, componentListener); + Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper(); + Handler eventHandler = new Handler(eventLooper); + renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener, + componentListener, componentListener); // Obtain counts of video and audio renderers. int videoRendererCount = 0; From 8bffe5d11ef5bbdbaa892226b4cba4273351967a Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 3 May 2017 01:32:22 -0700 Subject: [PATCH 015/133] Add DummySurface for use with MediaCodec A DummySurface is useful with MediaCodec on API levels 23+. Rather than having to release a MediaCodec instance when the app no longer has a real surface to output to, it's possible to retain the MediaCodec, using MediaCodec.setOutputSurface to target a DummySurface instance instead. When the app has a real surface to output to again, it can call swap this surface back in instantaneously. Without DummySurface a new MediaCodec has to be instantiated at this point, and decoding can only start from a key-frame in the media. A future change may hook this up internally in MediaCodecRenderer for supported use cases, although this looks a little awkward. If this approach isn't viable, we can require applications wanting this to set a DummySurface themselves. This isn't easy to do with the way SimpleExoPlayerView.setPlayer works at the moment, however, so some changes will be needed either way. Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=154931778 --- .../exoplayer2/video/DummySurface.java | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java b/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java new file mode 100644 index 0000000000..5298c82f61 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java @@ -0,0 +1,306 @@ +/* + * 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.video; + +import static android.opengl.EGL14.EGL_ALPHA_SIZE; +import static android.opengl.EGL14.EGL_BLUE_SIZE; +import static android.opengl.EGL14.EGL_CONFIG_CAVEAT; +import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; +import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; +import static android.opengl.EGL14.EGL_DEPTH_SIZE; +import static android.opengl.EGL14.EGL_GREEN_SIZE; +import static android.opengl.EGL14.EGL_HEIGHT; +import static android.opengl.EGL14.EGL_NONE; +import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; +import static android.opengl.EGL14.EGL_RED_SIZE; +import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; +import static android.opengl.EGL14.EGL_SURFACE_TYPE; +import static android.opengl.EGL14.EGL_TRUE; +import static android.opengl.EGL14.EGL_WIDTH; +import static android.opengl.EGL14.EGL_WINDOW_BIT; +import static android.opengl.EGL14.eglChooseConfig; +import static android.opengl.EGL14.eglCreateContext; +import static android.opengl.EGL14.eglCreatePbufferSurface; +import static android.opengl.EGL14.eglGetDisplay; +import static android.opengl.EGL14.eglInitialize; +import static android.opengl.EGL14.eglMakeCurrent; +import static android.opengl.GLES20.glDeleteTextures; +import static android.opengl.GLES20.glGenTextures; + +import android.annotation.TargetApi; +import android.graphics.SurfaceTexture; +import android.graphics.SurfaceTexture.OnFrameAvailableListener; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; +import android.view.Surface; +import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Util; +import javax.microedition.khronos.egl.EGL10; + +/** + * A dummy {@link Surface}. + */ +@TargetApi(17) +public final class DummySurface extends Surface { + + private static final String TAG = "DummySurface"; + + private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; + + /** + * Whether the device supports secure dummy surfaces. + */ + public static final boolean SECURE_SUPPORTED; + static { + if (Util.SDK_INT >= 17) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + String extensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS); + SECURE_SUPPORTED = extensions.contains("EGL_EXT_protected_content"); + } else { + SECURE_SUPPORTED = false; + } + } + + /** + * Whether the surface is secure. + */ + public final boolean secure; + + private final DummySurfaceThread thread; + private boolean threadReleased; + + /** + * Returns a newly created dummy surface. The surface must be released by calling {@link #release} + * when it's no longer required. + *

    + * Must only be called if {@link Util#SDK_INT} is 17 or higher. + * + * @param secure Whether a secure surface is required. Must only be requested if + * {@link #SECURE_SUPPORTED} is {@code true}. + */ + public static DummySurface newInstanceV17(boolean secure) { + assertApiLevel17OrHigher(); + Assertions.checkState(!secure || SECURE_SUPPORTED); + DummySurfaceThread thread = new DummySurfaceThread(); + return thread.init(secure); + } + + private DummySurface(DummySurfaceThread thread, SurfaceTexture surfaceTexture, boolean secure) { + super(surfaceTexture); + this.thread = thread; + this.secure = secure; + } + + @Override + public void release() { + super.release(); + // The Surface may be released multiple times (explicitly and by Surface.finalize()). The + // implementation of super.release() has its own deduplication logic. Below we need to + // deduplicate ourselves. Synchronization is required as we don't control the thread on which + // Surface.finalize() is called. + synchronized (thread) { + if (!threadReleased) { + thread.release(); + threadReleased = true; + } + } + } + + private static void assertApiLevel17OrHigher() { + if (Util.SDK_INT < 17) { + throw new UnsupportedOperationException("Unsupported prior to API level 17"); + } + } + + private static class DummySurfaceThread extends HandlerThread implements OnFrameAvailableListener, + Callback { + + private static final int MSG_INIT = 1; + private static final int MSG_UPDATE_TEXTURE = 2; + private static final int MSG_RELEASE = 3; + + private final int[] textureIdHolder; + private Handler handler; + private SurfaceTexture surfaceTexture; + + private Error initError; + private RuntimeException initException; + private DummySurface surface; + + public DummySurfaceThread() { + super("dummySurface"); + textureIdHolder = new int[1]; + } + + public DummySurface init(boolean secure) { + start(); + handler = new Handler(getLooper(), this); + boolean wasInterrupted = false; + synchronized (this) { + handler.obtainMessage(MSG_INIT, secure ? 1 : 0, 0).sendToTarget(); + while (surface == null && initException == null && initError == null) { + try { + wait(); + } catch (InterruptedException e) { + wasInterrupted = true; + } + } + } + if (wasInterrupted) { + // Restore the interrupted status. + Thread.currentThread().interrupt(); + } + if (initException != null) { + throw initException; + } else if (initError != null) { + throw initError; + } else { + return surface; + } + } + + public void release() { + handler.sendEmptyMessage(MSG_RELEASE); + } + + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + handler.sendEmptyMessage(MSG_UPDATE_TEXTURE); + } + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_INIT: + try { + initInternal(msg.arg1 != 0); + } catch (RuntimeException e) { + Log.e(TAG, "Failed to initialize dummy surface", e); + initException = e; + } catch (Error e) { + Log.e(TAG, "Failed to initialize dummy surface", e); + initError = e; + } finally { + synchronized (this) { + notify(); + } + } + return true; + case MSG_UPDATE_TEXTURE: + surfaceTexture.updateTexImage(); + return true; + case MSG_RELEASE: + try { + releaseInternal(); + } catch (Throwable e) { + Log.e(TAG, "Failed to release dummy surface", e); + } finally { + quit(); + } + return true; + default: + return true; + } + } + + private void initInternal(boolean secure) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + Assertions.checkState(display != null, "eglGetDisplay failed"); + + int[] version = new int[2]; + boolean eglInitialized = eglInitialize(display, version, 0, version, 1); + Assertions.checkState(eglInitialized, "eglInitialize failed"); + + int[] eglAttributes = new int[] { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + boolean eglChooseConfigSuccess = eglChooseConfig(display, eglAttributes, 0, configs, 0, 1, + numConfigs, 0); + Assertions.checkState(eglChooseConfigSuccess && numConfigs[0] > 0 && configs[0] != null, + "eglChooseConfig failed"); + + EGLConfig config = configs[0]; + int[] glAttributes; + if (secure) { + glAttributes = new int[] { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_PROTECTED_CONTENT_EXT, + EGL_TRUE, EGL_NONE}; + } else { + glAttributes = new int[] { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; + } + EGLContext context = eglCreateContext(display, config, android.opengl.EGL14.EGL_NO_CONTEXT, + glAttributes, 0); + Assertions.checkState(context != null, "eglCreateContext failed"); + + int[] pbufferAttributes; + if (secure) { + pbufferAttributes = new int[] { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_PROTECTED_CONTENT_EXT, EGL_TRUE, + EGL_NONE}; + } else { + pbufferAttributes = new int[] { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE}; + } + EGLSurface pbuffer = eglCreatePbufferSurface(display, config, pbufferAttributes, 0); + Assertions.checkState(pbuffer != null, "eglCreatePbufferSurface failed"); + + boolean eglMadeCurrent = eglMakeCurrent(display, pbuffer, pbuffer, context); + Assertions.checkState(eglMadeCurrent, "eglMakeCurrent failed"); + + glGenTextures(1, textureIdHolder, 0); + surfaceTexture = new SurfaceTexture(textureIdHolder[0]); + surfaceTexture.setOnFrameAvailableListener(this); + surface = new DummySurface(this, surfaceTexture, secure); + } + + private void releaseInternal() { + try { + surfaceTexture.release(); + } finally { + surface = null; + surfaceTexture = null; + glDeleteTextures(1, textureIdHolder, 0); + } + } + + } + +} From ed836d31e28867fc81005b4d2dff4891289e5a74 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 4 May 2017 03:24:19 -0700 Subject: [PATCH 016/133] Propagate EXT-X-DATERANGE tags with media playlists ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155062718 --- .../exoplayer2/source/hls/playlist/HlsMediaPlaylist.java | 9 ++++++--- .../source/hls/playlist/HlsPlaylistParser.java | 7 ++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java index c7708a1d2f..69b95e6d3d 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java @@ -91,12 +91,14 @@ public final class HlsMediaPlaylist extends HlsPlaylist { public final boolean hasProgramDateTime; public final Segment initializationSegment; public final List segments; + public final List dateRanges; public final long durationUs; public HlsMediaPlaylist(@PlaylistType int playlistType, String baseUri, long startOffsetUs, long startTimeUs, boolean hasDiscontinuitySequence, int discontinuitySequence, int mediaSequence, int version, long targetDurationUs, boolean hasEndTag, - boolean hasProgramDateTime, Segment initializationSegment, List segments) { + boolean hasProgramDateTime, Segment initializationSegment, List segments, + List dateRanges) { super(baseUri); this.playlistType = playlistType; this.startTimeUs = startTimeUs; @@ -117,6 +119,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { } this.startOffsetUs = startOffsetUs == C.TIME_UNSET ? C.TIME_UNSET : startOffsetUs >= 0 ? startOffsetUs : durationUs + startOffsetUs; + this.dateRanges = Collections.unmodifiableList(dateRanges); } /** @@ -155,7 +158,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs, true, discontinuitySequence, mediaSequence, version, targetDurationUs, hasEndTag, - hasProgramDateTime, initializationSegment, segments); + hasProgramDateTime, initializationSegment, segments, dateRanges); } /** @@ -170,7 +173,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { } return new HlsMediaPlaylist(playlistType, baseUri, startOffsetUs, startTimeUs, hasDiscontinuitySequence, discontinuitySequence, mediaSequence, version, targetDurationUs, - true, hasProgramDateTime, initializationSegment, segments); + true, hasProgramDateTime, initializationSegment, segments, dateRanges); } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index d24264cae6..8e01dec6fe 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -57,6 +57,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser segments = new ArrayList<>(); + List dateRanges = new ArrayList<>(); long segmentDurationUs = 0; boolean hasDiscontinuitySequence = false; @@ -343,6 +345,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Thu, 4 May 2017 03:29:10 -0700 Subject: [PATCH 017/133] Fix javadocs typos Issue:#2773 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155062917 --- .../google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java | 2 +- .../com/google/android/exoplayer2/upstream/cache/CacheUtil.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index c44c703bb1..4b629c8d2a 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -286,7 +286,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer { * * @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. + * {@link C#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. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java index f6251dbbf1..bb1f88e5ea 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java @@ -64,7 +64,7 @@ public final class CacheUtil { } /** - * Returns already cached and missing bytes in the {@cache} for the data defined by {@code + * Returns already cached and missing bytes in the {@code cache} for the data defined by {@code * dataSpec}. * * @param dataSpec Defines the data to be checked. From 943141fb076a4aeb7e7c10d4830aef32caa46aca Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 4 May 2017 11:30:25 -0700 Subject: [PATCH 018/133] Some minor cleanup related to track selection and caching ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155103828 --- .../exoplayer2/demo/PlayerActivity.java | 6 ++-- .../trackselection/DefaultTrackSelector.java | 16 ++++++++-- .../upstream/cache/CacheDataSource.java | 30 ++++++++++++++----- .../cache/CacheDataSourceFactory.java | 18 +++++++---- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 2542f23e95..d01a64a051 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -261,10 +261,10 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this, drmSessionManager, extensionRendererMode); - TrackSelection.Factory videoTrackSelectionFactory = + TrackSelection.Factory adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory); + trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory); + trackSelectionHelper = new TrackSelectionHelper(trackSelector, adaptiveTrackSelectionFactory); lastSeenTrackGroupArray = null; player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 941df66e4d..361fcf0b57 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -24,6 +24,7 @@ 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; +import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; @@ -376,10 +377,21 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final AtomicReference paramsReference; /** - * Constructs an instance that does not support adaptive tracks. + * Constructs an instance that does not support adaptive track selection. */ public DefaultTrackSelector() { - this(null); + this((TrackSelection.Factory) null); + } + + /** + * Constructs an instance that supports adaptive track selection. Adaptive track selections use + * the provided {@link BandwidthMeter} to determine which individual track should be used during + * playback. + * + * @param bandwidthMeter The {@link BandwidthMeter}. + */ + public DefaultTrackSelector(BandwidthMeter bandwidthMeter) { + this(new AdaptiveTrackSelection.Factory(bandwidthMeter)); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 86dc5cfedf..bb2a952b11 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -54,8 +54,8 @@ public final class CacheDataSource implements DataSource { FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}) public @interface Flags {} /** - * A flag indicating whether we will block reads if the cache key is locked. If this flag is - * set, then we will read from upstream if the cache key is locked. + * A flag indicating whether we will block reads if the cache key is locked. If unset then data is + * read from upstream if the cache key is locked, regardless of whether the data is cached. */ public static final int FLAG_BLOCK_ON_CACHE = 1 << 0; @@ -110,7 +110,23 @@ public final class CacheDataSource implements DataSource { /** * Constructs an instance with default {@link DataSource} and {@link DataSink} instances for - * reading and writing the cache and with {@link #DEFAULT_MAX_CACHE_FILE_SIZE}. + * reading and writing the cache. + * + * @param cache The cache. + * @param upstream A {@link DataSource} for reading data not in the cache. + */ + public CacheDataSource(Cache cache, DataSource upstream) { + this(cache, upstream, 0, DEFAULT_MAX_CACHE_FILE_SIZE); + } + + /** + * Constructs an instance with default {@link DataSource} and {@link DataSink} instances for + * reading and writing the cache. + * + * @param cache The cache. + * @param upstream A {@link DataSource} for reading data not in the cache. + * @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} + * and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. */ public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags) { this(cache, upstream, flags, DEFAULT_MAX_CACHE_FILE_SIZE); @@ -123,8 +139,8 @@ public final class CacheDataSource implements DataSource { * * @param cache The cache. * @param upstream A {@link DataSource} for reading data not in the cache. - * @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE} and {@link - * #FLAG_IGNORE_CACHE_ON_ERROR} or 0. + * @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} + * and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. * @param maxCacheFileSize The maximum size of a cache file, in bytes. If the cached data size * exceeds this value, then the data will be fragmented into multiple cache files. The * finer-grained this is the finer-grained the eviction policy can be. @@ -145,8 +161,8 @@ public final class CacheDataSource implements DataSource { * @param cacheReadDataSource A {@link DataSource} for reading data from the cache. * @param cacheWriteDataSink A {@link DataSink} for writing data to the cache. If null, cache is * accessed read-only. - * @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE} and {@link - * #FLAG_IGNORE_CACHE_ON_ERROR} or 0. + * @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} + * and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0. * @param eventListener An optional {@link EventListener} to receive events. */ public CacheDataSource(Cache cache, DataSource upstream, DataSource cacheReadDataSource, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java index b6fa3b4e2c..f0285da274 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java @@ -33,18 +33,26 @@ public final class CacheDataSourceFactory implements DataSource.Factory { private final int flags; private final EventListener eventListener; + /** + * @see CacheDataSource#CacheDataSource(Cache, DataSource) + */ + public CacheDataSourceFactory(Cache cache, DataSource.Factory upstreamFactory) { + this(cache, upstreamFactory, 0); + } + /** * @see CacheDataSource#CacheDataSource(Cache, DataSource, int) */ - public CacheDataSourceFactory(Cache cache, DataSource.Factory upstreamFactory, int flags) { + public CacheDataSourceFactory(Cache cache, DataSource.Factory upstreamFactory, + @CacheDataSource.Flags int flags) { this(cache, upstreamFactory, flags, CacheDataSource.DEFAULT_MAX_CACHE_FILE_SIZE); } /** * @see CacheDataSource#CacheDataSource(Cache, DataSource, int, long) */ - public CacheDataSourceFactory(Cache cache, DataSource.Factory upstreamFactory, int flags, - long maxCacheFileSize) { + public CacheDataSourceFactory(Cache cache, DataSource.Factory upstreamFactory, + @CacheDataSource.Flags int flags, long maxCacheFileSize) { this(cache, upstreamFactory, new FileDataSourceFactory(), new CacheDataSinkFactory(cache, maxCacheFileSize), flags, null); } @@ -54,8 +62,8 @@ public final class CacheDataSourceFactory implements DataSource.Factory { * EventListener) */ public CacheDataSourceFactory(Cache cache, Factory upstreamFactory, - Factory cacheReadDataSourceFactory, - DataSink.Factory cacheWriteDataSinkFactory, int flags, EventListener eventListener) { + Factory cacheReadDataSourceFactory, DataSink.Factory cacheWriteDataSinkFactory, + @CacheDataSource.Flags int flags, EventListener eventListener) { this.cache = cache; this.upstreamFactory = upstreamFactory; this.cacheReadDataSourceFactory = cacheReadDataSourceFactory; From 68b3e0b54d0c34842c7ec35174e431b2d8f0284b Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Fri, 5 May 2017 03:49:22 -0700 Subject: [PATCH 019/133] Expose no CC tracks if CLOSED-CAPTIONS=NONE is present Issue:#2743 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155182859 --- .../playlist/HlsMasterPlaylistParserTest.java | 14 ++++- .../exoplayer2/source/hls/HlsChunkSource.java | 3 +- .../exoplayer2/source/hls/HlsMediaChunk.java | 11 +++- .../hls/playlist/HlsMasterPlaylist.java | 62 ++++++++++++++++--- .../hls/playlist/HlsPlaylistParser.java | 13 +++- 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index aa279f23f4..912dcb28b2 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.MimeTypes; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.Charset; +import java.util.Collections; import java.util.List; import junit.framework.TestCase; @@ -56,16 +57,22 @@ public class HlsMasterPlaylistParserTest extends TestCase { private static final String MASTER_PLAYLIST_WITH_CC = " #EXTM3U \n" + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" - + "\n" + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + "http://example.com/low.m3u8\n"; + private static final String MASTER_PLAYLIST_WITHOUT_CC = " #EXTM3U \n" + + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," + + "CLOSED-CAPTIONS=NONE\n" + + "http://example.com/low.m3u8\n"; + public void testParseMasterPlaylist() throws IOException{ HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST); List variants = masterPlaylist.variants; assertNotNull(variants); assertEquals(5, variants.size()); + assertNull(masterPlaylist.muxedCaptionFormats); assertEquals(1280000, variants.get(0).format.bitrate); assertNotNull(variants.get(0).format.codecs); @@ -117,6 +124,11 @@ public class HlsMasterPlaylistParserTest extends TestCase { assertEquals("es", closedCaptionFormat.language); } + public void testPlaylistWithoutClosedCaptions() throws IOException { + HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST_WITHOUT_CC); + assertEquals(Collections.emptyList(), playlist.muxedCaptionFormats); + } + private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString) throws IOException { Uri playlistUri = Uri.parse(uri); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index ea99dae345..49c4d04abc 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -111,7 +111,8 @@ import java.util.Locale; * @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If * multiple {@link HlsChunkSource}s are used for a single playback, they should all share the * same provider. - * @param muxedCaptionFormats List of muxed caption {@link Format}s. + * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption + * information is available in the master playlist. */ public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants, HlsDataSourceFactory dataSourceFactory, TimestampAdjusterProvider timestampAdjusterProvider, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java index 6f516923f9..6997324f02 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java @@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.Util; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -104,7 +105,8 @@ import java.util.concurrent.atomic.AtomicInteger; * @param dataSpec Defines the data to be loaded. * @param initDataSpec Defines the initialization data to be fed to new extractors. May be null. * @param hlsUrl The url of the playlist from which this chunk was obtained. - * @param muxedCaptionFormats List of muxed caption {@link Format}s. + * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption + * information is available in the master playlist. * @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionData See {@link #trackSelectionData}. * @param startTimeUs The start time of the chunk in microseconds. @@ -356,9 +358,12 @@ import java.util.concurrent.atomic.AtomicInteger; // This flag ensures the change of pid between streams does not affect the sample queues. @DefaultTsPayloadReaderFactory.Flags int esReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM; - if (!muxedCaptionFormats.isEmpty()) { + List closedCaptionFormats = muxedCaptionFormats; + if (closedCaptionFormats != null) { // The playlist declares closed caption renditions, we should ignore descriptors. esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS; + } else { + closedCaptionFormats = Collections.emptyList(); } String codecs = trackFormat.codecs; if (!TextUtils.isEmpty(codecs)) { @@ -373,7 +378,7 @@ import java.util.concurrent.atomic.AtomicInteger; } } extractor = new TsExtractor(TsExtractor.MODE_HLS, timestampAdjuster, - new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, muxedCaptionFormats)); + new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, closedCaptionFormats)); } if (usingNewExtractor) { extractor.init(extractorOutput); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index 5a8c63f609..874c865049 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -30,15 +30,31 @@ public final class HlsMasterPlaylist extends HlsPlaylist { */ public static final class HlsUrl { + /** + * The http url from which the media playlist can be obtained. + */ public final String url; + /** + * Format information associated with the HLS url. + */ public final Format format; - public static HlsUrl createMediaPlaylistHlsUrl(String baseUri) { + /** + * Creates an HLS url from a given http url. + * + * @param url The url. + * @return An HLS url. + */ + public static HlsUrl createMediaPlaylistHlsUrl(String url) { Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null, null, Format.NO_VALUE, 0, null); - return new HlsUrl(baseUri, format); + return new HlsUrl(url, format); } + /** + * @param url See {@link #url}. + * @param format See {@link #format}. + */ public HlsUrl(String url, Format format) { this.url = url; this.format = format; @@ -46,13 +62,39 @@ public final class HlsMasterPlaylist extends HlsPlaylist { } + /** + * The list of variants declared by the playlist. + */ public final List variants; + /** + * The list of demuxed audios declared by the playlist. + */ public final List audios; + /** + * The list of subtitles declared by the playlist. + */ public final List subtitles; + /** + * The format of the audio muxed in the variants. May be null if the playlist does not declare any + * muxed audio. + */ public final Format muxedAudioFormat; + /** + * The format of the closed captions declared by the playlist. May be empty if the playlist + * explicitly declares no captions are available, or null if the playlist does not declare any + * captions information. + */ public final List muxedCaptionFormats; + /** + * @param baseUri The base uri. Used to resolve relative paths. + * @param variants See {@link #variants}. + * @param audios See {@link #audios}. + * @param subtitles See {@link #subtitles}. + * @param muxedAudioFormat See {@link #muxedAudioFormat}. + * @param muxedCaptionFormats See {@link #muxedCaptionFormats}. + */ public HlsMasterPlaylist(String baseUri, List variants, List audios, List subtitles, Format muxedAudioFormat, List muxedCaptionFormats) { super(baseUri); @@ -60,14 +102,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist { this.audios = Collections.unmodifiableList(audios); this.subtitles = Collections.unmodifiableList(subtitles); this.muxedAudioFormat = muxedAudioFormat; - this.muxedCaptionFormats = Collections.unmodifiableList(muxedCaptionFormats); + this.muxedCaptionFormats = muxedCaptionFormats != null + ? Collections.unmodifiableList(muxedCaptionFormats) : null; } - public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUri) { - List variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUri)); + /** + * Creates a playlist with a single variant. + * + * @param variantUrl The url of the single variant. + * @return A master playlist with a single variant for the provided url. + */ + public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) { + List variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUrl)); List emptyList = Collections.emptyList(); - return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null, - Collections.emptyList()); + return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null, null); } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 8e01dec6fe..664306baff 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -70,6 +71,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser audios = new ArrayList<>(); ArrayList subtitles = new ArrayList<>(); Format muxedAudioFormat = null; - ArrayList muxedCaptionFormats = new ArrayList<>(); + List muxedCaptionFormats = null; + boolean noClosedCaptions = false; String line; while (iterator.hasNext()) { @@ -210,6 +214,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser(); + } muxedCaptionFormats.add(Format.createTextContainerFormat(id, null, mimeType, null, Format.NO_VALUE, selectionFlags, language, accessibilityChannel)); break; @@ -221,6 +228,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Fri, 5 May 2017 07:21:48 -0700 Subject: [PATCH 020/133] Don't set MAX_INPUT_SIZE to unnecessarily large values If the codec isn't adaptive, there's no need to accommodate the width/height/input-size of streams that don't have the same resolution as the current stream. This is because we'll always need to instantiate a new codec anyway. Issue: #2607 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155194458 --- .../video/MediaCodecVideoRenderer.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index ac4bb36035..473d61e3fa 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -408,11 +408,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @Override protected boolean canReconfigureCodec(MediaCodec codec, boolean codecIsAdaptive, Format oldFormat, Format newFormat) { - return areAdaptationCompatible(oldFormat, newFormat) + return areAdaptationCompatible(codecIsAdaptive, oldFormat, newFormat) && newFormat.width <= codecMaxValues.width && newFormat.height <= codecMaxValues.height - && newFormat.maxInputSize <= codecMaxValues.inputSize - && (codecIsAdaptive - || (oldFormat.width == newFormat.width && oldFormat.height == newFormat.height)); + && newFormat.maxInputSize <= codecMaxValues.inputSize; } @Override @@ -664,7 +662,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } boolean haveUnknownDimensions = false; for (Format streamFormat : streamFormats) { - if (areAdaptationCompatible(format, streamFormat)) { + if (areAdaptationCompatible(codecInfo.adaptive, format, streamFormat)) { haveUnknownDimensions |= (streamFormat.width == Format.NO_VALUE || streamFormat.height == Format.NO_VALUE); maxWidth = Math.max(maxWidth, streamFormat.width); @@ -817,17 +815,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } /** - * Returns whether an adaptive codec with suitable {@link CodecMaxValues} will support adaptation - * between two {@link Format}s. + * Returns whether a codec with suitable {@link CodecMaxValues} will support adaptation between + * two {@link Format}s. * + * @param codecIsAdaptive Whether the codec supports seamless resolution switches. * @param first The first format. * @param second The second format. - * @return Whether an adaptive codec with suitable {@link CodecMaxValues} will support adaptation - * between two {@link Format}s. + * @return Whether the codec will support adaptation between the two {@link Format}s. */ - private static boolean areAdaptationCompatible(Format first, Format second) { + private static boolean areAdaptationCompatible(boolean codecIsAdaptive, Format first, + Format second) { return first.sampleMimeType.equals(second.sampleMimeType) - && getRotationDegrees(first) == getRotationDegrees(second); + && getRotationDegrees(first) == getRotationDegrees(second) + && (codecIsAdaptive || (first.width == second.width && first.height == second.height)); } private static float getPixelWidthHeightRatio(Format format) { From 65607b2736d3e51c59b66fa1a76712c98576b854 Mon Sep 17 00:00:00 2001 From: arakawa_yusuke Date: Mon, 8 May 2017 20:05:10 +0900 Subject: [PATCH 021/133] Remove fully-qualified name --- .../android/exoplayer2/video/MediaCodecVideoRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 473d61e3fa..dd0c5356ea 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -376,7 +376,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } @Override - protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) { + protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) { boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT) && outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM) && outputFormat.containsKey(KEY_CROP_TOP); From 2d2fcf1510be02a2315d41dd1a49e01aa0f71db4 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Sun, 7 May 2017 08:18:25 -0700 Subject: [PATCH 022/133] Use native byte order for SimpleOutputBuffers The default byte order for ByteBuffers is big endian, but platform decoder output buffers use native byte order. AudioProcessors handle native byte order input/output. When using a software audio decoding extension the Sonic audio processor would receive big endian input but was outputting to a native byte order buffer, which could be little endian. This mismatch caused audio output to be distorted. After this change both platform decoder and extension decoder output buffers should be in native byte order. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155320973 --- .../google/android/exoplayer2/decoder/SimpleOutputBuffer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java index 309c7fd144..49c7dafbd6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.decoder; import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** * Buffer for {@link SimpleDecoder} output. @@ -40,7 +41,7 @@ public class SimpleOutputBuffer extends OutputBuffer { public ByteBuffer init(long timeUs, int size) { this.timeUs = timeUs; if (data == null || data.capacity() < size) { - data = ByteBuffer.allocateDirect(size); + data = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); } data.position(0); data.limit(size); From 963b7cbf72104e1fe1d61d8b218f620c4a39f5c1 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 8 May 2017 00:25:59 -0700 Subject: [PATCH 023/133] Fix interpolation for rate/pitch adjustment Based on https://github.com/waywardgeek/sonic/commit/7b441933. Issue: #2774 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155349817 --- .../java/com/google/android/exoplayer2/audio/Sonic.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java index 5d6f01b6e0..ef7877ae1e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java @@ -374,8 +374,8 @@ import java.util.Arrays; } private short interpolate(short[] in, int inPos, int oldSampleRate, int newSampleRate) { - short left = in[inPos * numChannels]; - short right = in[inPos * numChannels + numChannels]; + short left = in[inPos]; + short right = in[inPos + numChannels]; int position = newRatePosition * oldSampleRate; int leftPosition = oldRatePosition * newSampleRate; int rightPosition = (oldRatePosition + 1) * newSampleRate; @@ -402,7 +402,7 @@ import java.util.Arrays; enlargeOutputBufferIfNeeded(1); for (int i = 0; i < numChannels; i++) { outputBuffer[numOutputSamples * numChannels + i] = - interpolate(pitchBuffer, position + i, oldSampleRate, newSampleRate); + interpolate(pitchBuffer, position * numChannels + i, oldSampleRate, newSampleRate); } newRatePosition++; numOutputSamples++; From 1d6816d94b3eb3238abfaa38c2edda1a1e4d9e0f Mon Sep 17 00:00:00 2001 From: olly Date: Sat, 7 Jan 2017 11:59:20 +0000 Subject: [PATCH 024/133] Avoid process death if OOM occurs on a loading thread This is most commonly caused by malformed media, where the media indicates that something we need to make an allocation for is *really huge*. Failing playback is appropriate for this case; killing the process is not. Issue: #2780 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155408062 --- .../google/android/exoplayer2/upstream/Loader.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java index bca90ddc5c..1bdebf7c17 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java @@ -33,11 +33,11 @@ import java.util.concurrent.ExecutorService; public final class Loader implements LoaderErrorThrower { /** - * Thrown when an unexpected exception is encountered during loading. + * Thrown when an unexpected exception or error is encountered during loading. */ public static final class UnexpectedLoaderException extends IOException { - public UnexpectedLoaderException(Exception cause) { + public UnexpectedLoaderException(Throwable cause) { super("Unexpected " + cause.getClass().getSimpleName() + ": " + cause.getMessage(), cause); } @@ -316,6 +316,14 @@ public final class Loader implements LoaderErrorThrower { if (!released) { obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); } + } catch (OutOfMemoryError e) { + // This can occur if a stream is malformed in a way that causes an extractor to think it + // needs to allocate a large amount of memory. We don't want the process to die in this + // case, but we do want the playback to fail. + Log.e(TAG, "OutOfMemory error loading stream", e); + if (!released) { + obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); + } } catch (Error e) { // We'd hope that the platform would kill the process if an Error is thrown here, but the // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from From 51132f58f9e08cd247a9d2f7149c860037cdb590 Mon Sep 17 00:00:00 2001 From: tasnimsunny Date: Mon, 8 May 2017 12:46:09 -0700 Subject: [PATCH 025/133] Make removal of non-existent cache span a no-op ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155413733 --- .../google/android/exoplayer2/upstream/cache/SimpleCache.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java index 14f006c850..bbff7dc4a2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java @@ -286,7 +286,9 @@ public final class SimpleCache implements Cache { private void removeSpan(CacheSpan span, boolean removeEmptyCachedContent) throws CacheException { CachedContent cachedContent = index.get(span.key); - Assertions.checkState(cachedContent.removeSpan(span)); + if (cachedContent == null || !cachedContent.removeSpan(span)) { + return; + } totalSpace -= span.length; if (removeEmptyCachedContent && cachedContent.isEmpty()) { index.removeEmpty(cachedContent.key); From 70b628526a742a433fdf5d1fccbb9e1995e4297d Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 9 May 2017 03:14:58 -0700 Subject: [PATCH 026/133] Propagate playlist loading error if it prevents playback This imitates DashMediaSource's behavior. Issue:#2623 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155485738 --- .../exoplayer2/source/hls/HlsChunkSource.java | 7 +++++++ .../exoplayer2/source/hls/HlsMediaSource.java | 2 +- .../hls/playlist/HlsPlaylistTracker.java | 19 +++++++++++++++---- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 49c4d04abc..795e2f0eaa 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -92,6 +92,7 @@ import java.util.Locale; private boolean isTimestampMaster; private byte[] scratchSpace; private IOException fatalError; + private HlsUrl expectedPlaylistUrl; private Uri encryptionKeyUri; private byte[] encryptionKey; @@ -143,6 +144,9 @@ import java.util.Locale; if (fatalError != null) { throw fatalError; } + if (expectedPlaylistUrl != null) { + playlistTracker.maybeThrowPlaylistRefreshError(expectedPlaylistUrl); + } } /** @@ -195,6 +199,7 @@ import java.util.Locale; public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChunkHolder out) { int oldVariantIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat); + expectedPlaylistUrl = null; // Use start time of the previous chunk rather than its end time because switching format will // require downloading overlapping segments. long bufferedDurationUs = previous == null ? 0 @@ -208,6 +213,7 @@ import java.util.Locale; HlsUrl selectedUrl = variants[selectedVariantIndex]; if (!playlistTracker.isSnapshotValid(selectedUrl)) { out.playlist = selectedUrl; + expectedPlaylistUrl = selectedUrl; // Retry when playlist is refreshed. return; } @@ -247,6 +253,7 @@ import java.util.Locale; out.endOfStream = true; } else /* Live */ { out.playlist = selectedUrl; + expectedPlaylistUrl = selectedUrl; } return; } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 3cd9f19522..1bfb8371a0 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -84,7 +84,7 @@ public final class HlsMediaSource implements MediaSource, @Override public void maybeThrowSourceInfoRefreshError() throws IOException { - playlistTracker.maybeThrowPlaylistRefreshError(); + playlistTracker.maybeThrowPrimaryPlaylistRefreshError(); } @Override diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 02a8e3f098..62b77a0575 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -200,18 +200,29 @@ public final class HlsPlaylistTracker implements Loader.Callback Date: Tue, 9 May 2017 03:20:40 -0700 Subject: [PATCH 027/133] Make MODE_SINGLE_PMT the default mode Even though this is not strictly spec compliant, this will make exoplayer behave like it used to before multiple program support. Developers who want to take advantage of the multiple program support are probably less than the ones who only want their stream to "just work". This is particularly useful for streams obtained after a filtering component, like a tv tuner. Issue:#2757 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155486122 --- .../extractor/ts/TsExtractorTest.java | 4 ++-- .../extractor/DefaultExtractorsFactory.java | 21 ++++++++++++++++++- .../exoplayer2/extractor/ts/TsExtractor.java | 21 ++++++++++++++----- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index 7bf722cd8f..efd653b8d9 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -75,7 +75,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { public void testCustomPesReader() throws Exception { CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false); - TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_NORMAL, new TimestampAdjuster(0), + TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) @@ -100,7 +100,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { public void testCustomInitialSectionReader() throws Exception { CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true); - TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_NORMAL, new TimestampAdjuster(0), + TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample_with_sdt.ts")) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java index 022ca1277d..c47a91b176 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java @@ -26,7 +26,9 @@ import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; import com.google.android.exoplayer2.extractor.ts.PsExtractor; import com.google.android.exoplayer2.extractor.ts.TsExtractor; +import com.google.android.exoplayer2.extractor.ts.TsPayloadReader; import com.google.android.exoplayer2.extractor.wav.WavExtractor; +import com.google.android.exoplayer2.util.TimestampAdjuster; import java.lang.reflect.Constructor; /** @@ -67,8 +69,13 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { private @MatroskaExtractor.Flags int matroskaFlags; private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; private @Mp3Extractor.Flags int mp3Flags; + private @TsExtractor.Mode int tsMode; private @DefaultTsPayloadReaderFactory.Flags int tsFlags; + public DefaultExtractorsFactory() { + tsMode = TsExtractor.MODE_SINGLE_PMT; + } + /** * Sets flags for {@link MatroskaExtractor} instances created by the factory. * @@ -107,6 +114,18 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { return this; } + /** + * Sets the mode for {@link TsExtractor} instances created by the factory. + * + * @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory). + * @param mode The mode to use. + * @return The factory, for convenience. + */ + public synchronized DefaultExtractorsFactory setTsExtractorMode(@TsExtractor.Mode int mode) { + tsMode = mode; + return this; + } + /** * Sets flags for {@link DefaultTsPayloadReaderFactory}s used by {@link TsExtractor} instances * created by the factory. @@ -130,7 +149,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { extractors[3] = new Mp3Extractor(mp3Flags); extractors[4] = new AdtsExtractor(); extractors[5] = new Ac3Extractor(); - extractors[6] = new TsExtractor(tsFlags); + extractors[6] = new TsExtractor(tsMode, tsFlags); extractors[7] = new FlvExtractor(); extractors[8] = new OggExtractor(); extractors[9] = new PsExtractor(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index df6efb722c..71b8375bd8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -65,13 +65,13 @@ public final class TsExtractor implements Extractor { * Modes for the extractor. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({MODE_NORMAL, MODE_SINGLE_PMT, MODE_HLS}) + @IntDef({MODE_MULTI_PMT, MODE_SINGLE_PMT, MODE_HLS}) public @interface Mode {} /** * Behave as defined in ISO/IEC 13818-1. */ - public static final int MODE_NORMAL = 0; + public static final int MODE_MULTI_PMT = 0; /** * Assume only one PMT will be contained in the stream, even if more are declared by the PAT. */ @@ -132,12 +132,23 @@ public final class TsExtractor implements Extractor { * {@code FLAG_*} values that control the behavior of the payload readers. */ public TsExtractor(@Flags int defaultTsPayloadReaderFlags) { - this(MODE_NORMAL, new TimestampAdjuster(0), - new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags)); + this(MODE_SINGLE_PMT, defaultTsPayloadReaderFlags); } /** - * @param mode Mode for the extractor. One of {@link #MODE_NORMAL}, {@link #MODE_SINGLE_PMT} + * @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} + * and {@link #MODE_HLS}. + * @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory} + * {@code FLAG_*} values that control the behavior of the payload readers. + */ + public TsExtractor(@Mode int mode, @Flags int defaultTsPayloadReaderFlags) { + this(mode, new TimestampAdjuster(0), + new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags)); + } + + + /** + * @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. From 7eadfb1ff019f61f53cfb7d323cd77ba96c20cd6 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 10 May 2017 19:36:06 -0700 Subject: [PATCH 028/133] Fix wrap_content handling in DefaultTimeBar Issue: #2788 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=155705318 --- .../google/android/exoplayer2/ui/DefaultTimeBar.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 12f31f5da1..06ecb1aa69 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -343,16 +343,18 @@ public class DefaultTimeBar extends View implements TimeBar { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int measureWidth = MeasureSpec.getSize(widthMeasureSpec); - int measureHeight = MeasureSpec.getSize(heightMeasureSpec); - setMeasuredDimension(measureWidth, measureHeight); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int height = heightMode == MeasureSpec.UNSPECIFIED ? touchTargetHeight + : heightMode == MeasureSpec.EXACTLY ? heightSize : Math.min(touchTargetHeight, heightSize); + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int width = right - left; int height = bottom - top; - int barY = height - touchTargetHeight; + int barY = (height - touchTargetHeight) / 2; int seekLeft = getPaddingLeft(); int seekRight = width - getPaddingRight(); int progressY = barY + (touchTargetHeight - barHeight) / 2; From 9a2cac7f741101c58900da7c0070d66c4a578740 Mon Sep 17 00:00:00 2001 From: arakawa_yusuke Date: Thu, 11 May 2017 21:11:21 +0900 Subject: [PATCH 029/133] Refactor: Change the position of variable. --- .../java/com/google/android/exoplayer2/demo/PlayerActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index d01a64a051..34e0365933 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -234,7 +234,6 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay Intent intent = getIntent(); boolean needNewPlayer = player == null; if (needNewPlayer) { - boolean preferExtensionDecoders = intent.getBooleanExtra(PREFER_EXTENSION_DECODERS, false); UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA) ? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null; DrmSessionManager drmSessionManager = null; @@ -253,6 +252,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay } } + boolean preferExtensionDecoders = intent.getBooleanExtra(PREFER_EXTENSION_DECODERS, false); @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = ((DemoApplication) getApplication()).useExtensionRenderers() ? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER From 1cda6e025e38608263572fb5a65b125c58b4334f Mon Sep 17 00:00:00 2001 From: sillywab8 Date: Fri, 12 May 2017 08:24:40 -0500 Subject: [PATCH 030/133] Add support for AVC Level 5.2 --- .../com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java index a09f6e26dd..2bb3603df9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java @@ -429,6 +429,7 @@ public final class MediaCodecUtil { case CodecProfileLevel.AVCLevel42: return 8704 * 16 * 16; case CodecProfileLevel.AVCLevel5: return 22080 * 16 * 16; case CodecProfileLevel.AVCLevel51: return 36864 * 16 * 16; + case CodecProfileLevel.AVCLevel52: return 36864 * 16 * 16; default: return -1; } } From e7ec4e6755e83162d1e2b3b44b6dce863348d01c Mon Sep 17 00:00:00 2001 From: bachinger Date: Sun, 14 May 2017 22:26:39 -0700 Subject: [PATCH 031/133] Enlarge size of data array of parsable packetArray if ogg packet size exceeds the current size. https://github.com/google/ExoPlayer/issues/2782 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156018137 --- .../assets/ogg/bear_vorbis.ogg.0.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.1.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.2.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.3.dump | 2 +- .../assets/ogg/bear_vorbis.ogg.unklen.dump | 2 +- .../exoplayer2/extractor/ogg/OggPacket.java | 19 +++++++++++++++++-- .../extractor/ogg/StreamReader.java | 5 ++--- .../extractor/ogg/VorbisReader.java | 2 +- 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump index 536f76adad..8e2c5125a3 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/vorbis - maxInputSize = 65025 + maxInputSize = -1 width = -1 height = -1 frameRate = -1.0 diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump index 7490773bd5..aa25303ac3 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/vorbis - maxInputSize = 65025 + maxInputSize = -1 width = -1 height = -1 frameRate = -1.0 diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump index 82ad16e701..58969058fa 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/vorbis - maxInputSize = 65025 + maxInputSize = -1 width = -1 height = -1 frameRate = -1.0 diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump index 810b66901c..4c789a8431 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/vorbis - maxInputSize = 65025 + maxInputSize = -1 width = -1 height = -1 frameRate = -1.0 diff --git a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump index 8e86ca340d..2f163572bf 100644 --- a/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump @@ -9,7 +9,7 @@ track 0: id = null containerMimeType = null sampleMimeType = audio/vorbis - maxInputSize = 65025 + maxInputSize = -1 width = -1 height = -1 frameRate = -1.0 diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java index 892f0a68af..c7f4e9489b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.IOException; +import java.util.Arrays; /** * OGG packet class. @@ -27,8 +28,8 @@ import java.io.IOException; /* package */ final class OggPacket { private final OggPageHeader pageHeader = new OggPageHeader(); - private final ParsableByteArray packetArray = - new ParsableByteArray(new byte[OggPageHeader.MAX_PAGE_PAYLOAD], 0); + private final ParsableByteArray packetArray = new ParsableByteArray( + new byte[OggPageHeader.MAX_PAGE_PAYLOAD], 0); private int currentSegmentIndex = C.INDEX_UNSET; private int segmentCount; @@ -85,6 +86,9 @@ import java.io.IOException; int size = calculatePacketSize(currentSegmentIndex); int segmentIndex = currentSegmentIndex + segmentCount; if (size > 0) { + if (packetArray.capacity() < packetArray.limit() + size) { + packetArray.data = Arrays.copyOf(packetArray.data, packetArray.limit() + size); + } input.readFully(packetArray.data, packetArray.limit(), size); packetArray.setLimit(packetArray.limit() + size); populated = pageHeader.laces[segmentIndex - 1] != 255; @@ -118,6 +122,17 @@ import java.io.IOException; return packetArray; } + /** + * Trims the packet data array. + */ + public void trimPayload() { + if (packetArray.data.length == OggPageHeader.MAX_PAGE_PAYLOAD) { + return; + } + packetArray.data = Arrays.copyOf(packetArray.data, Math.max(OggPageHeader.MAX_PAGE_PAYLOAD, + packetArray.limit())); + } + /** * Calculates the size of the packet starting from {@code startSegmentIndex}. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java index 6424155bd9..c203b0c6bd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java @@ -103,15 +103,12 @@ import java.io.IOException; switch (state) { case STATE_READ_HEADERS: return readHeaders(input); - case STATE_SKIP_HEADERS: input.skipFully((int) payloadStartPosition); state = STATE_READ_PAYLOAD; return Extractor.RESULT_CONTINUE; - case STATE_READ_PAYLOAD: return readPayload(input, seekPosition); - default: // Never happens. throw new IllegalStateException(); @@ -152,6 +149,8 @@ import java.io.IOException; setupData = null; state = STATE_READ_PAYLOAD; + // First payload packet. Trim the payload array of the ogg packet after headers have been read. + oggPacket.trimPayload(); return Extractor.RESULT_CONTINUE; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java index ae0a69ef7d..31ac6858be 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java @@ -101,7 +101,7 @@ import java.util.ArrayList; codecInitialisationData.add(vorbisSetup.setupHeaderData); setupData.format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_VORBIS, null, - this.vorbisSetup.idHeader.bitrateNominal, OggPageHeader.MAX_PAGE_PAYLOAD, + this.vorbisSetup.idHeader.bitrateNominal, Format.NO_VALUE, this.vorbisSetup.idHeader.channels, (int) this.vorbisSetup.idHeader.sampleRate, codecInitialisationData, null, 0, null); return true; From ecc4184e6c6ac8242fc796962f7eba936990077d Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 15 May 2017 11:14:04 -0700 Subject: [PATCH 032/133] Enable neon for libvpx on arm64-v8a ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156077889 --- extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh b/extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh index 15dbabdb1f..5f058d0551 100755 --- a/extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh +++ b/extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh @@ -51,8 +51,7 @@ config[3]+=" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx" config[3]+=" --disable-avx2 --enable-pic" arch[4]="arm64-v8a" -config[4]="--force-target=armv8-android-gcc --sdk-path=$ndk --disable-neon" -config[4]+=" --disable-neon-asm" +config[4]="--force-target=armv8-android-gcc --sdk-path=$ndk --enable-neon" arch[5]="x86_64" config[5]="--force-target=x86_64-android-gcc --sdk-path=$ndk --disable-sse2" From a0f4bf0ee18a5f883530202a2aad0f53604b99cb Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 15 May 2017 15:36:20 -0700 Subject: [PATCH 033/133] Constrain DefaultTimeBar maximum positions ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156113616 --- .../google/android/exoplayer2/ui/DefaultTimeBar.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 06ecb1aa69..fd05fdd5d0 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -465,12 +465,10 @@ public class DefaultTimeBar extends View implements TimeBar { scrubberBar.set(progressBar); long newScrubberTime = scrubbing ? scrubPosition : position; if (duration > 0) { - int bufferedPixelWidth = - (int) ((progressBar.width() * bufferedPosition) / duration); - bufferedBar.right = progressBar.left + bufferedPixelWidth; - int scrubberPixelPosition = - (int) ((progressBar.width() * newScrubberTime) / duration); - scrubberBar.right = progressBar.left + scrubberPixelPosition; + int bufferedPixelWidth = (int) ((progressBar.width() * bufferedPosition) / duration); + bufferedBar.right = Math.min(progressBar.left + bufferedPixelWidth, progressBar.right); + int scrubberPixelPosition = (int) ((progressBar.width() * newScrubberTime) / duration); + scrubberBar.right = Math.min(progressBar.left + scrubberPixelPosition, progressBar.right); } else { bufferedBar.right = progressBar.left; scrubberBar.right = progressBar.left; From a4a31546047a6bea63e41775fb8f4c8ff88cf9be Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 15 May 2017 18:06:20 -0700 Subject: [PATCH 034/133] Clear up BaseRenderer.disable - Call onDisabled last. onDisabled really shouldn't be doing anything with the stream, so pretty sure this is fine (and guarantees the stream is cleared properly even if onDisabled throws a RTE). - Remove super.onDisabled calls from Text/Metadata renderers. This is just for consistency; we don't make such calls in other direct descendants of BaseRenderer. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156130640 --- .../main/java/com/google/android/exoplayer2/BaseRenderer.java | 4 +--- .../google/android/exoplayer2/metadata/MetadataRenderer.java | 1 - .../java/com/google/android/exoplayer2/text/TextRenderer.java | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index 44fb6d68ae..396584a39e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -142,9 +142,9 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { public final void disable() { Assertions.checkState(state == STATE_ENABLED); state = STATE_DISABLED; - onDisabled(); stream = null; streamIsFinal = false; + onDisabled(); } // RendererCapabilities implementation. @@ -300,8 +300,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { /** * Returns whether the upstream source is ready. - * - * @return Whether the source is ready. */ protected final boolean isSourceReady() { return readEndOfStream ? streamIsFinal : stream.isReady(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java index 814238970b..70b2d8aab9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java @@ -153,7 +153,6 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { protected void onDisabled() { flushPendingMetadata(); decoder = null; - super.onDisabled(); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index 2f07fe5294..4950549b19 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -254,7 +254,6 @@ public final class TextRenderer extends BaseRenderer implements Callback { streamFormat = null; clearOutput(); releaseDecoder(); - super.onDisabled(); } @Override From fb3c19055691ed835c9594d9a92bdf76117b835f Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 15 May 2017 18:11:45 -0700 Subject: [PATCH 035/133] Clear the correct buffer in MediaCodecRenderer ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156131086 --- .../android/exoplayer2/mediacodec/MediaCodecRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 8fb9bc9271..25a5aa4dd3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -488,7 +488,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } if (format == null) { // We don't have a format yet, so try and read one. - buffer.clear(); + flagsOnlyBuffer.clear(); int result = readSource(formatHolder, flagsOnlyBuffer, true); if (result == C.RESULT_FORMAT_READ) { onInputFormatChanged(formatHolder.format); From bf71ad48838169326b7f5f95c6a4a3f5723c8870 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 22 May 2017 13:52:51 -0700 Subject: [PATCH 036/133] Fix TTML positioning Issue: #2824 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156781252 --- .../exoplayer2/text/ttml/TtmlDecoderTest.java | 34 +++--- .../exoplayer2/text/ttml/TtmlDecoder.java | 100 +++++++++++++----- .../exoplayer2/text/ttml/TtmlNode.java | 7 +- .../exoplayer2/text/ttml/TtmlRegion.java | 14 ++- 4 files changed, 101 insertions(+), 54 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java index 381aaa34ae..496e3f87de 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java @@ -157,39 +157,39 @@ public final class TtmlDecoderTest extends InstrumentationTestCase { assertEquals(2, output.size()); Cue ttmlCue = output.get(0); assertEquals("lorem", ttmlCue.text.toString()); - assertEquals(10.f / 100.f, ttmlCue.position); - assertEquals(10.f / 100.f, ttmlCue.line); - assertEquals(20.f / 100.f, ttmlCue.size); + assertEquals(10f / 100f, ttmlCue.position); + assertEquals(10f / 100f, ttmlCue.line); + assertEquals(20f / 100f, ttmlCue.size); ttmlCue = output.get(1); assertEquals("amet", ttmlCue.text.toString()); - assertEquals(60.f / 100.f, ttmlCue.position); - assertEquals(10.f / 100.f, ttmlCue.line); - assertEquals(20.f / 100.f, ttmlCue.size); + assertEquals(60f / 100f, ttmlCue.position); + assertEquals(10f / 100f, ttmlCue.line); + assertEquals(20f / 100f, ttmlCue.size); output = subtitle.getCues(5000000); assertEquals(1, output.size()); ttmlCue = output.get(0); assertEquals("ipsum", ttmlCue.text.toString()); - assertEquals(40.f / 100.f, ttmlCue.position); - assertEquals(40.f / 100.f, ttmlCue.line); - assertEquals(20.f / 100.f, ttmlCue.size); + assertEquals(40f / 100f, ttmlCue.position); + assertEquals(40f / 100f, ttmlCue.line); + assertEquals(20f / 100f, ttmlCue.size); output = subtitle.getCues(9000000); assertEquals(1, output.size()); ttmlCue = output.get(0); assertEquals("dolor", ttmlCue.text.toString()); - assertEquals(10.f / 100.f, ttmlCue.position); - assertEquals(80.f / 100.f, ttmlCue.line); - assertEquals(Cue.DIMEN_UNSET, ttmlCue.size); + assertEquals(10f / 100f, ttmlCue.position); + assertEquals(80f / 100f, ttmlCue.line); + assertEquals(1f, ttmlCue.size); output = subtitle.getCues(21000000); assertEquals(1, output.size()); ttmlCue = output.get(0); assertEquals("She first said this", ttmlCue.text.toString()); - assertEquals(45.f / 100.f, ttmlCue.position); - assertEquals(45.f / 100.f, ttmlCue.line); - assertEquals(35.f / 100.f, ttmlCue.size); + assertEquals(45f / 100f, ttmlCue.position); + assertEquals(45f / 100f, ttmlCue.line); + assertEquals(35f / 100f, ttmlCue.size); output = subtitle.getCues(25000000); ttmlCue = output.get(0); assertEquals("She first said this\nThen this", ttmlCue.text.toString()); @@ -197,8 +197,8 @@ public final class TtmlDecoderTest extends InstrumentationTestCase { assertEquals(1, output.size()); ttmlCue = output.get(0); assertEquals("She first said this\nThen this\nFinally this", ttmlCue.text.toString()); - assertEquals(45.f / 100.f, ttmlCue.position); - assertEquals(45.f / 100.f, ttmlCue.line); + assertEquals(45f / 100f, ttmlCue.position); + assertEquals(45f / 100f, ttmlCue.line); } public void testEmptyStyleAttribute() throws IOException, SubtitleDecoderException { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java index 71ce17eeed..0012ce2c22 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.text.ttml; import android.text.Layout; import android.util.Log; -import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.SimpleSubtitleDecoder; @@ -100,7 +99,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { XmlPullParser xmlParser = xmlParserFactory.newPullParser(); Map globalStyles = new HashMap<>(); Map regionMap = new HashMap<>(); - regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion()); + regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(null)); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length); xmlParser.setInput(inputStream, null); TtmlSubtitle ttmlSubtitle = null; @@ -211,9 +210,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { globalStyles.put(style.getId(), style); } } else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) { - Pair ttmlRegionInfo = parseRegionAttributes(xmlParser); - if (ttmlRegionInfo != null) { - globalRegions.put(ttmlRegionInfo.first, ttmlRegionInfo.second); + TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser); + if (ttmlRegion != null) { + globalRegions.put(ttmlRegion.id, ttmlRegion); } } } while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD)); @@ -221,41 +220,84 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { } /** - * Parses a region declaration. Supports origin and extent definition but only when defined in - * terms of percentage of the viewport. Regions that do not correctly declare origin are ignored. + * Parses a region declaration. + *

    + * If the region defines an origin and/or extent, it is required that they're defined as + * percentages of the viewport. Region declarations that define origin and/or extent in other + * formats are unsupported, and null is returned. */ - private Pair parseRegionAttributes(XmlPullParser xmlParser) { + private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser) { String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID); - String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN); - String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT); - if (regionOrigin == null || regionId == null) { + if (regionId == null) { return null; } - float position = Cue.DIMEN_UNSET; - float line = Cue.DIMEN_UNSET; - Matcher originMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin); - if (originMatcher.matches()) { - try { - position = Float.parseFloat(originMatcher.group(1)) / 100.f; - line = Float.parseFloat(originMatcher.group(2)) / 100.f; - } catch (NumberFormatException e) { - Log.w(TAG, "Ignoring region with malformed origin: '" + regionOrigin + "'", e); - position = Cue.DIMEN_UNSET; + + float position; + float line; + String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN); + if (regionOrigin != null) { + Matcher originMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin); + if (originMatcher.matches()) { + try { + position = Float.parseFloat(originMatcher.group(1)) / 100f; + line = Float.parseFloat(originMatcher.group(2)) / 100f; + } catch (NumberFormatException e) { + Log.w(TAG, "Ignoring region with malformed origin: " + regionOrigin); + return null; + } + } else { + Log.w(TAG, "Ignoring region with unsupported origin: " + regionOrigin); + return null; } + } else { + // Origin is omitted. Default to top left. + position = 0; + line = 0; } - float width = Cue.DIMEN_UNSET; + + float width; + float height; + String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT); if (regionExtent != null) { Matcher extentMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent); if (extentMatcher.matches()) { try { - width = Float.parseFloat(extentMatcher.group(1)) / 100.f; + width = Float.parseFloat(extentMatcher.group(1)) / 100f; + height = Float.parseFloat(extentMatcher.group(2)) / 100f; } catch (NumberFormatException e) { - Log.w(TAG, "Ignoring malformed region extent: '" + regionExtent + "'", e); + Log.w(TAG, "Ignoring region with malformed extent: " + regionOrigin); + return null; } + } else { + Log.w(TAG, "Ignoring region with unsupported extent: " + regionOrigin); + return null; + } + } else { + // Extent is omitted. Default to extent of parent. + width = 1; + height = 1; + } + + @Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START; + String displayAlign = XmlPullParserUtil.getAttributeValue(xmlParser, + TtmlNode.ATTR_TTS_DISPLAY_ALIGN); + if (displayAlign != null) { + switch (displayAlign.toLowerCase()) { + case "center": + lineAnchor = Cue.ANCHOR_TYPE_MIDDLE; + line += height / 2; + break; + case "after": + lineAnchor = Cue.ANCHOR_TYPE_END; + line += height; + break; + default: + // Default "before" case. Do nothing. + break; } } - return position != Cue.DIMEN_UNSET ? new Pair<>(regionId, new TtmlRegion(position, line, - Cue.LINE_TYPE_FRACTION, width)) : null; + + return new TtmlRegion(regionId, position, line, Cue.LINE_TYPE_FRACTION, lineAnchor, width); } private String[] parseStyleIds(String parentStyleIds) { @@ -277,7 +319,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { try { style.setBackgroundColor(ColorParser.parseTtmlColor(attributeValue)); } catch (IllegalArgumentException e) { - Log.w(TAG, "failed parsing background value: '" + attributeValue + "'"); + Log.w(TAG, "Failed parsing background value: " + attributeValue); } break; case TtmlNode.ATTR_TTS_COLOR: @@ -285,7 +327,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { try { style.setFontColor(ColorParser.parseTtmlColor(attributeValue)); } catch (IllegalArgumentException e) { - Log.w(TAG, "failed parsing color value: '" + attributeValue + "'"); + Log.w(TAG, "Failed parsing color value: " + attributeValue); } break; case TtmlNode.ATTR_TTS_FONT_FAMILY: @@ -296,7 +338,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { style = createIfNull(style); parseFontSize(attributeValue, style); } catch (SubtitleDecoderException e) { - Log.w(TAG, "failed parsing fontSize value: '" + attributeValue + "'"); + Log.w(TAG, "Failed parsing fontSize value: " + attributeValue); } break; case TtmlNode.ATTR_TTS_FONT_WEIGHT: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java index 18378df445..43fa7a1bd9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java @@ -50,14 +50,15 @@ import java.util.TreeSet; public static final String ANONYMOUS_REGION_ID = ""; public static final String ATTR_ID = "id"; - public static final String ATTR_TTS_BACKGROUND_COLOR = "backgroundColor"; + public static final String ATTR_TTS_ORIGIN = "origin"; public static final String ATTR_TTS_EXTENT = "extent"; + public static final String ATTR_TTS_DISPLAY_ALIGN = "displayAlign"; + public static final String ATTR_TTS_BACKGROUND_COLOR = "backgroundColor"; public static final String ATTR_TTS_FONT_STYLE = "fontStyle"; public static final String ATTR_TTS_FONT_SIZE = "fontSize"; public static final String ATTR_TTS_FONT_FAMILY = "fontFamily"; public static final String ATTR_TTS_FONT_WEIGHT = "fontWeight"; public static final String ATTR_TTS_COLOR = "color"; - public static final String ATTR_TTS_ORIGIN = "origin"; public static final String ATTR_TTS_TEXT_DECORATION = "textDecoration"; public static final String ATTR_TTS_TEXT_ALIGN = "textAlign"; @@ -179,7 +180,7 @@ import java.util.TreeSet; for (Entry entry : regionOutputs.entrySet()) { TtmlRegion region = regionMap.get(entry.getKey()); cues.add(new Cue(cleanUpText(entry.getValue()), null, region.line, region.lineType, - Cue.TYPE_UNSET, region.position, Cue.TYPE_UNSET, region.width)); + region.lineAnchor, region.position, Cue.TYPE_UNSET, region.width)); } return cues; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java index 5f30834b4d..98823d7a84 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java @@ -22,20 +22,24 @@ import com.google.android.exoplayer2.text.Cue; */ /* package */ final class TtmlRegion { + public final String id; public final float position; public final float line; - @Cue.LineType - public final int lineType; + @Cue.LineType public final int lineType; + @Cue.AnchorType public final int lineAnchor; public final float width; - public TtmlRegion() { - this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET); + public TtmlRegion(String id) { + this(id, Cue.DIMEN_UNSET, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET); } - public TtmlRegion(float position, float line, @Cue.LineType int lineType, float width) { + public TtmlRegion(String id, float position, float line, @Cue.LineType int lineType, + @Cue.AnchorType int lineAnchor, float width) { + this.id = id; this.position = position; this.line = line; this.lineType = lineType; + this.lineAnchor = lineAnchor; this.width = width; } From c14a14e51d5c9a6d29f101892fdf184d61f546f9 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 23 May 2017 07:45:53 -0700 Subject: [PATCH 037/133] Update gradle + bintray-release ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156860658 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cbc34cecd6..da75def90b 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.1' classpath 'com.novoda:bintray-release:0.4.0' } // Workaround for the following test coverage issue. Remove when fixed: From fe50459449413ae3a9f3a9bb720812547fbd63a8 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 23 May 2017 09:41:37 -0700 Subject: [PATCH 038/133] Bump version and update release notes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156872383 --- RELEASENOTES.md | 23 +++++++++++++++++++ build.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../exoplayer2/ExoPlayerLibraryInfo.java | 6 ++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d216e767b0..6a1defa809 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,28 @@ # Release notes # +### r2.4.1 ### + +* Stability: Avoid OutOfMemoryError in extractors when parsing malformed media + ([#2780](https://github.com/google/ExoPlayer/issues/2780)). +* Stability: Avoid native crash on Galaxy Nexus. Avoid unnecessarily large codec + input buffer allocations on all devices + ([#2607](https://github.com/google/ExoPlayer/issues/2607)). +* Variable speed playback: Fix interpolation for rate/pitch adjustment + ([#2774](https://github.com/google/ExoPlayer/issues/2774)). +* HLS: Include EXT-X-DATERANGE tags in HlsMediaPlaylist. +* HLS: Don't expose CEA-608 track if CLOSED-CAPTIONS=NONE + ([#2743](https://github.com/google/ExoPlayer/issues/2743)). +* HLS: Correctly propagate errors loading the media playlist + ([#2623](https://github.com/google/ExoPlayer/issues/2623)). +* UI: DefaultTimeBar enhancements and bug fixes + ([#2740](https://github.com/google/ExoPlayer/issues/2740)). +* Ogg: Fix failure to play some Ogg files + ([#2782](https://github.com/google/ExoPlayer/issues/2782)). +* Captions: Don't select text tack with no language by default. +* Captions: TTML positioning fixes + ([#2824](https://github.com/google/ExoPlayer/issues/2824)). +* Misc bugfixes. + ### r2.4.0 ### * New modular library structure. You can read more about depending on individual diff --git a/build.gradle b/build.gradle index da75def90b..258b11d2e6 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.4.0' + releaseVersion = 'r2.4.1' releaseWebsite = 'https://github.com/google/ExoPlayer' } if (it.hasProperty('externalBuildDir')) { diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 6580e687cc..1bb859028d 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2401" + android:versionName="2.4.1"> diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 13cf35d449..23c2ddbde9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -24,13 +24,13 @@ public interface 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. - String VERSION = "2.4.0"; + String VERSION = "2.4.1"; /** * The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - String VERSION_SLASHY = "ExoPlayerLib/2.4.0"; + String VERSION_SLASHY = "ExoPlayerLib/2.4.1"; /** * The version of the library expressed as an integer, for example 1002003. @@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - int VERSION_INT = 2004000; + int VERSION_INT = 2004001; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From 10f8944060356d575815c6570db9995a1a0d1e01 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 24 May 2017 07:38:42 -0700 Subject: [PATCH 039/133] Don't select more than one audio/video/text track by default Issue: #2618 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156986606 --- .../trackselection/DefaultTrackSelector.java | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 361fcf0b57..b37088e588 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -436,35 +436,48 @@ public class DefaultTrackSelector extends MappingTrackSelector { int rendererCount = rendererCapabilities.length; TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount]; Parameters params = paramsReference.get(); - boolean videoTrackAndRendererPresent = false; + boolean seenVideoRendererWithMappedTracks = false; + boolean selectedVideoTracks = false; for (int i = 0; i < rendererCount; i++) { if (C.TRACK_TYPE_VIDEO == rendererCapabilities[i].getTrackType()) { - rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i], - rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth, - params.maxVideoHeight, params.maxVideoBitrate, params.allowNonSeamlessAdaptiveness, - params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight, - params.orientationMayChange, adaptiveTrackSelectionFactory, - params.exceedVideoConstraintsIfNecessary, params.exceedRendererCapabilitiesIfNecessary); - videoTrackAndRendererPresent |= rendererTrackGroupArrays[i].length > 0; + if (!selectedVideoTracks) { + rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i], + rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth, + params.maxVideoHeight, params.maxVideoBitrate, params.allowNonSeamlessAdaptiveness, + params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight, + params.orientationMayChange, adaptiveTrackSelectionFactory, + params.exceedVideoConstraintsIfNecessary, + params.exceedRendererCapabilitiesIfNecessary); + selectedVideoTracks = rendererTrackSelections[i] != null; + } + seenVideoRendererWithMappedTracks |= rendererTrackGroupArrays[i].length > 0; } } + boolean selectedAudioTracks = false; + boolean selectedTextTracks = false; for (int i = 0; i < rendererCount; i++) { switch (rendererCapabilities[i].getTrackType()) { case C.TRACK_TYPE_VIDEO: // Already done. Do nothing. break; case C.TRACK_TYPE_AUDIO: - rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i], - rendererFormatSupports[i], params.preferredAudioLanguage, - params.exceedRendererCapabilitiesIfNecessary, params.allowMixedMimeAdaptiveness, - videoTrackAndRendererPresent ? null : adaptiveTrackSelectionFactory); + if (!selectedAudioTracks) { + rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i], + rendererFormatSupports[i], params.preferredAudioLanguage, + params.exceedRendererCapabilitiesIfNecessary, params.allowMixedMimeAdaptiveness, + seenVideoRendererWithMappedTracks ? null : adaptiveTrackSelectionFactory); + selectedAudioTracks = rendererTrackSelections[i] != null; + } break; case C.TRACK_TYPE_TEXT: - rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i], - rendererFormatSupports[i], params.preferredTextLanguage, - params.preferredAudioLanguage, params.exceedRendererCapabilitiesIfNecessary); + if (!selectedTextTracks) { + rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i], + rendererFormatSupports[i], params.preferredTextLanguage, + params.preferredAudioLanguage, params.exceedRendererCapabilitiesIfNecessary); + selectedTextTracks = rendererTrackSelections[i] != null; + } break; default: rendererTrackSelections[i] = selectOtherTrack(rendererCapabilities[i].getTrackType(), From 22137f621535fd765eea411ac31fbf00af805667 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Wed, 24 May 2017 09:48:39 -0700 Subject: [PATCH 040/133] Flexibilize Util.parseXsDateTime to allow single digit hour ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=156999955 --- .../java/com/google/android/exoplayer2/util/UtilTest.java | 1 + .../src/main/java/com/google/android/exoplayer2/util/Util.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/UtilTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/UtilTest.java index 923d1d8aaa..1d9aff0723 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/UtilTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/UtilTest.java @@ -146,6 +146,7 @@ public class UtilTest extends TestCase { assertEquals(1411161535000L, Util.parseXsDateTime("2014-09-19T13:18:55-08:00")); assertEquals(1411161535000L, Util.parseXsDateTime("2014-09-19T13:18:55-0800")); assertEquals(1411161535000L, Util.parseXsDateTime("2014-09-19T13:18:55.000-0800")); + assertEquals(1411161535000L, Util.parseXsDateTime("2014-09-19T13:18:55.000-800")); } public void testUnescapeInvalidFileName() { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index 206349fa07..50932cdf48 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -98,7 +98,7 @@ public final class Util { private static final Pattern XS_DATE_TIME_PATTERN = Pattern.compile( "(\\d\\d\\d\\d)\\-(\\d\\d)\\-(\\d\\d)[Tt]" + "(\\d\\d):(\\d\\d):(\\d\\d)([\\.,](\\d+))?" - + "([Zz]|((\\+|\\-)(\\d\\d):?(\\d\\d)))?"); + + "([Zz]|((\\+|\\-)(\\d?\\d):?(\\d\\d)))?"); private static final Pattern XS_DURATION_PATTERN = Pattern.compile("^(-)?P(([0-9]*)Y)?(([0-9]*)M)?(([0-9]*)D)?" + "(T(([0-9]*)H)?(([0-9]*)M)?(([0-9.]*)S)?)?$"); From 67cb7d8da5019af5f273b7be8885ef1281f302f7 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 25 May 2017 06:52:39 -0700 Subject: [PATCH 041/133] Fix SmoothStreaming Timeline There were a few things wrong. Specifically the case in the ref'd issue. Also, the timeline was being marked as non-dynamic in the empty-but-live case (it should be marked dynamic as segments may be added later). Issue: #2760 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157103727 --- .../source/smoothstreaming/SsMediaSource.java | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index e3fb8b606c..d16620d5b2 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -287,39 +287,41 @@ public final class SsMediaSource implements MediaSource, for (int i = 0; i < mediaPeriods.size(); i++) { mediaPeriods.get(i).updateManifest(manifest); } + + long startTimeUs = Long.MAX_VALUE; + long endTimeUs = Long.MIN_VALUE; + for (StreamElement element : manifest.streamElements) { + if (element.chunkCount > 0) { + startTimeUs = Math.min(startTimeUs, element.getStartTimeUs(0)); + endTimeUs = Math.max(endTimeUs, element.getStartTimeUs(element.chunkCount - 1) + + element.getChunkDurationUs(element.chunkCount - 1)); + } + } + Timeline timeline; - if (manifest.isLive) { - long startTimeUs = Long.MAX_VALUE; - long endTimeUs = Long.MIN_VALUE; - for (int i = 0; i < manifest.streamElements.length; i++) { - StreamElement element = manifest.streamElements[i]; - if (element.chunkCount > 0) { - startTimeUs = Math.min(startTimeUs, element.getStartTimeUs(0)); - endTimeUs = Math.max(endTimeUs, element.getStartTimeUs(element.chunkCount - 1) - + element.getChunkDurationUs(element.chunkCount - 1)); - } + if (startTimeUs == Long.MAX_VALUE) { + long periodDurationUs = manifest.isLive ? C.TIME_UNSET : 0; + timeline = new SinglePeriodTimeline(periodDurationUs, 0, 0, 0, true /* isSeekable */, + manifest.isLive /* isDynamic */); + } else if (manifest.isLive) { + if (manifest.dvrWindowLengthUs != C.TIME_UNSET && manifest.dvrWindowLengthUs > 0) { + startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs); } - if (startTimeUs == Long.MAX_VALUE) { - timeline = new SinglePeriodTimeline(C.TIME_UNSET, false); - } else { - if (manifest.dvrWindowLengthUs != C.TIME_UNSET - && manifest.dvrWindowLengthUs > 0) { - startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs); - } - long durationUs = endTimeUs - startTimeUs; - long defaultStartPositionUs = durationUs - C.msToUs(livePresentationDelayMs); - if (defaultStartPositionUs < MIN_LIVE_DEFAULT_START_POSITION_US) { - // The default start position is too close to the start of the live window. Set it to the - // minimum default start position provided the window is at least twice as big. Else set - // it to the middle of the window. - defaultStartPositionUs = Math.min(MIN_LIVE_DEFAULT_START_POSITION_US, durationUs / 2); - } - timeline = new SinglePeriodTimeline(C.TIME_UNSET, durationUs, startTimeUs, - defaultStartPositionUs, true /* isSeekable */, true /* isDynamic */); + long durationUs = endTimeUs - startTimeUs; + long defaultStartPositionUs = durationUs - C.msToUs(livePresentationDelayMs); + if (defaultStartPositionUs < MIN_LIVE_DEFAULT_START_POSITION_US) { + // The default start position is too close to the start of the live window. Set it to the + // minimum default start position provided the window is at least twice as big. Else set + // it to the middle of the window. + defaultStartPositionUs = Math.min(MIN_LIVE_DEFAULT_START_POSITION_US, durationUs / 2); } + timeline = new SinglePeriodTimeline(C.TIME_UNSET, durationUs, startTimeUs, + defaultStartPositionUs, true /* isSeekable */, true /* isDynamic */); } else { - boolean isSeekable = manifest.durationUs != C.TIME_UNSET; - timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable); + long durationUs = manifest.durationUs != C.TIME_UNSET ? manifest.durationUs + : endTimeUs - startTimeUs; + timeline = new SinglePeriodTimeline(startTimeUs + durationUs, durationUs, startTimeUs, 0, + true /* isSeekable */, false /* isDynamic */); } sourceListener.onSourceInfoRefreshed(timeline, manifest); } From 5a754fe7611856d333a74bc2c9dfb9f2c4abab28 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 26 May 2017 04:49:44 -0700 Subject: [PATCH 042/133] Don't fail if we find a track is unsupported Use AUDIO_UNKNOWN instead. This is in line with our handling of video tracks with VIDEO_UNKNOWN. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157209428 --- .../extractor/mkv/MatroskaExtractor.java | 32 ++++++++++++++----- .../android/exoplayer2/util/MimeTypes.java | 1 + 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index 8f3abf4688..2313392fcf 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.extractor.mkv; import android.support.annotation.IntDef; +import android.util.Log; import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -84,6 +85,8 @@ public final class MatroskaExtractor implements Extractor { */ public static final int FLAG_DISABLE_SEEK_FOR_CUES = 1; + private static final String TAG = "MatroskaExtractor"; + private static final int UNSET_ENTRY_ID = -1; private static final int BLOCK_STATE_START = 0; @@ -1558,7 +1561,12 @@ public final class MatroskaExtractor implements Extractor { break; case CODEC_ID_FOURCC: initializationData = parseFourCcVc1Private(new ParsableByteArray(codecPrivate)); - mimeType = initializationData == null ? MimeTypes.VIDEO_UNKNOWN : MimeTypes.VIDEO_VC1; + if (initializationData != null) { + mimeType = MimeTypes.VIDEO_VC1; + } else { + Log.w(TAG, "Unsupported FourCC. Setting mimeType to " + MimeTypes.VIDEO_UNKNOWN); + mimeType = MimeTypes.VIDEO_UNKNOWN; + } break; case CODEC_ID_THEORA: // TODO: This can be set to the real mimeType if/when we work out what initializationData @@ -1614,19 +1622,27 @@ public final class MatroskaExtractor implements Extractor { break; case CODEC_ID_ACM: mimeType = MimeTypes.AUDIO_RAW; - if (!parseMsAcmCodecPrivate(new ParsableByteArray(codecPrivate))) { - throw new ParserException("Non-PCM MS/ACM is unsupported"); - } - pcmEncoding = Util.getPcmEncoding(audioBitDepth); - if (pcmEncoding == C.ENCODING_INVALID) { - throw new ParserException("Unsupported PCM bit depth: " + audioBitDepth); + if (parseMsAcmCodecPrivate(new ParsableByteArray(codecPrivate))) { + pcmEncoding = Util.getPcmEncoding(audioBitDepth); + if (pcmEncoding == C.ENCODING_INVALID) { + pcmEncoding = Format.NO_VALUE; + mimeType = MimeTypes.AUDIO_UNKNOWN; + Log.w(TAG, "Unsupported PCM bit depth: " + audioBitDepth + ". Setting mimeType to " + + mimeType); + } + } else { + mimeType = MimeTypes.AUDIO_UNKNOWN; + Log.w(TAG, "Non-PCM MS/ACM is unsupported. Setting mimeType to " + mimeType); } break; case CODEC_ID_PCM_INT_LIT: mimeType = MimeTypes.AUDIO_RAW; pcmEncoding = Util.getPcmEncoding(audioBitDepth); if (pcmEncoding == C.ENCODING_INVALID) { - throw new ParserException("Unsupported PCM bit depth: " + audioBitDepth); + pcmEncoding = Format.NO_VALUE; + mimeType = MimeTypes.AUDIO_UNKNOWN; + Log.w(TAG, "Unsupported PCM bit depth: " + audioBitDepth + ". Setting mimeType to " + + mimeType); } break; case CODEC_ID_SUBRIP: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index ea669e6f2a..e227ea1068 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -61,6 +61,7 @@ public final class MimeTypes { public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb"; public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/x-flac"; public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac"; + public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; From 2795269daee2270fc2bda1512cefd86ef021f905 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Fri, 26 May 2017 07:48:43 -0700 Subject: [PATCH 043/133] Use AVERAGE-BANDWIDTH instead of BANDWIDTH when available Also prevent BANDWIDTH's regex from matching the AVERAGE-BANDWIDTH attribute. Issue:#2863 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157219453 --- .../playlist/HlsMasterPlaylistParserTest.java | 27 ++++++++++++++----- .../hls/playlist/HlsPlaylistParser.java | 26 ++++++++++-------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java index 912dcb28b2..f835c87466 100644 --- a/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java +++ b/library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java @@ -51,6 +51,15 @@ public class HlsMasterPlaylistParserTest extends TestCase { + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n" + "http://example.com/audio-only.m3u8"; + private static final String AVG_BANDWIDTH_MASTER_PLAYLIST = " #EXTM3U \n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + + "http://example.com/low.m3u8\n" + + "\n" + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000," + + "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n" + + "http://example.com/spaces_in_codecs.m3u8\n"; + private static final String PLAYLIST_WITH_INVALID_HEADER = "#EXTMU3\n" + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n" + "http://example.com/low.m3u8\n"; @@ -70,42 +79,48 @@ public class HlsMasterPlaylistParserTest extends TestCase { HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST); List variants = masterPlaylist.variants; - assertNotNull(variants); assertEquals(5, variants.size()); assertNull(masterPlaylist.muxedCaptionFormats); assertEquals(1280000, variants.get(0).format.bitrate); - assertNotNull(variants.get(0).format.codecs); assertEquals("mp4a.40.2,avc1.66.30", variants.get(0).format.codecs); assertEquals(304, variants.get(0).format.width); assertEquals(128, variants.get(0).format.height); assertEquals("http://example.com/low.m3u8", variants.get(0).url); assertEquals(1280000, variants.get(1).format.bitrate); - assertNotNull(variants.get(1).format.codecs); assertEquals("mp4a.40.2 , avc1.66.30 ", variants.get(1).format.codecs); assertEquals("http://example.com/spaces_in_codecs.m3u8", variants.get(1).url); assertEquals(2560000, variants.get(2).format.bitrate); - assertEquals(null, variants.get(2).format.codecs); + assertNull(variants.get(2).format.codecs); assertEquals(384, variants.get(2).format.width); assertEquals(160, variants.get(2).format.height); assertEquals("http://example.com/mid.m3u8", variants.get(2).url); assertEquals(7680000, variants.get(3).format.bitrate); - assertEquals(null, variants.get(3).format.codecs); + assertNull(variants.get(3).format.codecs); assertEquals(Format.NO_VALUE, variants.get(3).format.width); assertEquals(Format.NO_VALUE, variants.get(3).format.height); assertEquals("http://example.com/hi.m3u8", variants.get(3).url); assertEquals(65000, variants.get(4).format.bitrate); - assertNotNull(variants.get(4).format.codecs); assertEquals("mp4a.40.5", variants.get(4).format.codecs); assertEquals(Format.NO_VALUE, variants.get(4).format.width); assertEquals(Format.NO_VALUE, variants.get(4).format.height); assertEquals("http://example.com/audio-only.m3u8", variants.get(4).url); } + public void testMasterPlaylistWithBandwdithAverage() throws IOException { + HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, + AVG_BANDWIDTH_MASTER_PLAYLIST); + + List variants = masterPlaylist.variants; + + assertEquals(1280000, variants.get(0).format.bitrate); + assertEquals(1270000, variants.get(1).format.bitrate); + } + public void testPlaylistWithInvalidHeader() throws IOException { try { parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 664306baff..9552f5b8c8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -73,7 +73,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Wed, 31 May 2017 01:46:49 -0700 Subject: [PATCH 044/133] Fix DefaultTimeBar invalidation Issue: #2871 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157562792 --- .../java/com/google/android/exoplayer2/ui/DefaultTimeBar.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index fd05fdd5d0..d9754420bf 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -220,11 +220,13 @@ public class DefaultTimeBar extends View implements TimeBar { public void setPosition(long position) { this.position = position; setContentDescription(getProgressText()); + update(); } @Override public void setBufferedPosition(long bufferedPosition) { this.bufferedPosition = bufferedPosition; + update(); } @Override @@ -235,6 +237,7 @@ public class DefaultTimeBar extends View implements TimeBar { } else { updateScrubberState(); } + update(); } @Override @@ -242,6 +245,7 @@ public class DefaultTimeBar extends View implements TimeBar { Assertions.checkArgument(adBreakCount == 0 || adBreakTimesMs != null); this.adBreakCount = adBreakCount; this.adBreakTimesMs = adBreakTimesMs; + update(); } @Override From c80b60f4ac571c7ca22607f7049d90db2629e98a Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 31 May 2017 03:16:47 -0700 Subject: [PATCH 045/133] Constraint seeks within bounds for ConstantBitrateSeeker We do this everywhere for index based seeking already. Issue: #2876 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157568788 --- .../exoplayer2/extractor/mp3/ConstantBitrateSeeker.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java index c5de8d8284..df7748a910 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/ConstantBitrateSeeker.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.extractor.mp3; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.Util; /** * MP3 seeker that doesn't rely on metadata and seeks assuming the source has a constant bitrate. @@ -41,8 +42,11 @@ import com.google.android.exoplayer2.C; @Override public long getPosition(long timeUs) { - return durationUs == C.TIME_UNSET ? 0 - : firstFramePosition + (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE); + if (durationUs == C.TIME_UNSET) { + return 0; + } + timeUs = Util.constrainValue(timeUs, 0, durationUs); + return firstFramePosition + (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE); } @Override From 80600ffe1be73e83631c8cc38f03db8e3cd0dfce Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Wed, 31 May 2017 03:59:24 -0700 Subject: [PATCH 046/133] Ignore invalid EXT-X-PLAYLIST-TYPE values Issue:#2889 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157571216 --- .../exoplayer2/source/hls/playlist/HlsPlaylistParser.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 9552f5b8c8..a867659838 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -307,8 +307,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Wed, 31 May 2017 07:23:13 -0700 Subject: [PATCH 047/133] Move adaptation disabling workaround into MediaCodecUtil This is necessary to make sure that the correct thing happens where MediaCodecInfo.adaptive is queried directly (for example, MediaCodecVideoRenderer uses the field to determine how to size input buffers). Also disable adaptive on Nexus 10. Issue: #2806 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157583473 --- .../exoplayer2/mediacodec/MediaCodecInfo.java | 23 +++- .../mediacodec/MediaCodecRenderer.java | 16 +-- .../exoplayer2/mediacodec/MediaCodecUtil.java | 105 ++++++++++-------- 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java index 6914b2f52c..3c788a60a4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java @@ -71,7 +71,7 @@ public final class MediaCodecInfo { * @return The created instance. */ public static MediaCodecInfo newPassthroughInstance(String name) { - return new MediaCodecInfo(name, null, null); + return new MediaCodecInfo(name, null, null, false); } /** @@ -84,18 +84,29 @@ public final class MediaCodecInfo { */ public static MediaCodecInfo newInstance(String name, String mimeType, CodecCapabilities capabilities) { - return new MediaCodecInfo(name, mimeType, capabilities); + return new MediaCodecInfo(name, mimeType, capabilities, false); } /** - * @param name The name of the decoder. - * @param capabilities The capabilities of the decoder. + * Creates an instance. + * + * @param name The name of the {@link MediaCodec}. + * @param mimeType A mime type supported by the {@link MediaCodec}. + * @param capabilities The capabilities of the {@link MediaCodec} for the specified mime type. + * @param forceDisableAdaptive Whether {@link #adaptive} should be forced to {@code false}. + * @return The created instance. */ - private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities) { + public static MediaCodecInfo newInstance(String name, String mimeType, + CodecCapabilities capabilities, boolean forceDisableAdaptive) { + return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive); + } + + private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities, + boolean forceDisableAdaptive) { this.name = Assertions.checkNotNull(name); this.mimeType = mimeType; this.capabilities = capabilities; - adaptive = capabilities != null && isAdaptive(capabilities); + adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities); tunneling = capabilities != null && isTunneling(capabilities); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 25a5aa4dd3..f0e23ebfeb 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -339,7 +339,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } String codecName = decoderInfo.name; - codecIsAdaptive = decoderInfo.adaptive && !codecNeedsDisableAdaptationWorkaround(codecName); + codecIsAdaptive = decoderInfo.adaptive; codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName); @@ -1188,18 +1188,4 @@ public abstract class MediaCodecRenderer extends BaseRenderer { && "OMX.MTK.AUDIO.DECODER.MP3".equals(name); } - /** - * Returns whether the decoder is known to fail when adapting, despite advertising itself as an - * adaptive decoder. - *

    - * If true is returned then we explicitly disable adaptation for the decoder. - * - * @param name The decoder name. - * @return True if the decoder is known to fail when adapting. - */ - private static boolean codecNeedsDisableAdaptationWorkaround(String name) { - return Util.SDK_INT <= 19 && Util.MODEL.equals("ODROID-XU3") - && ("OMX.Exynos.AVC.Decoder".equals(name) || "OMX.Exynos.AVC.Decoder.secure".equals(name)); - } - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java index 2bb3603df9..5369dffeb6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java @@ -160,6 +160,55 @@ public final class MediaCodecUtil { return decoderInfos; } + /** + * Returns the maximum frame size supported by the default H264 decoder. + * + * @return The maximum frame size for an H264 stream that can be decoded on the device. + */ + public static int maxH264DecodableFrameSize() throws DecoderQueryException { + if (maxH264DecodableFrameSize == -1) { + int result = 0; + MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false); + if (decoderInfo != null) { + for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { + result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result); + } + // We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are + // the levels mandated by the Android CDD. + result = Math.max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360)); + } + maxH264DecodableFrameSize = result; + } + return maxH264DecodableFrameSize; + } + + /** + * Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given + * codec description string (as defined by RFC 6381). + * + * @param codec A codec description string, as defined by RFC 6381. + * @return A pair (profile constant, level constant) if {@code codec} is well-formed and + * recognized, or null otherwise + */ + public static Pair getCodecProfileAndLevel(String codec) { + if (codec == null) { + return null; + } + String[] parts = codec.split("\\."); + switch (parts[0]) { + case CODEC_ID_HEV1: + case CODEC_ID_HVC1: + return getHevcProfileAndLevel(codec, parts); + case CODEC_ID_AVC1: + case CODEC_ID_AVC2: + return getAvcProfileAndLevel(codec, parts); + default: + return null; + } + } + + // Internal methods. + private static List getDecoderInfosInternal( CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException { try { @@ -177,12 +226,14 @@ public final class MediaCodecUtil { try { CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType); boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities); + boolean forceDisableAdaptive = codecNeedsDisableAdaptationWorkaround(codecName); if ((secureDecodersExplicit && key.secure == secure) || (!secureDecodersExplicit && !key.secure)) { - decoderInfos.add(MediaCodecInfo.newInstance(codecName, mimeType, capabilities)); + decoderInfos.add(MediaCodecInfo.newInstance(codecName, mimeType, capabilities, + forceDisableAdaptive)); } else if (!secureDecodersExplicit && secure) { decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure", mimeType, - capabilities)); + capabilities, forceDisableAdaptive)); // It only makes sense to have one synthesized secure decoder, return immediately. return decoderInfos; } @@ -289,50 +340,16 @@ public final class MediaCodecUtil { } /** - * Returns the maximum frame size supported by the default H264 decoder. + * Returns whether the decoder is known to fail when adapting, despite advertising itself as an + * adaptive decoder. * - * @return The maximum frame size for an H264 stream that can be decoded on the device. + * @param name The decoder name. + * @return True if the decoder is known to fail when adapting. */ - public static int maxH264DecodableFrameSize() throws DecoderQueryException { - if (maxH264DecodableFrameSize == -1) { - int result = 0; - MediaCodecInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false); - if (decoderInfo != null) { - for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) { - result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result); - } - // We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are - // the levels mandated by the Android CDD. - result = Math.max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360)); - } - maxH264DecodableFrameSize = result; - } - return maxH264DecodableFrameSize; - } - - /** - * Returns profile and level (as defined by {@link CodecProfileLevel}) corresponding to the given - * codec description string (as defined by RFC 6381). - * - * @param codec A codec description string, as defined by RFC 6381. - * @return A pair (profile constant, level constant) if {@code codec} is well-formed and - * recognized, or null otherwise - */ - public static Pair getCodecProfileAndLevel(String codec) { - if (codec == null) { - return null; - } - String[] parts = codec.split("\\."); - switch (parts[0]) { - case CODEC_ID_HEV1: - case CODEC_ID_HVC1: - return getHevcProfileAndLevel(codec, parts); - case CODEC_ID_AVC1: - case CODEC_ID_AVC2: - return getAvcProfileAndLevel(codec, parts); - default: - return null; - } + private static boolean codecNeedsDisableAdaptationWorkaround(String name) { + return Util.SDK_INT <= 22 + && (Util.MODEL.equals("ODROID-XU3") || Util.MODEL.equals("Nexus 10")) + && ("OMX.Exynos.AVC.Decoder".equals(name) || "OMX.Exynos.AVC.Decoder.secure".equals(name)); } private static Pair getHevcProfileAndLevel(String codec, String[] parts) { From 9796a096983a55d95e29bc5f661489bca98bc9d6 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 2 Jun 2017 09:48:58 -0700 Subject: [PATCH 048/133] Assume CBR for MP3s with Info headers Issue: #2895 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157841519 --- .../extractor/mp3/Mp3Extractor.java | 118 +++++++++++------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index b0faad71c0..6e114137f1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -87,10 +87,12 @@ public final class Mp3Extractor implements Extractor { /** * Mask that includes the audio header values that must match between frames. */ - private static final int HEADER_MASK = 0xFFFE0C00; - private static final int XING_HEADER = Util.getIntegerCodeForString("Xing"); - private static final int INFO_HEADER = Util.getIntegerCodeForString("Info"); - private static final int VBRI_HEADER = Util.getIntegerCodeForString("VBRI"); + private static final int MPEG_AUDIO_HEADER_MASK = 0xFFFE0C00; + + private static final int SEEK_HEADER_XING = Util.getIntegerCodeForString("Xing"); + private static final int SEEK_HEADER_INFO = Util.getIntegerCodeForString("Info"); + private static final int SEEK_HEADER_VBRI = Util.getIntegerCodeForString("VBRI"); + private static final int SEEK_HEADER_UNSET = 0; @Flags private final int flags; private final long forcedFirstSampleTimestampUs; @@ -178,7 +180,11 @@ public final class Mp3Extractor implements Extractor { } } if (seeker == null) { - seeker = setupSeeker(input); + seeker = maybeReadSeekFrame(input); + if (seeker == null + || (!seeker.isSeekable() && (flags & FLAG_ENABLE_CONSTANT_BITRATE_SEEKING) != 0)) { + seeker = getConstantBitrateSeeker(input); + } extractorOutput.seekMap(seeker); trackOutput.format(Format.createAudioSampleFormat(null, synchronizedHeader.mimeType, null, Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, synchronizedHeader.channels, @@ -197,7 +203,7 @@ public final class Mp3Extractor implements Extractor { } scratch.setPosition(0); int sampleHeaderData = scratch.readInt(); - if ((sampleHeaderData & HEADER_MASK) != (synchronizedHeaderData & HEADER_MASK) + if (!headersMatch(sampleHeaderData, synchronizedHeaderData) || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) { // We have lost synchronization, so attempt to resynchronize starting at the next byte. extractorInput.skipFully(1); @@ -254,7 +260,7 @@ public final class Mp3Extractor implements Extractor { int headerData = scratch.readInt(); int frameSize; if ((candidateSynchronizedHeaderData != 0 - && (headerData & HEADER_MASK) != (candidateSynchronizedHeaderData & HEADER_MASK)) + && !headersMatch(headerData, candidateSynchronizedHeaderData)) || (frameSize = MpegAudioHeader.getFrameSize(headerData)) == C.LENGTH_UNSET) { // The header doesn't match the candidate header or is invalid. Try the next byte offset. if (searchedBytes++ == searchLimitBytes) { @@ -337,37 +343,27 @@ public final class Mp3Extractor implements Extractor { } /** - * Returns a {@link Seeker} to seek using metadata read from {@code input}, which should provide - * data from the start of the first frame in the stream. On returning, the input's position will - * be set to the start of the first frame of audio. + * Consumes the next frame from the {@code input} if it contains VBRI or Xing seeking metadata, + * returning a {@link Seeker} if the metadata was present and valid, or {@code null} otherwise. + * After this method returns, the input position is the start of the first frame of audio. * * @param input The {@link ExtractorInput} from which to read. + * @return A {@link Seeker} if seeking metadata was present and valid, or {@code null} otherwise. * @throws IOException Thrown if there was an error reading from the stream. Not expected if the * next two frames were already peeked during synchronization. * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if * the next two frames were already peeked during synchronization. - * @return a {@link Seeker}. */ - private Seeker setupSeeker(ExtractorInput input) throws IOException, InterruptedException { - // Read the first frame which may contain a Xing or VBRI header with seeking metadata. + private Seeker maybeReadSeekFrame(ExtractorInput input) throws IOException, InterruptedException { ParsableByteArray frame = new ParsableByteArray(synchronizedHeader.frameSize); input.peekFully(frame.data, 0, synchronizedHeader.frameSize); - - long position = input.getPosition(); - long length = input.getLength(); - int headerData = 0; - Seeker seeker = null; - - // Check if there is a Xing header. int xingBase = (synchronizedHeader.version & 1) != 0 ? (synchronizedHeader.channels != 1 ? 36 : 21) // MPEG 1 : (synchronizedHeader.channels != 1 ? 21 : 13); // MPEG 2 or 2.5 - if (frame.limit() >= xingBase + 4) { - frame.setPosition(xingBase); - headerData = frame.readInt(); - } - if (headerData == XING_HEADER || headerData == INFO_HEADER) { - seeker = XingSeeker.create(synchronizedHeader, frame, position, length); + int seekHeader = getSeekFrameHeader(frame, xingBase); + Seeker seeker; + if (seekHeader == SEEK_HEADER_XING || seekHeader == SEEK_HEADER_INFO) { + seeker = XingSeeker.create(synchronizedHeader, frame, input.getPosition(), input.getLength()); if (seeker != null && !gaplessInfoHolder.hasGaplessInfo()) { // If there is a Xing header, read gapless playback metadata at a fixed offset. input.resetPeekPosition(); @@ -377,28 +373,60 @@ public final class Mp3Extractor implements Extractor { gaplessInfoHolder.setFromXingHeaderValue(scratch.readUnsignedInt24()); } input.skipFully(synchronizedHeader.frameSize); - } else if (frame.limit() >= 40) { - // Check if there is a VBRI header. - frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes. - headerData = frame.readInt(); - if (headerData == VBRI_HEADER) { - seeker = VbriSeeker.create(synchronizedHeader, frame, position, length); - input.skipFully(synchronizedHeader.frameSize); + if (seeker != null && !seeker.isSeekable() && seekHeader == SEEK_HEADER_INFO) { + // Fall back to constant bitrate seeking for Info headers missing a table of contents. + return getConstantBitrateSeeker(input); + } + } else if (seekHeader == SEEK_HEADER_VBRI) { + seeker = VbriSeeker.create(synchronizedHeader, frame, input.getPosition(), input.getLength()); + input.skipFully(synchronizedHeader.frameSize); + } else { // seekerHeader == SEEK_HEADER_UNSET + // This frame doesn't contain seeking information, so reset the peek position. + seeker = null; + input.resetPeekPosition(); + } + return seeker; + } + + /** + * Peeks the next frame and returns a {@link ConstantBitrateSeeker} based on its bitrate. + */ + private Seeker getConstantBitrateSeeker(ExtractorInput input) + throws IOException, InterruptedException { + input.peekFully(scratch.data, 0, 4); + scratch.setPosition(0); + MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader); + return new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate, + input.getLength()); + } + + /** + * Returns whether the headers match in those bits masked by {@link #MPEG_AUDIO_HEADER_MASK}. + */ + private static boolean headersMatch(int headerA, long headerB) { + return (headerA & MPEG_AUDIO_HEADER_MASK) == (headerB & MPEG_AUDIO_HEADER_MASK); + } + + /** + * Returns {@link #SEEK_HEADER_XING}, {@link #SEEK_HEADER_INFO} or {@link #SEEK_HEADER_VBRI} if + * the provided {@code frame} may have seeking metadata, or {@link #SEEK_HEADER_UNSET} otherwise. + * If seeking metadata is present, {@code frame}'s position is advanced past the header. + */ + private static int getSeekFrameHeader(ParsableByteArray frame, int xingBase) { + if (frame.limit() >= xingBase + 4) { + frame.setPosition(xingBase); + int headerData = frame.readInt(); + if (headerData == SEEK_HEADER_XING || headerData == SEEK_HEADER_INFO) { + return headerData; } } - - if (seeker == null || (!seeker.isSeekable() - && (flags & FLAG_ENABLE_CONSTANT_BITRATE_SEEKING) != 0)) { - // Repopulate the synchronized header in case we had to skip an invalid seeking header, which - // would give an invalid CBR bitrate. - input.resetPeekPosition(); - input.peekFully(scratch.data, 0, 4); - scratch.setPosition(0); - MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader); - seeker = new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate, length); + if (frame.limit() >= 40) { + frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes. + if (frame.readInt() == SEEK_HEADER_VBRI) { + return SEEK_HEADER_VBRI; + } } - - return seeker; + return SEEK_HEADER_UNSET; } /** From cc748c30c72eb5b5c421de157358c8da8be999e9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Sun, 4 Jun 2017 07:02:13 -0700 Subject: [PATCH 049/133] Add a null check in DummySurface static initializer ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157958694 --- .../java/com/google/android/exoplayer2/video/DummySurface.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java b/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java index 5298c82f61..23d9941cf3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java @@ -76,7 +76,7 @@ public final class DummySurface extends Surface { if (Util.SDK_INT >= 17) { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); String extensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS); - SECURE_SUPPORTED = extensions.contains("EGL_EXT_protected_content"); + SECURE_SUPPORTED = extensions != null && extensions.contains("EGL_EXT_protected_content"); } else { SECURE_SUPPORTED = false; } From df5e75b76c236e941c24dbc4f769cdc1fd369a9a Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Mon, 5 Jun 2017 05:28:01 -0700 Subject: [PATCH 050/133] Pick the lowest quality video when capabilities are exceeded Issue:#2901 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158006727 --- .../exoplayer2/trackselection/DefaultTrackSelector.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index b37088e588..2a426c9c52 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -639,7 +639,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { continue; } int trackScore = isWithinConstraints ? 2 : 1; - if (isSupported(trackFormatSupport[trackIndex], false)) { + boolean isWithinCapabilities = isSupported(trackFormatSupport[trackIndex], false); + if (isWithinCapabilities) { trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS; } boolean selectTrack = trackScore > selectedTrackScore; @@ -655,7 +656,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } else { comparisonResult = compareFormatValues(format.bitrate, selectedBitrate); } - selectTrack = isWithinConstraints ? comparisonResult > 0 : comparisonResult < 0; + selectTrack = isWithinCapabilities && isWithinConstraints + ? comparisonResult > 0 : comparisonResult < 0; } if (selectTrack) { selectedGroup = trackGroup; From 8241bb8a6e5f91e0dfa70800f8e187cfd9318c23 Mon Sep 17 00:00:00 2001 From: hoangtc Date: Tue, 6 Jun 2017 06:58:07 -0700 Subject: [PATCH 051/133] Fix a minor bug with AdaptiveTrackSelection. When updating track selection, we should only revert back from ideal track selection to current track selection if the currently selected track is not black-listed. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158135644 --- .../exoplayer2/trackselection/AdaptiveTrackSelection.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java index dc78e28e56..50eaaa02e3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java @@ -156,13 +156,13 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { long nowMs = SystemClock.elapsedRealtime(); // Get the current and ideal selections. int currentSelectedIndex = selectedIndex; - Format currentFormat = getSelectedFormat(); int idealSelectedIndex = determineIdealSelectedIndex(nowMs); - Format idealFormat = getFormat(idealSelectedIndex); // Assume we can switch to the ideal selection. selectedIndex = idealSelectedIndex; // Revert back to the current selection if conditions are not suitable for switching. - if (currentFormat != null && !isBlacklisted(selectedIndex, nowMs)) { + if (!isBlacklisted(currentSelectedIndex, nowMs)) { + Format currentFormat = getFormat(currentSelectedIndex); + Format idealFormat = getFormat(idealSelectedIndex); if (idealFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { // The ideal track is a higher quality, but we have insufficient buffer to safely switch From 79048ffae67c44e08330bd3b9328ca0f95267cc9 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Jun 2017 07:31:01 -0700 Subject: [PATCH 052/133] Further cleanup of updateSelectedTrack - Return early if the selection is unchanged. - Remove unnecessary variables. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158138187 --- .../AdaptiveTrackSelection.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java index 50eaaa02e3..12f5952dd0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java @@ -154,23 +154,24 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { @Override public void updateSelectedTrack(long bufferedDurationUs) { long nowMs = SystemClock.elapsedRealtime(); - // Get the current and ideal selections. + // Stash the current selection, then make a new one. int currentSelectedIndex = selectedIndex; - int idealSelectedIndex = determineIdealSelectedIndex(nowMs); - // Assume we can switch to the ideal selection. - selectedIndex = idealSelectedIndex; - // Revert back to the current selection if conditions are not suitable for switching. + selectedIndex = determineIdealSelectedIndex(nowMs); + if (selectedIndex == currentSelectedIndex) { + return; + } if (!isBlacklisted(currentSelectedIndex, nowMs)) { + // Revert back to the current selection if conditions are not suitable for switching. Format currentFormat = getFormat(currentSelectedIndex); - Format idealFormat = getFormat(idealSelectedIndex); - if (idealFormat.bitrate > currentFormat.bitrate + Format selectedFormat = getFormat(selectedIndex); + if (selectedFormat.bitrate > currentFormat.bitrate && bufferedDurationUs < minDurationForQualityIncreaseUs) { - // The ideal track is a higher quality, but we have insufficient buffer to safely switch + // The selected track is a higher quality, but we have insufficient buffer to safely switch // up. Defer switching up for now. selectedIndex = currentSelectedIndex; - } else if (idealFormat.bitrate < currentFormat.bitrate + } else if (selectedFormat.bitrate < currentFormat.bitrate && bufferedDurationUs >= maxDurationForQualityDecreaseUs) { - // The ideal track is a lower quality, but we have sufficient buffer to defer switching + // The selected track is a lower quality, but we have sufficient buffer to defer switching // down for now. selectedIndex = currentSelectedIndex; } From a0c884849e501e110b04cd9cfc363119985791fc Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 6 Jun 2017 08:03:04 -0700 Subject: [PATCH 053/133] For HLS mode, pick the lowest PID track for each track type This prevents strange behaviors for streams that changes the track declaration order in the PMT. NOTE: This should not change ANY behavior other than the one described above. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158140890 --- .../exoplayer2/extractor/ts/TsExtractor.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 71b8375bd8..7b63ce813c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -382,10 +382,14 @@ public final class TsExtractor implements Extractor { private static final int TS_PMT_DESC_DVBSUBS = 0x59; private final ParsableBitArray pmtScratch; + private final SparseArray trackIdToReaderScratch; + private final SparseIntArray trackIdToPidScratch; private final int pid; public PmtReader(int pid) { pmtScratch = new ParsableBitArray(new byte[5]); + trackIdToReaderScratch = new SparseArray<>(); + trackIdToPidScratch = new SparseIntArray(); this.pid = pid; } @@ -436,6 +440,8 @@ public final class TsExtractor implements Extractor { new TrackIdGenerator(programNumber, TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE)); } + trackIdToReaderScratch.clear(); + trackIdToPidScratch.clear(); int remainingEntriesLength = sectionData.bytesLeft(); while (remainingEntriesLength > 0) { sectionData.readBytes(pmtScratch, 5); @@ -454,23 +460,30 @@ public final class TsExtractor implements Extractor { if (trackIds.get(trackId)) { continue; } - trackIds.put(trackId, true); - TsPayloadReader reader; - if (mode == MODE_HLS && streamType == TS_STREAM_TYPE_ID3) { - reader = id3Reader; - } else { - reader = payloadReaderFactory.createPayloadReader(streamType, esInfo); - if (reader != null) { + TsPayloadReader reader = mode == MODE_HLS && streamType == TS_STREAM_TYPE_ID3 ? id3Reader + : payloadReaderFactory.createPayloadReader(streamType, esInfo); + if (mode != MODE_HLS + || elementaryPid < trackIdToPidScratch.get(trackId, MAX_PID_PLUS_ONE)) { + trackIdToPidScratch.put(trackId, elementaryPid); + trackIdToReaderScratch.put(trackId, reader); + } + } + + int trackIdCount = trackIdToPidScratch.size(); + for (int i = 0; i < trackIdCount; i++) { + int trackId = trackIdToPidScratch.keyAt(i); + trackIds.put(trackId, true); + TsPayloadReader reader = trackIdToReaderScratch.valueAt(i); + if (reader != null) { + if (reader != id3Reader) { reader.init(timestampAdjuster, output, new TrackIdGenerator(programNumber, trackId, MAX_PID_PLUS_ONE)); } - } - - if (reader != null) { - tsPayloadReaders.put(elementaryPid, reader); + tsPayloadReaders.put(trackIdToPidScratch.valueAt(i), reader); } } + if (mode == MODE_HLS) { if (!tracksEnded) { output.endTracks(); From 643083194d2e520a443c24778f9a570d14d3787d Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Jun 2017 08:15:25 -0700 Subject: [PATCH 054/133] Pass non-null logger into DefaultDrmSessionManager Issue: #2903 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158142226 --- .../android/exoplayer2/demo/PlayerActivity.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 34e0365933..d0703f3496 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -234,6 +234,13 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay Intent intent = getIntent(); boolean needNewPlayer = player == null; if (needNewPlayer) { + TrackSelection.Factory adaptiveTrackSelectionFactory = + new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); + trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory); + trackSelectionHelper = new TrackSelectionHelper(trackSelector, adaptiveTrackSelectionFactory); + lastSeenTrackGroupArray = null; + eventLogger = new EventLogger(trackSelector); + UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA) ? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null; DrmSessionManager drmSessionManager = null; @@ -261,16 +268,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this, drmSessionManager, extensionRendererMode); - TrackSelection.Factory adaptiveTrackSelectionFactory = - new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); - trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory); - trackSelectionHelper = new TrackSelectionHelper(trackSelector, adaptiveTrackSelectionFactory); - lastSeenTrackGroupArray = null; - player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector); player.addListener(this); - - eventLogger = new EventLogger(trackSelector); player.addListener(eventLogger); player.setAudioDebugListener(eventLogger); player.setVideoDebugListener(eventLogger); From 1ac8420b7fea5e695bbf4dce3b373b7c00b34d6d Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Jun 2017 08:21:38 -0700 Subject: [PATCH 055/133] Constraint buffered percentage to [0,100] Issue: #2902 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158142754 --- .../java/com/google/android/exoplayer2/ExoPlayerImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index cb0958a3b1..c70d729fc3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -305,10 +305,10 @@ import java.util.concurrent.CopyOnWriteArraySet; if (timeline.isEmpty()) { return 0; } - long bufferedPosition = getBufferedPosition(); + long position = getBufferedPosition(); long duration = getDuration(); - return (bufferedPosition == C.TIME_UNSET || duration == C.TIME_UNSET) ? 0 - : (int) (duration == 0 ? 100 : (bufferedPosition * 100) / duration); + return position == C.TIME_UNSET || duration == C.TIME_UNSET ? 0 + : (duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100)); } @Override From fb7cb507ea6440a12b674a2e38a3d94fca833cf1 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Jun 2017 09:29:24 -0700 Subject: [PATCH 056/133] Expose current scrubber position through onScrubStart Issue: #2910 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158149904 --- .../java/com/google/android/exoplayer2/ui/DefaultTimeBar.java | 2 +- .../com/google/android/exoplayer2/ui/PlaybackControlView.java | 2 +- .../main/java/com/google/android/exoplayer2/ui/TimeBar.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index d9754420bf..4ede786175 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -442,7 +442,7 @@ public class DefaultTimeBar extends View implements TimeBar { parent.requestDisallowInterceptTouchEvent(true); } if (listener != null) { - listener.onScrubStart(this); + listener.onScrubStart(this, getScrubberPosition()); } } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index ce2e81020f..5f88f3a241 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -875,7 +875,7 @@ public class PlaybackControlView extends FrameLayout { OnClickListener { @Override - public void onScrubStart(TimeBar timeBar) { + public void onScrubStart(TimeBar timeBar, long position) { removeCallbacks(hideAction); scrubbing = true; } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TimeBar.java index aeb8e0255e..2fd5bff5eb 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TimeBar.java @@ -95,8 +95,9 @@ public interface TimeBar { * Called when the user starts moving the scrubber. * * @param timeBar The time bar. + * @param position The position of the scrubber, in milliseconds. */ - void onScrubStart(TimeBar timeBar); + void onScrubStart(TimeBar timeBar, long position); /** * Called when the user moves the scrubber. From df99922ac13d273ac51d982b733ea5c771f2e155 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 6 Jun 2017 10:03:08 -0700 Subject: [PATCH 057/133] Bump version + update release notes ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158153988 --- RELEASENOTES.md | 14 ++++++++++++++ build.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../android/exoplayer2/ExoPlayerLibraryInfo.java | 6 +++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6a1defa809..4f147e2bbd 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,19 @@ # Release notes # +### r2.4.2 ### + +* Stability: Work around Nexus 10 reboot when playing certain content + ([2806](https://github.com/google/ExoPlayer/issues/2806)). +* MP3: Correctly treat MP3s with INFO headers as constant bitrate + ([2895](https://github.com/google/ExoPlayer/issues/2895)). +* HLS: Use average rather than peak bandwidth when available + ([#2863](https://github.com/google/ExoPlayer/issues/2863)). +* SmoothStreaming: Fix timeline for live streams + ([#2760](https://github.com/google/ExoPlayer/issues/2760)). +* UI: Fix DefaultTimeBar invalidation + ([#2871](https://github.com/google/ExoPlayer/issues/2871)). +* Misc bugfixes. + ### r2.4.1 ### * Stability: Avoid OutOfMemoryError in extractors when parsing malformed media diff --git a/build.gradle b/build.gradle index 258b11d2e6..4f18e7c801 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.4.1' + releaseVersion = 'r2.4.2' releaseWebsite = 'https://github.com/google/ExoPlayer' } if (it.hasProperty('externalBuildDir')) { diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 1bb859028d..34256d41c1 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2402" + android:versionName="2.4.2"> diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 23c2ddbde9..c6fc139208 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -24,13 +24,13 @@ public interface 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. - String VERSION = "2.4.1"; + String VERSION = "2.4.2"; /** * The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - String VERSION_SLASHY = "ExoPlayerLib/2.4.1"; + String VERSION_SLASHY = "ExoPlayerLib/2.4.2"; /** * The version of the library expressed as an integer, for example 1002003. @@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - int VERSION_INT = 2004001; + int VERSION_INT = 2004002; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From 4510abf278bd5a8bf8eb2e8b310e701080cdce07 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 7 Jun 2017 08:01:28 -0700 Subject: [PATCH 058/133] Update handled schemes for timing element resolution. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158269487 --- .../exoplayer2/source/dash/DashMediaSource.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 5ab04ea7be..cd995f3739 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -410,12 +410,14 @@ public final class DashMediaSource implements MediaSource { private void resolveUtcTimingElement(UtcTimingElement timingElement) { String scheme = timingElement.schemeIdUri; - if (Util.areEqual(scheme, "urn:mpeg:dash:utc:direct:2012")) { + if (Util.areEqual(scheme, "urn:mpeg:dash:utc:direct:2014") + || Util.areEqual(scheme, "urn:mpeg:dash:utc:direct:2012")) { resolveUtcTimingElementDirect(timingElement); - } else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-iso:2014")) { + } else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-iso:2014") + || Util.areEqual(scheme, "urn:mpeg:dash:utc:http-iso:2012")) { resolveUtcTimingElementHttp(timingElement, new Iso8601Parser()); - } else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2012") - || Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2014")) { + } else if (Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2014") + || Util.areEqual(scheme, "urn:mpeg:dash:utc:http-xsdate:2012")) { resolveUtcTimingElementHttp(timingElement, new XsDateTimeParser()); } else { // Unsupported scheme. From 80be637dcc6c13110f157e07e7c28ba8d2d05b26 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 12 Jun 2017 01:37:01 -0700 Subject: [PATCH 059/133] Allow overriding of getCodecMaxValues ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158686545 --- .../android/exoplayer2/video/MediaCodecVideoRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index dd0c5356ea..0304c33b3c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -650,7 +650,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { * @return Suitable {@link CodecMaxValues}. * @throws DecoderQueryException If an error occurs querying {@code codecInfo}. */ - private static CodecMaxValues getCodecMaxValues(MediaCodecInfo codecInfo, Format format, + protected CodecMaxValues getCodecMaxValues(MediaCodecInfo codecInfo, Format format, Format[] streamFormats) throws DecoderQueryException { int maxWidth = format.width; int maxHeight = format.height; @@ -838,7 +838,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { return format.rotationDegrees == Format.NO_VALUE ? 0 : format.rotationDegrees; } - private static final class CodecMaxValues { + protected static final class CodecMaxValues { public final int width; public final int height; From 4e578a1b2164c4c0a358f6ae28153383e133473f Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 14 Jun 2017 03:34:19 -0700 Subject: [PATCH 060/133] Increase MP3 sniffing distance Issue: #2951 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158960483 --- .../google/android/exoplayer2/extractor/mp3/Mp3Extractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index 6e114137f1..8d33f95640 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -78,7 +78,7 @@ public final class Mp3Extractor implements Extractor { /** * The maximum number of bytes to peek when sniffing, excluding the ID3 header, before giving up. */ - private static final int MAX_SNIFF_BYTES = MpegAudioHeader.MAX_FRAME_SIZE_BYTES; + private static final int MAX_SNIFF_BYTES = 16 * 1024; /** * Maximum length of data read into {@link #scratch}. */ From facfa5267787fd51e3ab04b89ad13c763e8cbc41 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 14 Jun 2017 07:56:37 -0700 Subject: [PATCH 061/133] Log frame counts when we see a spurious audio timestamp ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=158977741 --- .../java/com/google/android/exoplayer2/audio/AudioTrack.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java index 44a96373f3..92838e34b0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java @@ -1292,7 +1292,7 @@ public final class AudioTrack { // The timestamp time base is probably wrong. String message = "Spurious audio timestamp (system clock mismatch): " + audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", " - + playbackPositionUs; + + playbackPositionUs + ", " + getSubmittedFrames() + ", " + getWrittenFrames(); if (failOnSpuriousAudioTimestamp) { throw new InvalidAudioTrackTimestampException(message); } @@ -1303,7 +1303,7 @@ public final class AudioTrack { // The timestamp frame position is probably wrong. String message = "Spurious audio timestamp (frame position mismatch): " + audioTimestampFramePosition + ", " + audioTimestampUs + ", " + systemClockUs + ", " - + playbackPositionUs; + + playbackPositionUs + ", " + getSubmittedFrames() + ", " + getWrittenFrames(); if (failOnSpuriousAudioTimestamp) { throw new InvalidAudioTrackTimestampException(message); } From e8ee868a9f65a1e9013dee2729b46df9aa54a83b Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 15 Jun 2017 02:36:46 -0700 Subject: [PATCH 062/133] Add support for mono input to the GVR extension Issue: #2710 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159082518 --- extensions/gvr/build.gradle | 2 +- .../google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/gvr/build.gradle b/extensions/gvr/build.gradle index f622a73758..e15c8b1ad8 100644 --- a/extensions/gvr/build.gradle +++ b/extensions/gvr/build.gradle @@ -25,7 +25,7 @@ android { dependencies { compile project(':library-core') - compile 'com.google.vr:sdk-audio:1.30.0' + compile 'com.google.vr:sdk-audio:1.60.1' } ext { diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java index 980424904d..a56bc7f0a9 100644 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java +++ b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java @@ -82,6 +82,9 @@ public final class GvrAudioProcessor implements AudioProcessor { maybeReleaseGvrAudioSurround(); int surroundFormat; switch (channelCount) { + case 1: + surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_MONO; + break; case 2: surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO; break; From e618080c7385e9ddf7217f668fd5635c86e34dec Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 7 Apr 2017 07:08:04 +0100 Subject: [PATCH 063/133] Adjust incorrect looking max-channel counts Issue: #2940 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159099602 --- .../extractor/flv/AudioTagPayloadReader.java | 2 +- .../exoplayer2/mediacodec/MediaCodecInfo.java | 38 ++++++++++++++++++- .../android/exoplayer2/util/MimeTypes.java | 5 ++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java index 8e3bd08375..2f21898007 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/AudioTagPayloadReader.java @@ -67,7 +67,7 @@ import java.util.Collections; hasOutputFormat = true; } else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) { String type = audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW - : MimeTypes.AUDIO_ULAW; + : MimeTypes.AUDIO_MLAW; int pcmEncoding = (header & 0x01) == 1 ? C.ENCODING_PCM_16BIT : C.ENCODING_PCM_8BIT; Format format = Format.createAudioSampleFormat(null, type, null, Format.NO_VALUE, Format.NO_VALUE, 1, 8000, pcmEncoding, null, null, 0, null); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java index 3c788a60a4..a7c237edb2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java @@ -264,7 +264,9 @@ public final class MediaCodecInfo { logNoSupport("channelCount.aCaps"); return false; } - if (audioCapabilities.getMaxInputChannelCount() < channelCount) { + int maxInputChannelCount = adjustMaxInputChannelCount(name, mimeType, + audioCapabilities.getMaxInputChannelCount()); + if (maxInputChannelCount < channelCount) { logNoSupport("channelCount.support, " + channelCount); return false; } @@ -281,6 +283,40 @@ public final class MediaCodecInfo { + Util.DEVICE_DEBUG_INFO + "]"); } + private static int adjustMaxInputChannelCount(String name, String mimeType, int maxChannelCount) { + if (maxChannelCount > 1 || (Util.SDK_INT >= 26 && maxChannelCount > 0)) { + // The maximum channel count looks like it's been set correctly. + return maxChannelCount; + } + if (MimeTypes.AUDIO_MPEG.equals(mimeType) + || MimeTypes.AUDIO_AMR_NB.equals(mimeType) + || MimeTypes.AUDIO_AMR_WB.equals(mimeType) + || MimeTypes.AUDIO_AAC.equals(mimeType) + || MimeTypes.AUDIO_VORBIS.equals(mimeType) + || MimeTypes.AUDIO_OPUS.equals(mimeType) + || MimeTypes.AUDIO_RAW.equals(mimeType) + || MimeTypes.AUDIO_FLAC.equals(mimeType) + || MimeTypes.AUDIO_ALAW.equals(mimeType) + || MimeTypes.AUDIO_MLAW.equals(mimeType) + || MimeTypes.AUDIO_MSGSM.equals(mimeType)) { + // Platform code should have set a default. + return maxChannelCount; + } + // The maximum channel count looks incorrect. Adjust it to an assumed default. + int assumedMaxChannelCount; + if (MimeTypes.AUDIO_AC3.equals(mimeType)) { + assumedMaxChannelCount = 6; + } else if (MimeTypes.AUDIO_E_AC3.equals(mimeType)) { + assumedMaxChannelCount = 16; + } else { + // Default to the platform limit, which is 30. + assumedMaxChannelCount = 30; + } + Log.w(TAG, "AssumedMaxChannelAdjustment: " + name + ", [" + maxChannelCount + " to " + + assumedMaxChannelCount + "]"); + return assumedMaxChannelCount; + } + private static boolean isAdaptive(CodecCapabilities capabilities) { return Util.SDK_INT >= 19 && isAdaptiveV19(capabilities); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index e227ea1068..db1122dbe7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -48,7 +48,7 @@ public final class MimeTypes { public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2"; public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw"; public static final String AUDIO_ALAW = BASE_TYPE_AUDIO + "/g711-alaw"; - public static final String AUDIO_ULAW = BASE_TYPE_AUDIO + "/g711-mlaw"; + public static final String AUDIO_MLAW = BASE_TYPE_AUDIO + "/g711-mlaw"; public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3"; public static final String AUDIO_E_AC3 = BASE_TYPE_AUDIO + "/eac3"; public static final String AUDIO_TRUEHD = BASE_TYPE_AUDIO + "/true-hd"; @@ -59,8 +59,9 @@ public final class MimeTypes { public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus"; public static final String AUDIO_AMR_NB = BASE_TYPE_AUDIO + "/3gpp"; public static final String AUDIO_AMR_WB = BASE_TYPE_AUDIO + "/amr-wb"; - public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/x-flac"; + public static final String AUDIO_FLAC = BASE_TYPE_AUDIO + "/flac"; public static final String AUDIO_ALAC = BASE_TYPE_AUDIO + "/alac"; + public static final String AUDIO_MSGSM = BASE_TYPE_AUDIO + "/gsm"; public static final String AUDIO_UNKNOWN = BASE_TYPE_AUDIO + "/x-unknown"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; From 92206b9fc2434027cd524be1901c13b8131d5eeb Mon Sep 17 00:00:00 2001 From: olly Date: Sun, 7 May 2017 05:37:26 +0100 Subject: [PATCH 064/133] TTML: Ignore regions that don't declare origin and extent Issue: #2953 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159218386 --- .../exoplayer2/text/ttml/TtmlDecoderTest.java | 10 ++++++--- .../exoplayer2/text/ttml/TtmlDecoder.java | 22 +++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java index 496e3f87de..492cf036b4 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java @@ -179,9 +179,13 @@ public final class TtmlDecoderTest extends InstrumentationTestCase { assertEquals(1, output.size()); ttmlCue = output.get(0); assertEquals("dolor", ttmlCue.text.toString()); - assertEquals(10f / 100f, ttmlCue.position); - assertEquals(80f / 100f, ttmlCue.line); - assertEquals(1f, ttmlCue.size); + assertEquals(Cue.DIMEN_UNSET, ttmlCue.position); + assertEquals(Cue.DIMEN_UNSET, ttmlCue.line); + assertEquals(Cue.DIMEN_UNSET, ttmlCue.size); + // TODO: Should be as below, once https://github.com/google/ExoPlayer/issues/2953 is fixed. + // assertEquals(10f / 100f, ttmlCue.position); + // assertEquals(80f / 100f, ttmlCue.line); + // assertEquals(1f, ttmlCue.size); output = subtitle.getCues(21000000); assertEquals(1, output.size()); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java index 0012ce2c22..e438aa1837 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java @@ -222,9 +222,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { /** * Parses a region declaration. *

    - * If the region defines an origin and/or extent, it is required that they're defined as - * percentages of the viewport. Region declarations that define origin and/or extent in other - * formats are unsupported, and null is returned. + * If the region defines an origin and extent, it is required that they're defined as percentages + * of the viewport. Region declarations that define origin and extent in other formats are + * unsupported, and null is returned. */ private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser) { String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID); @@ -250,9 +250,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { return null; } } else { + Log.w(TAG, "Ignoring region without an origin"); + return null; + // TODO: Should default to top left as below in this case, but need to fix + // https://github.com/google/ExoPlayer/issues/2953 first. // Origin is omitted. Default to top left. - position = 0; - line = 0; + // position = 0; + // line = 0; } float width; @@ -273,9 +277,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { return null; } } else { + Log.w(TAG, "Ignoring region without an extent"); + return null; + // TODO: Should default to extent of parent as below in this case, but need to fix + // https://github.com/google/ExoPlayer/issues/2953 first. // Extent is omitted. Default to extent of parent. - width = 1; - height = 1; + // width = 1; + // height = 1; } @Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START; From 76faa5b6d2d271474bcae6ee106177aa53de3ed3 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 19 Jun 2017 06:48:18 -0700 Subject: [PATCH 065/133] Move clearing of joining deadline back to onStopped ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159421000 --- .../google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java | 2 +- .../android/exoplayer2/video/MediaCodecVideoRenderer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index 4b629c8d2a..29423547b6 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -480,11 +480,11 @@ public final class LibvpxVideoRenderer extends BaseRenderer { protected void onStarted() { droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); - joiningDeadlineMs = C.TIME_UNSET; } @Override protected void onStopped() { + joiningDeadlineMs = C.TIME_UNSET; maybeNotifyDroppedFrames(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 0304c33b3c..990a29dc4e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -260,11 +260,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { super.onStarted(); droppedFrames = 0; droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); - joiningDeadlineMs = C.TIME_UNSET; } @Override protected void onStopped() { + joiningDeadlineMs = C.TIME_UNSET; maybeNotifyDroppedFrames(); super.onStopped(); } From 4b8cddbefd7f212b65b78c612ae7b12053bdb882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Wr=C3=B3tniak?= Date: Sat, 17 Jun 2017 16:18:43 +0200 Subject: [PATCH 066/133] Introduced failing unit test for ContentDataSource --- .../core/src/androidTest/AndroidManifest.xml | 3 + .../upstream/AndroidDataSourceTest.java | 31 +++++++++ .../exoplayer2/upstream/TestDataProvider.java | 64 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceTest.java create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java diff --git a/library/core/src/androidTest/AndroidManifest.xml b/library/core/src/androidTest/AndroidManifest.xml index 9eab386b51..8968ae59d6 100644 --- a/library/core/src/androidTest/AndroidManifest.xml +++ b/library/core/src/androidTest/AndroidManifest.xml @@ -24,6 +24,9 @@ android:allowBackup="false" tools:ignore="MissingApplicationIcon,HardcodedDebugMode"> + Date: Sat, 17 Jun 2017 16:26:29 +0200 Subject: [PATCH 067/133] InputStream creation for ContentDataSource changed --- .../google/android/exoplayer2/upstream/ContentDataSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java index f806f47410..5d0d9a80e9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java @@ -71,7 +71,7 @@ public final class ContentDataSource implements DataSource { try { uri = dataSpec.uri; assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r"); - inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor()); + inputStream = assetFileDescriptor.createInputStream(); long skipped = inputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { // We expect the skip to be satisfied in full. If it isn't then we're probably trying to From 66c461e65b57e289189846580ba42672539210e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Wr=C3=B3tniak?= Date: Mon, 19 Jun 2017 12:46:09 +0200 Subject: [PATCH 068/133] Comments from https://github.com/google/ExoPlayer/pull/2963#discussion_r122669328 applied --- .../upstream/AndroidDataSourceConstants.java | 8 +++++ .../upstream/AndroidDataSourceTest.java | 31 ------------------- .../upstream/AssetDataSourceTest.java | 21 +++++++++++++ .../upstream/ContentDataSourceTest.java | 21 +++++++++++++ .../upstream/ContentDataSource.java | 18 ++++++----- 5 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java delete mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceTest.java create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java new file mode 100644 index 0000000000..ad19b7a824 --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java @@ -0,0 +1,8 @@ +package com.google.android.exoplayer2.upstream; + +final class AndroidDataSourceConstants { + static final long SAMPLE_MP4_BYTES = 101597; + static final String SAMPLE_MP4_PATH = "/mp4/sample.mp4"; + + private AndroidDataSourceConstants() {} +} diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceTest.java deleted file mode 100644 index 42dadaf379..0000000000 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.google.android.exoplayer2.upstream; - -import android.content.Context; -import android.net.Uri; -import android.test.InstrumentationTestCase; - -public class AndroidDataSourceTest extends InstrumentationTestCase { - - private static final long SAMPLE_MP4_BYTES = 101597; - private static final String SAMPLE_MP4_PATH = "/mp4/sample.mp4"; - - public void testAssetDataSource() throws Exception { - final Context context = getInstrumentation().getContext(); - AssetDataSource dataSource = new AssetDataSource(context); - Uri assetUri = Uri.parse("file:///android_asset" + SAMPLE_MP4_PATH); - DataSpec dataSpec = new DataSpec(assetUri); - long sourceLengthBytes = dataSource.open(dataSpec); - - assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); - } - - public void testContentDataSource() throws Exception { - Context context = getInstrumentation().getContext(); - ContentDataSource dataSource = new ContentDataSource(context); - Uri contentUri = Uri.parse("content://exoplayer" + SAMPLE_MP4_PATH); - DataSpec dataSpec = new DataSpec(contentUri); - long sourceLengthBytes = dataSource.open(dataSpec); - - assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); - } -} diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java new file mode 100644 index 0000000000..178842bd4d --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java @@ -0,0 +1,21 @@ +package com.google.android.exoplayer2.upstream; + +import android.content.Context; +import android.net.Uri; +import android.test.InstrumentationTestCase; + +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_BYTES; +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_PATH; + +public class AssetDataSourceTest extends InstrumentationTestCase { + + public void testAssetDataSource() throws Exception { + final Context context = getInstrumentation().getContext(); + AssetDataSource dataSource = new AssetDataSource(context); + Uri assetUri = Uri.parse("file:///android_asset" + SAMPLE_MP4_PATH); + DataSpec dataSpec = new DataSpec(assetUri); + long sourceLengthBytes = dataSource.open(dataSpec); + + assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); + } +} diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java new file mode 100644 index 0000000000..b2edeea0cc --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -0,0 +1,21 @@ +package com.google.android.exoplayer2.upstream; + +import android.content.Context; +import android.net.Uri; +import android.test.InstrumentationTestCase; + +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_BYTES; +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_PATH; + +public class ContentDataSourceTest extends InstrumentationTestCase { + + public void testContentDataSource() throws Exception { + Context context = getInstrumentation().getContext(); + ContentDataSource dataSource = new ContentDataSource(context); + Uri contentUri = Uri.parse("content://exoplayer" + SAMPLE_MP4_PATH); + DataSpec dataSpec = new DataSpec(contentUri); + long sourceLengthBytes = dataSource.open(dataSpec); + + assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java index 5d0d9a80e9..9421b7ba03 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java @@ -71,7 +71,7 @@ public final class ContentDataSource implements DataSource { try { uri = dataSpec.uri; assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r"); - inputStream = assetFileDescriptor.createInputStream(); + inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor()); long skipped = inputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { // We expect the skip to be satisfied in full. If it isn't then we're probably trying to @@ -81,12 +81,16 @@ public final class ContentDataSource implements DataSource { if (dataSpec.length != C.LENGTH_UNSET) { bytesRemaining = dataSpec.length; } else { - bytesRemaining = inputStream.available(); - if (bytesRemaining == 0) { - // FileInputStream.available() returns 0 if the remaining length cannot be determined, or - // if it's greater than Integer.MAX_VALUE. We don't know the true length in either case, - // so treat as unbounded. - bytesRemaining = C.LENGTH_UNSET; + bytesRemaining = assetFileDescriptor.getLength(); + if (bytesRemaining == AssetFileDescriptor.UNKNOWN_LENGTH) { + // The asset must extend to the end of the file. + bytesRemaining = inputStream.available(); + if (bytesRemaining == 0) { + // FileInputStream.available() returns 0 if the remaining length cannot be determined, or + // if it's greater than Integer.MAX_VALUE. We don't know the true length in either case, + // so treat as unbounded. + bytesRemaining = C.LENGTH_UNSET; + } } } } catch (IOException e) { From 795e3be44029878b5772014f92a5fb435b335299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Wr=C3=B3tniak?= Date: Mon, 19 Jun 2017 16:09:54 +0200 Subject: [PATCH 069/133] null AssetFileDescriptors support added in `ContentDataSource` --- .../upstream/AndroidDataSourceConstants.java | 8 +++++++ .../upstream/ContentDataSourceTest.java | 23 +++++++++++++++++-- .../exoplayer2/upstream/TestDataProvider.java | 4 ++++ .../upstream/ContentDataSource.java | 4 ++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java index ad19b7a824..d11202ccf2 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java @@ -1,8 +1,16 @@ package com.google.android.exoplayer2.upstream; +import android.content.ContentResolver; +import android.net.Uri; + final class AndroidDataSourceConstants { static final long SAMPLE_MP4_BYTES = 101597; static final String SAMPLE_MP4_PATH = "/mp4/sample.mp4"; + static final String TEST_DATA_PROVIDER_AUTHORITY = "exoplayer"; + static final Uri NULL_DESCRIPTOR_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(TEST_DATA_PROVIDER_AUTHORITY) + .build(); private AndroidDataSourceConstants() {} } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java index b2edeea0cc..1cd14b45e1 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -1,21 +1,40 @@ package com.google.android.exoplayer2.upstream; +import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.test.InstrumentationTestCase; +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.NULL_DESCRIPTOR_URI; import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_BYTES; import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_PATH; +import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.TEST_DATA_PROVIDER_AUTHORITY; public class ContentDataSourceTest extends InstrumentationTestCase { - public void testContentDataSource() throws Exception { + public void testValidContentDataSource() throws Exception { Context context = getInstrumentation().getContext(); ContentDataSource dataSource = new ContentDataSource(context); - Uri contentUri = Uri.parse("content://exoplayer" + SAMPLE_MP4_PATH); + Uri contentUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(TEST_DATA_PROVIDER_AUTHORITY) + .path(SAMPLE_MP4_PATH).build(); DataSpec dataSpec = new DataSpec(contentUri); long sourceLengthBytes = dataSource.open(dataSpec); assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); } + + public void testNullContentDataSource() throws Exception { + Context context = getInstrumentation().getContext(); + ContentDataSource dataSource = new ContentDataSource(context); + DataSpec dataSpec = new DataSpec(NULL_DESCRIPTOR_URI); + + try { + dataSource.open(dataSpec); + fail("Expected exception not thrown."); + } catch (ContentDataSource.ContentDataSourceException e) { + // Expected. + } + } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java index 851e7b4b0c..f6e09a7067 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java @@ -29,6 +29,10 @@ public class TestDataProvider extends ContentProvider { @Nullable @Override public AssetFileDescriptor openAssetFile(@NonNull final Uri uri, @NonNull final String mode) throws FileNotFoundException { + if (uri.equals(AndroidDataSourceConstants.NULL_DESCRIPTOR_URI)) { + return null; + } + try { Context context = getContext(); assertNotNull(context); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java index 9421b7ba03..3a9be552d2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java @@ -22,6 +22,7 @@ import android.net.Uri; import com.google.android.exoplayer2.C; import java.io.EOFException; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -71,6 +72,9 @@ public final class ContentDataSource implements DataSource { try { uri = dataSpec.uri; assetFileDescriptor = resolver.openAssetFileDescriptor(uri, "r"); + if (assetFileDescriptor == null) { + throw new FileNotFoundException("Could not open file descriptor for: " + uri); + } inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor()); long skipped = inputStream.skip(dataSpec.position); if (skipped < dataSpec.position) { From 5eb64dbe1208a61c157d87f1e33fc33180ad85c3 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Fri, 23 Jun 2017 17:20:51 +0100 Subject: [PATCH 070/133] Fix ContentDataSource and enhance tests to validate read data --- .../core/src/androidTest/AndroidManifest.xml | 4 +- .../assets/binary/1024_incrementing_bytes.mp3 | Bin 0 -> 1024 bytes .../upstream/AndroidDataSourceConstants.java | 16 -- .../upstream/AssetDataSourceTest.java | 59 ++++++-- .../upstream/ContentDataSourceTest.java | 139 ++++++++++++++---- .../exoplayer2/upstream/TestDataProvider.java | 68 --------- .../upstream/ContentDataSource.java | 5 +- .../android/exoplayer2/testutil/TestUtil.java | 17 +++ 8 files changed, 185 insertions(+), 123 deletions(-) create mode 100644 library/core/src/androidTest/assets/binary/1024_incrementing_bytes.mp3 delete mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AndroidDataSourceConstants.java delete mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/TestDataProvider.java diff --git a/library/core/src/androidTest/AndroidManifest.xml b/library/core/src/androidTest/AndroidManifest.xml index 8968ae59d6..a50de35b62 100644 --- a/library/core/src/androidTest/AndroidManifest.xml +++ b/library/core/src/androidTest/AndroidManifest.xml @@ -25,8 +25,8 @@ tools:ignore="MissingApplicationIcon,HardcodedDebugMode"> + android:authorities="com.google.android.exoplayer2.core.test" + android:name="com.google.android.exoplayer2.upstream.ContentDataSourceTest$TestContentProvider"/> MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v` Date: Fri, 23 Jun 2017 17:37:28 +0100 Subject: [PATCH 071/133] Mini cleanup --- .../exoplayer2/upstream/ContentDataSourceTest.java | 10 ++++++---- .../android/exoplayer2/upstream/ContentDataSource.java | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java index 10a408c578..d8743a0a2c 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -33,6 +33,7 @@ import java.io.IOException; */ public final class ContentDataSourceTest extends InstrumentationTestCase { + private static final String AUTHORITY = "com.google.android.exoplayer2.core.test"; private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3"; private static final long DATA_LENGTH = 1024; @@ -40,7 +41,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext()); Uri contentUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) - .authority(TestContentProvider.AUTHORITY) + .authority(AUTHORITY) .path(DATA_PATH).build(); DataSpec dataSpec = new DataSpec(contentUri); try { @@ -57,7 +58,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext()); Uri contentUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) - .authority(TestContentProvider.AUTHORITY) + .authority(AUTHORITY) .build(); DataSpec dataSpec = new DataSpec(contentUri); try { @@ -70,10 +71,11 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { } } + /** + * A {@link ContentProvider} for the test. + */ public static final class TestContentProvider extends ContentProvider { - private static final String AUTHORITY = "com.google.android.exoplayer2.core.test"; - @Override public boolean onCreate() { return true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java index 507162519a..d118b91378 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/ContentDataSource.java @@ -91,9 +91,9 @@ public final class ContentDataSource implements DataSource { // The asset must extend to the end of the file. bytesRemaining = inputStream.available(); if (bytesRemaining == 0) { - // FileInputStream.available() returns 0 if the remaining length cannot be determined, or - // if it's greater than Integer.MAX_VALUE. We don't know the true length in either case, - // so treat as unbounded. + // FileInputStream.available() returns 0 if the remaining length cannot be determined, + // or if it's greater than Integer.MAX_VALUE. We don't know the true length in either + // case, so treat as unbounded. bytesRemaining = C.LENGTH_UNSET; } } From fd9b162d0fe635e9dbbbcffd0ff1a63ee45cfc08 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 23 Jun 2017 09:44:27 -0700 Subject: [PATCH 072/133] Fix setSelectionOverride(index, tracks, null) Issue: #2988 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159958591 --- .../MappingTrackSelectorTest.java | 196 ++++++++++++++++++ .../android/exoplayer2/source/TrackGroup.java | 2 +- .../trackselection/MappingTrackSelector.java | 8 +- 3 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java new file mode 100644 index 0000000000..c31c651384 --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/trackselection/MappingTrackSelectorTest.java @@ -0,0 +1,196 @@ +/* + * 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. + */ +package com.google.android.exoplayer2.trackselection; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlaybackException; +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; +import com.google.android.exoplayer2.util.MimeTypes; +import junit.framework.TestCase; + +/** + * Unit tests for {@link MappingTrackSelector}. + */ +public final class MappingTrackSelectorTest extends TestCase { + + private static final RendererCapabilities VIDEO_CAPABILITIES = + new FakeRendererCapabilities(C.TRACK_TYPE_VIDEO); + private static final RendererCapabilities AUDIO_CAPABILITIES = + new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO); + private static final RendererCapabilities[] RENDERER_CAPABILITIES = new RendererCapabilities[] { + VIDEO_CAPABILITIES, AUDIO_CAPABILITIES + }; + + private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup( + Format.createVideoSampleFormat("video", MimeTypes.VIDEO_H264, null, Format.NO_VALUE, + Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, null)); + private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup( + Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, + Format.NO_VALUE, 2, 44100, null, null, 0, null)); + private static final TrackGroupArray TRACK_GROUPS = new TrackGroupArray( + VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP); + + private static final TrackSelection[] TRACK_SELECTIONS = new TrackSelection[] { + new FixedTrackSelection(VIDEO_TRACK_GROUP, 0), + new FixedTrackSelection(AUDIO_TRACK_GROUP, 0) + }; + + /** + * Tests that the video and audio track groups are mapped onto the correct renderers. + */ + public void testMapping() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(); + trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); + trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP); + trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP); + } + + /** + * Tests that the video and audio track groups are mapped onto the correct renderers when the + * renderer ordering is reversed. + */ + public void testMappingReverseOrder() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(); + RendererCapabilities[] reverseOrderRendererCapabilities = new RendererCapabilities[] { + AUDIO_CAPABILITIES, VIDEO_CAPABILITIES}; + trackSelector.selectTracks(reverseOrderRendererCapabilities, TRACK_GROUPS); + trackSelector.assertMappedTrackGroups(0, AUDIO_TRACK_GROUP); + trackSelector.assertMappedTrackGroups(1, VIDEO_TRACK_GROUP); + } + + /** + * Tests video and audio track groups are mapped onto the correct renderers when there are + * multiple track groups of the same type. + */ + public void testMappingMulti() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(); + TrackGroupArray multiTrackGroups = new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP, + VIDEO_TRACK_GROUP); + trackSelector.selectTracks(RENDERER_CAPABILITIES, multiTrackGroups); + trackSelector.assertMappedTrackGroups(0, VIDEO_TRACK_GROUP, VIDEO_TRACK_GROUP); + trackSelector.assertMappedTrackGroups(1, AUDIO_TRACK_GROUP); + } + + /** + * Tests the result of {@link MappingTrackSelector#selectTracks(RendererCapabilities[], + * TrackGroupArray[], int[][][])} is propagated correctly to the result of + * {@link MappingTrackSelector#selectTracks(RendererCapabilities[], TrackGroupArray)}. + */ + public void testSelectTracks() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS); + TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); + assertEquals(TRACK_SELECTIONS[0], result.selections.get(0)); + assertEquals(TRACK_SELECTIONS[1], result.selections.get(1)); + } + + /** + * Tests that a null override clears a track selection. + */ + public void testSelectTracksWithNullOverride() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS); + trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); + TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); + assertNull(result.selections.get(0)); + assertEquals(TRACK_SELECTIONS[1], result.selections.get(1)); + } + + /** + * Tests that a null override can be cleared. + */ + public void testSelectTracksWithClearedNullOverride() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS); + trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); + trackSelector.clearSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP)); + TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); + assertEquals(TRACK_SELECTIONS[0], result.selections.get(0)); + assertEquals(TRACK_SELECTIONS[1], result.selections.get(1)); + } + + /** + * Tests that an override is not applied for a different set of available track groups. + */ + public void testSelectTracksWithNullOverrideForDifferentTracks() throws ExoPlaybackException { + FakeMappingTrackSelector trackSelector = new FakeMappingTrackSelector(TRACK_SELECTIONS); + trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); + TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, + new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP, VIDEO_TRACK_GROUP)); + assertEquals(TRACK_SELECTIONS[0], result.selections.get(0)); + assertEquals(TRACK_SELECTIONS[1], result.selections.get(1)); + } + + /** + * A {@link MappingTrackSelector} that returns a fixed result from + * {@link #selectTracks(RendererCapabilities[], TrackGroupArray[], int[][][])}. + */ + private static final class FakeMappingTrackSelector extends MappingTrackSelector { + + private final TrackSelection[] result; + private TrackGroupArray[] lastRendererTrackGroupArrays; + + public FakeMappingTrackSelector(TrackSelection... result) { + this.result = result.length == 0 ? null : result; + } + + @Override + protected TrackSelection[] selectTracks(RendererCapabilities[] rendererCapabilities, + TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) + throws ExoPlaybackException { + lastRendererTrackGroupArrays = rendererTrackGroupArrays; + return result == null ? new TrackSelection[rendererCapabilities.length] : result; + } + + public void assertMappedTrackGroups(int rendererIndex, TrackGroup... expected) { + assertEquals(expected.length, lastRendererTrackGroupArrays[rendererIndex].length); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], lastRendererTrackGroupArrays[rendererIndex].get(i)); + } + } + + } + + /** + * A {@link RendererCapabilities} that advertises adaptive support for all tracks of a given type. + */ + private static final class FakeRendererCapabilities implements RendererCapabilities { + + private final int trackType; + + public FakeRendererCapabilities(int trackType) { + this.trackType = trackType; + } + + @Override + public int getTrackType() { + return trackType; + } + + @Override + public int supportsFormat(Format format) throws ExoPlaybackException { + return MimeTypes.getTrackType(format.sampleMimeType) == trackType + ? (FORMAT_HANDLED | ADAPTIVE_SEAMLESS) : FORMAT_UNSUPPORTED_TYPE; + } + + @Override + public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + return ADAPTIVE_SEAMLESS; + } + + } + +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java index 393ac1988a..06410d5426 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java @@ -42,7 +42,7 @@ public final class TrackGroup { private int hashCode; /** - * @param formats The track formats. Must not be null or contain null elements. + * @param formats The track formats. Must not be null, contain null elements or be of length 0. */ public TrackGroup(Format... formats) { Assertions.checkState(formats.length > 0); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 690723cf15..3499efdb16 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -304,10 +304,10 @@ public abstract class MappingTrackSelector extends TrackSelector { trackSelections[i] = null; } else { TrackGroupArray rendererTrackGroup = rendererTrackGroupArrays[i]; - Map overrides = selectionOverrides.get(i); - SelectionOverride override = overrides == null ? null : overrides.get(rendererTrackGroup); - if (override != null) { - trackSelections[i] = override.createTrackSelection(rendererTrackGroup); + if (hasSelectionOverride(i, rendererTrackGroup)) { + SelectionOverride override = selectionOverrides.get(i).get(rendererTrackGroup); + trackSelections[i] = override == null ? null + : override.createTrackSelection(rendererTrackGroup); } } } From d4e598f4172352e1ab6eb916aed9de1c03686957 Mon Sep 17 00:00:00 2001 From: Alex Birkett Date: Fri, 23 Jun 2017 18:56:40 +0200 Subject: [PATCH 073/133] Make OkHttpDataSource userAgent parameter optional --- .../ext/okhttp/OkHttpDataSource.java | 21 ++++++++++++------- .../ext/okhttp/OkHttpDataSourceFactory.java | 11 ++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java index 47850c0637..fac35bd427 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java @@ -16,6 +16,8 @@ package com.google.android.exoplayer2.ext.okhttp; import android.net.Uri; +import android.support.annotation.Nullable; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSpec; @@ -67,11 +69,11 @@ public class OkHttpDataSource implements HttpDataSource { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string that should be used. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, + public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, Predicate contentTypePredicate) { this(callFactory, userAgent, contentTypePredicate, null); } @@ -79,13 +81,13 @@ public class OkHttpDataSource implements HttpDataSource { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string that should be used. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. * @param listener An optional listener. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, + public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, Predicate contentTypePredicate, TransferListener listener) { this(callFactory, userAgent, contentTypePredicate, listener, null, null); } @@ -93,7 +95,7 @@ public class OkHttpDataSource implements HttpDataSource { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string that should be used. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. @@ -102,11 +104,11 @@ public class OkHttpDataSource implements HttpDataSource { * @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to * the server as HTTP headers on every request. */ - public OkHttpDataSource(Call.Factory callFactory, String userAgent, + public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, Predicate contentTypePredicate, TransferListener listener, CacheControl cacheControl, RequestProperties defaultRequestProperties) { this.callFactory = Assertions.checkNotNull(callFactory); - this.userAgent = Assertions.checkNotEmpty(userAgent); + this.userAgent = userAgent; this.contentTypePredicate = contentTypePredicate; this.listener = listener; this.cacheControl = cacheControl; @@ -280,7 +282,10 @@ public class OkHttpDataSource implements HttpDataSource { } builder.addHeader("Range", rangeRequest); } - builder.addHeader("User-Agent", userAgent); + if (userAgent != null) { + builder.addHeader("User-Agent", userAgent); + } + if (!allowGzip) { builder.addHeader("Accept-Encoding", "identity"); } diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java index 5228065db1..6ee09df7de 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.ext.okhttp; +import android.support.annotation.Nullable; + import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; @@ -36,10 +38,10 @@ public final class OkHttpDataSourceFactory extends BaseFactory { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string that should be used. * @param listener An optional listener. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, + public OkHttpDataSourceFactory(Call.Factory callFactory, @Nullable String userAgent, TransferListener listener) { this(callFactory, userAgent, listener, null); } @@ -47,11 +49,12 @@ public final class OkHttpDataSourceFactory extends BaseFactory { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent The User-Agent string that should be used. + * @param userAgent An optional User-Agent string that should be used. * @param listener An optional listener. * @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, + public OkHttpDataSourceFactory(Call.Factory callFactory, + @Nullable String userAgent, TransferListener listener, CacheControl cacheControl) { this.callFactory = callFactory; this.userAgent = userAgent; From 045a153cb7afdffc69b46adb9de1aad8b14b909f Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Sun, 25 Jun 2017 15:16:11 +0100 Subject: [PATCH 074/133] Clean up okhttp datasource. --- .../ext/okhttp/OkHttpDataSource.java | 39 ++++++++++--------- .../ext/okhttp/OkHttpDataSourceFactory.java | 24 ++++++------ 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java index fac35bd427..167fc68e86 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java @@ -16,8 +16,8 @@ package com.google.android.exoplayer2.ext.okhttp; import android.net.Uri; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSourceException; import com.google.android.exoplayer2.upstream.DataSpec; @@ -47,13 +47,14 @@ public class OkHttpDataSource implements HttpDataSource { private static final AtomicReference skipBufferReference = new AtomicReference<>(); - private final Call.Factory callFactory; - private final String userAgent; - private final Predicate contentTypePredicate; - private final TransferListener listener; - private final CacheControl cacheControl; - private final RequestProperties defaultRequestProperties; - private final RequestProperties requestProperties; + @NonNull private final Call.Factory callFactory; + @NonNull private final RequestProperties requestProperties; + + @Nullable private final String userAgent; + @Nullable private final Predicate contentTypePredicate; + @Nullable private final TransferListener listener; + @Nullable private final CacheControl cacheControl; + @Nullable private final RequestProperties defaultRequestProperties; private DataSpec dataSpec; private Response response; @@ -69,33 +70,34 @@ public class OkHttpDataSource implements HttpDataSource { /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent An optional User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}. */ - public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, - Predicate contentTypePredicate) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate) { this(callFactory, userAgent, contentTypePredicate, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent An optional User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. * @param listener An optional listener. */ - public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, - Predicate contentTypePredicate, TransferListener listener) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener) { this(callFactory, userAgent, contentTypePredicate, listener, null, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the source. - * @param userAgent An optional User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * predicate then a {@link InvalidContentTypeException} is thrown from * {@link #open(DataSpec)}. @@ -104,9 +106,10 @@ public class OkHttpDataSource implements HttpDataSource { * @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to * the server as HTTP headers on every request. */ - public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent, - Predicate contentTypePredicate, TransferListener listener, - CacheControl cacheControl, RequestProperties defaultRequestProperties) { + public OkHttpDataSource(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable Predicate contentTypePredicate, + @Nullable TransferListener listener, + @Nullable CacheControl cacheControl, @Nullable RequestProperties defaultRequestProperties) { this.callFactory = Assertions.checkNotNull(callFactory); this.userAgent = userAgent; this.contentTypePredicate = contentTypePredicate; diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java index 6ee09df7de..32fc5a58cb 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSourceFactory.java @@ -15,8 +15,8 @@ */ package com.google.android.exoplayer2.ext.okhttp; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; @@ -30,32 +30,32 @@ import okhttp3.Call; */ public final class OkHttpDataSourceFactory extends BaseFactory { - private final Call.Factory callFactory; - private final String userAgent; - private final TransferListener listener; - private final CacheControl cacheControl; + @NonNull private final Call.Factory callFactory; + @Nullable private final String userAgent; + @Nullable private final TransferListener listener; + @Nullable private final CacheControl cacheControl; /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent An optional User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param listener An optional listener. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, @Nullable String userAgent, - TransferListener listener) { + public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable TransferListener listener) { this(callFactory, userAgent, listener, null); } /** * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use * by the sources created by the factory. - * @param userAgent An optional User-Agent string that should be used. + * @param userAgent An optional User-Agent string. * @param listener An optional listener. * @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header. */ - public OkHttpDataSourceFactory(Call.Factory callFactory, - @Nullable String userAgent, - TransferListener listener, CacheControl cacheControl) { + public OkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent, + @Nullable TransferListener listener, + @Nullable CacheControl cacheControl) { this.callFactory = callFactory; this.userAgent = userAgent; this.listener = listener; From a5eba0162b08f786d5a57b28746737a9ed83504e Mon Sep 17 00:00:00 2001 From: hoangtc Date: Mon, 26 Jun 2017 02:46:42 -0700 Subject: [PATCH 075/133] Update DrmSessionException. Make DrmSessionException takes in Throwable cause instead of Exception cause, which is more limiting and doesn't add any benefit. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160121486 --- .../java/com/google/android/exoplayer2/drm/DrmSession.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java index 538db9e1d9..cd694396b7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java @@ -28,11 +28,11 @@ import java.util.Map; @TargetApi(16) public interface DrmSession { - /** Wraps the exception which is the cause of the error state. */ + /** Wraps the throwable which is the cause of the error state. */ class DrmSessionException extends Exception { - public DrmSessionException(Exception e) { - super(e); + public DrmSessionException(Throwable cause) { + super(cause); } } From 2f7de7d3e8a7b29f29d429d1deaffee7a33a5883 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 26 Jun 2017 04:02:03 -0700 Subject: [PATCH 076/133] Merge ContentDataSource fixes + tests from GitHub https://github.com/google/ExoPlayer/pull/2963/files https://github.com/google/ExoPlayer/commit/8bb643976fe20d1ec684291aa7bf5337e474bec4 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160128047 --- .../upstream/AssetDataSourceTest.java | 35 +++++-------------- .../upstream/ContentDataSourceTest.java | 13 ++----- .../android/exoplayer2/testutil/TestUtil.java | 22 ++++++++++++ 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java index d8e61eb94c..102c89ec2b 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/AssetDataSourceTest.java @@ -15,10 +15,8 @@ */ package com.google.android.exoplayer2.upstream; -import android.content.Context; import android.net.Uri; import android.test.InstrumentationTestCase; -import android.test.MoreAsserts; import com.google.android.exoplayer2.testutil.TestUtil; /** @@ -27,36 +25,19 @@ import com.google.android.exoplayer2.testutil.TestUtil; public final class AssetDataSourceTest extends InstrumentationTestCase { private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3"; - private static final long DATA_LENGTH = 1024; public void testReadFileUri() throws Exception { - Context context = getInstrumentation().getContext(); - AssetDataSource dataSource = new AssetDataSource(context); - Uri assetUri = Uri.parse("file:///android_asset/" + DATA_PATH); - DataSpec dataSpec = new DataSpec(assetUri); - try { - long length = dataSource.open(dataSpec); - assertEquals(DATA_LENGTH, length); - byte[] readData = TestUtil.readToEnd(dataSource); - MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData); - } finally { - dataSource.close(); - } + AssetDataSource dataSource = new AssetDataSource(getInstrumentation().getContext()); + DataSpec dataSpec = new DataSpec(Uri.parse("file:///android_asset/" + DATA_PATH)); + TestUtil.assertDataSourceContent(dataSource, dataSpec, + TestUtil.getByteArray(getInstrumentation(), DATA_PATH)); } public void testReadAssetUri() throws Exception { - Context context = getInstrumentation().getContext(); - AssetDataSource dataSource = new AssetDataSource(context); - Uri assetUri = Uri.parse("asset:///" + DATA_PATH); - DataSpec dataSpec = new DataSpec(assetUri); - try { - long length = dataSource.open(dataSpec); - assertEquals(DATA_LENGTH, length); - byte[] readData = TestUtil.readToEnd(dataSource); - MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData); - } finally { - dataSource.close(); - } + AssetDataSource dataSource = new AssetDataSource(getInstrumentation().getContext()); + DataSpec dataSpec = new DataSpec(Uri.parse("asset:///" + DATA_PATH)); + TestUtil.assertDataSourceContent(dataSource, dataSpec, + TestUtil.getByteArray(getInstrumentation(), DATA_PATH)); } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java index d8743a0a2c..834e7e1374 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/upstream/ContentDataSourceTest.java @@ -23,7 +23,6 @@ import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.test.InstrumentationTestCase; -import android.test.MoreAsserts; import com.google.android.exoplayer2.testutil.TestUtil; import java.io.FileNotFoundException; import java.io.IOException; @@ -35,7 +34,6 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { private static final String AUTHORITY = "com.google.android.exoplayer2.core.test"; private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3"; - private static final long DATA_LENGTH = 1024; public void testReadValidUri() throws Exception { ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext()); @@ -44,14 +42,8 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { .authority(AUTHORITY) .path(DATA_PATH).build(); DataSpec dataSpec = new DataSpec(contentUri); - try { - long length = dataSource.open(dataSpec); - assertEquals(DATA_LENGTH, length); - byte[] readData = TestUtil.readToEnd(dataSource); - MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData); - } finally { - dataSource.close(); - } + TestUtil.assertDataSourceContent(dataSource, dataSpec, + TestUtil.getByteArray(getInstrumentation(), DATA_PATH)); } public void testReadInvalidUri() throws Exception { @@ -66,6 +58,7 @@ public final class ContentDataSourceTest extends InstrumentationTestCase { fail(); } catch (ContentDataSource.ContentDataSourceException e) { // Expected. + assertTrue(e.getCause() instanceof FileNotFoundException); } finally { dataSource.close(); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java index f75239318a..363f60b10d 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/TestUtil.java @@ -17,12 +17,14 @@ package com.google.android.exoplayer2.testutil; import android.app.Instrumentation; import android.test.InstrumentationTestCase; +import android.test.MoreAsserts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -390,4 +392,24 @@ public class TestUtil { } } + /** + * Asserts that data read from a {@link DataSource} matches {@code expected}. + * + * @param dataSource The {@link DataSource} through which to read. + * @param dataSpec The {@link DataSpec} to use when opening the {@link DataSource}. + * @param expectedData The expected data. + * @throws IOException If an error occurs reading fom the {@link DataSource}. + */ + public static void assertDataSourceContent(DataSource dataSource, DataSpec dataSpec, + byte[] expectedData) throws IOException { + try { + long length = dataSource.open(dataSpec); + Assert.assertEquals(length, expectedData.length); + byte[] readData = TestUtil.readToEnd(dataSource); + MoreAsserts.assertEquals(expectedData, readData); + } finally { + dataSource.close(); + } + } + } From 6c24d9380560adfc7c2577cdadf49d770541c383 Mon Sep 17 00:00:00 2001 From: eguven Date: Mon, 26 Jun 2017 06:51:52 -0700 Subject: [PATCH 077/133] Fix FLAC extension native part compilation In the latest NDK version (r15) compilation fails because 'memset' isn't defined. Included cstring header. Issue: #2977 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160139022 --- extensions/flac/src/main/jni/flac_parser.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/flac/src/main/jni/flac_parser.cc b/extensions/flac/src/main/jni/flac_parser.cc index e4925cb462..6c6e57f5f7 100644 --- a/extensions/flac/src/main/jni/flac_parser.cc +++ b/extensions/flac/src/main/jni/flac_parser.cc @@ -22,6 +22,7 @@ #include #include +#include #define LOG_TAG "FLACParser" #define ALOGE(...) \ From efd17f86c5507a68e2568b2d4a30f9280eb53bab Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 27 Jun 2017 09:38:31 -0700 Subject: [PATCH 078/133] Add URLs EXT-X-STREAM-INF uris only once This prevents ExoPlayer from thinking there are many more video tracks than there actually are. And will prevent downloading multiple times the same rendition once offline support for HLS is added. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160285777 --- .../source/hls/playlist/HlsPlaylistParser.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index a867659838..c64b98b5f3 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -174,6 +175,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variantUrls = new HashSet<>(); ArrayList variants = new ArrayList<>(); ArrayList audios = new ArrayList<>(); ArrayList subtitles = new ArrayList<>(); @@ -251,11 +253,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Thu, 29 Jun 2017 00:37:28 -0700 Subject: [PATCH 079/133] Show larger scrubber handle when focused Also remove updateScrubberState as it doesn't do anything useful. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160496133 --- .../android/exoplayer2/ui/DefaultTimeBar.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java index 4ede786175..2b699c8957 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java @@ -89,7 +89,6 @@ public class DefaultTimeBar extends View implements TimeBar { private final Formatter formatter; private final Runnable stopScrubbingRunnable; - private int scrubberSize; private OnScrubListener listener; private int keyCountIncrement; private long keyTimeIncrement; @@ -184,7 +183,6 @@ public class DefaultTimeBar extends View implements TimeBar { stopScrubbing(false); } }; - scrubberSize = scrubberEnabledSize; scrubberPadding = (Math.max(scrubberDisabledSize, Math.max(scrubberEnabledSize, scrubberDraggedSize)) + 1) / 2; @@ -234,8 +232,6 @@ public class DefaultTimeBar extends View implements TimeBar { this.duration = duration; if (scrubbing && duration == C.TIME_UNSET) { stopScrubbing(true); - } else { - updateScrubberState(); } update(); } @@ -251,7 +247,6 @@ public class DefaultTimeBar extends View implements TimeBar { @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); - updateScrubberState(); if (scrubbing && !enabled) { stopScrubbing(true); } @@ -436,7 +431,6 @@ public class DefaultTimeBar extends View implements TimeBar { private void startScrubbing() { scrubbing = true; - updateScrubberState(); ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); @@ -452,18 +446,12 @@ public class DefaultTimeBar extends View implements TimeBar { if (parent != null) { parent.requestDisallowInterceptTouchEvent(false); } - updateScrubberState(); invalidate(); if (listener != null) { listener.onScrubStop(this, getScrubberPosition(), canceled); } } - private void updateScrubberState() { - scrubberSize = scrubbing ? scrubberDraggedSize - : (isEnabled() && duration >= 0 ? scrubberEnabledSize : scrubberDisabledSize); - } - private void update() { bufferedBar.set(progressBar); scrubberBar.set(progressBar); @@ -543,6 +531,8 @@ public class DefaultTimeBar extends View implements TimeBar { if (duration <= 0) { return; } + int scrubberSize = (scrubbing || isFocused()) ? scrubberDraggedSize + : (isEnabled() ? scrubberEnabledSize : scrubberDisabledSize); int playheadRadius = scrubberSize / 2; int playheadCenter = Util.constrainValue(scrubberBar.right, scrubberBar.left, progressBar.right); From df84f2930cdb1f75d273668a96953c0aae102943 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 29 Jun 2017 04:16:16 -0700 Subject: [PATCH 080/133] Clarify JavaDoc of MediaPeriod. Two of MediaPeriod's methods are only called after the media period has been prepared. Added this to JavaDoc of these method to simplify implementations. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160510373 --- .../com/google/android/exoplayer2/source/MediaPeriod.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index 3b06542855..aaf4c89ff7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -106,6 +106,8 @@ public interface MediaPeriod extends SequenceableLoader { /** * Discards buffered media up to the specified position. + *

    + * This method should only be called after the period has been prepared. * * @param positionUs The position in microseconds. */ @@ -116,6 +118,8 @@ public interface MediaPeriod extends SequenceableLoader { *

    * After this method has returned a value other than {@link C#TIME_UNSET}, all * {@link SampleStream}s provided by the period are guaranteed to start from a key frame. + *

    + * This method should only be called after the period has been prepared. * * @return If a discontinuity was read then the playback position in microseconds after the * discontinuity. Else {@link C#TIME_UNSET}. From 79f7db7fcd1285e1248045fd486a109dade941e4 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Jun 2017 06:49:30 -0700 Subject: [PATCH 081/133] Prefer Google over MediaTek for PCM decoding pre-O. Issue: #2873 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160520136 --- .../exoplayer2/mediacodec/MediaCodecUtil.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java index 5369dffeb6..392162f607 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java @@ -56,8 +56,10 @@ public final class MediaCodecUtil { } private static final String TAG = "MediaCodecUtil"; + private static final String GOOGLE_RAW_DECODER_NAME = "OMX.google.raw.decoder"; + private static final String MTK_RAW_DECODER_NAME = "OMX.MTK.AUDIO.DECODER.RAW"; private static final MediaCodecInfo PASSTHROUGH_DECODER_INFO = - MediaCodecInfo.newPassthroughInstance("OMX.google.raw.decoder"); + MediaCodecInfo.newPassthroughInstance(GOOGLE_RAW_DECODER_NAME); private static final Pattern PROFILE_PATTERN = Pattern.compile("^\\D?(\\d+)$"); private static final HashMap> decoderInfosCache = new HashMap<>(); @@ -155,6 +157,7 @@ public final class MediaCodecUtil { + ". Assuming: " + decoderInfos.get(0).name); } } + applyWorkarounds(decoderInfos); decoderInfos = Collections.unmodifiableList(decoderInfos); decoderInfosCache.put(key, decoderInfos); return decoderInfos; @@ -339,6 +342,27 @@ public final class MediaCodecUtil { return true; } + /** + * Modifies a list of {@link MediaCodecInfo}s to apply workarounds where we know better than the + * platform. + * + * @param decoderInfos The list to modify. + */ + private static void applyWorkarounds(List decoderInfos) { + if (Util.SDK_INT < 26 && decoderInfos.size() > 1 + && MTK_RAW_DECODER_NAME.equals(decoderInfos.get(0).name)) { + // Prefer the Google raw decoder over the MediaTek one [Internal: b/62337687]. + for (int i = 1; i < decoderInfos.size(); i++) { + MediaCodecInfo decoderInfo = decoderInfos.get(i); + if (GOOGLE_RAW_DECODER_NAME.equals(decoderInfo.name)) { + decoderInfos.remove(i); + decoderInfos.add(0, decoderInfo); + break; + } + } + } + } + /** * Returns whether the decoder is known to fail when adapting, despite advertising itself as an * adaptive decoder. From a579b8d82dd74dca8a4e6b666c0df693aa14aa80 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 30 Jun 2017 02:52:20 -0700 Subject: [PATCH 082/133] Fix DvbParser bug Issue: #2957 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160628086 --- .../com/google/android/exoplayer2/text/dvb/DvbParser.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java index 96c8a89801..c0caf1e57a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/dvb/DvbParser.java @@ -667,13 +667,15 @@ import java.util.List; int runLength = 0; int clutIndex = 0; int peek = data.readBits(2); - if (!data.readBit()) { + if (peek != 0x00) { runLength = 1; clutIndex = peek; } else if (data.readBit()) { runLength = 3 + data.readBits(3); clutIndex = data.readBits(2); - } else if (!data.readBit()) { + } else if (data.readBit()) { + runLength = 1; + } else { switch (data.readBits(2)) { case 0x00: endOfPixelCodeString = true; From 43daf0f2bb96bb514d22561dd4821f6ce7562aa9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 15 Jun 2017 08:06:31 -0700 Subject: [PATCH 083/133] Update MIME type in FLAC test data ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=159104188 --- library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump | 2 +- library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump | 2 +- library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump | 2 +- library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump | 2 +- .../core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump | 2 +- .../src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump | 2 +- .../src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump | 2 +- .../src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump | 2 +- .../src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump | 2 +- .../assets/ogg/bear_flac_noseektable.ogg.unklen.dump | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump index 16816917b7..5ba8cc29ae 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.0.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump index fec523f971..f698fd28cf 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.1.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump index a4a60989ed..8d803d0bac 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.2.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump index a77575bb0c..09f6267270 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.3.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump index 16816917b7..5ba8cc29ae 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac.ogg.unklen.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump index 7be7d02493..73e537f8c8 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.0.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump index 34f19c6bce..3b7dc3fd1e 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.1.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump index 68484d2cf4..b6a6741fcc 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.2.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump index 8b2e7858b0..738002f7ef 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.3.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 diff --git a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump index 8d398efdb8..a237fd0dfc 100644 --- a/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump +++ b/library/core/src/androidTest/assets/ogg/bear_flac_noseektable.ogg.unklen.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = audio/x-flac + sampleMimeType = audio/flac maxInputSize = 768000 width = -1 height = -1 From 1b64d62e61dd68635df0df3a1a83d303db4d0b12 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 30 Jun 2017 10:48:03 -0700 Subject: [PATCH 084/133] Update release notes + bump version number ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160663100 --- RELEASENOTES.md | 22 +++++++++++++++++-- build.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../exoplayer2/ExoPlayerLibraryInfo.java | 6 ++--- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4f147e2bbd..f9f6b02c19 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,11 +1,29 @@ # Release notes # +### r2.4.3 ### + +* Audio: Workaround custom audio decoders misreporting their maximum supported + channel counts ([#2940](https://github.com/google/ExoPlayer/issues/2940)). +* Audio: Workaround for broken MediaTek raw decoder on some devices + ([#2873](https://github.com/google/ExoPlayer/issues/2873)). +* Captions: Fix TTML captions appearing at the top of the screen + ([#2953](https://github.com/google/ExoPlayer/issues/2953)). +* Captions: Fix handling of some DVB subtitles + ([#2957](https://github.com/google/ExoPlayer/issues/2957)). +* Track selection: Fix setSelectionOverride(index, tracks, null) + ([#2988](https://github.com/google/ExoPlayer/issues/2988)). +* GVR extension: Add support for mono input + ([#2710](https://github.com/google/ExoPlayer/issues/2710)). +* FLAC extension: Fix failing build + ([#2977](https://github.com/google/ExoPlayer/pull/2977)). +* Misc bugfixes. + ### r2.4.2 ### * Stability: Work around Nexus 10 reboot when playing certain content - ([2806](https://github.com/google/ExoPlayer/issues/2806)). + ([#2806](https://github.com/google/ExoPlayer/issues/2806)). * MP3: Correctly treat MP3s with INFO headers as constant bitrate - ([2895](https://github.com/google/ExoPlayer/issues/2895)). + ([#2895](https://github.com/google/ExoPlayer/issues/2895)). * HLS: Use average rather than peak bandwidth when available ([#2863](https://github.com/google/ExoPlayer/issues/2863)). * SmoothStreaming: Fix timeline for live streams diff --git a/build.gradle b/build.gradle index 4f18e7c801..01d8b6616c 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.4.2' + releaseVersion = 'r2.4.3' releaseWebsite = 'https://github.com/google/ExoPlayer' } if (it.hasProperty('externalBuildDir')) { diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 34256d41c1..addce60cad 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2403" + android:versionName="2.4.3"> diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index c6fc139208..650ce727cd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -24,13 +24,13 @@ public interface 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. - String VERSION = "2.4.2"; + String VERSION = "2.4.3"; /** * The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - String VERSION_SLASHY = "ExoPlayerLib/2.4.2"; + String VERSION_SLASHY = "ExoPlayerLib/2.4.3"; /** * The version of the library expressed as an integer, for example 1002003. @@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - int VERSION_INT = 2004002; + int VERSION_INT = 2004003; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From f94db63366f8649fab29978d008fea35c761b837 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 4 Jul 2017 03:32:23 -0700 Subject: [PATCH 085/133] Fix reporting of width/height 1. maybeRenotifyVideoSizeChanged should report reported* variables 2. Add check into maybeNotifyVideoSizeChanged to suppress reporting in the case that the width and height are still unknown. Issue: #3007 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160879625 --- .../exoplayer2/video/MediaCodecVideoRenderer.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 990a29dc4e..fbbcd9a99a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -578,9 +578,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } private void maybeNotifyVideoSizeChanged() { - if (reportedWidth != currentWidth || reportedHeight != currentHeight + if ((currentWidth != Format.NO_VALUE || currentHeight != Format.NO_VALUE) + && (reportedWidth != currentWidth || reportedHeight != currentHeight || reportedUnappliedRotationDegrees != currentUnappliedRotationDegrees - || reportedPixelWidthHeightRatio != currentPixelWidthHeightRatio) { + || reportedPixelWidthHeightRatio != currentPixelWidthHeightRatio)) { eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees, currentPixelWidthHeightRatio); reportedWidth = currentWidth; @@ -592,8 +593,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private void maybeRenotifyVideoSizeChanged() { if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) { - eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees, - currentPixelWidthHeightRatio); + eventDispatcher.videoSizeChanged(reportedWidth, reportedHeight, + reportedUnappliedRotationDegrees, reportedPixelWidthHeightRatio); } } From 4233f81ed7d95d3f40d277f93820b03f6c50890d Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 10 Jul 2017 16:00:12 -0700 Subject: [PATCH 086/133] Misc tweaks to UI components ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161454491 --- .../google/android/exoplayer2/ui/PlaybackControlView.java | 7 ++++++- .../google/android/exoplayer2/ui/SimpleExoPlayerView.java | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index 5f88f3a241..250c237772 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -712,7 +712,12 @@ public class PlaybackControlView extends FrameLayout { if (fastForwardMs <= 0) { return; } - seekTo(Math.min(player.getCurrentPosition() + fastForwardMs, player.getDuration())); + long durationMs = player.getDuration(); + long seekPositionMs = player.getCurrentPosition() + fastForwardMs; + if (durationMs != C.TIME_UNSET) { + seekPositionMs = Math.min(seekPositionMs, durationMs); + } + seekTo(seekPositionMs); } private void seekTo(long positionMs) { diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index fce05f5bc4..245999f8b5 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -574,7 +574,8 @@ public final class SimpleExoPlayerView extends FrameLayout { /** * Sets the rewind increment in milliseconds. * - * @param rewindMs The rewind increment in milliseconds. + * @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the + * rewind button to be disabled. */ public void setRewindIncrementMs(int rewindMs) { Assertions.checkState(controller != null); @@ -584,7 +585,8 @@ public final class SimpleExoPlayerView extends FrameLayout { /** * Sets the fast forward increment in milliseconds. * - * @param fastForwardMs The fast forward increment in milliseconds. + * @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will + * cause the fast forward button to be disabled. */ public void setFastForwardIncrementMs(int fastForwardMs) { Assertions.checkState(controller != null); From 2665e42f85b5c64dd38439b49247bfbfa983cda2 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 11 Jul 2017 04:36:06 -0700 Subject: [PATCH 087/133] Correctly propagate format identifier for CEA-608 in HLS Issue: #3033 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161512537 --- .../google/android/exoplayer2/extractor/ts/SeiReader.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java index 1e5d480ea1..907419f8fc 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java @@ -51,9 +51,10 @@ import java.util.List; Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType) || MimeTypes.APPLICATION_CEA708.equals(channelMimeType), "Invalid closed caption mime type provided: " + channelMimeType); - output.format(Format.createTextSampleFormat(idGenerator.getFormatId(), channelMimeType, null, - Format.NO_VALUE, channelFormat.selectionFlags, channelFormat.language, - channelFormat.accessibilityChannel, null)); + String formatId = channelFormat.id != null ? channelFormat.id : idGenerator.getFormatId(); + output.format(Format.createTextSampleFormat(formatId, channelMimeType, null, Format.NO_VALUE, + channelFormat.selectionFlags, channelFormat.language, channelFormat.accessibilityChannel, + null)); outputs[i] = output; } } From 4cb5b349777ed6a71ab704200e7ccc6a8d1d3fe2 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 12 Jul 2017 04:47:31 -0700 Subject: [PATCH 088/133] Don't use ParsableBitArray to parse TS packet headers Really low hanging fruit optimization for TS extraction. ParsableBitArray is quite expensive. In particular readBits contains at least 2 if blocks and a for loop, and was being called 5 times per 188 byte packet (4 times via readBit). A separate change will follow that optimizes readBit, but for this particular case there's no real value to using a ParsableBitArray anyway; use of ParsableBitArray IMO only really becomes useful when you need to parse a bitstream more than 4 bytes long, or where parsing the bitstream requires some control flow (if/for) to parse. There are probably other places where we're using ParsableBitArray over-zealously. I'll roll that into a tracking bug for looking in more detail at all extractors. Issue: #3040 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161650940 --- .../exoplayer2/extractor/ts/TsExtractor.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 7b63ce813c..1149856649 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -111,7 +111,6 @@ public final class TsExtractor implements Extractor { @Mode private final int mode; private final List timestampAdjusters; private final ParsableByteArray tsPacketBuffer; - private final ParsableBitArray tsScratch; private final SparseIntArray continuityCounters; private final TsPayloadReader.Factory payloadReaderFactory; private final SparseArray tsPayloadReaders; // Indexed by pid @@ -164,7 +163,6 @@ public final class TsExtractor implements Extractor { timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); - tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); @@ -250,24 +248,23 @@ public final class TsExtractor implements Extractor { return RESULT_CONTINUE; } - tsPacketBuffer.skipBytes(1); - tsPacketBuffer.readBytes(tsScratch, 3); - if (tsScratch.readBit()) { // transport_error_indicator + int tsPacketHeader = tsPacketBuffer.readInt(); + if ((tsPacketHeader & 0x800000) != 0) { // transport_error_indicator // There are uncorrectable errors in this packet. tsPacketBuffer.setPosition(endOfPacket); return RESULT_CONTINUE; } - boolean payloadUnitStartIndicator = tsScratch.readBit(); - tsScratch.skipBits(1); // transport_priority - int pid = tsScratch.readBits(13); - tsScratch.skipBits(2); // transport_scrambling_control - boolean adaptationFieldExists = tsScratch.readBit(); - boolean payloadExists = tsScratch.readBit(); + boolean payloadUnitStartIndicator = (tsPacketHeader & 0x400000) != 0; + // Ignoring transport_priority (tsPacketHeader & 0x200000) + int pid = (tsPacketHeader & 0x1FFF00) >> 8; + // Ignoring transport_scrambling_control (tsPacketHeader & 0xC0) + boolean adaptationFieldExists = (tsPacketHeader & 0x20) != 0; + boolean payloadExists = (tsPacketHeader & 0x10) != 0; // Discontinuity check. boolean discontinuityFound = false; - int continuityCounter = tsScratch.readBits(4); if (mode != MODE_HLS) { + int continuityCounter = tsPacketHeader & 0xF; int previousCounter = continuityCounters.get(pid, continuityCounter - 1); continuityCounters.put(pid, continuityCounter); if (previousCounter == continuityCounter) { @@ -276,7 +273,7 @@ public final class TsExtractor implements Extractor { tsPacketBuffer.setPosition(endOfPacket); return RESULT_CONTINUE; } - } else if (continuityCounter != (previousCounter + 1) % 16) { + } else if (continuityCounter != ((previousCounter + 1) & 0xF)) { discontinuityFound = true; } } @@ -296,7 +293,6 @@ public final class TsExtractor implements Extractor { } tsPacketBuffer.setLimit(endOfPacket); payloadReader.consume(tsPacketBuffer, payloadUnitStartIndicator); - Assertions.checkState(tsPacketBuffer.getPosition() <= endOfPacket); tsPacketBuffer.setLimit(limit); } } From fba0546774133c0d7b78b575e96ba8208a88cab5 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 12 Jul 2017 08:12:11 -0700 Subject: [PATCH 089/133] Optimize ParsableBitArray ParsableBitArray.readBit in particular was doing an excessive amount of work. The new implementation is ~20% faster on desktop. Issue: #3040 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161666420 --- .../exoplayer2/util/ParsableBitArrayTest.java | 139 ++++++++++++++++++ .../exoplayer2/util/ParsableBitArray.java | 73 ++++----- 2 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java new file mode 100644 index 0000000000..cfb9cd78be --- /dev/null +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableBitArrayTest.java @@ -0,0 +1,139 @@ +/* + * 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.util; + +import android.test.MoreAsserts; + +import junit.framework.TestCase; + +/** + * Tests for {@link ParsableBitArray}. + */ +public final class ParsableBitArrayTest extends TestCase { + + private static final byte[] TEST_DATA = new byte[] {0x3C, (byte) 0xD2, (byte) 0x5F, (byte) 0x01, + (byte) 0xFF, (byte) 0x14, (byte) 0x60, (byte) 0x99}; + + public void testReadAllBytes() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + byte[] bytesRead = new byte[TEST_DATA.length]; + testArray.readBytes(bytesRead, 0, TEST_DATA.length); + MoreAsserts.assertEquals(TEST_DATA, bytesRead); + assertEquals(TEST_DATA.length * 8, testArray.getPosition()); + assertEquals(TEST_DATA.length, testArray.getBytePosition()); + } + + public void testReadBit() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + assertReadBitsToEnd(0, testArray); + } + + public void testReadBits() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + assertEquals(getTestDataBits(0, 5), testArray.readBits(5)); + assertEquals(getTestDataBits(5, 3), testArray.readBits(3)); + assertEquals(getTestDataBits(8, 16), testArray.readBits(16)); + assertEquals(getTestDataBits(24, 3), testArray.readBits(3)); + assertEquals(getTestDataBits(27, 18), testArray.readBits(18)); + assertEquals(getTestDataBits(45, 5), testArray.readBits(5)); + assertEquals(getTestDataBits(50, 14), testArray.readBits(14)); + } + + public void testRead32BitsByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + assertEquals(getTestDataBits(0, 32), testArray.readBits(32)); + assertEquals(getTestDataBits(32, 32), testArray.readBits(32)); + } + + public void testRead32BitsNonByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + assertEquals(getTestDataBits(0, 5), testArray.readBits(5)); + assertEquals(getTestDataBits(5, 32), testArray.readBits(32)); + } + + public void testSkipBytes() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.skipBytes(2); + assertReadBitsToEnd(16, testArray); + } + + public void testSkipBitsByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.skipBits(16); + assertReadBitsToEnd(16, testArray); + } + + public void testSkipBitsNonByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.skipBits(5); + assertReadBitsToEnd(5, testArray); + } + + public void testSetPositionByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.setPosition(16); + assertReadBitsToEnd(16, testArray); + } + + public void testSetPositionNonByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.setPosition(5); + assertReadBitsToEnd(5, testArray); + } + + public void testByteAlignFromNonByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.setPosition(11); + testArray.byteAlign(); + assertEquals(2, testArray.getBytePosition()); + assertEquals(16, testArray.getPosition()); + assertReadBitsToEnd(16, testArray); + } + + public void testByteAlignFromByteAligned() { + ParsableBitArray testArray = new ParsableBitArray(TEST_DATA); + testArray.setPosition(16); + testArray.byteAlign(); // Should be a no-op. + assertEquals(2, testArray.getBytePosition()); + assertEquals(16, testArray.getPosition()); + assertReadBitsToEnd(16, testArray); + } + + private static void assertReadBitsToEnd(int expectedStartPosition, ParsableBitArray testArray) { + int position = testArray.getPosition(); + assertEquals(expectedStartPosition, position); + for (int i = position; i < TEST_DATA.length * 8; i++) { + assertEquals(getTestDataBit(i), testArray.readBit()); + assertEquals(i + 1, testArray.getPosition()); + } + } + + private static int getTestDataBits(int bitPosition, int length) { + int result = 0; + for (int i = 0; i < length; i++) { + result = result << 1; + if (getTestDataBit(bitPosition++)) { + result |= 0x1; + } + } + return result; + } + + private static boolean getTestDataBit(int bitPosition) { + return (TEST_DATA[bitPosition / 8] & (0x80 >>> (bitPosition % 8))) != 0; + } + +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java index df9f04f067..0456bcb879 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java @@ -110,14 +110,26 @@ public final class ParsableBitArray { assertValidOffset(); } + /** + * Skips a single bit. + */ + public void skipBit() { + if (++bitOffset == 8) { + bitOffset = 0; + byteOffset++; + } + assertValidOffset(); + } + /** * Skips bits and moves current reading position forward. * - * @param n The number of bits to skip. + * @param numBits The number of bits to skip. */ - public void skipBits(int n) { - byteOffset += (n / 8); - bitOffset += (n % 8); + public void skipBits(int numBits) { + int numBytes = numBits / 8; + byteOffset += numBytes; + bitOffset += numBits - (numBytes * 8); if (bitOffset > 7) { byteOffset++; bitOffset -= 8; @@ -131,7 +143,9 @@ public final class ParsableBitArray { * @return Whether the bit is set. */ public boolean readBit() { - return readBits(1) == 1; + boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0; + skipBit(); + return returnValue; } /** @@ -141,48 +155,18 @@ public final class ParsableBitArray { * @return An integer whose bottom n bits hold the read data. */ public int readBits(int numBits) { - if (numBits == 0) { - return 0; - } - int returnValue = 0; - - // Read as many whole bytes as we can. - int wholeBytes = (numBits / 8); - for (int i = 0; i < wholeBytes; i++) { - int byteValue; - if (bitOffset != 0) { - byteValue = ((data[byteOffset] & 0xFF) << bitOffset) - | ((data[byteOffset + 1] & 0xFF) >>> (8 - bitOffset)); - } else { - byteValue = data[byteOffset]; - } - numBits -= 8; - returnValue |= (byteValue & 0xFF) << numBits; + bitOffset += numBits; + while (bitOffset > 8) { + bitOffset -= 8; + returnValue |= (data[byteOffset++] & 0xFF) << bitOffset; + } + returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; + returnValue &= 0xFFFFFFFF >>> (32 - numBits); + if (bitOffset == 8) { + bitOffset = 0; byteOffset++; } - - // Read any remaining bits. - if (numBits > 0) { - int nextBit = bitOffset + numBits; - byte writeMask = (byte) (0xFF >> (8 - numBits)); - - if (nextBit > 8) { - // Combine bits from current byte and next byte. - returnValue |= ((((data[byteOffset] & 0xFF) << (nextBit - 8) - | ((data[byteOffset + 1] & 0xFF) >> (16 - nextBit))) & writeMask)); - byteOffset++; - } else { - // Bits to be read only within current byte. - returnValue |= (((data[byteOffset] & 0xFF) >> (8 - nextBit)) & writeMask); - if (nextBit == 8) { - byteOffset++; - } - } - - bitOffset = nextBit % 8; - } - assertValidOffset(); return returnValue; } @@ -231,7 +215,6 @@ public final class ParsableBitArray { private void assertValidOffset() { // It is fine for position to be at the end of the array, but no further. Assertions.checkState(byteOffset >= 0 - && (bitOffset >= 0 && bitOffset < 8) && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0))); } From 17e73bdc7859852cab8821501da6c61bbcb7dc29 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 12 Jul 2017 09:25:08 -0700 Subject: [PATCH 090/133] Optimize ParsableNalUnitBitArray Apply the same learnings as in ParsableBitArray. Issue: #3040 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161674119 --- .../util/ParsableNalUnitBitArrayTest.java | 2 +- .../exoplayer2/extractor/ts/H264Reader.java | 2 +- .../exoplayer2/extractor/ts/H265Reader.java | 14 +-- .../android/exoplayer2/util/NalUnitUtil.java | 10 +-- .../util/ParsableNalUnitBitArray.java | 85 ++++++++----------- 5 files changed, 48 insertions(+), 65 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java index b62aff46f5..294d3d352a 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArrayTest.java @@ -95,7 +95,7 @@ public final class ParsableNalUnitBitArrayTest extends TestCase { ParsableNalUnitBitArray array = new ParsableNalUnitBitArray(createByteArray(0, 0, 3, 128, 0), 0, 5); assertFalse(array.canReadExpGolombCodedNum()); - array.skipBits(1); + array.skipBit(); assertTrue(array.canReadExpGolombCodedNum()); assertEquals(32767, array.readUnsignedExpGolombCodedInt()); assertFalse(array.canReadBits(1)); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 8206ed7d6d..3cde946ce3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -316,7 +316,7 @@ public final class H264Reader implements ElementaryStreamReader { if (!bitArray.canReadBits(8)) { return; } - bitArray.skipBits(1); // forbidden_zero_bit + bitArray.skipBit(); // forbidden_zero_bit int nalRefIdc = bitArray.readBits(2); bitArray.skipBits(5); // nal_unit_type diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index 712ca8d69c..f6ae80ba56 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -225,7 +225,7 @@ public final class H265Reader implements ElementaryStreamReader { ParsableNalUnitBitArray bitArray = new ParsableNalUnitBitArray(sps.nalData, 0, sps.nalLength); bitArray.skipBits(40 + 4); // NAL header, sps_video_parameter_set_id int maxSubLayersMinus1 = bitArray.readBits(3); - bitArray.skipBits(1); // sps_temporal_id_nesting_flag + bitArray.skipBit(); // sps_temporal_id_nesting_flag // profile_tier_level(1, sps_max_sub_layers_minus1) bitArray.skipBits(88); // if (profilePresentFlag) {...} @@ -247,7 +247,7 @@ public final class H265Reader implements ElementaryStreamReader { bitArray.readUnsignedExpGolombCodedInt(); // sps_seq_parameter_set_id int chromaFormatIdc = bitArray.readUnsignedExpGolombCodedInt(); if (chromaFormatIdc == 3) { - bitArray.skipBits(1); // separate_colour_plane_flag + bitArray.skipBit(); // separate_colour_plane_flag } int picWidthInLumaSamples = bitArray.readUnsignedExpGolombCodedInt(); int picHeightInLumaSamples = bitArray.readUnsignedExpGolombCodedInt(); @@ -288,7 +288,7 @@ public final class H265Reader implements ElementaryStreamReader { bitArray.skipBits(8); bitArray.readUnsignedExpGolombCodedInt(); // log2_min_pcm_luma_coding_block_size_minus3 bitArray.readUnsignedExpGolombCodedInt(); // log2_diff_max_min_pcm_luma_coding_block_size - bitArray.skipBits(1); // pcm_loop_filter_disabled_flag + bitArray.skipBit(); // pcm_loop_filter_disabled_flag } // Skips all short term reference picture sets. skipShortTermRefPicSets(bitArray); @@ -365,11 +365,11 @@ public final class H265Reader implements ElementaryStreamReader { interRefPicSetPredictionFlag = bitArray.readBit(); } if (interRefPicSetPredictionFlag) { - bitArray.skipBits(1); // delta_rps_sign + bitArray.skipBit(); // delta_rps_sign bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1 for (int j = 0; j <= previousNumDeltaPocs; j++) { if (bitArray.readBit()) { // used_by_curr_pic_flag[j] - bitArray.skipBits(1); // use_delta_flag[j] + bitArray.skipBit(); // use_delta_flag[j] } } } else { @@ -378,11 +378,11 @@ public final class H265Reader implements ElementaryStreamReader { previousNumDeltaPocs = numNegativePics + numPositivePics; for (int i = 0; i < numNegativePics; i++) { bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s0_minus1[i] - bitArray.skipBits(1); // used_by_curr_pic_s0_flag[i] + bitArray.skipBit(); // used_by_curr_pic_s0_flag[i] } for (int i = 0; i < numPositivePics; i++) { bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s1_minus1[i] - bitArray.skipBits(1); // used_by_curr_pic_s1_flag[i] + bitArray.skipBit(); // used_by_curr_pic_s1_flag[i] } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java index ab2fec0db7..c4ed20546d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/NalUnitUtil.java @@ -265,7 +265,7 @@ public final class NalUnitUtil { } data.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8 data.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8 - data.skipBits(1); // qpprime_y_zero_transform_bypass_flag + data.skipBit(); // qpprime_y_zero_transform_bypass_flag boolean seqScalingMatrixPresentFlag = data.readBit(); if (seqScalingMatrixPresentFlag) { int limit = (chromaFormatIdc != 3) ? 8 : 12; @@ -295,17 +295,17 @@ public final class NalUnitUtil { } } data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames - data.skipBits(1); // gaps_in_frame_num_value_allowed_flag + data.skipBit(); // gaps_in_frame_num_value_allowed_flag int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1; int picHeightInMapUnits = data.readUnsignedExpGolombCodedInt() + 1; boolean frameMbsOnlyFlag = data.readBit(); int frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * picHeightInMapUnits; if (!frameMbsOnlyFlag) { - data.skipBits(1); // mb_adaptive_frame_field_flag + data.skipBit(); // mb_adaptive_frame_field_flag } - data.skipBits(1); // direct_8x8_inference_flag + data.skipBit(); // direct_8x8_inference_flag int frameWidth = picWidthInMbs * 16; int frameHeight = frameHeightInMbs * 16; boolean frameCroppingFlag = data.readBit(); @@ -368,7 +368,7 @@ public final class NalUnitUtil { data.skipBits(8); // nal_unit int picParameterSetId = data.readUnsignedExpGolombCodedInt(); int seqParameterSetId = data.readUnsignedExpGolombCodedInt(); - data.skipBits(1); // entropy_coding_mode_flag + data.skipBit(); // entropy_coding_mode_flag boolean bottomFieldPicOrderInFramePresentFlag = data.readBit(); return new PpsData(picParameterSetId, seqParameterSetId, bottomFieldPicOrderInFramePresentFlag); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java index 05d7a9929d..443c69909c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java @@ -54,15 +54,27 @@ public final class ParsableNalUnitBitArray { assertValidOffset(); } + /** + * Skips a single bit. + */ + public void skipBit() { + if (++bitOffset == 8) { + bitOffset = 0; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; + } + assertValidOffset(); + } + /** * Skips bits and moves current reading position forward. * - * @param n The number of bits to skip. + * @param numBits The number of bits to skip. */ - public void skipBits(int n) { + public void skipBits(int numBits) { int oldByteOffset = byteOffset; - byteOffset += (n / 8); - bitOffset += (n % 8); + int numBytes = numBits / 8; + byteOffset += numBytes; + bitOffset += numBits - (numBytes * 8); if (bitOffset > 7) { byteOffset++; bitOffset -= 8; @@ -81,13 +93,14 @@ public final class ParsableNalUnitBitArray { * Returns whether it's possible to read {@code n} bits starting from the current offset. The * offset is not modified. * - * @param n The number of bits. + * @param numBits The number of bits. * @return Whether it is possible to read {@code n} bits. */ - public boolean canReadBits(int n) { + public boolean canReadBits(int numBits) { int oldByteOffset = byteOffset; - int newByteOffset = byteOffset + (n / 8); - int newBitOffset = bitOffset + (n % 8); + int numBytes = numBits / 8; + int newByteOffset = byteOffset + numBytes; + int newBitOffset = bitOffset + numBits - (numBytes * 8); if (newBitOffset > 7) { newByteOffset++; newBitOffset -= 8; @@ -108,7 +121,9 @@ public final class ParsableNalUnitBitArray { * @return Whether the bit is set. */ public boolean readBit() { - return readBits(1) == 1; + boolean returnValue = (data[byteOffset] & (0x80 >> bitOffset)) != 0; + skipBit(); + return returnValue; } /** @@ -118,50 +133,19 @@ public final class ParsableNalUnitBitArray { * @return An integer whose bottom n bits hold the read data. */ public int readBits(int numBits) { - if (numBits == 0) { - return 0; - } - int returnValue = 0; - - // Read as many whole bytes as we can. - int wholeBytes = (numBits / 8); - for (int i = 0; i < wholeBytes; i++) { - int nextByteOffset = shouldSkipByte(byteOffset + 1) ? byteOffset + 2 : byteOffset + 1; - int byteValue; - if (bitOffset != 0) { - byteValue = ((data[byteOffset] & 0xFF) << bitOffset) - | ((data[nextByteOffset] & 0xFF) >>> (8 - bitOffset)); - } else { - byteValue = data[byteOffset]; - } - numBits -= 8; - returnValue |= (byteValue & 0xFF) << numBits; - byteOffset = nextByteOffset; + bitOffset += numBits; + while (bitOffset > 8) { + bitOffset -= 8; + returnValue |= (data[byteOffset] & 0xFF) << bitOffset; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; } - - // Read any remaining bits. - if (numBits > 0) { - int nextBit = bitOffset + numBits; - byte writeMask = (byte) (0xFF >> (8 - numBits)); - int nextByteOffset = shouldSkipByte(byteOffset + 1) ? byteOffset + 2 : byteOffset + 1; - - if (nextBit > 8) { - // Combine bits from current byte and next byte. - returnValue |= ((((data[byteOffset] & 0xFF) << (nextBit - 8) - | ((data[nextByteOffset] & 0xFF) >> (16 - nextBit))) & writeMask)); - byteOffset = nextByteOffset; - } else { - // Bits to be read only within current byte. - returnValue |= (((data[byteOffset] & 0xFF) >> (8 - nextBit)) & writeMask); - if (nextBit == 8) { - byteOffset = nextByteOffset; - } - } - - bitOffset = nextBit % 8; + returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; + returnValue &= 0xFFFFFFFF >>> (32 - numBits); + if (bitOffset == 8) { + bitOffset = 0; + byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; } - assertValidOffset(); return returnValue; } @@ -220,7 +204,6 @@ public final class ParsableNalUnitBitArray { private void assertValidOffset() { // It is fine for position to be at the end of the array, but no further. Assertions.checkState(byteOffset >= 0 - && (bitOffset >= 0 && bitOffset < 8) && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0))); } From a39ab8161e035174c862fb42b0c522baf42f22fb Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 12 Jul 2017 09:53:20 -0700 Subject: [PATCH 091/133] Fix FlacStreamInfo to not call readBits with a >32 value ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161677399 --- .../com/google/android/exoplayer2/util/FlacStreamInfo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamInfo.java index 8a69a6d095..6382f1130e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/FlacStreamInfo.java @@ -47,7 +47,8 @@ public final class FlacStreamInfo { this.sampleRate = scratch.readBits(20); this.channels = scratch.readBits(3) + 1; this.bitsPerSample = scratch.readBits(5) + 1; - this.totalSamples = scratch.readBits(36); + this.totalSamples = ((scratch.readBits(4) & 0xFL) << 32) + | (scratch.readBits(32) & 0xFFFFFFFFL); // Remaining 16 bytes is md5 value } From 977fb225e3ba58965b16bda99483f5e5eef9313e Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 12 Jul 2017 11:55:49 -0700 Subject: [PATCH 092/133] Minor tweak to demo app "Default (none)" is sometimes just wrong, since the track selector may attempt to select a track even if it exceeds the renderer's capabilities. Just "Default", as it used to be, was more accurate. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161695241 --- .../android/exoplayer2/demo/TrackSelectionHelper.java | 7 +------ demo/src/main/res/values/strings.xml | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java index 033b515767..fb7217f8fd 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java @@ -136,7 +136,6 @@ import java.util.Arrays; root.addView(defaultView); // Per-track views. - boolean haveSupportedTracks = false; boolean haveAdaptiveTracks = false; trackViews = new CheckedTextView[trackGroups.length][]; for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { @@ -159,7 +158,6 @@ import java.util.Arrays; trackView.setFocusable(true); trackView.setTag(Pair.create(groupIndex, trackIndex)); trackView.setOnClickListener(this); - haveSupportedTracks = true; } else { trackView.setFocusable(false); trackView.setEnabled(false); @@ -169,10 +167,7 @@ import java.util.Arrays; } } - if (!haveSupportedTracks) { - // Indicate that the default selection will be nothing. - defaultView.setText(R.string.selection_default_none); - } else if (haveAdaptiveTracks) { + if (haveAdaptiveTracks) { // View for using random adaptation. enableRandomAdaptationView = (CheckedTextView) inflater.inflate( android.R.layout.simple_list_item_multiple_choice, root, false); diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml index ac17ad4443..4eb2b89324 100644 --- a/demo/src/main/res/values/strings.xml +++ b/demo/src/main/res/values/strings.xml @@ -30,8 +30,6 @@ Default - Default (none) - Unexpected intent action: %1$s Enable random adaptation From 1855423734287861163ccb82258779555159dad8 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 13 Jul 2017 05:59:15 -0700 Subject: [PATCH 093/133] Simplify + optimize VorbisBitArray ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161796758 --- .../extractor/ogg/VorbisBitArrayTest.java | 131 ------------------ .../extractor/ogg/VorbisBitArray.java | 85 ++++-------- 2 files changed, 28 insertions(+), 188 deletions(-) diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArrayTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArrayTest.java index 9a65cad6a5..a24cb1599b 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArrayTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArrayTest.java @@ -25,36 +25,26 @@ public final class VorbisBitArrayTest extends TestCase { public void testReadBit() { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x5c, 0x50)); - assertFalse(bitArray.readBit()); assertFalse(bitArray.readBit()); assertTrue(bitArray.readBit()); assertTrue(bitArray.readBit()); - assertTrue(bitArray.readBit()); assertFalse(bitArray.readBit()); assertTrue(bitArray.readBit()); assertFalse(bitArray.readBit()); - assertFalse(bitArray.readBit()); assertFalse(bitArray.readBit()); assertFalse(bitArray.readBit()); assertFalse(bitArray.readBit()); - assertTrue(bitArray.readBit()); assertFalse(bitArray.readBit()); assertTrue(bitArray.readBit()); assertFalse(bitArray.readBit()); - - try { - assertFalse(bitArray.readBit()); - fail(); - } catch (IllegalStateException e) {/* ignored */} } public void testSkipBits() { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F)); - bitArray.skipBits(10); assertEquals(10, bitArray.getPosition()); assertTrue(bitArray.readBit()); @@ -64,27 +54,10 @@ public final class VorbisBitArrayTest extends TestCase { assertEquals(14, bitArray.getPosition()); assertFalse(bitArray.readBit()); assertFalse(bitArray.readBit()); - try { - bitArray.readBit(); - fail(); - } catch (IllegalStateException e) { - // ignored - } - } - - - public void testSkipBitsThrowsErrorIfEOB() { - VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F)); - - try { - bitArray.skipBits(17); - fail(); - } catch (IllegalStateException e) {/* ignored */} } public void testGetPosition() throws Exception { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F)); - assertEquals(0, bitArray.getPosition()); bitArray.readBit(); assertEquals(1, bitArray.getPosition()); @@ -96,35 +69,11 @@ public final class VorbisBitArrayTest extends TestCase { public void testSetPosition() throws Exception { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F)); - assertEquals(0, bitArray.getPosition()); bitArray.setPosition(4); assertEquals(4, bitArray.getPosition()); - bitArray.setPosition(15); assertFalse(bitArray.readBit()); - try { - bitArray.readBit(); - fail(); - } catch (IllegalStateException e) {/* ignored */} - - } - public void testSetPositionIllegalPositions() throws Exception { - VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xF0, 0x0F)); - - try { - bitArray.setPosition(16); - fail(); - } catch (IllegalArgumentException e) { - assertEquals(0, bitArray.getPosition()); - } - - try { - bitArray.setPosition(-1); - fail(); - } catch (IllegalArgumentException e) { - assertEquals(0, bitArray.getPosition()); - } } public void testReadInt32() { @@ -136,13 +85,11 @@ public final class VorbisBitArrayTest extends TestCase { public void testReadBits() throws Exception { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0x03, 0x22)); - assertEquals(3, bitArray.readBits(2)); bitArray.skipBits(6); assertEquals(2, bitArray.readBits(2)); bitArray.skipBits(2); assertEquals(2, bitArray.readBits(2)); - bitArray.reset(); assertEquals(0x2203, bitArray.readBits(16)); } @@ -156,7 +103,6 @@ public final class VorbisBitArrayTest extends TestCase { public void testReadBitsBeyondByteBoundaries() throws Exception { VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xFF, 0x0F, 0xFF, 0x0F)); - assertEquals(0x0FFF0FFF, bitArray.readBits(32)); bitArray.reset(); @@ -188,83 +134,6 @@ public final class VorbisBitArrayTest extends TestCase { assertEquals(0, bitArray.getPosition()); bitArray.readBit(); assertEquals(1, bitArray.getPosition()); - - try { - bitArray.readBits(24); - fail(); - } catch (IllegalStateException e) { - assertEquals(1, bitArray.getPosition()); - } - } - - public void testLimit() { - VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xc0, 0x02), 1); - - try { - bitArray.skipBits(9); - fail(); - } catch (IllegalStateException e) { - assertEquals(0, bitArray.getPosition()); - } - - try { - bitArray.readBits(9); - fail(); - } catch (IllegalStateException e) { - assertEquals(0, bitArray.getPosition()); - } - - int byteValue = bitArray.readBits(8); - assertEquals(0xc0, byteValue); - assertEquals(8, bitArray.getPosition()); - try { - bitArray.readBit(); - fail(); - } catch (IllegalStateException e) { - assertEquals(8, bitArray.getPosition()); - } - } - - public void testBitsLeft() { - VorbisBitArray bitArray = new VorbisBitArray(TestUtil.createByteArray(0xc0, 0x02)); - - assertEquals(16, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.skipBits(1); - assertEquals(15, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.skipBits(3); - assertEquals(12, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.setPosition(6); - assertEquals(10, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.skipBits(1); - assertEquals(9, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.skipBits(1); - assertEquals(8, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.readBits(4); - assertEquals(4, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - bitArray.readBits(4); - assertEquals(0, bitArray.bitsLeft()); - assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); - - try { - bitArray.readBit(); - fail(); - } catch (IllegalStateException e) { - assertEquals(0, bitArray.bitsLeft()); - } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArray.java index ae52e80299..958a2ef955 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisBitArray.java @@ -25,8 +25,9 @@ import com.google.android.exoplayer2.util.Assertions; */ /* package */ final class VorbisBitArray { - public final byte[] data; - private final int limit; + private final byte[] data; + private final int byteLimit; + private int byteOffset; private int bitOffset; @@ -36,18 +37,8 @@ import com.google.android.exoplayer2.util.Assertions; * @param data the array to wrap. */ public VorbisBitArray(byte[] data) { - this(data, data.length); - } - - /** - * Creates a new instance that wraps an existing array. - * - * @param data the array to wrap. - * @param limit the limit in bytes. - */ - public VorbisBitArray(byte[] data, int limit) { this.data = data; - this.limit = limit * 8; + byteLimit = data.length; } /** @@ -64,7 +55,9 @@ import com.google.android.exoplayer2.util.Assertions; * @return {@code true} if the bit is set, {@code false} otherwise. */ public boolean readBit() { - return readBits(1) == 1; + boolean returnValue = (((data[byteOffset] & 0xFF) >> bitOffset) & 0x01) == 1; + skipBits(1); + return returnValue; } /** @@ -74,53 +67,32 @@ import com.google.android.exoplayer2.util.Assertions; * @return An integer whose bottom {@code numBits} bits hold the read data. */ public int readBits(int numBits) { - Assertions.checkState(getPosition() + numBits <= limit); - if (numBits == 0) { - return 0; + int tempByteOffset = byteOffset; + int bitsRead = Math.min(numBits, 8 - bitOffset); + int returnValue = ((data[tempByteOffset++] & 0xFF) >> bitOffset) & (0xFF >> (8 - bitsRead)); + while (bitsRead < numBits) { + returnValue |= (data[tempByteOffset++] & 0xFF) << bitsRead; + bitsRead += 8; } - int result = 0; - int bitCount = 0; - if (bitOffset != 0) { - bitCount = Math.min(numBits, 8 - bitOffset); - int mask = 0xFF >>> (8 - bitCount); - result = (data[byteOffset] >>> bitOffset) & mask; - bitOffset += bitCount; - if (bitOffset == 8) { - byteOffset++; - bitOffset = 0; - } - } - - if (numBits - bitCount > 7) { - int numBytes = (numBits - bitCount) / 8; - for (int i = 0; i < numBytes; i++) { - result |= (data[byteOffset++] & 0xFFL) << bitCount; - bitCount += 8; - } - } - - if (numBits > bitCount) { - int bitsOnNextByte = numBits - bitCount; - int mask = 0xFF >>> (8 - bitsOnNextByte); - result |= (data[byteOffset] & mask) << bitCount; - bitOffset += bitsOnNextByte; - } - return result; + returnValue &= 0xFFFFFFFF >>> (32 - numBits); + skipBits(numBits); + return returnValue; } /** * Skips {@code numberOfBits} bits. * - * @param numberOfBits The number of bits to skip. + * @param numBits The number of bits to skip. */ - public void skipBits(int numberOfBits) { - Assertions.checkState(getPosition() + numberOfBits <= limit); - byteOffset += numberOfBits / 8; - bitOffset += numberOfBits % 8; + public void skipBits(int numBits) { + int numBytes = numBits / 8; + byteOffset += numBytes; + bitOffset += numBits - (numBytes * 8); if (bitOffset > 7) { byteOffset++; bitOffset -= 8; } + assertValidOffset(); } /** @@ -136,23 +108,22 @@ import com.google.android.exoplayer2.util.Assertions; * @param position The new reading position in bits. */ public void setPosition(int position) { - Assertions.checkArgument(position < limit && position >= 0); byteOffset = position / 8; bitOffset = position - (byteOffset * 8); + assertValidOffset(); } /** * Returns the number of remaining bits. */ public int bitsLeft() { - return limit - getPosition(); + return (byteLimit - byteOffset) * 8 - bitOffset; } - /** - * Returns the limit in bits. - **/ - public int limit() { - return limit; + private void assertValidOffset() { + // It is fine for position to be at the end of the array, but no further. + Assertions.checkState(byteOffset >= 0 + && (byteOffset < byteLimit || (byteOffset == byteLimit && bitOffset == 0))); } } From 055abc759269e48dd4a9c7a1b315012d3abf0ac1 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 4 Jul 2017 09:38:57 -0700 Subject: [PATCH 094/133] Detect playlist stuck and playlist reset conditions in HLS This CL aims that the player fails upon: - Playlist that don't change in a suspiciously long time, which might mean there are server side issues. - Playlist with a media sequence lower that its last snapshot and no overlapping segments. This two error conditions are propagated through the renderer, but not through MediaSource#maybeThrowSourceInfoRefreshError. Issue:#2872 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160899995 --- .../hls/playlist/HlsPlaylistTracker.java | 82 ++++++++++++++++--- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java index 62b77a0575..567dbd4af6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java @@ -40,6 +40,38 @@ import java.util.List; */ public final class HlsPlaylistTracker implements Loader.Callback> { + /** + * Thrown when a playlist is considered to be stuck due to a server side error. + */ + public static final class PlaylistStuckException extends IOException { + + /** + * The url of the stuck playlist. + */ + public final String url; + + private PlaylistStuckException(String url) { + this.url = url; + } + + } + + /** + * Thrown when the media sequence of a new snapshot indicates the server has reset. + */ + public static final class PlaylistResetException extends IOException { + + /** + * The url of the reset playlist. + */ + public final String url; + + private PlaylistResetException(String url) { + this.url = url; + } + + } + /** * Listener for primary playlist changes. */ @@ -75,6 +107,11 @@ public final class HlsPlaylistTracker implements Loader.Callback C.usToMs(playlistSnapshot.targetDurationUs) + * PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT) { + // The playlist seems to be stuck, we blacklist it. + playlistError = new PlaylistStuckException(playlistUrl.url); + blacklistPlaylist(); + } else if (loadedPlaylist.mediaSequence + loadedPlaylist.segments.size() + < playlistSnapshot.mediaSequence) { + // The media sequence has jumped backwards. The server has likely reset. + playlistError = new PlaylistResetException(playlistUrl.url); + } refreshDelayUs = playlistSnapshot.targetDurationUs / 2; } if (refreshDelayUs != C.TIME_UNSET) { @@ -554,6 +610,12 @@ public final class HlsPlaylistTracker implements Loader.Callback Date: Wed, 19 Jul 2017 11:52:12 -0700 Subject: [PATCH 095/133] Update release notes + bump version number ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=162514848 --- RELEASENOTES.md | 11 +++++++++++ build.gradle | 2 +- demo/src/main/AndroidManifest.xml | 4 ++-- .../android/exoplayer2/ExoPlayerLibraryInfo.java | 6 +++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f9f6b02c19..ff1bd42fde 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,16 @@ # Release notes # +### r2.4.4 ### + +* HLS/MPEG-TS: Some initial optimizations of MPEG-TS extractor performance + ([#3040](https://github.com/google/ExoPlayer/issues/3040)). +* HLS: Fix propagation of format identifier for CEA-608 + ([#3033](https://github.com/google/ExoPlayer/issues/3033)). +* HLS: Detect playlist stuck and reset conditions + ([#2872](https://github.com/google/ExoPlayer/issues/2872)). +* Video: Fix video dimension reporting on some devices + ([#3007](https://github.com/google/ExoPlayer/issues/3007)). + ### r2.4.3 ### * Audio: Workaround custom audio decoders misreporting their maximum supported diff --git a/build.gradle b/build.gradle index 01d8b6616c..a4ae1f175e 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ allprojects { releaseRepoName = getBintrayRepo() releaseUserOrg = 'google' releaseGroupId = 'com.google.android.exoplayer' - releaseVersion = 'r2.4.3' + releaseVersion = 'r2.4.4' releaseWebsite = 'https://github.com/google/ExoPlayer' } if (it.hasProperty('externalBuildDir')) { diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index addce60cad..afcddccac9 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2404" + android:versionName="2.4.4"> diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 650ce727cd..73d91f293e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -24,13 +24,13 @@ public interface 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. - String VERSION = "2.4.3"; + String VERSION = "2.4.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. - String VERSION_SLASHY = "ExoPlayerLib/2.4.3"; + String VERSION_SLASHY = "ExoPlayerLib/2.4.4"; /** * The version of the library expressed as an integer, for example 1002003. @@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - int VERSION_INT = 2004003; + int VERSION_INT = 2004004; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} From 8e7a52483a38543d16a7cfd1d5a8c0717f5533e5 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Sat, 22 Jul 2017 02:17:02 -0700 Subject: [PATCH 096/133] Fix PlaybackControlView's repeat mode button update when player is null ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=162824522 --- .../google/android/exoplayer2/ui/PlaybackControlView.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index 7c4afa772a..a99c2dfde2 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -660,6 +660,11 @@ public class PlaybackControlView extends FrameLayout { repeatToggleButton.setVisibility(View.GONE); return; } + if (player == null) { + setButtonEnabled(false, repeatToggleButton); + return; + } + setButtonEnabled(true, repeatToggleButton); switch (player.getRepeatMode()) { case Player.REPEAT_MODE_OFF: repeatToggleButton.setImageDrawable(repeatOffButtonDrawable); From ad5c8af01957975bb8eb61dc7f3edf60e2e4aa39 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 28 Jul 2017 04:45:52 -0700 Subject: [PATCH 097/133] Allow apps to handle ad clicked/tapped events Issue: #3106 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163455563 --- .../exoplayer2/ext/ima/ImaAdsLoader.java | 22 ++++++++++- .../exoplayer2/ext/ima/ImaAdsMediaSource.java | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index 6e2206d6ae..fc8000b397 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -61,7 +61,7 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, /** * Listener for ad loader events. All methods are called on the main thread. */ - public interface EventListener { + /* package */ interface EventListener { /** * Called when the ad playback state has been updated. @@ -77,6 +77,16 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, */ void onLoadError(IOException error); + /** + * Called when the user clicks through an ad (for example, following a 'learn more' link). + */ + void onAdClicked(); + + /** + * Called when the user taps a non-clickthrough part of an ad. + */ + void onAdTapped(); + } static { @@ -337,6 +347,16 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, imaPausedContent = true; pauseContentInternal(); break; + case TAPPED: + if (eventListener != null) { + eventListener.onAdTapped(); + } + break; + case CLICKED: + if (eventListener != null) { + eventListener.onAdClicked(); + } + break; case CONTENT_RESUME_REQUESTED: imaPausedContent = false; resumeContentInternal(); diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java index 9c2eb4b404..0bf5773d2c 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java @@ -53,6 +53,16 @@ public final class ImaAdsMediaSource implements MediaSource { */ void onAdLoadError(IOException error); + /** + * Called when the user clicks through an ad (for example, following a 'learn more' link). + */ + void onAdClicked(); + + /** + * Called when the user taps a non-clickthrough part of an ad. + */ + void onAdTapped(); + } private static final String TAG = "ImaAdsMediaSource"; @@ -305,6 +315,34 @@ public final class ImaAdsMediaSource implements MediaSource { }); } + @Override + public void onAdClicked() { + if (eventHandler != null && eventListener != null) { + eventHandler.post(new Runnable() { + @Override + public void run() { + if (!released) { + eventListener.onAdClicked(); + } + } + }); + } + } + + @Override + public void onAdTapped() { + if (eventHandler != null && eventListener != null) { + eventHandler.post(new Runnable() { + @Override + public void run() { + if (!released) { + eventListener.onAdTapped(); + } + } + }); + } + } + } } From aeb2f620f11717aa0f1a413a5c7d5cd57ff48056 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 31 Jul 2017 03:30:09 -0700 Subject: [PATCH 098/133] Handle release() while initializing the ads manager Also don't detach any attached player in release() to prevent a possible NullPointerException if ImaAdsLoader.release() runs first, then the MediaSource is released and detaches the player. This is safe because if the loader was attached it's guaranteed to be detached. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163673750 --- .../exoplayer2/ext/ima/ImaAdsLoader.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index fc8000b397..6541dad0ac 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -187,6 +187,10 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, * Whether {@link #getContentProgress()} has sent {@link #pendingContentPositionMs} to IMA. */ private boolean sentPendingContentPositionMs; + /** + * Whether {@link #release()} has been called. + */ + private boolean released; /** * Creates a new IMA ads loader. @@ -252,7 +256,7 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, if (imaPausedContent) { adsManager.resume(); } - } else if (adTagUri != null) { + } else { requestAds(); } } @@ -278,12 +282,10 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, * Releases the loader. Must be called when the instance is no longer needed. */ public void release() { + released = true; if (adsManager != null) { adsManager.destroy(); adsManager = null; - if (player != null) { - detachPlayer(); - } } } @@ -291,7 +293,12 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, @Override public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) { - adsManager = adsManagerLoadedEvent.getAdsManager(); + AdsManager adsManager = adsManagerLoadedEvent.getAdsManager(); + if (released) { + adsManager.destroy(); + return; + } + this.adsManager = adsManager; adsManager.addAdErrorListener(this); adsManager.addAdEventListener(this); if (ENABLE_PRELOADING) { From ecbcf09804c80f7a66d03c94f51f44be88b11803 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 31 Jul 2017 07:00:10 -0700 Subject: [PATCH 099/133] Check for null entries in ConcatenatingMediaSource We do this in the dynamic case, and I think we've seen a few GitHub issues where developers do this and don't understand what they've done wrong (because the failure comes later). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163688557 --- .../android/exoplayer2/source/ConcatenatingMediaSource.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index de42df9a14..2c998e8a06 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -57,6 +57,9 @@ public final class ConcatenatingMediaSource implements MediaSource { * {@link MediaSource} instance to be present more than once in the array. */ public ConcatenatingMediaSource(boolean isRepeatOneAtomic, MediaSource... mediaSources) { + for (MediaSource mediaSource : mediaSources) { + Assertions.checkNotNull(mediaSource); + } this.mediaSources = mediaSources; this.isRepeatOneAtomic = isRepeatOneAtomic; timelines = new Timeline[mediaSources.length]; From 2b5bd800e3b3f2e85731f8bd7a7565bdac77610e Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 31 Jul 2017 07:53:08 -0700 Subject: [PATCH 100/133] Robustness fix for malformed ID3 metadata Issue: #3116 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163693235 --- .../exoplayer2/metadata/id3/Id3Decoder.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java index df3353fb18..0e4256be5c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java @@ -483,13 +483,8 @@ public final class Id3Decoder implements MetadataDecoder { int ownerEndIndex = indexOfZeroByte(data, 0); String owner = new String(data, 0, ownerEndIndex, "ISO-8859-1"); - byte[] privateData; int privateDataStartIndex = ownerEndIndex + 1; - if (privateDataStartIndex < data.length) { - privateData = Arrays.copyOfRange(data, privateDataStartIndex, data.length); - } else { - privateData = new byte[0]; - } + byte[] privateData = copyOfRangeIfValid(data, privateDataStartIndex, data.length); return new PrivFrame(owner, privateData); } @@ -516,7 +511,7 @@ public final class Id3Decoder implements MetadataDecoder { descriptionEndIndex - descriptionStartIndex, charset); int objectDataStartIndex = descriptionEndIndex + delimiterLength(encoding); - byte[] objectData = Arrays.copyOfRange(data, objectDataStartIndex, data.length); + byte[] objectData = copyOfRangeIfValid(data, objectDataStartIndex, data.length); return new GeobFrame(mimeType, filename, description, objectData); } @@ -553,7 +548,7 @@ public final class Id3Decoder implements MetadataDecoder { descriptionEndIndex - descriptionStartIndex, charset); int pictureDataStartIndex = descriptionEndIndex + delimiterLength(encoding); - byte[] pictureData = Arrays.copyOfRange(data, pictureDataStartIndex, data.length); + byte[] pictureData = copyOfRangeIfValid(data, pictureDataStartIndex, data.length); return new ApicFrame(mimeType, description, pictureType, pictureData); } @@ -749,6 +744,22 @@ public final class Id3Decoder implements MetadataDecoder { ? 1 : 2; } + /** + * Copies the specified range of an array, or returns a zero length array if the range is invalid. + * + * @param data The array from which to copy. + * @param from The start of the range to copy (inclusive). + * @param to The end of the range to copy (exclusive). + * @return The copied data, or a zero length array if the range is invalid. + */ + private static byte[] copyOfRangeIfValid(byte[] data, int from, int to) { + if (to <= from) { + // Invalid or zero length range. + return new byte[0]; + } + return Arrays.copyOfRange(data, from, data.length); + } + private static final class Id3Header { private final int majorVersion; From bf4a460e4c8ae9c772f99fad69ebecd42849496b Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 31 Jul 2017 08:08:29 -0700 Subject: [PATCH 101/133] Fix typo in Id3Decoder ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163694825 --- .../com/google/android/exoplayer2/metadata/id3/Id3Decoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java b/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java index 0e4256be5c..6b2e5c3675 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/Id3Decoder.java @@ -757,7 +757,7 @@ public final class Id3Decoder implements MetadataDecoder { // Invalid or zero length range. return new byte[0]; } - return Arrays.copyOfRange(data, from, data.length); + return Arrays.copyOfRange(data, from, to); } private static final class Id3Header { From 85445536c31809d78351023ec26d3da18eea7f58 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 31 Jul 2017 09:50:08 -0700 Subject: [PATCH 102/133] Fix 2.5.0 lint errors - Publish IMA extension - Force IMA to use the correct version of the support library - Add missing sr translations for repeat mode strings ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163705883 --- extensions/ima/build.gradle | 17 +++++++++++++++++ .../exoplayer2/mediacodec/MediaCodecInfo.java | 2 +- library/ui/src/main/res/values-sr/strings.xml | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/extensions/ima/build.gradle b/extensions/ima/build.gradle index 7732751296..fc7dc088e8 100644 --- a/extensions/ima/build.gradle +++ b/extensions/ima/build.gradle @@ -14,6 +14,12 @@ android { dependencies { compile project(modulePrefix + 'library-core') + // This dependency is necessary to force the supportLibraryVersion of + // com.android.support:support-v4 to be used. Else an older version (25.2.0) is included via: + // com.google.android.gms:play-services-ads:11.0.2 + // |-- com.google.android.gms:play-services-ads-lite:[11.0.2] -> 11.0.2 + // |-- com.google.android.gms:play-services-basement:[11.0.2] -> 11.0.2 + // |-- com.android.support:support-v4:25.2.0 compile 'com.android.support:support-annotations:' + supportLibraryVersion compile 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4' compile 'com.google.android.gms:play-services-ads:11.0.2' @@ -23,3 +29,14 @@ dependencies { androidTestCompile 'org.mockito:mockito-core:' + mockitoVersion androidTestCompile 'com.android.support.test:runner:' + testSupportLibraryVersion } + +ext { + javadocTitle = 'IMA extension' +} +apply from: '../../javadoc_library.gradle' + +ext { + releaseArtifact = 'extension-ima' + releaseDescription = 'Interactive Media Ads extension for ExoPlayer.' +} +apply from: '../../publish.gradle' diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java index 17ef2c4456..2e5b04f4a9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecInfo.java @@ -64,7 +64,7 @@ public final class MediaCodecInfo { /** * Whether the decoder is secure. * - * @see CodecCapabilities#isFeatureRequired(String) + * @see CodecCapabilities#isFeatureSupported(String) * @see CodecCapabilities#FEATURE_SecurePlayback */ public final boolean secure; diff --git a/library/ui/src/main/res/values-sr/strings.xml b/library/ui/src/main/res/values-sr/strings.xml index 175ad4fe7f..0d54de5f6a 100644 --- a/library/ui/src/main/res/values-sr/strings.xml +++ b/library/ui/src/main/res/values-sr/strings.xml @@ -22,4 +22,7 @@ "Заустави" "Премотај уназад" "Премотај унапред" + "Понови све" + "Понављање је искључено" + "Понови једну" From 9c2528a70f6fa56db6c9b10b794c5bf945731b1d Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 31 Jul 2017 21:28:22 +0100 Subject: [PATCH 103/133] Correctly handle reading 0 bits --- .../com/google/android/exoplayer2/util/ParsableBitArray.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java index 0456bcb879..199ceff892 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java @@ -155,6 +155,9 @@ public final class ParsableBitArray { * @return An integer whose bottom n bits hold the read data. */ public int readBits(int numBits) { + if (numBits == 0) { + return 0; + } int returnValue = 0; bitOffset += numBits; while (bitOffset > 8) { From 4a6a5b527fd411340a3a8300b29d7c222685f0f5 Mon Sep 17 00:00:00 2001 From: Danny Brain Date: Wed, 26 Jul 2017 11:30:02 +1000 Subject: [PATCH 104/133] Allow PlaybackControlView to be overridden in SimpleExoPlayerView --- .../android/exoplayer2/ui/SimpleExoPlayerView.java | 14 +++++++++++++- library/ui/src/main/res/values/ids.xml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index a4083c940f..1c39b558bb 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -168,6 +168,15 @@ import java.util.List; *

  • Type: {@link View}
  • * * + *
  • {@code exo_controller} - An already inflated instance of + * {@link PlaybackControlView}. Allows you to use your own {@link PlaybackControlView} instead + * of default. Note: attrs such as rewind_increment will not be passed through to this + * instance and should be set at creation. {@code exo_controller_placeholder} will be ignored + * if this is set. + *
      + *
    • Type: {@link View}
    • + *
    + *
  • *
  • {@code exo_overlay} - A {@link FrameLayout} positioned on top of the player which * the app can access via {@link #getOverlayFrameLayout()}, provided for convenience. *
      @@ -315,8 +324,11 @@ public final class SimpleExoPlayerView extends FrameLayout { } // Playback control view. + PlaybackControlView customController = (PlaybackControlView) findViewById(R.id.exo_controller); View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder); - if (controllerPlaceholder != null) { + if (customController != null) { + this.controller = customController; + } else if (controllerPlaceholder != null) { // Note: rewindMs and fastForwardMs are passed via attrs, so we don't need to make explicit // calls to set them. this.controller = new PlaybackControlView(context, attrs); diff --git a/library/ui/src/main/res/values/ids.xml b/library/ui/src/main/res/values/ids.xml index 815487a54e..b16b1729da 100644 --- a/library/ui/src/main/res/values/ids.xml +++ b/library/ui/src/main/res/values/ids.xml @@ -20,6 +20,7 @@ + From 893c7f34ba333d290bdbffcf16c186b0b6dcbbe8 Mon Sep 17 00:00:00 2001 From: Michael Goffioul Date: Thu, 27 Jul 2017 15:44:28 -0400 Subject: [PATCH 105/133] Fix H262 segmentation. Prepend sequence headers to the next frame, instead of appending them to the previous frame. Tested decoders like FFMPEG and Google's Android/MPEG2 expects to read the sequence headers before the first frame they apply to. When sequence headers are appended to the previous frame, these are ignored and this leads to incorrect decoding. --- .../exoplayer2/extractor/ts/H262Reader.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index 7266f847c4..92c8e8d800 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -51,7 +51,7 @@ public final class H262Reader implements ElementaryStreamReader { // State that should be reset on seek. private final boolean[] prefixFlags; private final CsdBuffer csdBuffer; - private boolean foundFirstFrameInGroup; + private boolean foundPicture; private long totalBytesWritten; // Per packet state that gets reset at the start of each packet. @@ -60,8 +60,8 @@ public final class H262Reader implements ElementaryStreamReader { // Per sample state that gets reset at the start of each frame. private boolean isKeyframe; - private long framePosition; - private long frameTimeUs; + private long samplePosition; + private long sampleTimeUs; public H262Reader() { prefixFlags = new boolean[4]; @@ -73,7 +73,8 @@ public final class H262Reader implements ElementaryStreamReader { NalUnitUtil.clearPrefixFlags(prefixFlags); csdBuffer.reset(); pesPtsUsAvailable = false; - foundFirstFrameInGroup = false; + foundPicture = false; + samplePosition = C.POSITION_UNSET; totalBytesWritten = 0; } @@ -136,25 +137,28 @@ public final class H262Reader implements ElementaryStreamReader { } } - if (hasOutputFormat && (startCodeValue == START_GROUP || startCodeValue == START_PICTURE)) { + if (hasOutputFormat && (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER)) { int bytesWrittenPastStartCode = limit - startCodeOffset; - if (foundFirstFrameInGroup) { + boolean resetSample = (samplePosition == C.POSITION_UNSET); + if (foundPicture) { @C.BufferFlags int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; - int size = (int) (totalBytesWritten - framePosition) - bytesWrittenPastStartCode; - output.sampleMetadata(frameTimeUs, flags, size, bytesWrittenPastStartCode, null); + int size = (int) (totalBytesWritten - samplePosition) - bytesWrittenPastStartCode; + output.sampleMetadata(sampleTimeUs, flags, size, bytesWrittenPastStartCode, null); isKeyframe = false; + resetSample = true; } - if (startCodeValue == START_GROUP) { - foundFirstFrameInGroup = false; - isKeyframe = true; - } else /* startCodeValue == START_PICTURE */ { - frameTimeUs = pesPtsUsAvailable ? pesTimeUs : (frameTimeUs + frameDurationUs); - framePosition = totalBytesWritten - bytesWrittenPastStartCode; + foundPicture = (startCodeValue == START_PICTURE); + if (resetSample) { + samplePosition = totalBytesWritten - bytesWrittenPastStartCode; + sampleTimeUs = (pesPtsUsAvailable ? pesTimeUs : sampleTimeUs + frameDurationUs); pesPtsUsAvailable = false; - foundFirstFrameInGroup = true; } } + if (hasOutputFormat && startCodeValue == START_GROUP) { + isKeyframe = true; + } + offset = startCodeOffset; searchOffset = offset + 3; } From 395249a950ec73b3475ff84239f4d14dffa048bc Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 1 Aug 2017 04:44:02 -0700 Subject: [PATCH 106/133] Fix sequence extension position calculation ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163814942 --- .../com/google/android/exoplayer2/extractor/ts/H262Reader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index 92c8e8d800..add8079105 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -257,7 +257,7 @@ public final class H262Reader implements ElementaryStreamReader { public boolean onStartCode(int startCodeValue, int bytesAlreadyPassed) { if (isFilling) { if (sequenceExtensionPosition == 0 && startCodeValue == START_EXTENSION) { - sequenceExtensionPosition = length; + sequenceExtensionPosition = length - bytesAlreadyPassed; } else { length -= bytesAlreadyPassed; isFilling = false; From b2da61f70bfb9b1fea13db63fd2ce6dac92dc3ad Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 1 Aug 2017 07:50:12 -0700 Subject: [PATCH 107/133] Clean up MediaSessionExt documentation ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163828712 --- .../DefaultPlaybackController.java | 42 +++--- .../mediasession/MediaSessionConnector.java | 126 ++++++++---------- .../RepeatModeActionProvider.java | 19 +-- .../mediasession/TimelineQueueNavigator.java | 25 ++-- .../exoplayer2/ui/PlaybackControlView.java | 16 ++- 5 files changed, 112 insertions(+), 116 deletions(-) diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java index 231c1f1ea5..c3586b29e6 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/DefaultPlaybackController.java @@ -21,40 +21,50 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; /** - * A default implementation of the {@link MediaSessionConnector.PlaybackController}. You can safely - * override any method for instance to intercept calls for a given action. + * A default implementation of {@link MediaSessionConnector.PlaybackController}. + *

      + * Methods can be safely overridden by subclasses to intercept calls for given actions. */ public class DefaultPlaybackController implements MediaSessionConnector.PlaybackController { + /** + * The default fast forward increment, in milliseconds. + */ + public static final int DEFAULT_FAST_FORWARD_MS = 15000; + /** + * The default rewind increment, in milliseconds. + */ + public static final int DEFAULT_REWIND_MS = 5000; + private static final long BASE_ACTIONS = PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_STOP; - protected final long fastForwardIncrementMs; protected final long rewindIncrementMs; + protected final long fastForwardIncrementMs; /** - * Creates a new {@link DefaultPlaybackController}. This is equivalent to calling - * {@code DefaultPlaybackController(15000L, 5000L)}. + * Creates a new instance. + *

      + * Equivalent to {@code DefaultPlaybackController( + * DefaultPlaybackController.DEFAULT_REWIND_MS, + * DefaultPlaybackController.DEFAULT_FAST_FORWARD_MS)}. */ public DefaultPlaybackController() { - this(15000L, 5000L); + this(DEFAULT_REWIND_MS, DEFAULT_FAST_FORWARD_MS); } /** - * Creates a new {@link DefaultPlaybackController} and sets the fast forward and rewind increments - * in milliseconds. + * Creates a new instance with the given fast forward and rewind increments. * - * @param fastForwardIncrementMs A positive value will cause the - * {@link PlaybackStateCompat#ACTION_FAST_FORWARD} playback action to be added. A zero or a - * negative value will cause it to be removed. - * @param rewindIncrementMs A positive value will cause the - * {@link PlaybackStateCompat#ACTION_REWIND} playback action to be added. A zero or a - * negative value will cause it to be removed. + * @param rewindIncrementMs The rewind increment in milliseconds. A zero or negative value will + * cause the rewind action to be disabled. + * @param fastForwardIncrementMs The fast forward increment in milliseconds. A zero or negative + * value will cause the fast forward action to be removed. */ - public DefaultPlaybackController(long fastForwardIncrementMs, long rewindIncrementMs) { - this.fastForwardIncrementMs = fastForwardIncrementMs; + public DefaultPlaybackController(long rewindIncrementMs, long fastForwardIncrementMs) { this.rewindIncrementMs = rewindIncrementMs; + this.fastForwardIncrementMs = fastForwardIncrementMs; } @Override diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index a300acfffa..0e839b8083 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -44,30 +44,27 @@ import java.util.List; import java.util.Map; /** - * Mediates between a {@link MediaSessionCompat} and an {@link Player} instance set with - * {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. + * Connects a {@link MediaSessionCompat} to a {@link Player}. *

      - * The {@code MediaSessionConnector} listens for media actions sent by a media controller and - * realizes these actions by calling appropriate ExoPlayer methods. Further, the state of ExoPlayer - * will be synced automatically with the {@link PlaybackStateCompat} of the media session to - * broadcast state transitions to clients. You can optionally extend this behaviour by providing - * various collaborators. - *

      - * Media actions to initiate media playback ({@code PlaybackStateCompat#ACTION_PREPARE_*} and - * {@code PlaybackStateCompat#ACTION_PLAY_*} need to be handled by a {@link PlaybackPreparer} which - * build a {@link com.google.android.exoplayer2.source.MediaSource} to prepare ExoPlayer. Deploy - * your preparer by calling {@link #setPlaybackPreparer(PlaybackPreparer)}. - *

      - * To support a media session queue and navigation within this queue, you can set a - * {@link QueueNavigator} to maintain the queue yourself and implement queue navigation commands - * (like 'skip to next') sent by controllers. It's recommended to use the - * {@link TimelineQueueNavigator} to allow users navigating the windows of the ExoPlayer timeline. - *

      - * If you want to allow media controllers to manipulate the queue, implement a {@link QueueEditor} - * and deploy it with {@link #setQueueEditor(QueueEditor)}. - *

      - * Set an {@link ErrorMessageProvider} to provide an error code and a human readable error message - * to be broadcast to controllers. + * The connector listens for actions sent by the media session's controller and implements these + * actions by calling appropriate ExoPlayer methods. The playback state of the media session is + * automatically synced with the player. The connector can also be optionally extended by providing + * various collaborators: + *

        + *
      • Actions to initiate media playback ({@code PlaybackStateCompat#ACTION_PREPARE_*} and + * {@code PlaybackStateCompat#ACTION_PLAY_*}) can be handled by a {@link PlaybackPreparer} passed + * when calling {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. Custom + * actions can be handled by passing one or more {@link CustomActionProvider}s in a similar way. + *
      • + *
      • To enable a media queue and navigation within it, you can set a {@link QueueNavigator} by + * calling {@link #setQueueNavigator(QueueNavigator)}. Use of {@link TimelineQueueNavigator} is + * recommended for most use cases.
      • + *
      • To enable editing of the media queue, you can set a {@link QueueEditor} by calling + * {@link #setQueueEditor(QueueEditor)}.
      • + *
      • An {@link ErrorMessageProvider} for providing human readable error messages and + * corresponding error codes can be set by calling + * {@link #setErrorMessageProvider(ErrorMessageProvider)}.
      • + *
      */ public final class MediaSessionConnector { @@ -78,12 +75,7 @@ public final class MediaSessionConnector { public static final String EXTRAS_PITCH = "EXO_PITCH"; /** - * Interface to which media controller commands regarding preparing playback for a given media - * clip are delegated to. - *

      - * Normally preparing playback includes preparing the player with a - * {@link com.google.android.exoplayer2.source.MediaSource} and setting up the media session queue - * with a corresponding list of queue items. + * Interface to which playback preparation actions are delegated. */ public interface PlaybackPreparer { @@ -131,7 +123,7 @@ public final class MediaSessionConnector { } /** - * Controller to handle playback actions. + * Interface to which playback actions are delegated. */ public interface PlaybackController { @@ -178,8 +170,8 @@ public final class MediaSessionConnector { } /** - * Navigator to handle queue navigation actions and maintain the media session queue with - * {#link MediaSessionCompat#setQueue(List)} to provide the active queue item to the connector. + * Handles queue navigation actions, and updates the media session queue by calling + * {@code MediaSessionCompat.setQueue()}. */ public interface QueueNavigator { @@ -211,7 +203,7 @@ public final class MediaSessionConnector { */ void onCurrentWindowIndexChanged(Player player); /** - * Gets the id of the currently active queue item or + * Gets the id of the currently active queue item, or * {@link MediaSessionCompat.QueueItem#UNKNOWN_ID} if the active item is unknown. *

      * To let the connector publish metadata for the active queue item, the queue item with the @@ -241,7 +233,7 @@ public final class MediaSessionConnector { } /** - * Editor to manipulate the queue. + * Handles media session queue edits. */ public interface QueueEditor { @@ -302,12 +294,12 @@ public final class MediaSessionConnector { } /** - * Provides an user readable error code and a message for {@link ExoPlaybackException}s. + * Converts an exception into an error code and a user readable error message. */ public interface ErrorMessageProvider { /** - * Returns a pair of an error code and a user readable error message for a given - * {@link ExoPlaybackException}. + * Returns a pair consisting of an error code and a user readable error message for a given + * exception. */ Pair getErrorMessage(ExoPlaybackException playbackException); } @@ -316,6 +308,7 @@ public final class MediaSessionConnector { * The wrapped {@link MediaSessionCompat}. */ public final MediaSessionCompat mediaSession; + private final MediaControllerCompat mediaController; private final Handler handler; private final boolean doMaintainMetadata; @@ -334,11 +327,10 @@ public final class MediaSessionConnector { private ExoPlaybackException playbackException; /** - * Creates a {@code MediaSessionConnector}. This is equivalent to calling - * {@code #MediaSessionConnector(mediaSession, new DefaultPlaybackController)}. + * Creates an instance. Must be called on the same thread that is used to construct the player + * instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. *

      - * Constructing the {@link MediaSessionConnector} needs to be done on the same thread as - * constructing the player instance. + * Equivalent to {@code MediaSessionConnector(mediaSession, new DefaultPlaybackController())}. * * @param mediaSession The {@link MediaSessionCompat} to connect to. */ @@ -347,14 +339,13 @@ public final class MediaSessionConnector { } /** - * Creates a {@code MediaSessionConnector}. This is equivalent to calling - * {@code #MediaSessionConnector(mediaSession, playbackController, true)}. + * Creates an instance. Must be called on the same thread that is used to construct the player + * instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. *

      - * Constructing the {@link MediaSessionConnector} needs to be done on the same thread as - * constructing the player instance. + * Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, true)}. * * @param mediaSession The {@link MediaSessionCompat} to connect to. - * @param playbackController The {@link PlaybackController}. + * @param playbackController A {@link PlaybackController} for handling playback actions. */ public MediaSessionConnector(MediaSessionCompat mediaSession, PlaybackController playbackController) { @@ -362,19 +353,14 @@ public final class MediaSessionConnector { } /** - * Creates a {@code MediaSessionConnector} with {@link CustomActionProvider}s. - *

      - * If you choose to pass {@code false} for {@code doMaintainMetadata} you need to maintain the - * metadata of the media session yourself (provide at least the duration to allow clients to show - * a progress bar). - *

      - * Constructing the {@link MediaSessionConnector} needs to be done on the same thread as - * constructing the player instance. + * Creates an instance. Must be called on the same thread that is used to construct the player + * instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. * * @param mediaSession The {@link MediaSessionCompat} to connect to. - * @param playbackController The {@link PlaybackController}. - * @param doMaintainMetadata Sets whether the connector should maintain the metadata of the - * session. + * @param playbackController A {@link PlaybackController} for handling playback actions. + * @param doMaintainMetadata Whether the connector should maintain the metadata of the session. If + * {@code false}, you need to maintain the metadata of the media session yourself (provide at + * least the duration to allow clients to show a progress bar). */ public MediaSessionConnector(MediaSessionCompat mediaSession, PlaybackController playbackController, boolean doMaintainMetadata) { @@ -392,17 +378,14 @@ public final class MediaSessionConnector { } /** - * Sets the player to which media commands sent by a media controller are delegated. - *

      - * The media session callback is set if the {@code player} is not {@code null} and the callback is - * removed if the {@code player} is {@code null}. + * Sets the player to be connected to the media session. *

      * The order in which any {@link CustomActionProvider}s are passed determines the order of the * actions published with the playback state of the session. * * @param player The player to be connected to the {@code MediaSession}. - * @param playbackPreparer The playback preparer for the player. - * @param customActionProviders Optional {@link CustomActionProvider}s to publish and handle + * @param playbackPreparer An optional {@link PlaybackPreparer} for preparing the player. + * @param customActionProviders An optional {@link CustomActionProvider}s to publish and handle * custom actions. */ public void setPlayer(Player player, PlaybackPreparer playbackPreparer, @@ -411,7 +394,7 @@ public final class MediaSessionConnector { this.player.removeListener(exoPlayerEventListener); mediaSession.setCallback(null); } - setPlaybackPreparer(playbackPreparer); + this.playbackPreparer = playbackPreparer; this.player = player; this.customActionProviders = (player != null && customActionProviders != null) ? customActionProviders : new CustomActionProvider[0]; @@ -424,9 +407,9 @@ public final class MediaSessionConnector { } /** - * Sets the optional {@link ErrorMessageProvider}. + * Sets the {@link ErrorMessageProvider}. * - * @param errorMessageProvider The {@link ErrorMessageProvider}. + * @param errorMessageProvider The error message provider. */ public void setErrorMessageProvider(ErrorMessageProvider errorMessageProvider) { this.errorMessageProvider = errorMessageProvider; @@ -437,25 +420,21 @@ public final class MediaSessionConnector { * {@code ACTION_SKIP_TO_PREVIOUS}, {@code ACTION_SKIP_TO_QUEUE_ITEM} and * {@code ACTION_SET_SHUFFLE_MODE_ENABLED}. * - * @param queueNavigator The navigator to handle queue navigation. + * @param queueNavigator The queue navigator. */ public void setQueueNavigator(QueueNavigator queueNavigator) { this.queueNavigator = queueNavigator; } /** - * Sets the queue editor to handle commands to manipulate the queue sent by a media controller. + * Sets the {@link QueueEditor} to handle queue edits sent by the media controller. * - * @param queueEditor The editor to handle queue manipulation actions. + * @param queueEditor The queue editor. */ public void setQueueEditor(QueueEditor queueEditor) { this.queueEditor = queueEditor; } - private void setPlaybackPreparer(PlaybackPreparer playbackPreparer) { - this.playbackPreparer = playbackPreparer; - } - private void updateMediaSessionPlaybackState() { PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder(); if (player == null) { @@ -603,6 +582,7 @@ public final class MediaSessionConnector { } private class ExoPlayerEventListener implements Player.EventListener { + @Override public void onTimelineChanged(Timeline timeline, Object manifest) { if (queueNavigator != null) { diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java index 1f33245059..abefe533ce 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java @@ -26,10 +26,13 @@ import com.google.android.exoplayer2.util.RepeatModeUtil; */ public final class RepeatModeActionProvider implements MediaSessionConnector.CustomActionProvider { + /** + * The default repeat toggle modes. + */ + public static final @RepeatModeUtil.RepeatToggleModes int DEFAULT_REPEAT_TOGGLE_MODES = + RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE | RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL; + private static final String ACTION_REPEAT_MODE = "ACTION_EXO_REPEAT_MODE"; - @RepeatModeUtil.RepeatToggleModes - private static final int DEFAULT_REPEAT_MODES = RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE - | RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL; private final Player player; @RepeatModeUtil.RepeatToggleModes @@ -39,20 +42,20 @@ public final class RepeatModeActionProvider implements MediaSessionConnector.Cus private final CharSequence repeatOffDescription; /** - * Creates a new {@link RepeatModeActionProvider}. + * Creates a new instance. *

      - * This is equivalent to calling the two argument constructor with - * {@code RepeatModeUtil#REPEAT_TOGGLE_MODE_ONE | RepeatModeUtil#REPEAT_TOGGLE_MODE_ALL}. + * Equivalent to {@code RepeatModeActionProvider(context, player, + * RepeatModeActionProvider.DEFAULT_REPEAT_TOGGLE_MODES)}. * * @param context The context. * @param player The player on which to toggle the repeat mode. */ public RepeatModeActionProvider(Context context, Player player) { - this(context, player, DEFAULT_REPEAT_MODES); + this(context, player, DEFAULT_REPEAT_TOGGLE_MODES); } /** - * Creates a new {@link RepeatModeActionProvider} for the given repeat toggle modes. + * Creates a new instance enabling the given repeat toggle modes. * * @param context The context. * @param player The player on which to toggle the repeat mode. diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java index 60aa5a5ba0..521b4cd6e3 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java @@ -29,9 +29,8 @@ import java.util.Collections; import java.util.List; /** - * An abstract implementation of the {@link MediaSessionConnector.QueueNavigator} that's based on an - * {@link Player}'s current {@link Timeline} and maps the timeline of the player to the media - * session queue. + * An abstract implementation of the {@link MediaSessionConnector.QueueNavigator} that maps the + * windows of a {@link Player}'s {@link Timeline} to the media session queue. */ public abstract class TimelineQueueNavigator implements MediaSessionConnector.QueueNavigator { @@ -44,10 +43,9 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu private long activeQueueItemId; /** - * Creates a new timeline queue navigator for a given {@link MediaSessionCompat}. + * Creates an instance for a given {@link MediaSessionCompat}. *

      - * This is equivalent to calling - * {@code #TimelineQueueNavigator(mediaSession, DEFAULT_MAX_QUEUE_SIZE)}. + * Equivalent to {@code TimelineQueueNavigator(mediaSession, DEFAULT_MAX_QUEUE_SIZE)}. * * @param mediaSession The {@link MediaSessionCompat}. */ @@ -56,12 +54,11 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu } /** - * Creates a new timeline queue navigator for a given {@link MediaSessionCompat} and a maximum - * queue size of {@code maxQueueSize}. + * Creates an instance for a given {@link MediaSessionCompat} and maximum queue size. *

      - * If the actual queue size is larger than {@code maxQueueSize} a floating window of - * {@code maxQueueSize} is applied and moved back and forth when the user is navigating within the - * queue. + * If the number of windows in the {@link Player}'s {@link Timeline} exceeds {@code maxQueueSize}, + * the media session queue will correspond to {@code maxQueueSize} windows centered on the one + * currently being played. * * @param mediaSession The {@link MediaSessionCompat}. * @param maxQueueSize The maximum queue size. @@ -80,12 +77,6 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu */ public abstract MediaDescriptionCompat getMediaDescription(int windowIndex); - /** - * Supports the following media actions: {@code PlaybackStateCompat.ACTION_SKIP_TO_NEXT | - * PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM}. - * - * @return The bit mask of the supported media actions. - */ @Override public long getSupportedQueueNavigatorActions(Player player) { if (player == null || player.getCurrentTimeline().getWindowCount() < 2) { diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index a99c2dfde2..6ddbfed973 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -249,11 +249,23 @@ public class PlaybackControlView extends FrameLayout { }; + /** + * The default fast forward increment, in milliseconds. + */ public static final int DEFAULT_FAST_FORWARD_MS = 15000; + /** + * The default rewind increment, in milliseconds. + */ public static final int DEFAULT_REWIND_MS = 5000; + /** + * The default show timeout, in milliseconds. + */ public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000; - public static final @RepeatModeUtil.RepeatToggleModes int DEFAULT_REPEAT_TOGGLE_MODES - = RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE; + /** + * The default repeat toggle modes. + */ + public static final @RepeatModeUtil.RepeatToggleModes int DEFAULT_REPEAT_TOGGLE_MODES = + RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE; /** * The maximum number of windows that can be shown in a multi-window time bar. From 18bb04d6d9fcca6fc3a077a75e97c32fd5305ddf Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 1 Aug 2017 08:08:58 -0700 Subject: [PATCH 108/133] Bump version to 2.5.0-beta2 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163830353 --- RELEASENOTES.md | 8 +++++--- constants.gradle | 2 +- .../google/android/exoplayer2/ExoPlayerLibraryInfo.java | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 24da37808b..379b84b4e7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,11 +1,11 @@ # Release notes # -### r2.5.0-beta1 ### +### r2.5.0 (beta) ### * IMA extension: Wraps the Google Interactive Media Ads (IMA) SDK to provide an easy and seamless way of incorporating display ads into ExoPlayer playbacks. - You can read more about the IMA extension *A link to a blog post about this - extension will be added here prior to the stable 2.5.0 release.* + You can read more about the IMA extension + [here](https://medium.com/google-exoplayer/playing-ads-with-exoplayer-and-ima-868dfd767ea). * MediaSession extension: Provides an easy to to connect ExoPlayer with MediaSessionCompat in the Android Support Library. *A link to a blog post about this extension will be added here prior to the stable 2.5.0 release.* @@ -48,6 +48,8 @@ ([#889](https://github.com/google/ExoPlayer/issues/889)). * AndroidTV: Fixed issue where tunneled video playback would not start on some devices ([#2985](https://github.com/google/ExoPlayer/issues/2985)). +* MPEG-TS: Fixed segmentation issue when parsing H262 + ([#2891](https://github.com/google/ExoPlayer/issues/2891)). * Cronet extension: Support for a user-defined fallback if Cronet library is not present. * Misc bugfixes. diff --git a/constants.gradle b/constants.gradle index 73b80f6a83..0db74945c4 100644 --- a/constants.gradle +++ b/constants.gradle @@ -24,7 +24,7 @@ project.ext { supportLibraryVersion = '25.4.0' dexmakerVersion = '1.2' mockitoVersion = '1.9.5' - releaseVersion = 'r2.5.0-beta1' + releaseVersion = 'r2.5.0-beta2' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 2abdfe5aee..c04a777e14 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -31,13 +31,13 @@ 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.5.0-beta1"; + public static final String VERSION = "2.5.0-beta2"; /** * 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.5.0-beta1"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.5.0-beta2"; /** * The version of the library expressed as an integer, for example 1002003. From 0717782fdc4e0dedf5b253bb294d10640beae262 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 1 Aug 2017 18:20:54 +0100 Subject: [PATCH 109/133] Minor cleanup --- .../src/androidTest/assets/ts/sample.ps.0.dump | 2 +- .../src/androidTest/assets/ts/sample.ts.0.dump | 4 ++-- .../exoplayer2/extractor/ts/H262Reader.java | 7 ++++--- .../exoplayer2/ui/SimpleExoPlayerView.java | 16 ++++++++-------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/library/core/src/androidTest/assets/ts/sample.ps.0.dump b/library/core/src/androidTest/assets/ts/sample.ps.0.dump index 3b44fb6fb9..98f3c6a85a 100644 --- a/library/core/src/androidTest/assets/ts/sample.ps.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ps.0.dump @@ -69,7 +69,7 @@ track 224: sample 0: time = 40000 flags = 1 - data = length 20616, hash CA38A5B5 + data = length 20646, hash 576390B sample 1: time = 80000 flags = 0 diff --git a/library/core/src/androidTest/assets/ts/sample.ts.0.dump b/library/core/src/androidTest/assets/ts/sample.ts.0.dump index 26c6665aaa..83f1337816 100644 --- a/library/core/src/androidTest/assets/ts/sample.ts.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ts.0.dump @@ -28,9 +28,9 @@ track 256: data = length 22, hash CE183139 sample count = 2 sample 0: - time = 33366 + time = 0 flags = 1 - data = length 20669, hash 26DABA0F + data = length 20711, hash 34341E8 sample 1: time = 66733 flags = 0 diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index add8079105..a00bace56c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -137,9 +137,10 @@ public final class H262Reader implements ElementaryStreamReader { } } - if (hasOutputFormat && (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER)) { + if (hasOutputFormat + && (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER)) { int bytesWrittenPastStartCode = limit - startCodeOffset; - boolean resetSample = (samplePosition == C.POSITION_UNSET); + boolean resetSample = samplePosition == C.POSITION_UNSET; if (foundPicture) { @C.BufferFlags int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (totalBytesWritten - samplePosition) - bytesWrittenPastStartCode; @@ -147,7 +148,7 @@ public final class H262Reader implements ElementaryStreamReader { isKeyframe = false; resetSample = true; } - foundPicture = (startCodeValue == START_PICTURE); + foundPicture = startCodeValue == START_PICTURE; if (resetSample) { samplePosition = totalBytesWritten - bytesWrittenPastStartCode; sampleTimeUs = (pesPtsUsAvailable ? pesTimeUs : sampleTimeUs + frameDurationUs); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index 1c39b558bb..2bba9071fd 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -126,7 +126,8 @@ import java.util.List; *

    • Default: {@code R.id.exo_playback_control_view}
    • *
    *
  • All attributes that can be set on a {@link PlaybackControlView} can also be set on a - * SimpleExoPlayerView, and will be propagated to the inflated {@link PlaybackControlView}. + * SimpleExoPlayerView, and will be propagated to the inflated {@link PlaybackControlView} + * unless the layout is overridden to specify a custom {@code exo_controller} (see below). *
  • * * @@ -163,18 +164,17 @@ import java.util.List; * * *
  • {@code exo_controller_placeholder} - A placeholder that's replaced with the inflated - * {@link PlaybackControlView}. + * {@link PlaybackControlView}. Ignored if an {@code exo_controller} view exists. *
      *
    • Type: {@link View}
    • *
    *
  • - *
  • {@code exo_controller} - An already inflated instance of - * {@link PlaybackControlView}. Allows you to use your own {@link PlaybackControlView} instead - * of default. Note: attrs such as rewind_increment will not be passed through to this - * instance and should be set at creation. {@code exo_controller_placeholder} will be ignored - * if this is set. + *
  • {@code exo_controller} - An already inflated {@link PlaybackControlView}. Allows use + * of a custom extension of {@link PlaybackControlView}. Note that attributes such as + * {@code rewind_increment} will not be automatically propagated through to this instance. If + * a view exists with this id, any {@code exo_controller_placeholder} view will be ignored. *
      - *
    • Type: {@link View}
    • + *
    • Type: {@link PlaybackControlView}
    • *
    *
  • *
  • {@code exo_overlay} - A {@link FrameLayout} positioned on top of the player which From e604daaa0954381f1e6917d66fb410627a967935 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 2 Aug 2017 01:25:41 -0700 Subject: [PATCH 110/133] Fix handling of H.262 CSD The start code for H.262 codec-specific data may be across a packet boundary. Before this change the offset passed to CsdBuffer.onData may have been before the start point of the data in the newData buffer. After this change, start codes are added directly to the CSD buffer when it's filling and any start code bytes added by onData (at the end of a packet) are discarded. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163943584 --- .../exoplayer2/extractor/ts/H262Reader.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index a00bace56c..160a9c5a71 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -103,9 +103,8 @@ public final class H262Reader implements ElementaryStreamReader { totalBytesWritten += data.bytesLeft(); output.sampleData(data, data.bytesLeft()); - int searchOffset = offset; while (true) { - int startCodeOffset = NalUnitUtil.findNalUnit(dataArray, searchOffset, limit, prefixFlags); + int startCodeOffset = NalUnitUtil.findNalUnit(dataArray, offset, limit, prefixFlags); if (startCodeOffset == limit) { // We've scanned to the end of the data without finding another start code. @@ -126,7 +125,7 @@ public final class H262Reader implements ElementaryStreamReader { csdBuffer.onData(dataArray, offset, startCodeOffset); } // This is the number of bytes belonging to the next start code that have already been - // passed to csdDataTargetBuffer. + // passed to csdBuffer. int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0; if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) { // The csd data is complete, so we can decode and output the media format. @@ -160,8 +159,7 @@ public final class H262Reader implements ElementaryStreamReader { isKeyframe = true; } - offset = startCodeOffset; - searchOffset = offset + 3; + offset = startCodeOffset + 3; } } @@ -226,6 +224,8 @@ public final class H262Reader implements ElementaryStreamReader { private static final class CsdBuffer { + private static final byte[] START_CODE = new byte[] {0, 0, 1}; + private boolean isFilling; public int length; @@ -249,24 +249,25 @@ public final class H262Reader implements ElementaryStreamReader { * Called when a start code is encountered in the stream. * * @param startCodeValue The start code value. - * @param bytesAlreadyPassed The number of bytes of the start code that have already been - * passed to {@link #onData(byte[], int, int)}, or 0. + * @param bytesAlreadyPassed The number of bytes of the start code that have been passed to + * {@link #onData(byte[], int, int)}, or 0. * @return Whether the csd data is now complete. If true is returned, neither - * this method or {@link #onData(byte[], int, int)} should be called again without an + * this method nor {@link #onData(byte[], int, int)} should be called again without an * interleaving call to {@link #reset()}. */ public boolean onStartCode(int startCodeValue, int bytesAlreadyPassed) { if (isFilling) { + length -= bytesAlreadyPassed; if (sequenceExtensionPosition == 0 && startCodeValue == START_EXTENSION) { - sequenceExtensionPosition = length - bytesAlreadyPassed; + sequenceExtensionPosition = length; } else { - length -= bytesAlreadyPassed; isFilling = false; return true; } } else if (startCodeValue == START_SEQUENCE_HEADER) { isFilling = true; } + onData(START_CODE, 0, START_CODE.length); return false; } From cad25e5a4dba6e2b3d113c80ab5e25b5c8026e1d Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 2 Aug 2017 03:07:56 -0700 Subject: [PATCH 111/133] Further fix H262 segmentation Issue: #2891 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163951910 --- .../androidTest/assets/ts/sample.ts.0.dump | 2 +- .../exoplayer2/extractor/ts/H262Reader.java | 46 ++++++++----------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/library/core/src/androidTest/assets/ts/sample.ts.0.dump b/library/core/src/androidTest/assets/ts/sample.ts.0.dump index 83f1337816..91e48b1722 100644 --- a/library/core/src/androidTest/assets/ts/sample.ts.0.dump +++ b/library/core/src/androidTest/assets/ts/sample.ts.0.dump @@ -28,7 +28,7 @@ track 256: data = length 22, hash CE183139 sample count = 2 sample 0: - time = 0 + time = 33366 flags = 1 data = length 20711, hash 34341E8 sample 1: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index 160a9c5a71..a3502a3242 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -51,17 +51,17 @@ public final class H262Reader implements ElementaryStreamReader { // State that should be reset on seek. private final boolean[] prefixFlags; private final CsdBuffer csdBuffer; - private boolean foundPicture; private long totalBytesWritten; + private boolean startedFirstSample; // Per packet state that gets reset at the start of each packet. private long pesTimeUs; - private boolean pesPtsUsAvailable; - // Per sample state that gets reset at the start of each frame. - private boolean isKeyframe; + // Per sample state that gets reset at the start of each sample. private long samplePosition; private long sampleTimeUs; + private boolean sampleIsKeyframe; + private boolean sampleHasPicture; public H262Reader() { prefixFlags = new boolean[4]; @@ -72,10 +72,8 @@ public final class H262Reader implements ElementaryStreamReader { public void seek() { NalUnitUtil.clearPrefixFlags(prefixFlags); csdBuffer.reset(); - pesPtsUsAvailable = false; - foundPicture = false; - samplePosition = C.POSITION_UNSET; totalBytesWritten = 0; + startedFirstSample = false; } @Override @@ -87,10 +85,7 @@ public final class H262Reader implements ElementaryStreamReader { @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { - pesPtsUsAvailable = pesTimeUs != C.TIME_UNSET; - if (pesPtsUsAvailable) { - this.pesTimeUs = pesTimeUs; - } + this.pesTimeUs = pesTimeUs; } @Override @@ -136,27 +131,26 @@ public final class H262Reader implements ElementaryStreamReader { } } - if (hasOutputFormat - && (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER)) { + if (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER) { int bytesWrittenPastStartCode = limit - startCodeOffset; - boolean resetSample = samplePosition == C.POSITION_UNSET; - if (foundPicture) { - @C.BufferFlags int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; + if (startedFirstSample && sampleHasPicture && hasOutputFormat) { + // Output the sample. + @C.BufferFlags int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (totalBytesWritten - samplePosition) - bytesWrittenPastStartCode; output.sampleMetadata(sampleTimeUs, flags, size, bytesWrittenPastStartCode, null); - isKeyframe = false; - resetSample = true; } - foundPicture = startCodeValue == START_PICTURE; - if (resetSample) { + if (!startedFirstSample || sampleHasPicture) { + // Start the next sample. samplePosition = totalBytesWritten - bytesWrittenPastStartCode; - sampleTimeUs = (pesPtsUsAvailable ? pesTimeUs : sampleTimeUs + frameDurationUs); - pesPtsUsAvailable = false; + sampleTimeUs = pesTimeUs != C.TIME_UNSET ? pesTimeUs + : (startedFirstSample ? (sampleTimeUs + frameDurationUs) : 0); + sampleIsKeyframe = false; + pesTimeUs = C.TIME_UNSET; + startedFirstSample = true; } - } - - if (hasOutputFormat && startCodeValue == START_GROUP) { - isKeyframe = true; + sampleHasPicture = startCodeValue == START_PICTURE; + } else if (startCodeValue == START_GROUP) { + sampleIsKeyframe = true; } offset = startCodeOffset + 3; From a3df29a2462bed0f0c9a6c31d022de785baab9e9 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 2 Aug 2017 09:26:04 -0700 Subject: [PATCH 112/133] Some extractor fixes - Fix Ogg extractor to work without sniffing. - Fix extractors to handle seek() before init(). - Add tests for both issues. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163992343 --- .../ext/flac/FlacExtractorTest.java | 2 +- .../exoplayer2/ext/flac/FlacExtractor.java | 10 ++- .../extractor/flv/FlvExtractorTest.java | 2 +- .../extractor/mkv/MatroskaExtractorTest.java | 6 +- .../extractor/mp3/Mp3ExtractorTest.java | 4 +- .../mp4/FragmentedMp4ExtractorTest.java | 6 +- .../extractor/mp4/Mp4ExtractorTest.java | 2 +- .../extractor/ogg/OggExtractorTest.java | 9 +- .../extractor/rawcc/RawCcExtractorTest.java | 2 +- .../extractor/ts/Ac3ExtractorTest.java | 2 +- .../extractor/ts/AdtsExtractorTest.java | 2 +- .../extractor/ts/PsExtractorTest.java | 2 +- .../extractor/ts/TsExtractorTest.java | 2 +- .../extractor/wav/WavExtractorTest.java | 2 +- .../exoplayer2/extractor/Extractor.java | 3 +- .../extractor/ogg/OggExtractor.java | 66 ++++++++------ .../extractor/ogg/StreamReader.java | 9 +- .../exoplayer2/extractor/ts/Ac3Extractor.java | 4 +- .../extractor/ts/AdtsExtractor.java | 5 +- .../exoplayer2/testutil/ExtractorAsserts.java | 87 +++++++++++-------- 20 files changed, 132 insertions(+), 95 deletions(-) diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorTest.java index 5954985100..7b193997c3 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public class FlacExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new FlacExtractor(); diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index d13194793e..7b71b5c743 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -159,13 +159,17 @@ public final class FlacExtractor implements Extractor { if (position == 0) { metadataParsed = false; } - decoderJni.reset(position); + if (decoderJni != null) { + decoderJni.reset(position); + } } @Override public void release() { - decoderJni.release(); - decoderJni = null; + if (decoderJni != null) { + decoderJni.release(); + decoderJni = null; + } } } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/flv/FlvExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/flv/FlvExtractorTest.java index 4587c98317..fc8d181eac 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/flv/FlvExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/flv/FlvExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class FlvExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new FlvExtractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java index 57beec3ac6..624a5ccb7e 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class MatroskaExtractorTest extends InstrumentationTestCase { public void testMkvSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new MatroskaExtractor(); @@ -35,7 +35,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase { } public void testWebmSubsampleEncryption() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new MatroskaExtractor(); @@ -44,7 +44,7 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase { } public void testWebmSubsampleEncryptionWithAltrefFrames() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new MatroskaExtractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java index 3ad6a74bc9..0f98624d69 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class Mp3ExtractorTest extends InstrumentationTestCase { public void testMp3Sample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new Mp3Extractor(); @@ -35,7 +35,7 @@ public final class Mp3ExtractorTest extends InstrumentationTestCase { } public void testTrimmedMp3Sample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new Mp3Extractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java index d8da8760e4..76c13495c1 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java @@ -27,13 +27,13 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class FragmentedMp4ExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts - .assertOutput(getExtractorFactory(), "mp4/sample_fragmented.mp4", getInstrumentation()); + ExtractorAsserts.assertBehavior(getExtractorFactory(), "mp4/sample_fragmented.mp4", + getInstrumentation()); } public void testSampleWithSeiPayloadParsing() throws Exception { // Enabling the CEA-608 track enables SEI payload parsing. - ExtractorAsserts.assertOutput( + ExtractorAsserts.assertBehavior( getExtractorFactory(FragmentedMp4Extractor.FLAG_ENABLE_CEA608_TRACK), "mp4/sample_fragmented_sei.mp4", getInstrumentation()); } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java index a534d6dd24..5e327e5502 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/Mp4ExtractorTest.java @@ -28,7 +28,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class Mp4ExtractorTest extends InstrumentationTestCase { public void testMp4Sample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new Mp4Extractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java index 26b7991869..3be23422cc 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ogg/OggExtractorTest.java @@ -36,20 +36,21 @@ public final class OggExtractorTest extends InstrumentationTestCase { }; public void testOpus() throws Exception { - ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation()); + ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear.opus", getInstrumentation()); } public void testFlac() throws Exception { - ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg", getInstrumentation()); + ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac.ogg", + getInstrumentation()); } public void testFlacNoSeektable() throws Exception { - ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg", + ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_flac_noseektable.ogg", getInstrumentation()); } public void testVorbis() throws Exception { - ExtractorAsserts.assertOutput(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg", + ExtractorAsserts.assertBehavior(OGG_EXTRACTOR_FACTORY, "ogg/bear_vorbis.ogg", getInstrumentation()); } diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractorTest.java index 5a9d60512c..18050f48a3 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractorTest.java @@ -30,7 +30,7 @@ import com.google.android.exoplayer2.util.MimeTypes; public final class RawCcExtractorTest extends InstrumentationTestCase { public void testRawCcSample() throws Exception { - ExtractorAsserts.assertOutput( + ExtractorAsserts.assertBehavior( new ExtractorFactory() { @Override public Extractor create() { diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/Ac3ExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/Ac3ExtractorTest.java index 1c18e44373..31633361db 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/Ac3ExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/Ac3ExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class Ac3ExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new Ac3Extractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractorTest.java index bc05be6fa8..9eb65d2091 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class AdtsExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new AdtsExtractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/PsExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/PsExtractorTest.java index e6937ccbc8..78ef05a769 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/PsExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/PsExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class PsExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new PsExtractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index 09c9facab0..b6eddb5112 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -45,7 +45,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new TsExtractor(); diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java index 7c969fd386..36c05aa72e 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.testutil.ExtractorAsserts.ExtractorFactory; public final class WavExtractorTest extends InstrumentationTestCase { public void testSample() throws Exception { - ExtractorAsserts.assertOutput(new ExtractorFactory() { + ExtractorAsserts.assertBehavior(new ExtractorFactory() { @Override public Extractor create() { return new WavExtractor(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java index de3dfd5266..7a2bc15da9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/Extractor.java @@ -63,7 +63,8 @@ public interface Extractor { void init(ExtractorOutput output); /** - * Extracts data read from a provided {@link ExtractorInput}. + * Extracts data read from a provided {@link ExtractorInput}. Must not be called before + * {@link #init(ExtractorOutput)}. *

    * A single call to this method will block until some progress has been made, but will not block * for longer than this. Hence each call will consume only a small amount of input data. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggExtractor.java index cc3c5de311..54e168c665 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggExtractor.java @@ -45,30 +45,14 @@ public class OggExtractor implements Extractor { private static final int MAX_VERIFICATION_BYTES = 8; + private ExtractorOutput output; private StreamReader streamReader; + private boolean streamReaderInitialized; @Override public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { try { - OggPageHeader header = new OggPageHeader(); - if (!header.populate(input, true) || (header.type & 0x02) != 0x02) { - return false; - } - - int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES); - ParsableByteArray scratch = new ParsableByteArray(length); - input.peekFully(scratch.data, 0, length); - - if (FlacReader.verifyBitstreamType(resetPosition(scratch))) { - streamReader = new FlacReader(); - } else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) { - streamReader = new VorbisReader(); - } else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) { - streamReader = new OpusReader(); - } else { - return false; - } - return true; + return sniffInternal(input); } catch (ParserException e) { return false; } @@ -76,15 +60,14 @@ public class OggExtractor implements Extractor { @Override public void init(ExtractorOutput output) { - TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO); - output.endTracks(); - // TODO: fix the case if sniff() isn't called - streamReader.init(output, trackOutput); + this.output = output; } @Override public void seek(long position, long timeUs) { - streamReader.seek(position, timeUs); + if (streamReader != null) { + streamReader.seek(position, timeUs); + } } @Override @@ -95,12 +78,41 @@ public class OggExtractor implements Extractor { @Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { + if (streamReader == null) { + if (!sniffInternal(input)) { + throw new ParserException("Failed to determine bitstream type"); + } + input.resetPeekPosition(); + } + if (!streamReaderInitialized) { + TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO); + output.endTracks(); + streamReader.init(output, trackOutput); + streamReaderInitialized = true; + } return streamReader.read(input, seekPosition); } - //@VisibleForTesting - /* package */ StreamReader getStreamReader() { - return streamReader; + private boolean sniffInternal(ExtractorInput input) throws IOException, InterruptedException { + OggPageHeader header = new OggPageHeader(); + if (!header.populate(input, true) || (header.type & 0x02) != 0x02) { + return false; + } + + int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES); + ParsableByteArray scratch = new ParsableByteArray(length); + input.peekFully(scratch.data, 0, length); + + if (FlacReader.verifyBitstreamType(resetPosition(scratch))) { + streamReader = new FlacReader(); + } else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) { + streamReader = new VorbisReader(); + } else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) { + streamReader = new OpusReader(); + } else { + return false; + } + return true; } private static ParsableByteArray resetPosition(ParsableByteArray scratch) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java index c203b0c6bd..d136468faa 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java @@ -41,7 +41,8 @@ import java.io.IOException; OggSeeker oggSeeker; } - private OggPacket oggPacket; + private final OggPacket oggPacket; + private TrackOutput trackOutput; private ExtractorOutput extractorOutput; private OggSeeker oggSeeker; @@ -55,11 +56,13 @@ import java.io.IOException; private boolean seekMapSet; private boolean formatSet; + public StreamReader() { + oggPacket = new OggPacket(); + } + void init(ExtractorOutput output, TrackOutput trackOutput) { this.extractorOutput = output; this.trackOutput = trackOutput; - this.oggPacket = new OggPacket(); - reset(true); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java index 9c9536beec..8bab6b7ed1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java @@ -56,9 +56,9 @@ public final class Ac3Extractor implements Extractor { private static final int ID3_TAG = Util.getIntegerCodeForString("ID3"); private final long firstSampleTimestampUs; + private final Ac3Reader reader; private final ParsableByteArray sampleData; - private Ac3Reader reader; private boolean startedPacket; public Ac3Extractor() { @@ -67,6 +67,7 @@ public final class Ac3Extractor implements Extractor { public Ac3Extractor(long firstSampleTimestampUs) { this.firstSampleTimestampUs = firstSampleTimestampUs; + reader = new Ac3Reader(); sampleData = new ParsableByteArray(MAX_SYNC_FRAME_SIZE); } @@ -117,7 +118,6 @@ public final class Ac3Extractor implements Extractor { @Override public void init(ExtractorOutput output) { - reader = new Ac3Reader(); // TODO: Add support for embedded ID3. reader.createTracks(output, new TrackIdGenerator(0, 1)); output.endTracks(); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java index f7dadd51b2..a1851aa0ea 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java @@ -55,10 +55,9 @@ public final class AdtsExtractor implements Extractor { private static final int MAX_SNIFF_BYTES = 8 * 1024; private final long firstSampleTimestampUs; + private final AdtsReader reader; private final ParsableByteArray packetBuffer; - // Accessed only by the loading thread. - private AdtsReader reader; private boolean startedPacket; public AdtsExtractor() { @@ -67,6 +66,7 @@ public final class AdtsExtractor implements Extractor { public AdtsExtractor(long firstSampleTimestampUs) { this.firstSampleTimestampUs = firstSampleTimestampUs; + reader = new AdtsReader(true); packetBuffer = new ParsableByteArray(MAX_PACKET_SIZE); } @@ -127,7 +127,6 @@ public final class AdtsExtractor implements Extractor { @Override public void init(ExtractorOutput output) { - reader = new AdtsReader(true); reader.createTracks(output, new TrackIdGenerator(0, 1)); output.endTracks(); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java index fb78b3a634..db63662c45 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java @@ -18,6 +18,8 @@ package com.google.android.exoplayer2.testutil; import android.app.Instrumentation; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.ExtractorInput; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; @@ -42,46 +44,57 @@ public final class ExtractorAsserts { private static final String UNKNOWN_LENGTH_EXTENSION = ".unklen" + DUMP_EXTENSION; /** - * Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, - * boolean)} with all possible combinations of "simulate" parameters. + * Asserts that an extractor behaves correctly given valid input data: + *

      + *
    • Calls {@link Extractor#seek(long, long)} and {@link Extractor#release()} without calling + * {@link Extractor#init(ExtractorOutput)} to check these calls do not fail.
    • + *
    • Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, + * boolean, boolean)} with all possible combinations of "simulate" parameters.
    • + *
    * * @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor} * class which is to be tested. - * @param sampleFile The path to the input sample. + * @param file The path to the input sample. * @param instrumentation To be used to load the sample file. * @throws IOException If reading from the input fails. * @throws InterruptedException If interrupted while reading from the input. - * @see #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, boolean) */ - public static void assertOutput(ExtractorFactory factory, String sampleFile, + public static void assertBehavior(ExtractorFactory factory, String file, Instrumentation instrumentation) throws IOException, InterruptedException { - byte[] fileData = TestUtil.getByteArray(instrumentation, sampleFile); - assertOutput(factory, sampleFile, fileData, instrumentation); + // Check behavior prior to initialization. + Extractor extractor = factory.create(); + extractor.seek(0, 0); + extractor.release(); + // Assert output. + byte[] fileData = TestUtil.getByteArray(instrumentation, file); + assertOutput(factory, file, fileData, instrumentation); } /** * Calls {@link #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, - * boolean)} with all possible combinations of "simulate" parameters. + * boolean, boolean)} with all possible combinations of "simulate" parameters with + * {@code sniffFirst} set to true, and makes one additional call with the "simulate" and + * {@code sniffFirst} parameters all set to false. * * @param factory An {@link ExtractorFactory} which creates instances of the {@link Extractor} * class which is to be tested. - * @param sampleFile The path to the input sample. - * @param fileData Content of the input file. + * @param file The path to the input sample. + * @param data Content of the input file. * @param instrumentation To be used to load the sample file. * @throws IOException If reading from the input fails. * @throws InterruptedException If interrupted while reading from the input. - * @see #assertOutput(Extractor, String, byte[], Instrumentation, boolean, boolean, boolean) */ - public static void assertOutput(ExtractorFactory factory, String sampleFile, byte[] fileData, + public static void assertOutput(ExtractorFactory factory, String file, byte[] data, Instrumentation instrumentation) throws IOException, InterruptedException { - assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, false, false); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, false, false); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, true, false); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, true, false); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, false, true); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, false, true); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, false, true, true); - assertOutput(factory.create(), sampleFile, fileData, instrumentation, true, true, true); + assertOutput(factory.create(), file, data, instrumentation, true, false, false, false); + assertOutput(factory.create(), file, data, instrumentation, true, false, false, true); + assertOutput(factory.create(), file, data, instrumentation, true, false, true, false); + assertOutput(factory.create(), file, data, instrumentation, true, false, true, true); + assertOutput(factory.create(), file, data, instrumentation, true, true, false, false); + assertOutput(factory.create(), file, data, instrumentation, true, true, false, true); + assertOutput(factory.create(), file, data, instrumentation, true, true, true, false); + assertOutput(factory.create(), file, data, instrumentation, true, true, true, true); + assertOutput(factory.create(), file, data, instrumentation, false, false, false, false); } /** @@ -91,34 +104,38 @@ public final class ExtractorAsserts { * #UNKNOWN_LENGTH_EXTENSION}" exists, it's preferred. * * @param extractor The {@link Extractor} to be tested. - * @param sampleFile The path to the input sample. - * @param fileData Content of the input file. + * @param file The path to the input sample. + * @param data Content of the input file. * @param instrumentation To be used to load the sample file. - * @param simulateIOErrors If true simulates IOErrors. - * @param simulateUnknownLength If true simulates unknown input length. - * @param simulatePartialReads If true simulates partial reads. + * @param sniffFirst Whether to sniff the data by calling {@link Extractor#sniff(ExtractorInput)} + * prior to consuming it. + * @param simulateIOErrors Whether to simulate IO errors. + * @param simulateUnknownLength Whether to simulate unknown input length. + * @param simulatePartialReads Whether to simulate partial reads. * @return The {@link FakeExtractorOutput} used in the test. * @throws IOException If reading from the input fails. * @throws InterruptedException If interrupted while reading from the input. */ - public static FakeExtractorOutput assertOutput(Extractor extractor, String sampleFile, - byte[] fileData, Instrumentation instrumentation, boolean simulateIOErrors, + public static FakeExtractorOutput assertOutput(Extractor extractor, String file, byte[] data, + Instrumentation instrumentation, boolean sniffFirst, boolean simulateIOErrors, boolean simulateUnknownLength, boolean simulatePartialReads) throws IOException, InterruptedException { - FakeExtractorInput input = new FakeExtractorInput.Builder().setData(fileData) + FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data) .setSimulateIOErrors(simulateIOErrors) .setSimulateUnknownLength(simulateUnknownLength) .setSimulatePartialReads(simulatePartialReads).build(); - Assert.assertTrue(TestUtil.sniffTestData(extractor, input)); - input.resetPeekPosition(); - FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true); + if (sniffFirst) { + Assert.assertTrue(TestUtil.sniffTestData(extractor, input)); + input.resetPeekPosition(); + } + FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true); if (simulateUnknownLength - && assetExists(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION)) { - extractorOutput.assertOutput(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION); + && assetExists(instrumentation, file + UNKNOWN_LENGTH_EXTENSION)) { + extractorOutput.assertOutput(instrumentation, file + UNKNOWN_LENGTH_EXTENSION); } else { - extractorOutput.assertOutput(instrumentation, sampleFile + ".0" + DUMP_EXTENSION); + extractorOutput.assertOutput(instrumentation, file + ".0" + DUMP_EXTENSION); } SeekMap seekMap = extractorOutput.seekMap; @@ -133,7 +150,7 @@ public final class ExtractorAsserts { } consumeTestData(extractor, input, timeUs, extractorOutput, false); - extractorOutput.assertOutput(instrumentation, sampleFile + '.' + j + DUMP_EXTENSION); + extractorOutput.assertOutput(instrumentation, file + '.' + j + DUMP_EXTENSION); } } From 19aa69e0f4a0f57816942fef0eeddf33f6c16a7e Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 2 Aug 2017 09:26:58 -0700 Subject: [PATCH 113/133] Remove child data holder helper from AbstractConcatenatedTimeline. This helper class required a scratch instance to write on. Such a scratch instance may violate the immuatability of the timelines if used by multiple threads simultaneously. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=163992458 --- .../source/AbstractConcatenatedTimeline.java | 141 ++++++++---------- .../source/ConcatenatingMediaSource.java | 61 ++++---- .../DynamicConcatenatingMediaSource.java | 45 +++--- .../exoplayer2/source/LoopingMediaSource.java | 40 +++-- 4 files changed, 148 insertions(+), 139 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java index 3bee3cc73f..42ac938677 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java @@ -25,68 +25,25 @@ import com.google.android.exoplayer2.Timeline; */ /* package */ abstract class AbstractConcatenatedTimeline extends Timeline { - /** - * Meta data of a child timeline. - */ - protected static final class ChildDataHolder { + private final int childCount; - /** - * Child timeline. - */ - public Timeline timeline; - - /** - * First period index belonging to the child timeline. - */ - public int firstPeriodIndexInChild; - - /** - * First window index belonging to the child timeline. - */ - public int firstWindowIndexInChild; - - /** - * UID of child timeline. - */ - public Object uid; - - /** - * Set child holder data. - * - * @param timeline Child timeline. - * @param firstPeriodIndexInChild First period index belonging to the child timeline. - * @param firstWindowIndexInChild First window index belonging to the child timeline. - * @param uid UID of child timeline. - */ - public void setData(Timeline timeline, int firstPeriodIndexInChild, int firstWindowIndexInChild, - Object uid) { - this.timeline = timeline; - this.firstPeriodIndexInChild = firstPeriodIndexInChild; - this.firstWindowIndexInChild = firstWindowIndexInChild; - this.uid = uid; - } - - } - - private final ChildDataHolder childDataHolder; - - public AbstractConcatenatedTimeline() { - childDataHolder = new ChildDataHolder(); + public AbstractConcatenatedTimeline(int childCount) { + this.childCount = childCount; } @Override public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode) { - getChildDataByWindowIndex(windowIndex, childDataHolder); - int firstWindowIndexInChild = childDataHolder.firstWindowIndexInChild; - int nextWindowIndexInChild = childDataHolder.timeline.getNextWindowIndex( + int childIndex = getChildIndexByWindowIndex(windowIndex); + int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); + int nextWindowIndexInChild = getTimelineByChildIndex(childIndex).getNextWindowIndex( windowIndex - firstWindowIndexInChild, repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode); if (nextWindowIndexInChild != C.INDEX_UNSET) { return firstWindowIndexInChild + nextWindowIndexInChild; } else { - firstWindowIndexInChild += childDataHolder.timeline.getWindowCount(); - if (firstWindowIndexInChild < getWindowCount()) { - return firstWindowIndexInChild; + int nextChildIndex = childIndex + 1; + if (nextChildIndex < childCount) { + return getFirstWindowIndexByChildIndex(nextChildIndex); } else if (repeatMode == Player.REPEAT_MODE_ALL) { return 0; } else { @@ -97,9 +54,9 @@ import com.google.android.exoplayer2.Timeline; @Override public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode) { - getChildDataByWindowIndex(windowIndex, childDataHolder); - int firstWindowIndexInChild = childDataHolder.firstWindowIndexInChild; - int previousWindowIndexInChild = childDataHolder.timeline.getPreviousWindowIndex( + int childIndex = getChildIndexByWindowIndex(windowIndex); + int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); + int previousWindowIndexInChild = getTimelineByChildIndex(childIndex).getPreviousWindowIndex( windowIndex - firstWindowIndexInChild, repeatMode == Player.REPEAT_MODE_ALL ? Player.REPEAT_MODE_OFF : repeatMode); if (previousWindowIndexInChild != C.INDEX_UNSET) { @@ -118,11 +75,11 @@ import com.google.android.exoplayer2.Timeline; @Override public final Window getWindow(int windowIndex, Window window, boolean setIds, long defaultPositionProjectionUs) { - getChildDataByWindowIndex(windowIndex, childDataHolder); - int firstWindowIndexInChild = childDataHolder.firstWindowIndexInChild; - int firstPeriodIndexInChild = childDataHolder.firstPeriodIndexInChild; - childDataHolder.timeline.getWindow(windowIndex - firstWindowIndexInChild, window, setIds, - defaultPositionProjectionUs); + int childIndex = getChildIndexByWindowIndex(windowIndex); + int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); + int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex); + getTimelineByChildIndex(childIndex).getWindow(windowIndex - firstWindowIndexInChild, window, + setIds, defaultPositionProjectionUs); window.firstPeriodIndex += firstPeriodIndexInChild; window.lastPeriodIndex += firstPeriodIndexInChild; return window; @@ -130,13 +87,14 @@ import com.google.android.exoplayer2.Timeline; @Override public final Period getPeriod(int periodIndex, Period period, boolean setIds) { - getChildDataByPeriodIndex(periodIndex, childDataHolder); - int firstWindowIndexInChild = childDataHolder.firstWindowIndexInChild; - int firstPeriodIndexInChild = childDataHolder.firstPeriodIndexInChild; - childDataHolder.timeline.getPeriod(periodIndex - firstPeriodIndexInChild, period, setIds); + int childIndex = getChildIndexByPeriodIndex(periodIndex); + int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); + int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex); + getTimelineByChildIndex(childIndex).getPeriod(periodIndex - firstPeriodIndexInChild, period, + setIds); period.windowIndex += firstWindowIndexInChild; if (setIds) { - period.uid = Pair.create(childDataHolder.uid, period.uid); + period.uid = Pair.create(getChildUidByChildIndex(childIndex), period.uid); } return period; } @@ -149,37 +107,64 @@ import com.google.android.exoplayer2.Timeline; Pair childUidAndPeriodUid = (Pair) uid; Object childUid = childUidAndPeriodUid.first; Object periodUid = childUidAndPeriodUid.second; - if (!getChildDataByChildUid(childUid, childDataHolder)) { + int childIndex = getChildIndexByChildUid(childUid); + if (childIndex == C.INDEX_UNSET) { return C.INDEX_UNSET; } - int periodIndexInChild = childDataHolder.timeline.getIndexOfPeriod(periodUid); + int periodIndexInChild = getTimelineByChildIndex(childIndex).getIndexOfPeriod(periodUid); return periodIndexInChild == C.INDEX_UNSET ? C.INDEX_UNSET - : childDataHolder.firstPeriodIndexInChild + periodIndexInChild; + : getFirstPeriodIndexByChildIndex(childIndex) + periodIndexInChild; } /** - * Populates {@link ChildDataHolder} for the child timeline containing the given period index. + * Returns the index of the child timeline containing the given period index. * * @param periodIndex A valid period index within the bounds of the timeline. - * @param childData A data holder to be populated. */ - protected abstract void getChildDataByPeriodIndex(int periodIndex, ChildDataHolder childData); + protected abstract int getChildIndexByPeriodIndex(int periodIndex); /** - * Populates {@link ChildDataHolder} for the child timeline containing the given window index. + * Returns the index of the child timeline containing the given window index. * * @param windowIndex A valid window index within the bounds of the timeline. - * @param childData A data holder to be populated. */ - protected abstract void getChildDataByWindowIndex(int windowIndex, ChildDataHolder childData); + protected abstract int getChildIndexByWindowIndex(int windowIndex); /** - * Populates {@link ChildDataHolder} for the child timeline with the given UID. + * Returns the index of the child timeline with the given UID or {@link C#INDEX_UNSET} if not + * found. * * @param childUid A child UID. - * @param childData A data holder to be populated. - * @return Whether a child with the given UID was found. + * @return Index of child timeline or {@link C#INDEX_UNSET} if UID was not found. */ - protected abstract boolean getChildDataByChildUid(Object childUid, ChildDataHolder childData); + protected abstract int getChildIndexByChildUid(Object childUid); + + /** + * Returns the child timeline for the child with the given index. + * + * @param childIndex A valid child index within the bounds of the timeline. + */ + protected abstract Timeline getTimelineByChildIndex(int childIndex); + + /** + * Returns the first period index belonging to the child timeline with the given index. + * + * @param childIndex A valid child index within the bounds of the timeline. + */ + protected abstract int getFirstPeriodIndexByChildIndex(int childIndex); + + /** + * Returns the first window index belonging to the child timeline with the given index. + * + * @param childIndex A valid child index within the bounds of the timeline. + */ + protected abstract int getFirstWindowIndexByChildIndex(int childIndex); + + /** + * Returns the UID of the child timeline with the given index. + * + * @param childIndex A valid child index within the bounds of the timeline. + */ + protected abstract Object getChildUidByChildIndex(int childIndex); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 2c998e8a06..5d2bbcc33e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; @@ -97,7 +98,7 @@ public final class ConcatenatingMediaSource implements MediaSource { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { int sourceIndex = timeline.getChildIndexByPeriodIndex(id.periodIndex); MediaPeriodId periodIdInSource = - new MediaPeriodId(id.periodIndex - timeline.getFirstPeriodIndexInChild(sourceIndex)); + new MediaPeriodId(id.periodIndex - timeline.getFirstPeriodIndexByChildIndex(sourceIndex)); MediaPeriod mediaPeriod = mediaSources[sourceIndex].createPeriod(periodIdInSource, allocator); sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex); return mediaPeriod; @@ -166,6 +167,7 @@ public final class ConcatenatingMediaSource implements MediaSource { private final boolean isRepeatOneAtomic; public ConcatenatedTimeline(Timeline[] timelines, boolean isRepeatOneAtomic) { + super(timelines.length); int[] sourcePeriodOffsets = new int[timelines.length]; int[] sourceWindowOffsets = new int[timelines.length]; long periodCount = 0; @@ -212,44 +214,43 @@ public final class ConcatenatingMediaSource implements MediaSource { } @Override - protected void getChildDataByPeriodIndex(int periodIndex, ChildDataHolder childData) { - int childIndex = getChildIndexByPeriodIndex(periodIndex); - getChildDataByChildIndex(childIndex, childData); - } - - @Override - protected void getChildDataByWindowIndex(int windowIndex, ChildDataHolder childData) { - int childIndex = Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1; - getChildDataByChildIndex(childIndex, childData); - } - - @Override - protected boolean getChildDataByChildUid(Object childUid, ChildDataHolder childData) { - if (!(childUid instanceof Integer)) { - return false; - } - int childIndex = (Integer) childUid; - getChildDataByChildIndex(childIndex, childData); - return true; - } - - private void getChildDataByChildIndex(int childIndex, ChildDataHolder childData) { - childData.setData(timelines[childIndex], getFirstPeriodIndexInChild(childIndex), - getFirstWindowIndexInChild(childIndex), childIndex); - } - - private int getChildIndexByPeriodIndex(int periodIndex) { + protected int getChildIndexByPeriodIndex(int periodIndex) { return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1; } - private int getFirstPeriodIndexInChild(int childIndex) { + @Override + protected int getChildIndexByWindowIndex(int windowIndex) { + return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1; + } + + @Override + protected int getChildIndexByChildUid(Object childUid) { + if (!(childUid instanceof Integer)) { + return C.INDEX_UNSET; + } + return (Integer) childUid; + } + + @Override + protected Timeline getTimelineByChildIndex(int childIndex) { + return timelines[childIndex]; + } + + @Override + protected int getFirstPeriodIndexByChildIndex(int childIndex) { return childIndex == 0 ? 0 : sourcePeriodOffsets[childIndex - 1]; } - private int getFirstWindowIndexInChild(int childIndex) { + @Override + protected int getFirstWindowIndexByChildIndex(int childIndex) { return childIndex == 0 ? 0 : sourceWindowOffsets[childIndex - 1]; } + @Override + protected Object getChildUidByChildIndex(int childIndex) { + return childIndex; + } + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java index ad2e154f6d..b00732e839 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java @@ -397,6 +397,7 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl public ConcatenatedTimeline(Collection mediaSourceHolders, int windowCount, int periodCount) { + super(mediaSourceHolders.size()); this.windowCount = windowCount; this.periodCount = periodCount; int childCount = mediaSourceHolders.size(); @@ -416,28 +417,42 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl } @Override - protected void getChildDataByPeriodIndex(int periodIndex, ChildDataHolder childDataHolder) { - int index = Util.binarySearchFloor(firstPeriodInChildIndices, periodIndex, true, false); - setChildData(index, childDataHolder); + protected int getChildIndexByPeriodIndex(int periodIndex) { + return Util.binarySearchFloor(firstPeriodInChildIndices, periodIndex, true, false); } @Override - protected void getChildDataByWindowIndex(int windowIndex, ChildDataHolder childDataHolder) { - int index = Util.binarySearchFloor(firstWindowInChildIndices, windowIndex, true, false); - setChildData(index, childDataHolder); + protected int getChildIndexByWindowIndex(int windowIndex) { + return Util.binarySearchFloor(firstWindowInChildIndices, windowIndex, true, false); } @Override - protected boolean getChildDataByChildUid(Object childUid, ChildDataHolder childDataHolder) { + protected int getChildIndexByChildUid(Object childUid) { if (!(childUid instanceof Integer)) { - return false; + return C.INDEX_UNSET; } int index = childIndexByUid.get((int) childUid, -1); - if (index == -1) { - return false; - } - setChildData(index, childDataHolder); - return true; + return index == -1 ? C.INDEX_UNSET : index; + } + + @Override + protected Timeline getTimelineByChildIndex(int childIndex) { + return timelines[childIndex]; + } + + @Override + protected int getFirstPeriodIndexByChildIndex(int childIndex) { + return firstPeriodInChildIndices[childIndex]; + } + + @Override + protected int getFirstWindowIndexByChildIndex(int childIndex) { + return firstWindowInChildIndices[childIndex]; + } + + @Override + protected Object getChildUidByChildIndex(int childIndex) { + return uids[childIndex]; } @Override @@ -450,10 +465,6 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl return periodCount; } - private void setChildData(int srcIndex, ChildDataHolder dest) { - dest.setData(timelines[srcIndex], firstPeriodInChildIndices[srcIndex], - firstWindowInChildIndices[srcIndex], uids[srcIndex]); - } } private static final class DeferredTimeline extends Timeline { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java index f0032e0ee0..a6e93a92b9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java @@ -101,6 +101,7 @@ public final class LoopingMediaSource implements MediaSource { private final int loopCount; public LoopingTimeline(Timeline childTimeline, int loopCount) { + super(loopCount); this.childTimeline = childTimeline; childPeriodCount = childTimeline.getPeriodCount(); childWindowCount = childTimeline.getWindowCount(); @@ -120,30 +121,41 @@ public final class LoopingMediaSource implements MediaSource { } @Override - protected void getChildDataByPeriodIndex(int periodIndex, ChildDataHolder childData) { - int childIndex = periodIndex / childPeriodCount; - getChildDataByChildIndex(childIndex, childData); + protected int getChildIndexByPeriodIndex(int periodIndex) { + return periodIndex / childPeriodCount; } @Override - protected void getChildDataByWindowIndex(int windowIndex, ChildDataHolder childData) { - int childIndex = windowIndex / childWindowCount; - getChildDataByChildIndex(childIndex, childData); + protected int getChildIndexByWindowIndex(int windowIndex) { + return windowIndex / childWindowCount; } @Override - protected boolean getChildDataByChildUid(Object childUid, ChildDataHolder childData) { + protected int getChildIndexByChildUid(Object childUid) { if (!(childUid instanceof Integer)) { - return false; + return C.INDEX_UNSET; } - int childIndex = (Integer) childUid; - getChildDataByChildIndex(childIndex, childData); - return true; + return (Integer) childUid; } - private void getChildDataByChildIndex(int childIndex, ChildDataHolder childData) { - childData.setData(childTimeline, childIndex * childPeriodCount, childIndex * childWindowCount, - childIndex); + @Override + protected Timeline getTimelineByChildIndex(int childIndex) { + return childTimeline; + } + + @Override + protected int getFirstPeriodIndexByChildIndex(int childIndex) { + return childIndex * childPeriodCount; + } + + @Override + protected int getFirstWindowIndexByChildIndex(int childIndex) { + return childIndex * childWindowCount; + } + + @Override + protected Object getChildUidByChildIndex(int childIndex) { + return childIndex; } } From fbee0c85212088212a3c1e1db217263b1807e639 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 3 Aug 2017 00:50:47 -0700 Subject: [PATCH 114/133] Replace README reference to source with reference to javadoc ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164090619 --- extensions/ima/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ima/README.md b/extensions/ima/README.md index b5afcec94a..f328bb44cb 100644 --- a/extensions/ima/README.md +++ b/extensions/ima/README.md @@ -7,7 +7,7 @@ The IMA extension is a [MediaSource][] implementation wrapping the alongside content. [IMA]: https://developers.google.com/interactive-media-ads/docs/sdks/android/ -[MediaSource]: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java +[MediaSource]: https://google.github.io/ExoPlayer/doc/reference/index.html?com/google/android/exoplayer2/source/MediaSource.html ## Getting the extension ## From 838c4414eb63ce4a90119793b46b510e8acee639 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 3 Aug 2017 03:33:22 -0700 Subject: [PATCH 115/133] Fix targetSdkVersion to be consistent with gradle builds The manifest value is always overridden in gradle builds, so this is for internal builds only. The value should be the same (i.e. 25!). Also fix IMA build to force the right support library version, attempt 2! ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164103183 --- demo/src/main/AndroidManifest.xml | 2 +- extensions/ima/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index a39023353a..0e04d9a435 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ - + 11.0.2 // |-- com.google.android.gms:play-services-basement:[11.0.2] -> 11.0.2 // |-- com.android.support:support-v4:25.2.0 - compile 'com.android.support:support-annotations:' + supportLibraryVersion + compile 'com.android.support:support-v4:' + supportLibraryVersion compile 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4' compile 'com.google.android.gms:play-services-ads:11.0.2' androidTestCompile project(modulePrefix + 'library') From 587704a2a0d9a4c4ce1a2281e0918f5615248b93 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 3 Aug 2017 04:10:10 -0700 Subject: [PATCH 116/133] Add some missing @param Javadoc to extensions ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164105607 --- .../android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java | 4 ++++ .../google/android/exoplayer2/ext/flac/FlacLibrary.java | 2 ++ .../android/exoplayer2/ext/gvr/GvrAudioProcessor.java | 5 +++++ .../android/exoplayer2/ext/ima/ImaAdsMediaSource.java | 2 ++ .../android/exoplayer2/ext/opus/LibopusAudioRenderer.java | 7 +++++++ .../google/android/exoplayer2/ext/opus/OpusLibrary.java | 2 ++ .../com/google/android/exoplayer2/ext/vp9/VpxLibrary.java | 2 ++ .../exoplayer2/ext/vp9/VpxOutputBufferRenderer.java | 2 ++ 8 files changed, 26 insertions(+) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java index 0c065549ca..9b3bbbb6ab 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java @@ -37,6 +37,8 @@ public final class FfmpegLibrary { * Override the names of the FFmpeg native libraries. If an application wishes to call this * method, it must do so before calling any other method defined by this class, and before * instantiating a {@link FfmpegAudioRenderer} instance. + * + * @param libraries The names of the FFmpeg native libraries. */ public static void setLibraries(String... libraries) { LOADER.setLibraries(libraries); @@ -58,6 +60,8 @@ public final class FfmpegLibrary { /** * Returns whether the underlying library supports the specified MIME type. + * + * @param mimeType The MIME type to check. */ public static boolean supportsFormat(String mimeType) { if (!isAvailable()) { diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacLibrary.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacLibrary.java index 4130c27c59..d8b9b808a6 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacLibrary.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacLibrary.java @@ -35,6 +35,8 @@ public final class FlacLibrary { * Override the names of the Flac native libraries. If an application wishes to call this method, * it must do so before calling any other method defined by this class, and before instantiating * any {@link LibflacAudioRenderer} and {@link FlacExtractor} instances. + * + * @param libraries The names of the Flac native libraries. */ public static void setLibraries(String... libraries) { LOADER.setLibraries(libraries); diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java index c6e09cf4cc..5750f5f04d 100644 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java +++ b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java @@ -61,6 +61,11 @@ public final class GvrAudioProcessor implements AudioProcessor { /** * Updates the listener head orientation. May be called on any thread. See * {@code GvrAudioSurround.updateNativeOrientation}. + * + * @param w The w component of the quaternion. + * @param x The x component of the quaternion. + * @param y The y component of the quaternion. + * @param z The z component of the quaternion. */ public synchronized void updateOrientation(float w, float x, float y, float z) { this.w = w; diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java index 0bf5773d2c..d56a3ad41f 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java @@ -50,6 +50,8 @@ public final class ImaAdsMediaSource implements MediaSource { * Called if there was an error loading ads. The media source will load the content without ads * if ads can't be loaded, so listen for this event if you need to implement additional handling * (for example, stopping the player). + * + * @param error The error. */ void onAdLoadError(IOException error); diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java index 93fe033a31..730473ddad 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java @@ -54,6 +54,13 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer { * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. + * @param drmSessionManager For use with encrypted media. May be null if support for encrypted + * media is not required. + * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. + * For example a media file may start with a short clear region so as to allow playback to + * begin in parallel with key acquisition. This parameter specifies whether the renderer is + * permitted to play clear regions of encrypted media files before {@code drmSessionManager} + * has obtained the keys necessary to decrypt encrypted regions of the media. * @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output. */ public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusLibrary.java b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusLibrary.java index fb8fb738ff..22985ea497 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusLibrary.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusLibrary.java @@ -35,6 +35,8 @@ public final class OpusLibrary { * Override the names of the Opus native libraries. If an application wishes to call this method, * it must do so before calling any other method defined by this class, and before instantiating a * {@link LibopusAudioRenderer} instance. + * + * @param libraries The names of the Opus native libraries. */ public static void setLibraries(String... libraries) { LOADER.setLibraries(libraries); diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxLibrary.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxLibrary.java index 09f242f1ea..854576b4b2 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxLibrary.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxLibrary.java @@ -35,6 +35,8 @@ public final class VpxLibrary { * Override the names of the Vpx native libraries. If an application wishes to call this method, * it must do so before calling any other method defined by this class, and before instantiating a * {@link LibvpxVideoRenderer} instance. + * + * @param libraries The names of the Vpx native libraries. */ public static void setLibraries(String... libraries) { LOADER.setLibraries(libraries); diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBufferRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBufferRenderer.java index 8f43a0207b..d07e24d920 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBufferRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBufferRenderer.java @@ -22,6 +22,8 @@ public interface VpxOutputBufferRenderer { /** * Sets the output buffer to be rendered. The renderer is responsible for releasing the buffer. + * + * @param outputBuffer The output buffer to be rendered. */ void setOutputBuffer(VpxOutputBuffer outputBuffer); From c2049ba1a7946c3e4c49b02098104a283ca13167 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 3 Aug 2017 04:11:08 -0700 Subject: [PATCH 117/133] Fix some lint warnings ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164105662 --- .../main/java/com/google/android/exoplayer2/Timeline.java | 2 +- .../com/google/android/exoplayer2/text/ssa/SsaDecoder.java | 3 ++- .../com/google/android/exoplayer2/text/ttml/TtmlDecoder.java | 2 +- .../main/java/com/google/android/exoplayer2/util/Util.java | 2 +- .../google/android/exoplayer2/source/hls/HlsChunkSource.java | 3 +-- .../google/android/exoplayer2/ui/DebugTextViewHelper.java | 5 ++++- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java index 7ce23e67ec..414c0804ad 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -95,7 +95,7 @@ import com.google.android.exoplayer2.util.Assertions; * of the on-demand stream ends, playback of the live stream will start from its default position * near the live edge. * - *

    On-demand stream with mid-roll ads

    + *

    On-demand stream with mid-roll ads

    *

    * Example timeline for an on-demand
  *       stream with mid-roll ad groups diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java index 11489e7b35..d2f5a67c27 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.text.SimpleSubtitleDecoder; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.LongArray; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -132,7 +133,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { formatEndIndex = C.INDEX_UNSET; formatTextIndex = C.INDEX_UNSET; for (int i = 0; i < formatKeyCount; i++) { - String key = values[i].trim().toLowerCase(); + String key = Util.toLowerInvariant(values[i].trim()); switch (key) { case "start": formatStartIndex = i; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java index e438aa1837..a215bf3cc9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java @@ -290,7 +290,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { String displayAlign = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_DISPLAY_ALIGN); if (displayAlign != null) { - switch (displayAlign.toLowerCase()) { + switch (Util.toLowerInvariant(displayAlign)) { case "center": lineAnchor = Cue.ANCHOR_TYPE_MIDDLE; line += height / 2; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java index c00d7fa36c..b958a54244 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -895,7 +895,7 @@ public final class Util { */ @C.ContentType public static int inferContentType(String fileName) { - fileName = fileName.toLowerCase(); + fileName = Util.toLowerInvariant(fileName); if (fileName.endsWith(".mpd")) { return C.TYPE_DASH; } else if (fileName.endsWith(".m3u8")) { diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 38c3da8194..bca62ed230 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -39,7 +39,6 @@ import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import java.util.List; -import java.util.Locale; /** * Source of Hls (possibly adaptive) chunks. @@ -366,7 +365,7 @@ import java.util.Locale; private void setEncryptionData(Uri keyUri, String iv, byte[] secretKey) { String trimmedIv; - if (iv.toLowerCase(Locale.getDefault()).startsWith("0x")) { + if (Util.toLowerInvariant(iv).startsWith("0x")) { trimmedIv = iv.substring(2); } else { trimmedIv = iv; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java index 2b8705bb74..cb5e3465f8 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.ui; +import android.annotation.SuppressLint; import android.widget.TextView; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; @@ -25,6 +26,7 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import java.util.Locale; /** * A helper class for periodically updating a {@link TextView} with debug information obtained from @@ -125,6 +127,7 @@ public final class DebugTextViewHelper implements Runnable, Player.EventListener // Private methods. + @SuppressLint("SetTextI18n") private void updateAndPost() { textView.setText(getPlayerStateString() + getPlayerWindowIndexString() + getVideoString() + getAudioString()); @@ -191,7 +194,7 @@ public final class DebugTextViewHelper implements Runnable, Player.EventListener private static String getPixelAspectRatioString(float pixelAspectRatio) { return pixelAspectRatio == Format.NO_VALUE || pixelAspectRatio == 1f ? "" - : (" par:" + String.format("%.02f", pixelAspectRatio)); + : (" par:" + String.format(Locale.US, "%.02f", pixelAspectRatio)); } } From 5fbb58c62f77707eef49709348815f5b5f46afaa Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 3 Aug 2017 05:19:02 -0700 Subject: [PATCH 118/133] Take into account init data size for input buffer size Issue: #2900 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164110904 --- .../video/MediaCodecVideoRenderer.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 07c45dcd25..9a2927cc3f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -477,7 +477,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { Format oldFormat, Format newFormat) { return areAdaptationCompatible(codecIsAdaptive, oldFormat, newFormat) && newFormat.width <= codecMaxValues.width && newFormat.height <= codecMaxValues.height - && newFormat.maxInputSize <= codecMaxValues.inputSize; + && getMaxInputSize(newFormat) <= codecMaxValues.inputSize; } @Override @@ -854,18 +854,27 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } /** - * Returns a maximum input size for a given format. + * Returns a maximum input buffer size for a given format. * * @param format The format. - * @return A maximum input size in bytes, or {@link Format#NO_VALUE} if a maximum could not be - * determined. + * @return A maximum input buffer size in bytes, or {@link Format#NO_VALUE} if a maximum could not + * be determined. */ private static int getMaxInputSize(Format format) { if (format.maxInputSize != Format.NO_VALUE) { - // The format defines an explicit maximum input size. - return format.maxInputSize; + // The format defines an explicit maximum input size. Add the total size of initialization + // data buffers, as they may need to be queued in the same input buffer as the largest sample. + int totalInitializationDataSize = 0; + int initializationDataCount = format.initializationData.size(); + for (int i = 0; i < initializationDataCount; i++) { + totalInitializationDataSize += format.initializationData.get(i).length; + } + return format.maxInputSize + totalInitializationDataSize; + } else { + // Calculated maximum input sizes are overestimates, so it's not necessary to add the size of + // initialization data. + return getMaxInputSize(format.sampleMimeType, format.width, format.height); } - return getMaxInputSize(format.sampleMimeType, format.width, format.height); } /** From de1f538e14738e0e8e84cc14156d005f264918d9 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 3 Aug 2017 08:26:44 -0700 Subject: [PATCH 119/133] Bump version ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164126494 --- RELEASENOTES.md | 2 ++ constants.gradle | 2 +- .../com/google/android/exoplayer2/ExoPlayerLibraryInfo.java | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 379b84b4e7..e96cd9ddab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,6 +52,8 @@ ([#2891](https://github.com/google/ExoPlayer/issues/2891)). * Cronet extension: Support for a user-defined fallback if Cronet library is not present. +* Fix buffer too small IllegalStateException issue affecting some composite + media playbacks ([#2900](https://github.com/google/ExoPlayer/issues/2900)). * Misc bugfixes. ### r2.4.4 ### diff --git a/constants.gradle b/constants.gradle index 0db74945c4..93284fd897 100644 --- a/constants.gradle +++ b/constants.gradle @@ -24,7 +24,7 @@ project.ext { supportLibraryVersion = '25.4.0' dexmakerVersion = '1.2' mockitoVersion = '1.9.5' - releaseVersion = 'r2.5.0-beta2' + releaseVersion = 'r2.5.0-beta3' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index c04a777e14..153c6cda9c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -31,13 +31,13 @@ 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.5.0-beta2"; + public static final String VERSION = "2.5.0-beta3"; /** * 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.5.0-beta2"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.5.0-beta3"; /** * The version of the library expressed as an integer, for example 1002003. From 6bf967263ba5816467516fae1bc86e796a08ac60 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 3 Aug 2017 10:06:28 -0700 Subject: [PATCH 120/133] Remove dead sample link Issue: #3135 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164138761 --- demo/src/main/assets/media.exolist.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/demo/src/main/assets/media.exolist.json b/demo/src/main/assets/media.exolist.json index e0110df80b..59d8259d37 100644 --- a/demo/src/main/assets/media.exolist.json +++ b/demo/src/main/assets/media.exolist.json @@ -325,18 +325,6 @@ } ] }, - { - "name": "ClearKey DASH", - "samples": [ - { - "name": "Big Buck Bunny (CENC ClearKey)", - "uri": "http://html5.cablelabs.com:8100/cenc/ck/dash.mpd", - "extension": "mpd", - "drm_scheme": "cenc", - "drm_license_url": "https://wasabeef.jp/demos/cenc-ck-dash.json" - } - ] - }, { "name": "SmoothStreaming", "samples": [ From 1203b534062b4b9ceefa59549fd72e796e0146ca Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 4 Aug 2017 00:57:05 -0700 Subject: [PATCH 121/133] Decrease number of Vpx input buffers I think they're excessively sized also, but changing that is a little more risky. And we should look at investigating the input buffer size for all our decoder extensions, rather than just this one. Issue: #3120 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164234087 --- .../ext/ffmpeg/FfmpegAudioRenderer.java | 7 +++++++ .../ext/vp9/LibvpxVideoRenderer.java | 18 +++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java index 8d75ca3dbb..453a18476e 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java @@ -30,7 +30,14 @@ import com.google.android.exoplayer2.util.MimeTypes; */ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer { + /** + * The number of input and output buffers. + */ private static final int NUM_BUFFERS = 16; + /** + * The initial input buffer size. Input buffers are reallocated dynamically if this value is + * insufficient. + */ private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6; private FfmpegDecoder decoder; diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index 9b0355a9e7..a947378de5 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -77,11 +77,18 @@ public final class LibvpxVideoRenderer extends BaseRenderer { public static final int MSG_SET_OUTPUT_BUFFER_RENDERER = C.MSG_CUSTOM_BASE; /** - * The number of input buffers and the number of output buffers. The renderer may limit the - * minimum possible value due to requiring multiple output buffers to be dequeued at a time for it - * to make progress. + * The number of input buffers. + */ + private static final int NUM_INPUT_BUFFERS = 8; + /** + * The number of output buffers. The renderer may limit the minimum possible value due to + * requiring multiple output buffers to be dequeued at a time for it to make progress. + */ + private static final int NUM_OUTPUT_BUFFERS = 16; + /** + * The initial input buffer size. Input buffers are reallocated dynamically if this value is + * insufficient. */ - private static final int NUM_BUFFERS = 16; private static final int INITIAL_INPUT_BUFFER_SIZE = 768 * 1024; // Value based on cs/SoftVpx.cpp. private final boolean scaleToFit; @@ -564,7 +571,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer { try { long codecInitializingTimestamp = SystemClock.elapsedRealtime(); TraceUtil.beginSection("createVpxDecoder"); - decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto); + decoder = new VpxDecoder(NUM_INPUT_BUFFERS, NUM_OUTPUT_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, + mediaCrypto); decoder.setOutputMode(outputMode); TraceUtil.endSection(); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); From c72278d23eb7da24f4f33d1a1f280499d316c8d9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 4 Aug 2017 06:20:38 -0700 Subject: [PATCH 122/133] Separate handling of oldTimeline == null case ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164254522 --- .../exoplayer2/ExoPlayerImplInternal.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 633250a784..f77b32082b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -986,26 +986,29 @@ import java.io.IOException; // The seek position was valid for the timeline that it was performed into, but the // timeline has changed and a suitable seek position could not be resolved in the new one. handleSourceInfoRefreshEndedPlayback(manifest, processedInitialSeekCount); - return; + } else { + int periodIndex = periodPosition.first; + long positionUs = periodPosition.second; + MediaPeriodId periodId = + mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs); + playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : positionUs, positionUs); + notifySourceInfoRefresh(manifest, processedInitialSeekCount); } - int periodIndex = periodPosition.first; - long positionUs = periodPosition.second; - MediaPeriodId periodId = - mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs); - playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : positionUs, positionUs); } else if (playbackInfo.startPositionUs == C.TIME_UNSET) { if (timeline.isEmpty()) { handleSourceInfoRefreshEndedPlayback(manifest, processedInitialSeekCount); - return; + } else { + Pair defaultPosition = getPeriodPosition(0, C.TIME_UNSET); + int periodIndex = defaultPosition.first; + long startPositionUs = defaultPosition.second; + MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, + startPositionUs); + playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : startPositionUs, + startPositionUs); + notifySourceInfoRefresh(manifest, processedInitialSeekCount); } - Pair defaultPosition = getPeriodPosition(0, C.TIME_UNSET); - int periodIndex = defaultPosition.first; - long startPositionUs = defaultPosition.second; - MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, - startPositionUs); - playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : startPositionUs, - startPositionUs); } + return; } MediaPeriodHolder periodHolder = playingPeriodHolder != null ? playingPeriodHolder From 42eaee3db8cee10802ffdfae1fb22f655b10c961 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 4 Aug 2017 06:51:38 -0700 Subject: [PATCH 123/133] Update playbackInfo even if there's no period holder This is required to correctly update the playbackInfo.periodId when seeking close to the end of a period with ads, as the seek operation leads to an immediate source info refresh when midroll ads are marked as played. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164257099 --- .../exoplayer2/ExoPlayerImplInternal.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index f77b32082b..cb04501fc0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -1011,20 +1011,20 @@ import java.io.IOException; return; } + int playingPeriodIndex = playbackInfo.periodId.periodIndex; MediaPeriodHolder periodHolder = playingPeriodHolder != null ? playingPeriodHolder : loadingPeriodHolder; - if (periodHolder == null) { - // We don't have any period holders, so we're done. + if (periodHolder == null && playingPeriodIndex >= oldTimeline.getPeriodCount()) { notifySourceInfoRefresh(manifest, processedInitialSeekCount); return; } - - int periodIndex = timeline.getIndexOfPeriod(periodHolder.uid); + Object playingPeriodUid = periodHolder == null + ? oldTimeline.getPeriod(playingPeriodIndex, period, true).uid : periodHolder.uid; + int periodIndex = timeline.getIndexOfPeriod(playingPeriodUid); if (periodIndex == C.INDEX_UNSET) { // We didn't find the current period in the new timeline. Attempt to resolve a subsequent // period whose window we can restart from. - int newPeriodIndex = resolveSubsequentPeriod(periodHolder.info.id.periodIndex, oldTimeline, - timeline); + int newPeriodIndex = resolveSubsequentPeriod(playingPeriodIndex, oldTimeline, timeline); if (newPeriodIndex == C.INDEX_UNSET) { // We failed to resolve a suitable restart position. handleSourceInfoRefreshEndedPlayback(manifest, processedInitialSeekCount); @@ -1036,17 +1036,19 @@ import java.io.IOException; newPeriodIndex = defaultPosition.first; long newPositionUs = defaultPosition.second; timeline.getPeriod(newPeriodIndex, period, true); - // Clear the index of each holder that doesn't contain the default position. If a holder - // contains the default position then update its index so it can be re-used when seeking. - Object newPeriodUid = period.uid; - periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET); - while (periodHolder.next != null) { - periodHolder = periodHolder.next; - if (periodHolder.uid.equals(newPeriodUid)) { - periodHolder.info = mediaPeriodInfoSequence.getUpdatedMediaPeriodInfo(periodHolder.info, - newPeriodIndex); - } else { - periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET); + if (periodHolder != null) { + // Clear the index of each holder that doesn't contain the default position. If a holder + // contains the default position then update its index so it can be re-used when seeking. + Object newPeriodUid = period.uid; + periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET); + while (periodHolder.next != null) { + periodHolder = periodHolder.next; + if (periodHolder.uid.equals(newPeriodUid)) { + periodHolder.info = mediaPeriodInfoSequence.getUpdatedMediaPeriodInfo(periodHolder.info, + newPeriodIndex); + } else { + periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET); + } } } // Actually do the seek. @@ -1057,8 +1059,13 @@ import java.io.IOException; return; } - // If playing an ad, check that it hasn't been marked as played. If it has, skip forward. + // The current period is in the new timeline. Update the playback info. + if (periodIndex != playingPeriodIndex) { + playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex); + } + if (playbackInfo.periodId.isAd()) { + // Check that the playing ad hasn't been marked as played. If it has, skip forward. MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, playbackInfo.contentPositionUs); if (!periodId.isAd() || periodId.adIndexInAdGroup != playbackInfo.periodId.adIndexInAdGroup) { @@ -1070,14 +1077,15 @@ import java.io.IOException; } } - // The current period is in the new timeline. Update the holder and playbackInfo. - periodHolder = updatePeriodInfo(periodHolder, periodIndex); - if (periodIndex != playbackInfo.periodId.periodIndex) { - playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex); + if (periodHolder == null) { + // We don't have any period holders, so we're done. + notifySourceInfoRefresh(manifest, processedInitialSeekCount); + return; } - // If there are subsequent holders, update the index for each of them. If we find a holder - // that's inconsistent with the new timeline then take appropriate action. + // Update the holder indices. If we find a subsequent holder that's inconsistent with the new + // timeline then take appropriate action. + periodHolder = updatePeriodInfo(periodHolder, periodIndex); while (periodHolder.next != null) { MediaPeriodHolder previousPeriodHolder = periodHolder; periodHolder = periodHolder.next; From ba46e472af2b6c5f5fad59337a72b5050880a093 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 4 Aug 2017 07:26:19 -0700 Subject: [PATCH 124/133] Fix playing local content after permission granted. After maybeRequestReadExternalStoragePermission and the subsequent granting of the permission, the media source would never be created. I can't see a case where initializePlayer shouldn't create a new MediaSource, so I've just removed the condition. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164260074 --- .../exoplayer2/demo/PlayerActivity.java | 108 +++++++++--------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 40e77452ea..a98ab599ff 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -119,7 +119,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi private DefaultTrackSelector trackSelector; private TrackSelectionHelper trackSelectionHelper; private DebugTextViewHelper debugViewHelper; - private boolean needRetrySource; + private boolean inErrorState; private TrackGroupArray lastSeenTrackGroupArray; private boolean shouldAutoPlay; @@ -297,60 +297,58 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper.start(); } - if (needNewPlayer || needRetrySource) { - String action = intent.getAction(); - Uri[] uris; - String[] extensions; - if (ACTION_VIEW.equals(action)) { - uris = new Uri[] {intent.getData()}; - extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; - } else if (ACTION_VIEW_LIST.equals(action)) { - String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); - uris = new Uri[uriStrings.length]; - for (int i = 0; i < uriStrings.length; i++) { - uris[i] = Uri.parse(uriStrings[i]); - } - extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA); - if (extensions == null) { - extensions = new String[uriStrings.length]; - } - } else { - showToast(getString(R.string.unexpected_intent_action, action)); - return; + String action = intent.getAction(); + Uri[] uris; + String[] extensions; + if (ACTION_VIEW.equals(action)) { + uris = new Uri[]{intent.getData()}; + extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)}; + } else if (ACTION_VIEW_LIST.equals(action)) { + String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); + uris = new Uri[uriStrings.length]; + for (int i = 0; i < uriStrings.length; i++) { + uris[i] = Uri.parse(uriStrings[i]); } - if (Util.maybeRequestReadExternalStoragePermission(this, uris)) { - // The player will be reinitialized if the permission is granted. - return; + extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA); + if (extensions == null) { + extensions = new String[uriStrings.length]; } - MediaSource[] mediaSources = new MediaSource[uris.length]; - for (int i = 0; i < uris.length; i++) { - mediaSources[i] = buildMediaSource(uris[i], extensions[i]); - } - MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] - : new ConcatenatingMediaSource(mediaSources); - String adTagUriString = intent.getStringExtra(AD_TAG_URI_EXTRA); - if (adTagUriString != null) { - Uri adTagUri = Uri.parse(adTagUriString); - if (!adTagUri.equals(loadedAdTagUri)) { - releaseAdsLoader(); - loadedAdTagUri = adTagUri; - } - try { - mediaSource = createAdsMediaSource(mediaSource, Uri.parse(adTagUriString)); - } catch (Exception e) { - showToast(R.string.ima_not_loaded); - } - } else { - releaseAdsLoader(); - } - boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; - if (haveResumePosition) { - player.seekTo(resumeWindow, resumePosition); - } - player.prepare(mediaSource, !haveResumePosition, false); - needRetrySource = false; - updateButtonVisibilities(); + } else { + showToast(getString(R.string.unexpected_intent_action, action)); + return; } + if (Util.maybeRequestReadExternalStoragePermission(this, uris)) { + // The player will be reinitialized if the permission is granted. + return; + } + MediaSource[] mediaSources = new MediaSource[uris.length]; + for (int i = 0; i < uris.length; i++) { + mediaSources[i] = buildMediaSource(uris[i], extensions[i]); + } + MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] + : new ConcatenatingMediaSource(mediaSources); + String adTagUriString = intent.getStringExtra(AD_TAG_URI_EXTRA); + if (adTagUriString != null) { + Uri adTagUri = Uri.parse(adTagUriString); + if (!adTagUri.equals(loadedAdTagUri)) { + releaseAdsLoader(); + loadedAdTagUri = adTagUri; + } + try { + mediaSource = createAdsMediaSource(mediaSource, Uri.parse(adTagUriString)); + } catch (Exception e) { + showToast(R.string.ima_not_loaded); + } + } else { + releaseAdsLoader(); + } + boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; + if (haveResumePosition) { + player.seekTo(resumeWindow, resumePosition); + } + player.prepare(mediaSource, !haveResumePosition, false); + inErrorState = false; + updateButtonVisibilities(); } private MediaSource buildMediaSource(Uri uri, String overrideExtension) { @@ -502,7 +500,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi @Override public void onPositionDiscontinuity() { - if (needRetrySource) { + if (inErrorState) { // This will only occur if the user has performed a seek whilst in the error state. Update the // resume position so that if the user then retries, playback will resume from the position to // which they seeked. @@ -548,7 +546,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi if (errorString != null) { showToast(errorString); } - needRetrySource = true; + inErrorState = true; if (isBehindLiveWindow(e)) { clearResumePosition(); initializePlayer(); @@ -584,7 +582,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi private void updateButtonVisibilities() { debugRootView.removeAllViews(); - retryButton.setVisibility(needRetrySource ? View.VISIBLE : View.GONE); + retryButton.setVisibility(inErrorState ? View.VISIBLE : View.GONE); debugRootView.addView(retryButton); if (player == null) { From fdcee8f1b6a13dc22972ae3cc28279edcebdc219 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 13 Jul 2017 08:03:45 +0100 Subject: [PATCH 125/133] Fix detection of postroll when seeking to duration Also mark all ads as played when the postroll plays, in the case the player is backgrounded then resumed and the user seeks back. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164262738 --- .../exoplayer2/ext/ima/ImaAdsLoader.java | 28 +++++++++++++------ .../ext/ima/SinglePeriodAdTimeline.java | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index 6541dad0ac..c00e0731ee 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -572,15 +572,25 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, return; } if (!playingAd && !player.isPlayingAd()) { - long positionUs = C.msToUs(player.getCurrentPosition()); - int adGroupIndex = timeline.getPeriod(0, period).getAdGroupIndexForPositionUs(positionUs); - if (adGroupIndex != C.INDEX_UNSET) { - sentPendingContentPositionMs = false; - pendingContentPositionMs = player.getCurrentPosition(); + checkForContentComplete(); + if (sentContentComplete) { + for (int i = 0; i < adPlaybackState.adGroupCount; i++) { + if (adPlaybackState.adGroupTimesUs[i] != C.TIME_END_OF_SOURCE) { + adPlaybackState.playedAdGroup(i); + } + } + updateAdPlaybackState(); + } else { + long positionMs = player.getCurrentPosition(); + timeline.getPeriod(0, period); + if (period.getAdGroupIndexForPositionUs(C.msToUs(positionMs)) != C.INDEX_UNSET) { + sentPendingContentPositionMs = false; + pendingContentPositionMs = positionMs; + } } - return; + } else { + updateImaStateForPlayerState(); } - updateImaStateForPlayerState(); } @Override @@ -672,8 +682,8 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, } private void checkForContentComplete() { - if (contentDurationMs != C.TIME_UNSET - && player.getCurrentPosition() + END_OF_CONTENT_POSITION_THRESHOLD_MS >= contentDurationMs + if (contentDurationMs != C.TIME_UNSET && pendingContentPositionMs == C.TIME_UNSET + && player.getContentPosition() + END_OF_CONTENT_POSITION_THRESHOLD_MS >= contentDurationMs && !sentContentComplete) { adsLoader.contentComplete(); if (DEBUG) { diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/SinglePeriodAdTimeline.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/SinglePeriodAdTimeline.java index c93f1e8f28..1d73234286 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/SinglePeriodAdTimeline.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/SinglePeriodAdTimeline.java @@ -22,7 +22,7 @@ import com.google.android.exoplayer2.util.Assertions; /** * A {@link Timeline} for sources that have ads. */ -public final class SinglePeriodAdTimeline extends Timeline { +/* package */ final class SinglePeriodAdTimeline extends Timeline { private final Timeline contentTimeline; private final long[] adGroupTimesUs; From b407b19296a75f34764ede59ed430a252f248b5c Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 4 Aug 2017 08:55:43 -0700 Subject: [PATCH 126/133] Log IMA LOG AdEvent ad data ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164267555 --- .../android/exoplayer2/ext/ima/ImaAdsLoader.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index c00e0731ee..8c4fb4c51c 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -26,6 +26,7 @@ import com.google.ads.interactivemedia.v3.api.AdErrorEvent; import com.google.ads.interactivemedia.v3.api.AdErrorEvent.AdErrorListener; import com.google.ads.interactivemedia.v3.api.AdEvent; import com.google.ads.interactivemedia.v3.api.AdEvent.AdEventListener; +import com.google.ads.interactivemedia.v3.api.AdEvent.AdEventType; import com.google.ads.interactivemedia.v3.api.AdPodInfo; import com.google.ads.interactivemedia.v3.api.AdsLoader; import com.google.ads.interactivemedia.v3.api.AdsLoader.AdsLoadedListener; @@ -51,6 +52,7 @@ import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Loads ads using the IMA SDK. All methods are called on the main thread. @@ -324,14 +326,21 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, @Override public void onAdEvent(AdEvent adEvent) { - Ad ad = adEvent.getAd(); - if (DEBUG) { - Log.d(TAG, "onAdEvent " + adEvent.getType()); + AdEventType adEventType = adEvent.getType(); + boolean isLogAdEvent = adEventType == AdEventType.LOG; + if (DEBUG || isLogAdEvent) { + Log.w(TAG, "onAdEvent: " + adEventType); + if (isLogAdEvent) { + for (Map.Entry entry : adEvent.getAdData().entrySet()) { + Log.w(TAG, " " + entry.getKey() + ": " + entry.getValue()); + } + } } if (adsManager == null) { Log.w(TAG, "Dropping ad event after release: " + adEvent); return; } + Ad ad = adEvent.getAd(); switch (adEvent.getType()) { case LOADED: // The ad position is not always accurate when using preloading. See [Internal: b/62613240]. From b0da1f98f6d24a852968841833fb904d1af71c4b Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Fri, 4 Aug 2017 09:32:51 -0700 Subject: [PATCH 127/133] Remove unnecessary API level check in PlayerActivity of the demo app ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164271226 --- .../exoplayer2/demo/PlayerActivity.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index a98ab599ff..9e53dff857 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -264,13 +264,19 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi if (drmSchemeUuid != null) { String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL); String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES); - try { - drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl, - keyRequestPropertiesArray); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + int errorStringId = R.string.error_drm_unknown; + if (Util.SDK_INT < 18) { + errorStringId = R.string.error_drm_not_supported; + } else { + try { + drmSessionManager = buildDrmSessionManagerV18(drmSchemeUuid, drmLicenseUrl, + keyRequestPropertiesArray); + } catch (UnsupportedDrmException e) { + errorStringId = e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown; + } + } + if (drmSessionManager == null) { showToast(errorStringId); return; } @@ -372,11 +378,8 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi } } - private DrmSessionManager buildDrmSessionManager(UUID uuid, + private DrmSessionManager buildDrmSessionManagerV18(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { - if (Util.SDK_INT < 18) { - return null; - } HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, buildHttpDataSourceFactory(false)); if (keyRequestPropertiesArray != null) { From 13a580fdbca297f1406b8d9ab6f8e37bd4eb1a07 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Aug 2017 01:48:44 -0700 Subject: [PATCH 128/133] Finalize r2.5.0 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164434615 --- RELEASENOTES.md | 8 +++----- constants.gradle | 2 +- .../google/android/exoplayer2/ExoPlayerLibraryInfo.java | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e96cd9ddab..4101caad47 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,14 +1,13 @@ # Release notes # -### r2.5.0 (beta) ### +### r2.5.0 ### * IMA extension: Wraps the Google Interactive Media Ads (IMA) SDK to provide an easy and seamless way of incorporating display ads into ExoPlayer playbacks. You can read more about the IMA extension [here](https://medium.com/google-exoplayer/playing-ads-with-exoplayer-and-ima-868dfd767ea). * MediaSession extension: Provides an easy to to connect ExoPlayer with - MediaSessionCompat in the Android Support Library. *A link to a blog post - about this extension will be added here prior to the stable 2.5.0 release.* + MediaSessionCompat in the Android Support Library. * RTMP extension: An extension for playing streams over RTMP. * Build: Made it easier for application developers to depend on a local checkout of ExoPlayer. You can learn how to do this @@ -18,8 +17,7 @@ playback of progressive streams ([#2926](https://github.com/google/ExoPlayer/issues/2926)). * New DynamicConcatenatingMediaSource class to support playback of dynamic - playlists. *A link to a blog post about DynamicConcatenatingMediaSource will - be added here prior to the stable 2.5.0 release.* + playlists. * New ExoPlayer.setRepeatMode method for dynamic toggling of repeat mode during playback. Use of setRepeatMode should be preferred to LoopingMediaSource for most looping use cases. You can read more about diff --git a/constants.gradle b/constants.gradle index 93284fd897..7d126ccd89 100644 --- a/constants.gradle +++ b/constants.gradle @@ -24,7 +24,7 @@ project.ext { supportLibraryVersion = '25.4.0' dexmakerVersion = '1.2' mockitoVersion = '1.9.5' - releaseVersion = 'r2.5.0-beta3' + releaseVersion = 'r2.5.0' modulePrefix = ':' if (gradle.ext.has('exoplayerModulePrefix')) { modulePrefix += gradle.ext.exoplayerModulePrefix diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 153c6cda9c..fd5ead5c85 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -31,13 +31,13 @@ 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.5.0-beta3"; + public static final String VERSION = "2.5.0"; /** * 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.5.0-beta3"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.5.0"; /** * The version of the library expressed as an integer, for example 1002003. From 1f66f30ccdb51eea086619c6933cad0f73de4d15 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Aug 2017 01:51:26 -0700 Subject: [PATCH 129/133] Clean up extension READMEs Issue: #1157 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164434768 --- extensions/cronet/README.md | 33 ++++++++++++++++++++++++++----- extensions/ffmpeg/README.md | 2 +- extensions/flac/README.md | 6 +++--- extensions/gvr/README.md | 2 +- extensions/mediasession/README.md | 4 ++-- extensions/okhttp/README.md | 29 +++++++++++++++++++++++++-- extensions/opus/README.md | 6 +++--- extensions/rtmp/README.md | 4 ++-- extensions/vp9/README.md | 6 +++--- 9 files changed, 70 insertions(+), 22 deletions(-) diff --git a/extensions/cronet/README.md b/extensions/cronet/README.md index 30409fa99e..2287c4c19b 100644 --- a/extensions/cronet/README.md +++ b/extensions/cronet/README.md @@ -1,15 +1,13 @@ -# ExoPlayer Cronet Extension # +# ExoPlayer Cronet extension # ## Description ## -[Cronet][] is Chromium's Networking stack packaged as a library. - -The Cronet Extension is an [HttpDataSource][] implementation using [Cronet][]. +The Cronet extension is an [HttpDataSource][] implementation using [Cronet][]. [HttpDataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/upstream/HttpDataSource.html [Cronet]: https://chromium.googlesource.com/chromium/src/+/master/components/cronet?autodive=0%2F%2F -## Build Instructions ## +## Build instructions ## To use this extension you need to clone the ExoPlayer repository and depend on its modules locally. Instructions for doing this can be found in ExoPlayer's @@ -33,3 +31,28 @@ gradle.ext.exoplayerIncludeCronetExtension = true; [top level README]: https://github.com/google/ExoPlayer/blob/release-v2/README.md [here]: https://console.cloud.google.com/storage/browser/chromium-cronet/android + +## Using the extension ## + +ExoPlayer requests data through `DataSource` instances. These instances are +either instantiated and injected from application code, or obtained from +instances of `DataSource.Factory` that are instantiated and injected from +application code. + +If your application only needs to play http(s) content, using the Cronet +extension is as simple as updating any `DataSource`s and `DataSource.Factory` +instantiations in your application code to use `CronetDataSource` and +`CronetDataSourceFactory` respectively. If your application also needs to play +non-http(s) content such as local files, use +``` +new DefaultDataSource( + ... + new CronetDataSource(...) /* baseDataSource argument */); +``` +and +``` +new DefaultDataSourceFactory( + ... + new CronetDataSourceFactory(...) /* baseDataSourceFactory argument */); +``` +respectively. diff --git a/extensions/ffmpeg/README.md b/extensions/ffmpeg/README.md index ab3e5ffb94..b4514effbc 100644 --- a/extensions/ffmpeg/README.md +++ b/extensions/ffmpeg/README.md @@ -1,4 +1,4 @@ -# FfmpegAudioRenderer # +# ExoPlayer FFmpeg extension # ## Description ## diff --git a/extensions/flac/README.md b/extensions/flac/README.md index a35dac7858..9db2e5727d 100644 --- a/extensions/flac/README.md +++ b/extensions/flac/README.md @@ -1,14 +1,14 @@ -# ExoPlayer Flac Extension # +# ExoPlayer Flac extension # ## Description ## -The Flac Extension is a [Renderer][] implementation that helps you bundle +The Flac extension is a [Renderer][] implementation that helps you bundle libFLAC (the Flac decoding library) into your app and use it along with ExoPlayer to play Flac audio on Android devices. [Renderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/Renderer.html -## Build Instructions ## +## Build instructions ## To use this extension you need to clone the ExoPlayer repository and depend on its modules locally. Instructions for doing this can be found in ExoPlayer's diff --git a/extensions/gvr/README.md b/extensions/gvr/README.md index ad28569121..7e072d070c 100644 --- a/extensions/gvr/README.md +++ b/extensions/gvr/README.md @@ -1,4 +1,4 @@ -# ExoPlayer GVR Extension # +# ExoPlayer GVR extension # ## Description ## diff --git a/extensions/mediasession/README.md b/extensions/mediasession/README.md index 7515cf9eef..3acf8e4c79 100644 --- a/extensions/mediasession/README.md +++ b/extensions/mediasession/README.md @@ -1,8 +1,8 @@ -# ExoPlayer MediaSession Extension # +# ExoPlayer MediaSession extension # ## Description ## -The MediaSession Extension mediates between an ExoPlayer instance and a +The MediaSession extension mediates between an ExoPlayer instance and a [MediaSession][]. It automatically retrieves and implements playback actions and syncs the player state with the state of the media session. The behaviour can be extended to support other playback and custom actions. diff --git a/extensions/okhttp/README.md b/extensions/okhttp/README.md index 52d5fabf38..b10c4ba629 100644 --- a/extensions/okhttp/README.md +++ b/extensions/okhttp/README.md @@ -1,8 +1,8 @@ -# ExoPlayer OkHttp Extension # +# ExoPlayer OkHttp extension # ## Description ## -The OkHttp Extension is an [HttpDataSource][] implementation using Square's +The OkHttp extension is an [HttpDataSource][] implementation using Square's [OkHttp][]. [HttpDataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/HttpDataSource.html @@ -24,3 +24,28 @@ locally. Instructions for doing this can be found in ExoPlayer's [top level README][]. [top level README]: https://github.com/google/ExoPlayer/blob/release-v2/README.md + +## Using the extension ## + +ExoPlayer requests data through `DataSource` instances. These instances are +either instantiated and injected from application code, or obtained from +instances of `DataSource.Factory` that are instantiated and injected from +application code. + +If your application only needs to play http(s) content, using the OkHttp +extension is as simple as updating any `DataSource`s and `DataSource.Factory` +instantiations in your application code to use `OkHttpDataSource` and +`OkHttpDataSourceFactory` respectively. If your application also needs to play +non-http(s) content such as local files, use +``` +new DefaultDataSource( + ... + new OkHttpDataSource(...) /* baseDataSource argument */); +``` +and +``` +new DefaultDataSourceFactory( + ... + new OkHttpDataSourceFactory(...) /* baseDataSourceFactory argument */); +``` +respectively. diff --git a/extensions/opus/README.md b/extensions/opus/README.md index ae42a9c310..e5f5bcb168 100644 --- a/extensions/opus/README.md +++ b/extensions/opus/README.md @@ -1,14 +1,14 @@ -# ExoPlayer Opus Extension # +# ExoPlayer Opus extension # ## Description ## -The Opus Extension is a [Renderer][] implementation that helps you bundle +The Opus extension is a [Renderer][] implementation that helps you bundle libopus (the Opus decoding library) into your app and use it along with ExoPlayer to play Opus audio on Android devices. [Renderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/Renderer.html -## Build Instructions ## +## Build instructions ## To use this extension you need to clone the ExoPlayer repository and depend on its modules locally. Instructions for doing this can be found in ExoPlayer's diff --git a/extensions/rtmp/README.md b/extensions/rtmp/README.md index 2cfa6b8ff4..042d7078dc 100644 --- a/extensions/rtmp/README.md +++ b/extensions/rtmp/README.md @@ -1,8 +1,8 @@ -# ExoPlayer RTMP Extension # +# ExoPlayer RTMP extension # ## Description ## -The RTMP Extension is a [DataSource][] implementation for playing [RTMP][] +The RTMP extension is a [DataSource][] implementation for playing [RTMP][] streams using [LibRtmp Client for Android][]. [DataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/DataSource.html diff --git a/extensions/vp9/README.md b/extensions/vp9/README.md index 8bdfe652e6..87c5c8d54f 100644 --- a/extensions/vp9/README.md +++ b/extensions/vp9/README.md @@ -1,14 +1,14 @@ -# ExoPlayer VP9 Extension # +# ExoPlayer VP9 extension # ## Description ## -The VP9 Extension is a [Renderer][] implementation that helps you bundle libvpx +The VP9 extension is a [Renderer][] implementation that helps you bundle libvpx (the VP9 decoding library) into your app and use it along with ExoPlayer to play VP9 video on Android devices. [Renderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/Renderer.html -## Build Instructions ## +## Build instructions ## To use this extension you need to clone the ExoPlayer repository and depend on its modules locally. Instructions for doing this can be found in ExoPlayer's From df05195f5fdaf12f83fa3e615d3cdc20e4dce401 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Aug 2017 01:53:09 -0700 Subject: [PATCH 130/133] Set correct Content-Type for ClearKey requests Issue: #3138 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164434858 --- .../exoplayer2/drm/HttpMediaDrmCallback.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java index f9d5efffb1..f08d9b59b5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java @@ -38,14 +38,6 @@ import java.util.UUID; @TargetApi(18) public final class HttpMediaDrmCallback implements MediaDrmCallback { - private static final Map PLAYREADY_KEY_REQUEST_PROPERTIES; - static { - PLAYREADY_KEY_REQUEST_PROPERTIES = new HashMap<>(); - PLAYREADY_KEY_REQUEST_PROPERTIES.put("Content-Type", "text/xml"); - PLAYREADY_KEY_REQUEST_PROPERTIES.put("SOAPAction", - "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"); - } - private final HttpDataSource.Factory dataSourceFactory; private final String defaultUrl; private final Map keyRequestProperties; @@ -124,10 +116,15 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback { url = defaultUrl; } Map requestProperties = new HashMap<>(); - requestProperties.put("Content-Type", "application/octet-stream"); + // Add standard request properties for supported schemes. + String contentType = C.PLAYREADY_UUID.equals(uuid) ? "text/xml" + : (C.CLEARKEY_UUID.equals(uuid) ? "application/json" : "application/octet-stream"); + requestProperties.put("Content-Type", contentType); if (C.PLAYREADY_UUID.equals(uuid)) { - requestProperties.putAll(PLAYREADY_KEY_REQUEST_PROPERTIES); + requestProperties.put("SOAPAction", + "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"); } + // Add additional request properties. synchronized (keyRequestProperties) { requestProperties.putAll(keyRequestProperties); } From 15bcdf3b71f02b4ce2f8328625fb43a5b0f25fe6 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Aug 2017 01:54:51 -0700 Subject: [PATCH 131/133] Don't use TextureView's SurfaceTexture unless available ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164434943 --- .../java/com/google/android/exoplayer2/SimpleExoPlayer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index ebfe380b6b..08e178878b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -276,7 +276,8 @@ public class SimpleExoPlayer implements ExoPlayer { Log.w(TAG, "Replacing existing SurfaceTextureListener."); } textureView.setSurfaceTextureListener(componentListener); - SurfaceTexture surfaceTexture = textureView.getSurfaceTexture(); + SurfaceTexture surfaceTexture = textureView.isAvailable() ? textureView.getSurfaceTexture() + : null; setVideoSurfaceInternal(surfaceTexture == null ? null : new Surface(surfaceTexture), true); } } From f88149385a63c407f0589f496b87a864a1aa32cd Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Aug 2017 03:45:10 -0700 Subject: [PATCH 132/133] Avoid rollover calculating sample offsets I considered using Util.scaleLargeTimestamp for this, but given sample offsets are relative and should always be small (<<1s), it really shouldn't be necessary. Issue: #3139 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164443795 --- .../exoplayer2/extractor/mp4/FragmentedMp4Extractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 6dcae9c2d6..6b2077ef76 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -815,7 +815,7 @@ public final class FragmentedMp4Extractor implements Extractor { // here, because unsigned integers will still be parsed correctly (unless their top bit is // set, which is never true in practice because sample offsets are always small). int sampleOffset = trun.readInt(); - sampleCompositionTimeOffsetTable[i] = (int) ((sampleOffset * 1000) / timescale); + sampleCompositionTimeOffsetTable[i] = (int) ((sampleOffset * 1000L) / timescale); } else { sampleCompositionTimeOffsetTable[i] = 0; } From e713ddc22d6976b55aa4e178e4cd1343e7219df0 Mon Sep 17 00:00:00 2001 From: eguven Date: Mon, 7 Aug 2017 05:22:15 -0700 Subject: [PATCH 133/133] Disable test coverage again https://issuetracker.google.com/issues/37019591 causes local variables can't be found while debugging. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=164449443 --- library/core/build.gradle | 8 +++++--- library/dash/build.gradle | 8 +++++--- library/hls/build.gradle | 8 +++++--- library/smoothstreaming/build.gradle | 8 +++++--- library/ui/build.gradle | 8 +++++--- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/library/core/build.gradle b/library/core/build.gradle index 65a7353607..ecad1e58b5 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -31,9 +31,11 @@ android { } buildTypes { - debug { - testCoverageEnabled = true - } + // Re-enable test coverage when the following issue is fixed: + // https://issuetracker.google.com/issues/37019591 + // debug { + // testCoverageEnabled = true + // } } } diff --git a/library/dash/build.gradle b/library/dash/build.gradle index aa8031467e..2220e5b250 100644 --- a/library/dash/build.gradle +++ b/library/dash/build.gradle @@ -24,9 +24,11 @@ android { } buildTypes { - debug { - testCoverageEnabled = true - } + // Re-enable test coverage when the following issue is fixed: + // https://issuetracker.google.com/issues/37019591 + // debug { + // testCoverageEnabled = true + // } } } diff --git a/library/hls/build.gradle b/library/hls/build.gradle index 77680569f0..ac77725ca5 100644 --- a/library/hls/build.gradle +++ b/library/hls/build.gradle @@ -24,9 +24,11 @@ android { } buildTypes { - debug { - testCoverageEnabled = true - } + // Re-enable test coverage when the following issue is fixed: + // https://issuetracker.google.com/issues/37019591 + // debug { + // testCoverageEnabled = true + // } } } diff --git a/library/smoothstreaming/build.gradle b/library/smoothstreaming/build.gradle index b5f918075f..ee5a8c4e73 100644 --- a/library/smoothstreaming/build.gradle +++ b/library/smoothstreaming/build.gradle @@ -24,9 +24,11 @@ android { } buildTypes { - debug { - testCoverageEnabled = true - } + // Re-enable test coverage when the following issue is fixed: + // https://issuetracker.google.com/issues/37019591 + // debug { + // testCoverageEnabled = true + // } } } diff --git a/library/ui/build.gradle b/library/ui/build.gradle index c036bc9819..89734ed806 100644 --- a/library/ui/build.gradle +++ b/library/ui/build.gradle @@ -24,9 +24,11 @@ android { } buildTypes { - debug { - testCoverageEnabled = true - } + // Re-enable test coverage when the following issue is fixed: + // https://issuetracker.google.com/issues/37019591 + // debug { + // testCoverageEnabled = true + // } } }