Commit 90ee038f by Daniel Thompson Committed by GitHub

Merge branch 'dev-v2' into dev-v2

parents cb46e875 8ffd40ab
Showing with 985 additions and 624 deletions
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
([#3622](https://github.com/google/ExoPlayer/issues/3622)). ([#3622](https://github.com/google/ExoPlayer/issues/3622)).
* Use long for media sequence numbers * Use long for media sequence numbers
([#3747](https://github.com/google/ExoPlayer/issues/3747)) ([#3747](https://github.com/google/ExoPlayer/issues/3747))
* Add initial support for the EXT-X-GAP tag.
* New Cast extension: Simplifies toggling between local and Cast playbacks. * New Cast extension: Simplifies toggling between local and Cast playbacks.
* Audio: * Audio:
* Support TrueHD passthrough for rechunked samples in Matroska files * Support TrueHD passthrough for rechunked samples in Matroska files
...@@ -76,8 +77,12 @@ ...@@ -76,8 +77,12 @@
* Support resampling 24-bit and 32-bit integer to 32-bit float for high * Support resampling 24-bit and 32-bit integer to 32-bit float for high
resolution output in `DefaultAudioSink` resolution output in `DefaultAudioSink`
([#3635](https://github.com/google/ExoPlayer/pull/3635)). ([#3635](https://github.com/google/ExoPlayer/pull/3635)).
* Captions: Initial support for PGS subtitles * Captions:
* Initial support for PGS subtitles
([#3008](https://github.com/google/ExoPlayer/issues/3008)). ([#3008](https://github.com/google/ExoPlayer/issues/3008)).
* Fix issue handling CEA-608 captions where multiple buffers have the same
presentation timestamp
([#3782](https://github.com/google/ExoPlayer/issues/3782)).
* CacheDataSource: Check periodically if it's possible to read from/write to * CacheDataSource: Check periodically if it's possible to read from/write to
cache after deciding to bypass cache. cache after deciding to bypass cache.
* IMA extension: * IMA extension:
...@@ -85,17 +90,36 @@ ...@@ -85,17 +90,36 @@
([#3584](https://github.com/google/ExoPlayer/issues/3584)). ([#3584](https://github.com/google/ExoPlayer/issues/3584)).
* Work around loadAd not being called beore the LOADED AdEvent arrives * Work around loadAd not being called beore the LOADED AdEvent arrives
([#3552](https://github.com/google/ExoPlayer/issues/3552)). ([#3552](https://github.com/google/ExoPlayer/issues/3552)).
* Handle asset mismatch errors
([#3801](https://github.com/google/ExoPlayer/issues/3801)).
* Add support for playing non-Extractor content MediaSources in * Add support for playing non-Extractor content MediaSources in
the IMA demo app the IMA demo app
([#3676](https://github.com/google/ExoPlayer/issues/3676)). ([#3676](https://github.com/google/ExoPlayer/issues/3676)).
* Fix handling of ad tags where ad groups are out of order
([#3716](https://github.com/google/ExoPlayer/issues/3716)).
* Fix handling of ad tags with only preroll/postroll ad groups
([#3715](https://github.com/google/ExoPlayer/issues/3715)).
* Propagate ad media preparation errors to IMA so that the ads can be
skipped.
* `EventLogger` moved from the demo app into the core library. * `EventLogger` moved from the demo app into the core library.
* Fix ANR issue on Huawei P8 Lite * Fix ANR issue on the Huawei P8 Lite, Huawei Y6II, Moto C+, Meizu M5C,
([#3724](https://github.com/google/ExoPlayer/issues/3724)). Lenovo K4 Note and Sony Xperia E5.
([#3724](https://github.com/google/ExoPlayer/issues/3724),
[#3835](https://github.com/google/ExoPlayer/issues/3835)).
* Fix potential NPE when removing media sources from a * Fix potential NPE when removing media sources from a
DynamicConcatenatingMediaSource DynamicConcatenatingMediaSource
([#3796](https://github.com/google/ExoPlayer/issues/3796)). ([#3796](https://github.com/google/ExoPlayer/issues/3796)).
* Open source DownloadService, DownloadManager and related classes * Open source DownloadService, DownloadManager and related classes
([#2643](https://github.com/google/ExoPlayer/issues/2643)). ([#2643](https://github.com/google/ExoPlayer/issues/2643)).
* Check `sys.display-size` on Philips ATVs
([#3807](https://github.com/google/ExoPlayer/issues/3807)).
* Release `Extractor`s on the loading thread to avoid potentially leaking
resources when the playback thread has quit by the time the loading task has
completed.
* ID3: Better handle malformed ID3 data
([#3792](https://github.com/google/ExoPlayer/issues/3792).
* Support 14-bit mode and little endianness in DTS PES packets
([#3340](https://github.com/google/ExoPlayer/issues/3340)).
### 2.6.1 ### ### 2.6.1 ###
......
...@@ -34,11 +34,14 @@ public class FlacExtractorTest extends InstrumentationTestCase { ...@@ -34,11 +34,14 @@ public class FlacExtractorTest extends InstrumentationTestCase {
} }
public void testSample() throws Exception { public void testSample() throws Exception {
ExtractorAsserts.assertBehavior(new ExtractorFactory() { ExtractorAsserts.assertBehavior(
new ExtractorFactory() {
@Override @Override
public Extractor create() { public Extractor create() {
return new FlacExtractor(); return new FlacExtractor();
} }
}, "bear.flac", getInstrumentation()); },
"bear.flac",
getInstrumentation().getContext());
} }
} }
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Herhaal alles"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Herhaal niks"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Herhaal een"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Herhaal niks"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Herhaal een"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Herhaal alles"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"ሁሉንም ድገም"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"ምንም አትድገም"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"አንዱን ድገም"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"ምንም አትድገም"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"አንድ ድገም"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"ሁሉንም ድገም"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"تكرار الكل"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"عدم التكرار"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"تكرار مقطع واحد"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"عدم التكرار"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"تكرار مقطع صوتي واحد"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"تكرار الكل"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi sve"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Ne ponavljaj nijednu"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ponovi jednu"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Ne ponavljaj nijednu"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Ponovi jednu"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ponovi sve"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Повтаряне на всички"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Без повтаряне"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Повтаряне на един елемент"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Без повтаряне"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Повтаряне на един елемент"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Повтаряне на всички"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeteix-ho tot"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"No en repeteixis cap"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repeteix-ne un"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"No en repeteixis cap"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repeteix una"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repeteix tot"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Opakovat vše"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Neopakovat"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Opakovat jednu položku"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Neopakovat"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Opakovat jednu"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Opakovat vše"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Gentag alle"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Gentag ingen"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Gentag en"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Gentag ingen"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Gentag én"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Gentag alle"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Alle wiederholen"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Keinen Titel wiederholen"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Einen Titel wiederholen"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Keinen wiederholen"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Einen wiederholen"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Alle wiederholen"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Επανάληψη όλων"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Καμία επανάληψη"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Επανάληψη ενός στοιχείου"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Καμία επανάληψη"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Επανάληψη ενός κομματιού"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Επανάληψη όλων"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Repeat none"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repeat one"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repeat one"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repeat all"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Repeat none"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repeat one"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repeat one"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repeat all"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Repeat none"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repeat one"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repeat one"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repeat all"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir todo"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"No repetir"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repetir uno"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"No repetir"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repetir uno"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repetir todo"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir todo"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"No repetir"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repetir uno"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"No repetir"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repetir uno"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repetir todo"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"تکرار همه"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"تکرار هیچ‌کدام"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"یک‌بار تکرار"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"تکرار هیچ‌کدام"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"یکبار تکرار"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"تکرار همه"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Toista kaikki"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Toista ei mitään"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Toista yksi"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Ei uudelleentoistoa"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Toista yksi uudelleen"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Toista kaikki uudelleen"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Tout lire en boucle"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Aucune répétition"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Répéter un élément"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Ne rien lire en boucle"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Lire une chanson en boucle"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Tout lire en boucle"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Tout lire en boucle"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Ne rien lire en boucle"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Lire en boucle un élément"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Ne rien lire en boucle"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Lire un titre en boucle"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Tout lire en boucle"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"सभी को दोहराएं"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"कुछ भी न दोहराएं"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"एक दोहराएं"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"किसी को न दोहराएं"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"एक को दोहराएं"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"सभी को दोहराएं"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi sve"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Bez ponavljanja"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ponovi jedno"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Bez ponavljanja"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Ponovi jedno"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ponovi sve"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Összes ismétlése"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Nincs ismétlés"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Egy ismétlése"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Nincs ismétlés"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Egy szám ismétlése"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Összes szám ismétlése"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ulangi Semua"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Jangan Ulangi"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ulangi Satu"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Jangan ulangi"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Ulangi 1"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ulangi semua"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ripeti tutti"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Non ripetere nessuno"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ripeti uno"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Non ripetere nulla"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Ripeti uno"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ripeti tutto"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"חזור על הכל"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"אל תחזור על כלום"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"חזור על פריט אחד"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"אל תחזור על אף פריט"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"חזור על פריט אחד"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"חזור על הכול"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"全曲を繰り返し"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"繰り返しなし"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"1曲を繰り返し"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"リピートなし"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"1 曲をリピート"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"全曲をリピート"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"전체 반복"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"반복 안함"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"한 항목 반복"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"반복 안함"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"현재 미디어 반복"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"모두 반복"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Kartoti viską"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Nekartoti nieko"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Kartoti vieną"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Nekartoti nieko"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Kartoti vieną"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Kartoti viską"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Atkārtot visu"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Neatkārtot nevienu"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Atkārtot vienu"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Neatkārtot nevienu"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Atkārtot vienu"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Atkārtot visu"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Gjenta alle"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Ikke gjenta noen"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Gjenta én"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Ikke gjenta noen"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Gjenta én"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Gjenta alle"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Alles herhalen"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Niet herhalen"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Eén herhalen"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Niets herhalen"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Eén herhalen"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Alles herhalen"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Powtórz wszystkie"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Nie powtarzaj"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Powtórz jeden"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Nie powtarzaj"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Powtórz jeden"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Powtórz wszystkie"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir tudo"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Não repetir"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repetir um"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Não repetir nenhum"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repetir um"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repetir tudo"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir tudo"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Não repetir"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repetir uma"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Não repetir"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repetir uma"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repetir tudo"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetați toate"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Repetați niciuna"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Repetați unul"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Nu repetați niciunul"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Repetați unul"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Repetați-le pe toate"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Повторять все"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Не повторять"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Повторять один элемент"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Не повторять"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Повторять трек"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Повторять все"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Opakovať všetko"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Neopakovať"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Opakovať jednu položku"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Neopakovať"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Opakovať jednu"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Opakovať všetko"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi vse"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Ne ponovi"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ponovi eno"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Brez ponavljanja"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Ponavljanje ene"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ponavljanje vseh"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,6 +12,11 @@ ...@@ -13,6 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Не понављај ниједну"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Понови једну"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Понови све"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Upprepa alla"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Upprepa inga"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Upprepa en"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Upprepa inga"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Upprepa en"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Upprepa alla"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Rudia zote"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Usirudie Yoyote"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Rudia Moja"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Usirudie yoyote"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Rudia moja"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Rudia zote"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"เล่นซ้ำทั้งหมด"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"ไม่เล่นซ้ำ"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"เล่นซ้ำรายการเดียว"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"ไม่เล่นซ้ำ"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"เล่นซ้ำเพลงเดียว"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"เล่นซ้ำทั้งหมด"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Ulitin Lahat"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Walang Uulitin"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Ulitin ang Isa"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Walang uulitin"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Mag-ulit ng isa"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Ulitin lahat"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Tümünü Tekrarla"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Hiçbirini Tekrarlama"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Birini Tekrarla"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Hiçbirini tekrarlama"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Bir şarkıyı tekrarla"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Tümünü tekrarla"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Повторити все"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Не повторювати"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Повторити один елемент"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Не повторювати"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Повторити 1"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Повторити всі"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Lặp lại tất cả"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Không lặp lại"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Lặp lại một mục"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Không lặp lại"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Lặp lại một"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Lặp lại tất cả"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"重复播放全部"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"不重复播放"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"重复播放单个视频"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"不重复播放"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"重复播放一项"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"全部重复播放"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"重複播放所有媒體項目"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"不重複播放任何媒體項目"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"重複播放一個媒體項目"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"不重複播放"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"重複播放一個"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"全部重複播放"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"重複播放所有媒體項目"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"不重複播放"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"重複播放單一媒體項目"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"不重複播放"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"重複播放單一項目"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"重複播放所有項目"</string>
</resources> </resources>
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Copyright (C) 2016 The Android Open Source Project
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
...@@ -13,9 +12,11 @@ ...@@ -13,9 +12,11 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<resources>
<string name="exo_media_action_repeat_all_description">"Phinda konke"</string> <resources xmlns:android="http://schemas.android.com/apk/res/android"
<string name="exo_media_action_repeat_off_description">"Ungaphindi lutho"</string> xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="exo_media_action_repeat_one_description">"Phida okukodwa"</string> <string name="exo_media_action_repeat_off_description" msgid="160802313171921598">"Phinda okungekho"</string>
<string name="exo_media_action_repeat_one_description" msgid="120730756187958757">"Phinda okukodwa"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Phinda konke"</string>
</resources> </resources>
...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.C; ...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener; import com.google.android.exoplayer2.offline.DownloadManager.DownloadListener;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State; import com.google.android.exoplayer2.offline.DownloadManager.DownloadState.State;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.MockitoUtil; import com.google.android.exoplayer2.testutil.MockitoUtil;
import com.google.android.exoplayer2.upstream.DummyDataSource; import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.android.exoplayer2.upstream.cache.Cache;
...@@ -53,12 +54,13 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -53,12 +54,13 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private DownloadManager downloadManager; private DownloadManager downloadManager;
private File actionFile; private File actionFile;
private TestDownloadListener testDownloadListener; private TestDownloadListener testDownloadListener;
private DummyMainThread dummyMainThread;
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
MockitoUtil.setUpMockito(this); MockitoUtil.setUpMockito(this);
dummyMainThread = new DummyMainThread();
actionFile = Util.createTempFile(getInstrumentation().getContext(), "ExoPlayerTest"); actionFile = Util.createTempFile(getInstrumentation().getContext(), "ExoPlayerTest");
testDownloadListener = new TestDownloadListener(); testDownloadListener = new TestDownloadListener();
setUpDownloadManager(100); setUpDownloadManager(100);
...@@ -68,6 +70,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -68,6 +70,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
public void tearDown() throws Exception { public void tearDown() throws Exception {
releaseDownloadManager(); releaseDownloadManager();
actionFile.delete(); actionFile.delete();
dummyMainThread.release();
super.tearDown(); super.tearDown();
} }
...@@ -76,7 +79,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -76,7 +79,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
releaseDownloadManager(); releaseDownloadManager();
} }
try { try {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -98,7 +101,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -98,7 +101,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private void releaseDownloadManager() throws Exception { private void releaseDownloadManager() throws Exception {
try { try {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -345,7 +348,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -345,7 +348,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
remove2Action.post().assertStarted(); remove2Action.post().assertStarted();
download2Action.post().assertDoesNotStart(); download2Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -368,7 +371,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -368,7 +371,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// New download actions can be added but they don't start. // New download actions can be added but they don't start.
download3Action.post().assertDoesNotStart(); download3Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -393,7 +396,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -393,7 +396,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download3Action doesn't start as DM was configured to run two downloads in parallel. // download3Action doesn't start as DM was configured to run two downloads in parallel.
download3Action.post().assertDoesNotStart(); download3Action.post().assertDoesNotStart();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -404,7 +407,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -404,7 +407,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download1Action doesn't stop yet as it ignores interrupts. // download1Action doesn't stop yet as it ignores interrupts.
download2Action.assertStopped(); download2Action.assertStopped();
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -462,6 +465,10 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -462,6 +465,10 @@ public class DownloadManagerTest extends InstrumentationTestCase {
return new FakeDownloadAction(mediaId, true); return new FakeDownloadAction(mediaId, true);
} }
private void runOnMainThread(final Runnable r) throws Throwable {
dummyMainThread.runOnMainThread(r);
}
private static final class TestDownloadListener implements DownloadListener { private static final class TestDownloadListener implements DownloadListener {
private ConditionVariable downloadFinishedCondition; private ConditionVariable downloadFinishedCondition;
...@@ -544,7 +551,7 @@ public class DownloadManagerTest extends InstrumentationTestCase { ...@@ -544,7 +551,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
} }
private FakeDownloadAction post() throws Throwable { private FakeDownloadAction post() throws Throwable {
runTestOnUiThread( runOnMainThread(
new Runnable() { new Runnable() {
@Override @Override
public void run() { public void run() {
......
...@@ -41,7 +41,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult; ...@@ -41,7 +41,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
this( this(
timeline, timeline,
/* manifest= */ null, /* manifest= */ null,
new MediaPeriodId(0), new MediaPeriodId(/* periodIndex= */ 0),
startPositionUs, startPositionUs,
/* contentPositionUs =*/ C.TIME_UNSET, /* contentPositionUs =*/ C.TIME_UNSET,
Player.STATE_IDLE, Player.STATE_IDLE,
......
...@@ -413,53 +413,30 @@ public abstract class Timeline { ...@@ -413,53 +413,30 @@ public abstract class Timeline {
* @return Whether the ad group at index {@code adGroupIndex} has been played. * @return Whether the ad group at index {@code adGroupIndex} has been played.
*/ */
public boolean hasPlayedAdGroup(int adGroupIndex) { public boolean hasPlayedAdGroup(int adGroupIndex) {
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex]; return !adPlaybackState.adGroups[adGroupIndex].hasUnplayedAds();
return adGroup.getFirstAdIndexToPlay() == adGroup.count;
} }
/** /**
* Returns the index of the ad group at or before {@code positionUs}, if that ad group is * Returns the index of the ad group at or before {@code positionUs}, if that ad group is
* unplayed. Returns {@link C#INDEX_UNSET} if the ad group before {@code positionUs} has been * unplayed. Returns {@link C#INDEX_UNSET} if the ad group at or before {@code positionUs} has
* played, or if there is no such ad group. * no ads remaining to be played, or if there is no such ad group.
* *
* @param positionUs The position at or before which to find an ad group, in microseconds. * @param positionUs The position at or before which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}. * @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/ */
public int getAdGroupIndexForPositionUs(long positionUs) { public int getAdGroupIndexForPositionUs(long positionUs) {
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs; return adPlaybackState.getAdGroupIndexForPositionUs(positionUs);
if (adGroupTimesUs == null) {
return C.INDEX_UNSET;
}
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = adGroupTimesUs.length - 1;
while (index >= 0 && (adGroupTimesUs[index] == C.TIME_END_OF_SOURCE
|| adGroupTimesUs[index] > positionUs)) {
index--;
}
return index >= 0 && !hasPlayedAdGroup(index) ? index : C.INDEX_UNSET;
} }
/** /**
* Returns the index of the next unplayed ad group after {@code positionUs}. Returns * Returns the index of the next ad group after {@code positionUs} that has ads remaining to be
* {@link C#INDEX_UNSET} if there is no such ad group. * played. Returns {@link C#INDEX_UNSET} if there is no such ad group.
* *
* @param positionUs The position after which to find an ad group, in microseconds. * @param positionUs The position after which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}. * @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/ */
public int getAdGroupIndexAfterPositionUs(long positionUs) { public int getAdGroupIndexAfterPositionUs(long positionUs) {
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs; return adPlaybackState.getAdGroupIndexAfterPositionUs(positionUs);
if (adGroupTimesUs == null) {
return C.INDEX_UNSET;
}
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = 0;
while (index < adGroupTimesUs.length && adGroupTimesUs[index] != C.TIME_END_OF_SOURCE
&& (positionUs >= adGroupTimesUs[index] || hasPlayedAdGroup(index))) {
index++;
}
return index < adGroupTimesUs.length ? index : C.INDEX_UNSET;
} }
/** /**
......
...@@ -20,12 +20,22 @@ import com.google.android.exoplayer2.drm.DrmInitData; ...@@ -20,12 +20,22 @@ import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
/** /**
* Utility methods for parsing DTS frames. * Utility methods for parsing DTS frames.
*/ */
public final class DtsUtil { public final class DtsUtil {
private static final int SYNC_VALUE_BE = 0x7FFE8001;
private static final int SYNC_VALUE_14B_BE = 0x1FFFE800;
private static final int SYNC_VALUE_LE = 0xFE7F0180;
private static final int SYNC_VALUE_14B_LE = 0xFF1F00E8;
private static final byte FIRST_BYTE_BE = (byte) (SYNC_VALUE_BE >>> 24);
private static final byte FIRST_BYTE_14B_BE = (byte) (SYNC_VALUE_14B_BE >>> 24);
private static final byte FIRST_BYTE_LE = (byte) (SYNC_VALUE_LE >>> 24);
private static final byte FIRST_BYTE_14B_LE = (byte) (SYNC_VALUE_14B_LE >>> 24);
/** /**
* Maps AMODE to the number of channels. See ETSI TS 102 114 table 5.4. * Maps AMODE to the number of channels. See ETSI TS 102 114 table 5.4.
*/ */
...@@ -46,6 +56,20 @@ public final class DtsUtil { ...@@ -46,6 +56,20 @@ public final class DtsUtil {
2823, 2944, 3072, 3840, 4096, 6144, 7680}; 2823, 2944, 3072, 3840, 4096, 6144, 7680};
/** /**
* Returns whether a given integer matches a DTS sync word. Synchronization and storage modes are
* defined in ETSI TS 102 114 V1.1.1 (2002-08), Section 5.3.
*
* @param word An integer.
* @return Whether a given integer matches a DTS sync word.
*/
public static boolean isSyncWord(int word) {
return word == SYNC_VALUE_BE
|| word == SYNC_VALUE_LE
|| word == SYNC_VALUE_14B_BE
|| word == SYNC_VALUE_14B_LE;
}
/**
* Returns the DTS format given {@code data} containing the DTS frame according to ETSI TS 102 114 * Returns the DTS format given {@code data} containing the DTS frame according to ETSI TS 102 114
* subsections 5.3/5.4. * subsections 5.3/5.4.
* *
...@@ -57,8 +81,8 @@ public final class DtsUtil { ...@@ -57,8 +81,8 @@ public final class DtsUtil {
*/ */
public static Format parseDtsFormat(byte[] frame, String trackId, String language, public static Format parseDtsFormat(byte[] frame, String trackId, String language,
DrmInitData drmInitData) { DrmInitData drmInitData) {
ParsableBitArray frameBits = new ParsableBitArray(frame); ParsableBitArray frameBits = getNormalizedFrameHeader(frame);
frameBits.skipBits(4 * 8 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
int amode = frameBits.readBits(6); int amode = frameBits.readBits(6);
int channelCount = CHANNELS_BY_AMODE[amode]; int channelCount = CHANNELS_BY_AMODE[amode];
int sfreq = frameBits.readBits(4); int sfreq = frameBits.readBits(4);
...@@ -79,8 +103,21 @@ public final class DtsUtil { ...@@ -79,8 +103,21 @@ public final class DtsUtil {
* @return The number of audio samples represented by the frame. * @return The number of audio samples represented by the frame.
*/ */
public static int parseDtsAudioSampleCount(byte[] data) { public static int parseDtsAudioSampleCount(byte[] data) {
// See ETSI TS 102 114 subsection 5.4.1. int nblks;
int nblks = ((data[4] & 0x01) << 6) | ((data[5] & 0xFC) >> 2); switch (data[0]) {
case FIRST_BYTE_LE:
nblks = ((data[5] & 0x01) << 6) | ((data[4] & 0xFC) >> 2);
break;
case FIRST_BYTE_14B_LE:
nblks = ((data[4] & 0x07) << 4) | ((data[7] & 0x3C) >> 2);
break;
case FIRST_BYTE_14B_BE:
nblks = ((data[5] & 0x07) << 4) | ((data[6] & 0x3C) >> 2);
break;
default:
// We blindly assume FIRST_BYTE_BE if none of the others match.
nblks = ((data[4] & 0x01) << 6) | ((data[5] & 0xFC) >> 2);
}
return (nblks + 1) * 32; return (nblks + 1) * 32;
} }
...@@ -94,8 +131,21 @@ public final class DtsUtil { ...@@ -94,8 +131,21 @@ public final class DtsUtil {
public static int parseDtsAudioSampleCount(ByteBuffer buffer) { public static int parseDtsAudioSampleCount(ByteBuffer buffer) {
// See ETSI TS 102 114 subsection 5.4.1. // See ETSI TS 102 114 subsection 5.4.1.
int position = buffer.position(); int position = buffer.position();
int nblks = ((buffer.get(position + 4) & 0x01) << 6) int nblks;
| ((buffer.get(position + 5) & 0xFC) >> 2); switch (buffer.get(position)) {
case FIRST_BYTE_LE:
nblks = ((buffer.get(position + 5) & 0x01) << 6) | ((buffer.get(position + 4) & 0xFC) >> 2);
break;
case FIRST_BYTE_14B_LE:
nblks = ((buffer.get(position + 4) & 0x07) << 4) | ((buffer.get(position + 7) & 0x3C) >> 2);
break;
case FIRST_BYTE_14B_BE:
nblks = ((buffer.get(position + 5) & 0x07) << 4) | ((buffer.get(position + 6) & 0x3C) >> 2);
break;
default:
// We blindly assume FIRST_BYTE_BE if none of the others match.
nblks = ((buffer.get(position + 4) & 0x01) << 6) | ((buffer.get(position + 5) & 0xFC) >> 2);
}
return (nblks + 1) * 32; return (nblks + 1) * 32;
} }
...@@ -106,9 +156,59 @@ public final class DtsUtil { ...@@ -106,9 +156,59 @@ public final class DtsUtil {
* @return The frame's size in bytes. * @return The frame's size in bytes.
*/ */
public static int getDtsFrameSize(byte[] data) { public static int getDtsFrameSize(byte[] data) {
return (((data[5] & 0x02) << 12) int fsize;
| ((data[6] & 0xFF) << 4) boolean uses14BitPerWord = false;
| ((data[7] & 0xF0) >> 4)) + 1; switch (data[0]) {
case FIRST_BYTE_14B_BE:
fsize = (((data[6] & 0x03) << 12) | ((data[7] & 0xFF) << 4) | ((data[8] & 0x3C) >> 2)) + 1;
uses14BitPerWord = true;
break;
case FIRST_BYTE_LE:
fsize = (((data[4] & 0x03) << 12) | ((data[7] & 0xFF) << 4) | ((data[6] & 0xF0) >> 4)) + 1;
break;
case FIRST_BYTE_14B_LE:
fsize = (((data[7] & 0x03) << 12) | ((data[6] & 0xFF) << 4) | ((data[9] & 0x3C) >> 2)) + 1;
uses14BitPerWord = true;
break;
default:
// We blindly assume FIRST_BYTE_BE if none of the others match.
fsize = (((data[5] & 0x03) << 12) | ((data[6] & 0xFF) << 4) | ((data[7] & 0xF0) >> 4)) + 1;
}
// If the frame is stored in 14-bit mode, adjust the frame size to reflect the actual byte size.
return uses14BitPerWord ? fsize * 16 / 14 : fsize;
}
private static ParsableBitArray getNormalizedFrameHeader(byte[] frameHeader) {
if (frameHeader[0] == FIRST_BYTE_BE) {
// The frame is already 16-bit mode, big endian.
return new ParsableBitArray(frameHeader);
}
// Data is not normalized, but we don't want to modify frameHeader.
frameHeader = Arrays.copyOf(frameHeader, frameHeader.length);
if (isLittleEndianFrameHeader(frameHeader)) {
// Change endianness.
for (int i = 0; i < frameHeader.length - 1; i += 2) {
byte temp = frameHeader[i];
frameHeader[i] = frameHeader[i + 1];
frameHeader[i + 1] = temp;
}
}
ParsableBitArray frameBits = new ParsableBitArray(frameHeader);
if (frameHeader[0] == (byte) (SYNC_VALUE_14B_BE >> 24)) {
// Discard the 2 most significant bits of each 16 bit word.
ParsableBitArray scratchBits = new ParsableBitArray(frameHeader);
while (scratchBits.bitsLeft() >= 16) {
scratchBits.skipBits(2);
frameBits.putInt(scratchBits.readBits(14), 14);
}
}
frameBits.reset(frameHeader);
return frameBits;
}
private static boolean isLittleEndianFrameHeader(byte[] frameHeader) {
return frameHeader[0] == FIRST_BYTE_LE || frameHeader[0] == FIRST_BYTE_14B_LE;
} }
private DtsUtil() {} private DtsUtil() {}
......
...@@ -32,9 +32,7 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -32,9 +32,7 @@ public final class DtsReader implements ElementaryStreamReader {
private static final int STATE_READING_HEADER = 1; private static final int STATE_READING_HEADER = 1;
private static final int STATE_READING_SAMPLE = 2; private static final int STATE_READING_SAMPLE = 2;
private static final int HEADER_SIZE = 15; private static final int HEADER_SIZE = 18;
private static final int SYNC_VALUE = 0x7FFE8001;
private static final int SYNC_VALUE_SIZE = 4;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
private final String language; private final String language;
...@@ -63,10 +61,6 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -63,10 +61,6 @@ public final class DtsReader implements ElementaryStreamReader {
*/ */
public DtsReader(String language) { public DtsReader(String language) {
headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]); headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
headerScratchBytes.data[0] = (byte) ((SYNC_VALUE >> 24) & 0xFF);
headerScratchBytes.data[1] = (byte) ((SYNC_VALUE >> 16) & 0xFF);
headerScratchBytes.data[2] = (byte) ((SYNC_VALUE >> 8) & 0xFF);
headerScratchBytes.data[3] = (byte) (SYNC_VALUE & 0xFF);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
this.language = language; this.language = language;
} }
...@@ -96,7 +90,6 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -96,7 +90,6 @@ public final class DtsReader implements ElementaryStreamReader {
switch (state) { switch (state) {
case STATE_FINDING_SYNC: case STATE_FINDING_SYNC:
if (skipToNextSync(data)) { if (skipToNextSync(data)) {
bytesRead = SYNC_VALUE_SIZE;
state = STATE_READING_HEADER; state = STATE_READING_HEADER;
} }
break; break;
...@@ -154,7 +147,12 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -154,7 +147,12 @@ public final class DtsReader implements ElementaryStreamReader {
while (pesBuffer.bytesLeft() > 0) { while (pesBuffer.bytesLeft() > 0) {
syncBytes <<= 8; syncBytes <<= 8;
syncBytes |= pesBuffer.readUnsignedByte(); syncBytes |= pesBuffer.readUnsignedByte();
if (syncBytes == SYNC_VALUE) { if (DtsUtil.isSyncWord(syncBytes)) {
headerScratchBytes.data[0] = (byte) ((syncBytes >> 24) & 0xFF);
headerScratchBytes.data[1] = (byte) ((syncBytes >> 16) & 0xFF);
headerScratchBytes.data[2] = (byte) ((syncBytes >> 8) & 0xFF);
headerScratchBytes.data[3] = (byte) (syncBytes & 0xFF);
bytesRead = 4;
syncBytes = 0; syncBytes = 0;
return true; return true;
} }
......
...@@ -405,14 +405,9 @@ public final class Id3Decoder implements MetadataDecoder { ...@@ -405,14 +405,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding); int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset); String description = new String(data, 0, descriptionEndIndex, charset);
String value;
int valueStartIndex = descriptionEndIndex + delimiterLength(encoding); int valueStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (valueStartIndex < data.length) {
int valueEndIndex = indexOfEos(data, valueStartIndex, encoding); int valueEndIndex = indexOfEos(data, valueStartIndex, encoding);
value = new String(data, valueStartIndex, valueEndIndex - valueStartIndex, charset); String value = decodeStringIfValid(data, valueStartIndex, valueEndIndex, charset);
} else {
value = "";
}
return new TextInformationFrame("TXXX", description, value); return new TextInformationFrame("TXXX", description, value);
} }
...@@ -452,14 +447,9 @@ public final class Id3Decoder implements MetadataDecoder { ...@@ -452,14 +447,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding); int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset); String description = new String(data, 0, descriptionEndIndex, charset);
String url;
int urlStartIndex = descriptionEndIndex + delimiterLength(encoding); int urlStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (urlStartIndex < data.length) {
int urlEndIndex = indexOfZeroByte(data, urlStartIndex); int urlEndIndex = indexOfZeroByte(data, urlStartIndex);
url = new String(data, urlStartIndex, urlEndIndex - urlStartIndex, "ISO-8859-1"); String url = decodeStringIfValid(data, urlStartIndex, urlEndIndex, "ISO-8859-1");
} else {
url = "";
}
return new UrlLinkFrame("WXXX", description, url); return new UrlLinkFrame("WXXX", description, url);
} }
...@@ -502,13 +492,12 @@ public final class Id3Decoder implements MetadataDecoder { ...@@ -502,13 +492,12 @@ public final class Id3Decoder implements MetadataDecoder {
int filenameStartIndex = mimeTypeEndIndex + 1; int filenameStartIndex = mimeTypeEndIndex + 1;
int filenameEndIndex = indexOfEos(data, filenameStartIndex, encoding); int filenameEndIndex = indexOfEos(data, filenameStartIndex, encoding);
String filename = new String(data, filenameStartIndex, filenameEndIndex - filenameStartIndex, String filename = decodeStringIfValid(data, filenameStartIndex, filenameEndIndex, charset);
charset);
int descriptionStartIndex = filenameEndIndex + delimiterLength(encoding); int descriptionStartIndex = filenameEndIndex + delimiterLength(encoding);
int descriptionEndIndex = indexOfEos(data, descriptionStartIndex, encoding); int descriptionEndIndex = indexOfEos(data, descriptionStartIndex, encoding);
String description = new String(data, descriptionStartIndex, String description =
descriptionEndIndex - descriptionStartIndex, charset); decodeStringIfValid(data, descriptionStartIndex, descriptionEndIndex, charset);
int objectDataStartIndex = descriptionEndIndex + delimiterLength(encoding); int objectDataStartIndex = descriptionEndIndex + delimiterLength(encoding);
byte[] objectData = copyOfRangeIfValid(data, objectDataStartIndex, data.length); byte[] objectData = copyOfRangeIfValid(data, objectDataStartIndex, data.length);
...@@ -573,14 +562,9 @@ public final class Id3Decoder implements MetadataDecoder { ...@@ -573,14 +562,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding); int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset); String description = new String(data, 0, descriptionEndIndex, charset);
String text;
int textStartIndex = descriptionEndIndex + delimiterLength(encoding); int textStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (textStartIndex < data.length) {
int textEndIndex = indexOfEos(data, textStartIndex, encoding); int textEndIndex = indexOfEos(data, textStartIndex, encoding);
text = new String(data, textStartIndex, textEndIndex - textStartIndex, charset); String text = decodeStringIfValid(data, textStartIndex, textEndIndex, charset);
} else {
text = "";
}
return new CommentFrame(language, description, text); return new CommentFrame(language, description, text);
} }
...@@ -760,6 +744,25 @@ public final class Id3Decoder implements MetadataDecoder { ...@@ -760,6 +744,25 @@ public final class Id3Decoder implements MetadataDecoder {
return Arrays.copyOfRange(data, from, to); return Arrays.copyOfRange(data, from, to);
} }
/**
* Returns a string obtained by decoding the specified range of {@code data} using the specified
* {@code charsetName}. An empty string is returned if the range is invalid.
*
* @param data The array from which to decode the string.
* @param from The start of the range.
* @param to The end of the range (exclusive).
* @param charsetName The name of the Charset to use.
* @return The decoded string, or an empty string if the range is invalid.
* @throws UnsupportedEncodingException If the Charset is not supported.
*/
private static String decodeStringIfValid(byte[] data, int from, int to, String charsetName)
throws UnsupportedEncodingException {
if (to <= from || to > data.length) {
return "";
}
return new String(data, from, to - from, charsetName);
}
private static final class Id3Header { private static final class Id3Header {
private final int majorVersion; private final int majorVersion;
......
...@@ -115,7 +115,12 @@ public final class DownloadManager { ...@@ -115,7 +115,12 @@ public final class DownloadManager {
tasks = new ArrayList<>(); tasks = new ArrayList<>();
activeDownloadTasks = new ArrayList<>(); activeDownloadTasks = new ArrayList<>();
handler = new Handler(Looper.getMainLooper());
Looper looper = Looper.myLooper();
if (looper == null) {
looper = Looper.getMainLooper();
}
handler = new Handler(looper);
fileIOThread = new HandlerThread("DownloadManager file i/o"); fileIOThread = new HandlerThread("DownloadManager file i/o");
fileIOThread.start(); fileIOThread.start();
......
...@@ -40,17 +40,12 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction { ...@@ -40,17 +40,12 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
public DownloadAction readFromStream(int version, DataInputStream input) throws IOException { public DownloadAction readFromStream(int version, DataInputStream input) throws IOException {
Uri manifestUri = Uri.parse(input.readUTF()); Uri manifestUri = Uri.parse(input.readUTF());
String data = input.readUTF(); String data = input.readUTF();
boolean removeAction = input.readBoolean();
int keyCount = input.readInt(); int keyCount = input.readInt();
boolean removeAction = keyCount == -1; K[] keys = createKeyArray(keyCount);
K[] keys;
if (removeAction) {
keys = null;
} else {
keys = createKeyArray(keyCount);
for (int i = 0; i < keyCount; i++) { for (int i = 0; i < keyCount; i++) {
keys[i] = readKey(input); keys[i] = readKey(input);
} }
}
return createDownloadAction(manifestUri, removeAction, data, keys); return createDownloadAction(manifestUri, removeAction, data, keys);
} }
...@@ -74,12 +69,15 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction { ...@@ -74,12 +69,15 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
* @param manifestUri The {@link Uri} of the manifest to be downloaded. * @param manifestUri The {@link Uri} of the manifest to be downloaded.
* @param removeAction Whether the data will be removed. If {@code false} it will be downloaded. * @param removeAction Whether the data will be removed. If {@code false} it will be downloaded.
* @param data Optional custom data for this action. If null, an empty string is used. * @param data Optional custom data for this action. If null, an empty string is used.
* @param keys Keys of representations to be downloaded. If empty or null, all representations are * @param keys Keys of representations to be downloaded. If empty, all representations are
* downloaded. If {@code removeAction} is true, this is ignored. * downloaded. If {@code removeAction} is true, {@code keys} should be an empty array.
*/ */
protected SegmentDownloadAction(Uri manifestUri, boolean removeAction, String data, K[] keys) { protected SegmentDownloadAction(Uri manifestUri, boolean removeAction, String data, K[] keys) {
super(data); super(data);
Assertions.checkArgument(!removeAction || keys == null || keys.length == 0); Assertions.checkNotNull(keys);
if (removeAction) {
Assertions.checkArgument(keys.length == 0);
}
this.manifestUri = manifestUri; this.manifestUri = manifestUri;
this.keys = keys; this.keys = keys;
this.removeAction = removeAction; this.removeAction = removeAction;
...@@ -94,15 +92,12 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction { ...@@ -94,15 +92,12 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
public final void writeToStream(DataOutputStream output) throws IOException { public final void writeToStream(DataOutputStream output) throws IOException {
output.writeUTF(manifestUri.toString()); output.writeUTF(manifestUri.toString());
output.writeUTF(getData()); output.writeUTF(getData());
if (isRemoveAction()) { output.writeBoolean(removeAction);
output.writeInt(-1);
} else {
output.writeInt(keys.length); output.writeInt(keys.length);
for (K key : keys) { for (K key : keys) {
writeKey(output, key); writeKey(output, key);
} }
} }
}
/** Serializes the {@code key} into the {@code output}. */ /** Serializes the {@code key} into the {@code output}. */
protected abstract void writeKey(DataOutputStream output, K key) throws IOException; protected abstract void writeKey(DataOutputStream output, K key) throws IOException;
...@@ -124,11 +119,9 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction { ...@@ -124,11 +119,9 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
} }
SegmentDownloadAction<?> that = (SegmentDownloadAction<?>) o; SegmentDownloadAction<?> that = (SegmentDownloadAction<?>) o;
return manifestUri.equals(that.manifestUri) return manifestUri.equals(that.manifestUri)
&& (keys == null || keys.length == 0 && removeAction == that.removeAction
? (that.keys == null || that.keys.length == 0) && keys.length == that.keys.length
: (that.keys != null && Arrays.asList(keys).containsAll(Arrays.asList(that.keys));
&& that.keys.length == keys.length
&& Arrays.asList(keys).containsAll(Arrays.asList(that.keys))));
} }
@Override @Override
......
...@@ -101,10 +101,10 @@ public abstract class SegmentDownloader<M, K> implements Downloader { ...@@ -101,10 +101,10 @@ public abstract class SegmentDownloader<M, K> implements Downloader {
/** /**
* Selects multiple representations pointed to by the keys for downloading, checking status. Any * Selects multiple representations pointed to by the keys for downloading, checking status. Any
* previous selection is cleared. If keys are null or empty, all representations are downloaded. * previous selection is cleared. If keys array is empty, all representations are downloaded.
*/ */
public final void selectRepresentations(K[] keys) { public final void selectRepresentations(K[] keys) {
this.keys = (keys != null && keys.length > 0) ? keys.clone() : null; this.keys = keys.length > 0 ? keys.clone() : null;
resetCounters(); resetCounters();
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
...@@ -29,6 +30,15 @@ import java.io.IOException; ...@@ -29,6 +30,15 @@ import java.io.IOException;
*/ */
public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callback { public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
/** Listener for preparation errors. */
public interface PrepareErrorListener {
/**
* Called the first time an error occurs while refreshing source info or preparing the period.
*/
void onPrepareError(IOException exception);
}
public final MediaSource mediaSource; public final MediaSource mediaSource;
private final MediaPeriodId id; private final MediaPeriodId id;
...@@ -37,7 +47,16 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -37,7 +47,16 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
private MediaPeriod mediaPeriod; private MediaPeriod mediaPeriod;
private Callback callback; private Callback callback;
private long preparePositionUs; private long preparePositionUs;
private @Nullable PrepareErrorListener listener;
private boolean notifiedPrepareError;
/**
* Creates a new deferred media period.
*
* @param mediaSource The media source to wrap.
* @param id The identifier for the media period to create when {@link #createPeriod()} is called.
* @param allocator The allocator used to create the media period.
*/
public DeferredMediaPeriod(MediaSource mediaSource, MediaPeriodId id, Allocator allocator) { public DeferredMediaPeriod(MediaSource mediaSource, MediaPeriodId id, Allocator allocator) {
this.id = id; this.id = id;
this.allocator = allocator; this.allocator = allocator;
...@@ -45,6 +64,17 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -45,6 +64,17 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
/** /**
* Sets a listener for preparation errors.
*
* @param listener An listener to be notified of media period preparation errors. If a listener is
* set, {@link #maybeThrowPrepareError()} will not throw but will instead pass the first
* preparation error (if any) to the listener.
*/
public void setPrepareErrorListener(PrepareErrorListener listener) {
this.listener = listener;
}
/**
* Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator)} on the wrapped source then * Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator)} on the wrapped source then
* prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()} * prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()}
* to release the period. * to release the period.
...@@ -76,11 +106,21 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -76,11 +106,21 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override @Override
public void maybeThrowPrepareError() throws IOException { public void maybeThrowPrepareError() throws IOException {
try {
if (mediaPeriod != null) { if (mediaPeriod != null) {
mediaPeriod.maybeThrowPrepareError(); mediaPeriod.maybeThrowPrepareError();
} else { } else {
mediaSource.maybeThrowSourceInfoRefreshError(); mediaSource.maybeThrowSourceInfoRefreshError();
} }
} catch (final IOException e) {
if (listener == null) {
throw e;
}
if (!notifiedPrepareError) {
notifiedPrepareError = true;
listener.onPrepareError(e);
}
}
} }
@Override @Override
......
...@@ -780,7 +780,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -780,7 +780,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) { public Period getPeriod(int periodIndex, Period period, boolean setIds) {
timeline.getPeriod(periodIndex, period, setIds); timeline.getPeriod(periodIndex, period, setIds);
if (period.uid == replacedId) { if (Util.areEqual(period.uid, replacedId)) {
period.uid = DUMMY_ID; period.uid = DUMMY_ID;
} }
return period; return period;
...@@ -788,7 +788,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -788,7 +788,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
@Override @Override
public int getIndexOfPeriod(Object uid) { public int getIndexOfPeriod(Object uid) {
return timeline.getIndexOfPeriod(uid == DUMMY_ID ? replacedId : uid); return timeline.getIndexOfPeriod(DUMMY_ID.equals(uid) ? replacedId : uid);
} }
} }
......
...@@ -179,24 +179,24 @@ import java.util.Arrays; ...@@ -179,24 +179,24 @@ import java.util.Arrays;
} }
public void release() { public void release() {
boolean releasedSynchronously = loader.release(this); if (prepared) {
if (prepared && !releasedSynchronously) {
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise // Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// sampleQueues may still be being modified by the loading thread. // sampleQueues may still be being modified by the loading thread.
for (SampleQueue sampleQueue : sampleQueues) { for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.discardToEnd(); sampleQueue.discardToEnd();
} }
} }
loader.release(this);
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
released = true; released = true;
} }
@Override @Override
public void onLoaderReleased() { public void onLoaderReleased() {
extractorHolder.release();
for (SampleQueue sampleQueue : sampleQueues) { for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset(); sampleQueue.reset();
} }
extractorHolder.release();
} }
@Override @Override
......
...@@ -64,12 +64,6 @@ public interface MediaSource { ...@@ -64,12 +64,6 @@ public interface MediaSource {
final class MediaPeriodId { final class MediaPeriodId {
/** /**
* Value for unset media period identifiers.
*/
public static final MediaPeriodId UNSET =
new MediaPeriodId(C.INDEX_UNSET, C.INDEX_UNSET, C.INDEX_UNSET);
/**
* The timeline period index. * The timeline period index.
*/ */
public final int periodIndex; public final int periodIndex;
...@@ -87,12 +81,31 @@ public interface MediaSource { ...@@ -87,12 +81,31 @@ public interface MediaSource {
public final int adIndexInAdGroup; public final int adIndexInAdGroup;
/** /**
* The sequence number of the window in the buffered sequence of windows this media period is
* part of. {@link C#INDEX_UNSET} if the media period id is not part of a buffered sequence of
* windows.
*/
public final long windowSequenceNumber;
/**
* Creates a media period identifier for a dummy period which is not part of a buffered sequence
* of windows.
*
* @param periodIndex The period index.
*/
public MediaPeriodId(int periodIndex) {
this(periodIndex, C.INDEX_UNSET);
}
/**
* Creates a media period identifier for the specified period in the timeline. * Creates a media period identifier for the specified period in the timeline.
* *
* @param periodIndex The timeline period index. * @param periodIndex The timeline period index.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this media period is part of.
*/ */
public MediaPeriodId(int periodIndex) { public MediaPeriodId(int periodIndex, long windowSequenceNumber) {
this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET); this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber);
} }
/** /**
...@@ -102,19 +115,24 @@ public interface MediaSource { ...@@ -102,19 +115,24 @@ public interface MediaSource {
* @param periodIndex The index of the timeline period that contains the ad group. * @param periodIndex The index of the timeline period that contains the ad group.
* @param adGroupIndex The index of the ad group. * @param adGroupIndex The index of the ad group.
* @param adIndexInAdGroup The index of the ad in the ad group. * @param adIndexInAdGroup The index of the ad in the ad group.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this media period is part of.
*/ */
public MediaPeriodId(int periodIndex, int adGroupIndex, int adIndexInAdGroup) { public MediaPeriodId(
int periodIndex, int adGroupIndex, int adIndexInAdGroup, long windowSequenceNumber) {
this.periodIndex = periodIndex; this.periodIndex = periodIndex;
this.adGroupIndex = adGroupIndex; this.adGroupIndex = adGroupIndex;
this.adIndexInAdGroup = adIndexInAdGroup; this.adIndexInAdGroup = adIndexInAdGroup;
this.windowSequenceNumber = windowSequenceNumber;
} }
/** /**
* Returns a copy of this period identifier but with {@code newPeriodIndex} as its period index. * Returns a copy of this period identifier but with {@code newPeriodIndex} as its period index.
*/ */
public MediaPeriodId copyWithPeriodIndex(int newPeriodIndex) { public MediaPeriodId copyWithPeriodIndex(int newPeriodIndex) {
return periodIndex == newPeriodIndex ? this return periodIndex == newPeriodIndex
: new MediaPeriodId(newPeriodIndex, adGroupIndex, adIndexInAdGroup); ? this
: new MediaPeriodId(newPeriodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
} }
/** /**
...@@ -134,8 +152,10 @@ public interface MediaSource { ...@@ -134,8 +152,10 @@ public interface MediaSource {
} }
MediaPeriodId periodId = (MediaPeriodId) obj; MediaPeriodId periodId = (MediaPeriodId) obj;
return periodIndex == periodId.periodIndex && adGroupIndex == periodId.adGroupIndex return periodIndex == periodId.periodIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup; && adGroupIndex == periodId.adGroupIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup
&& windowSequenceNumber == periodId.windowSequenceNumber;
} }
@Override @Override
...@@ -144,6 +164,7 @@ public interface MediaSource { ...@@ -144,6 +164,7 @@ public interface MediaSource {
result = 31 * result + periodIndex; result = 31 * result + periodIndex;
result = 31 * result + adGroupIndex; result = 31 * result + adGroupIndex;
result = 31 * result + adIndexInAdGroup; result = 31 * result + adIndexInAdGroup;
result = 31 * result + (int) windowSequenceNumber;
return result; return result;
} }
......
...@@ -91,6 +91,11 @@ public final class AdPlaybackState { ...@@ -91,6 +91,11 @@ public final class AdPlaybackState {
return nextAdIndexToPlay; return nextAdIndexToPlay;
} }
/** Returns whether the ad group has at least one ad that still needs to be played. */
public boolean hasUnplayedAds() {
return count == C.LENGTH_UNSET || getFirstAdIndexToPlay() < count;
}
/** /**
* Returns a new instance with the ad count set to {@code count}. This method may only be called * Returns a new instance with the ad count set to {@code count}. This method may only be called
* if this instance's ad count has not yet been specified. * if this instance's ad count has not yet been specified.
...@@ -271,6 +276,44 @@ public final class AdPlaybackState { ...@@ -271,6 +276,44 @@ public final class AdPlaybackState {
} }
/** /**
* Returns the index of the ad group at or before {@code positionUs}, if that ad group is
* unplayed. Returns {@link C#INDEX_UNSET} if the ad group at or before {@code positionUs} has no
* ads remaining to be played, or if there is no such ad group.
*
* @param positionUs The position at or before which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/
public int getAdGroupIndexForPositionUs(long positionUs) {
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = adGroupTimesUs.length - 1;
while (index >= 0
&& (adGroupTimesUs[index] == C.TIME_END_OF_SOURCE || adGroupTimesUs[index] > positionUs)) {
index--;
}
return index >= 0 && adGroups[index].hasUnplayedAds() ? index : C.INDEX_UNSET;
}
/**
* Returns the index of the next ad group after {@code positionUs} that has ads remaining to be
* played. Returns {@link C#INDEX_UNSET} if there is no such ad group.
*
* @param positionUs The position after which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/
public int getAdGroupIndexAfterPositionUs(long positionUs) {
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = 0;
while (index < adGroupTimesUs.length
&& adGroupTimesUs[index] != C.TIME_END_OF_SOURCE
&& (positionUs >= adGroupTimesUs[index] || !adGroups[index].hasUnplayedAds())) {
index++;
}
return index < adGroupTimesUs.length ? index : C.INDEX_UNSET;
}
/**
* Returns an instance with the number of ads in {@code adGroupIndex} resolved to {@code adCount}. * Returns an instance with the number of ads in {@code adGroupIndex} resolved to {@code adCount}.
* The ad count must be greater than zero. * The ad count must be greater than zero.
*/ */
......
...@@ -103,4 +103,13 @@ public interface AdsLoader { ...@@ -103,4 +103,13 @@ public interface AdsLoader {
*/ */
void release(); void release();
/**
* Notifies the ads loader that the player was not able to prepare media for a given ad.
* Implementations should update the ad playback state as the specified ad has failed to load.
*
* @param adGroupIndex The index of the ad group.
* @param adIndexInAdGroup The index of the ad in the ad group.
* @param exception The preparation error.
*/
void handlePrepareError(int adGroupIndex, int adIndexInAdGroup, IOException exception);
} }
...@@ -235,7 +235,12 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> { ...@@ -235,7 +235,12 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
} }
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup]; MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
DeferredMediaPeriod deferredMediaPeriod = DeferredMediaPeriod deferredMediaPeriod =
new DeferredMediaPeriod(mediaSource, new MediaPeriodId(0), allocator); new DeferredMediaPeriod(
mediaSource,
new MediaPeriodId(/* periodIndex= */ 0, id.windowSequenceNumber),
allocator);
deferredMediaPeriod.setPrepareErrorListener(
new AdPrepareErrorListener(adGroupIndex, adIndexInAdGroup));
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource); List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
if (mediaPeriods == null) { if (mediaPeriods == null) {
deferredMediaPeriod.createPeriod(); deferredMediaPeriod.createPeriod();
...@@ -433,4 +438,25 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> { ...@@ -433,4 +438,25 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
} }
private final class AdPrepareErrorListener implements DeferredMediaPeriod.PrepareErrorListener {
private final int adGroupIndex;
private final int adIndexInAdGroup;
public AdPrepareErrorListener(int adGroupIndex, int adIndexInAdGroup) {
this.adGroupIndex = adGroupIndex;
this.adIndexInAdGroup = adIndexInAdGroup;
}
@Override
public void onPrepareError(final IOException exception) {
mainHandler.post(
new Runnable() {
@Override
public void run() {
adsLoader.handlePrepareError(adGroupIndex, adIndexInAdGroup, exception);
}
});
}
}
} }
...@@ -73,7 +73,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S ...@@ -73,7 +73,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private final BaseMediaChunkOutput mediaChunkOutput; private final BaseMediaChunkOutput mediaChunkOutput;
private Format primaryDownstreamTrackFormat; private Format primaryDownstreamTrackFormat;
private ReleaseCallback<T> releaseCallback; private @Nullable ReleaseCallback<T> releaseCallback;
private long pendingResetPositionUs; private long pendingResetPositionUs;
private long lastSeekPositionUs; private long lastSeekPositionUs;
/* package */ long decodeOnlyUntilPositionUs; /* package */ long decodeOnlyUntilPositionUs;
...@@ -306,20 +306,17 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S ...@@ -306,20 +306,17 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
* <p>This method should be called when the stream is no longer required. Either this method or * <p>This method should be called when the stream is no longer required. Either this method or
* {@link #release()} can be used to release this stream. * {@link #release()} can be used to release this stream.
* *
* @param callback A callback to be called when the release ends. Will be called synchronously * @param callback An optional callback to be called on the loading thread once the loader has
* from this method if no load is in progress, or asynchronously once the load has been * been released.
* canceled otherwise.
*/ */
public void release(@Nullable ReleaseCallback<T> callback) { public void release(@Nullable ReleaseCallback<T> callback) {
this.releaseCallback = callback; this.releaseCallback = callback;
boolean releasedSynchronously = loader.release(this);
if (!releasedSynchronously) {
// Discard as much as we can synchronously. // Discard as much as we can synchronously.
primarySampleQueue.discardToEnd(); primarySampleQueue.discardToEnd();
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) { for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.discardToEnd(); embeddedSampleQueue.discardToEnd();
} }
} loader.release(this);
} }
@Override @Override
......
...@@ -15,15 +15,11 @@ ...@@ -15,15 +15,11 @@
*/ */
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
/** /** A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}. */
* A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}. public class SubtitleInputBuffer extends DecoderInputBuffer {
*/
public final class SubtitleInputBuffer extends DecoderInputBuffer
implements Comparable<SubtitleInputBuffer> {
/** /**
* An offset that must be added to the subtitle's event times after it's been decoded, or * An offset that must be added to the subtitle's event times after it's been decoded, or
...@@ -35,16 +31,4 @@ public final class SubtitleInputBuffer extends DecoderInputBuffer ...@@ -35,16 +31,4 @@ public final class SubtitleInputBuffer extends DecoderInputBuffer
super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
} }
@Override
public int compareTo(@NonNull SubtitleInputBuffer other) {
if (isEndOfStream() != other.isEndOfStream()) {
return isEndOfStream() ? 1 : -1;
}
long delta = timeUs - other.timeUs;
if (delta == 0) {
return 0;
}
return delta > 0 ? 1 : -1;
}
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.text.cea; package com.google.android.exoplayer2.text.cea;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.Subtitle;
...@@ -34,21 +35,22 @@ import java.util.PriorityQueue; ...@@ -34,21 +35,22 @@ import java.util.PriorityQueue;
private static final int NUM_INPUT_BUFFERS = 10; private static final int NUM_INPUT_BUFFERS = 10;
private static final int NUM_OUTPUT_BUFFERS = 2; private static final int NUM_OUTPUT_BUFFERS = 2;
private final LinkedList<SubtitleInputBuffer> availableInputBuffers; private final LinkedList<CeaInputBuffer> availableInputBuffers;
private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers; private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers;
private final PriorityQueue<SubtitleInputBuffer> queuedInputBuffers; private final PriorityQueue<CeaInputBuffer> queuedInputBuffers;
private SubtitleInputBuffer dequeuedInputBuffer; private CeaInputBuffer dequeuedInputBuffer;
private long playbackPositionUs; private long playbackPositionUs;
private long queuedInputBufferCount;
public CeaDecoder() { public CeaDecoder() {
availableInputBuffers = new LinkedList<>(); availableInputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
availableInputBuffers.add(new SubtitleInputBuffer()); availableInputBuffers.add(new CeaInputBuffer());
} }
availableOutputBuffers = new LinkedList<>(); availableOutputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) {
availableOutputBuffers.add(new CeaOutputBuffer(this)); availableOutputBuffers.add(new CeaOutputBuffer());
} }
queuedInputBuffers = new PriorityQueue<>(); queuedInputBuffers = new PriorityQueue<>();
} }
...@@ -77,9 +79,10 @@ import java.util.PriorityQueue; ...@@ -77,9 +79,10 @@ import java.util.PriorityQueue;
if (inputBuffer.isDecodeOnly()) { if (inputBuffer.isDecodeOnly()) {
// We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow // We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow
// for decoding to begin mid-stream. // for decoding to begin mid-stream.
releaseInputBuffer(inputBuffer); releaseInputBuffer(dequeuedInputBuffer);
} else { } else {
queuedInputBuffers.add(inputBuffer); dequeuedInputBuffer.queuedInputBufferCount = queuedInputBufferCount++;
queuedInputBuffers.add(dequeuedInputBuffer);
} }
dequeuedInputBuffer = null; dequeuedInputBuffer = null;
} }
...@@ -94,7 +97,7 @@ import java.util.PriorityQueue; ...@@ -94,7 +97,7 @@ import java.util.PriorityQueue;
// be deferred until they would be applicable // be deferred until they would be applicable
while (!queuedInputBuffers.isEmpty() while (!queuedInputBuffers.isEmpty()
&& queuedInputBuffers.peek().timeUs <= playbackPositionUs) { && queuedInputBuffers.peek().timeUs <= playbackPositionUs) {
SubtitleInputBuffer inputBuffer = queuedInputBuffers.poll(); CeaInputBuffer inputBuffer = queuedInputBuffers.poll();
// If the input buffer indicates we've reached the end of the stream, we can // If the input buffer indicates we've reached the end of the stream, we can
// return immediately with an output buffer propagating that // return immediately with an output buffer propagating that
...@@ -126,7 +129,7 @@ import java.util.PriorityQueue; ...@@ -126,7 +129,7 @@ import java.util.PriorityQueue;
return null; return null;
} }
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { private void releaseInputBuffer(CeaInputBuffer inputBuffer) {
inputBuffer.clear(); inputBuffer.clear();
availableInputBuffers.add(inputBuffer); availableInputBuffers.add(inputBuffer);
} }
...@@ -138,6 +141,7 @@ import java.util.PriorityQueue; ...@@ -138,6 +141,7 @@ import java.util.PriorityQueue;
@Override @Override
public void flush() { public void flush() {
queuedInputBufferCount = 0;
playbackPositionUs = 0; playbackPositionUs = 0;
while (!queuedInputBuffers.isEmpty()) { while (!queuedInputBuffers.isEmpty()) {
releaseInputBuffer(queuedInputBuffers.poll()); releaseInputBuffer(queuedInputBuffers.poll());
...@@ -169,4 +173,32 @@ import java.util.PriorityQueue; ...@@ -169,4 +173,32 @@ import java.util.PriorityQueue;
*/ */
protected abstract void decode(SubtitleInputBuffer inputBuffer); protected abstract void decode(SubtitleInputBuffer inputBuffer);
private static final class CeaInputBuffer extends SubtitleInputBuffer
implements Comparable<CeaInputBuffer> {
private long queuedInputBufferCount;
@Override
public int compareTo(@NonNull CeaInputBuffer other) {
if (isEndOfStream() != other.isEndOfStream()) {
return isEndOfStream() ? 1 : -1;
}
long delta = timeUs - other.timeUs;
if (delta == 0) {
delta = queuedInputBufferCount - other.queuedInputBufferCount;
if (delta == 0) {
return 0;
}
}
return delta > 0 ? 1 : -1;
}
}
private final class CeaOutputBuffer extends SubtitleOutputBuffer {
@Override
public final void release() {
releaseOutputBuffer(this);
}
}
} }
...@@ -19,18 +19,19 @@ import android.util.Log; ...@@ -19,18 +19,19 @@ import android.util.Log;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
/** /** Utility methods for handling CEA-608/708 messages. Defined in A/53 Part 4:2009. */
* Utility methods for handling CEA-608/708 messages.
*/
public final class CeaUtil { public final class CeaUtil {
private static final String TAG = "CeaUtil"; private static final String TAG = "CeaUtil";
private static final int PAYLOAD_TYPE_CC = 4; private static final int PAYLOAD_TYPE_CC = 4;
private static final int COUNTRY_CODE = 0xB5; private static final int COUNTRY_CODE = 0xB5;
private static final int PROVIDER_CODE = 0x31; private static final int PROVIDER_CODE_ATSC = 0x31;
private static final int USER_ID = 0x47413934; // "GA94" private static final int PROVIDER_CODE_DIRECTV = 0x2F;
private static final int USER_ID_GA94 = Util.getIntegerCodeForString("GA94");
private static final int USER_ID_DTG1 = Util.getIntegerCodeForString("DTG1");
private static final int USER_DATA_TYPE_CODE = 0x3; private static final int USER_DATA_TYPE_CODE = 0x3;
/** /**
...@@ -46,15 +47,32 @@ public final class CeaUtil { ...@@ -46,15 +47,32 @@ public final class CeaUtil {
while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) { while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) {
int payloadType = readNon255TerminatedValue(seiBuffer); int payloadType = readNon255TerminatedValue(seiBuffer);
int payloadSize = readNon255TerminatedValue(seiBuffer); int payloadSize = readNon255TerminatedValue(seiBuffer);
int nextPayloadPosition = seiBuffer.getPosition() + payloadSize;
// Process the payload. // Process the payload.
if (payloadSize == -1 || payloadSize > seiBuffer.bytesLeft()) { if (payloadSize == -1 || payloadSize > seiBuffer.bytesLeft()) {
// This might occur if we're trying to read an encrypted SEI NAL unit. // This might occur if we're trying to read an encrypted SEI NAL unit.
Log.w(TAG, "Skipping remainder of malformed SEI NAL unit."); Log.w(TAG, "Skipping remainder of malformed SEI NAL unit.");
seiBuffer.setPosition(seiBuffer.limit()); nextPayloadPosition = seiBuffer.limit();
} else if (isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) { } else if (payloadType == PAYLOAD_TYPE_CC && payloadSize >= 8) {
// Ignore country_code (1) + provider_code (2) + user_identifier (4) int countryCode = seiBuffer.readUnsignedByte();
// + user_data_type_code (1). int providerCode = seiBuffer.readUnsignedShort();
seiBuffer.skipBytes(8); int userIdentifier = 0;
if (providerCode == PROVIDER_CODE_ATSC) {
userIdentifier = seiBuffer.readInt();
}
int userDataTypeCode = seiBuffer.readUnsignedByte();
if (providerCode == PROVIDER_CODE_DIRECTV) {
seiBuffer.skipBytes(1); // user_data_length.
}
boolean messageIsSupportedCeaCaption =
countryCode == COUNTRY_CODE
&& (providerCode == PROVIDER_CODE_ATSC || providerCode == PROVIDER_CODE_DIRECTV)
&& userDataTypeCode == USER_DATA_TYPE_CODE;
if (providerCode == PROVIDER_CODE_ATSC) {
messageIsSupportedCeaCaption &=
userIdentifier == USER_ID_GA94 || userIdentifier == USER_ID_DTG1;
}
if (messageIsSupportedCeaCaption) {
// Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1). // Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1).
int ccCount = seiBuffer.readUnsignedByte() & 0x1F; int ccCount = seiBuffer.readUnsignedByte() & 0x1F;
// Ignore em_data (1) // Ignore em_data (1)
...@@ -66,13 +84,12 @@ public final class CeaUtil { ...@@ -66,13 +84,12 @@ public final class CeaUtil {
for (TrackOutput output : outputs) { for (TrackOutput output : outputs) {
seiBuffer.setPosition(sampleStartPosition); seiBuffer.setPosition(sampleStartPosition);
output.sampleData(seiBuffer, sampleLength); output.sampleData(seiBuffer, sampleLength);
output.sampleMetadata(presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null); output.sampleMetadata(
presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null);
}
} }
// Ignore trailing information in SEI, if any.
seiBuffer.skipBytes(payloadSize - (10 + ccCount * 3));
} else {
seiBuffer.skipBytes(payloadSize);
} }
seiBuffer.setPosition(nextPayloadPosition);
} }
} }
...@@ -97,31 +114,6 @@ public final class CeaUtil { ...@@ -97,31 +114,6 @@ public final class CeaUtil {
return value; return value;
} }
/**
* Inspects an sei message to determine whether it contains CEA-608.
* <p>
* The position of {@code payload} is left unchanged.
*
* @param payloadType The payload type of the message.
* @param payloadLength The length of the payload.
* @param payload A {@link ParsableByteArray} containing the payload.
* @return Whether the sei message contains CEA-608.
*/
private static boolean isSeiMessageCea608(int payloadType, int payloadLength,
ParsableByteArray payload) {
if (payloadType != PAYLOAD_TYPE_CC || payloadLength < 8) {
return false;
}
int startPosition = payload.getPosition();
int countryCode = payload.readUnsignedByte();
int providerCode = payload.readUnsignedShort();
int userIdentifier = payload.readInt();
int userDataTypeCode = payload.readUnsignedByte();
payload.setPosition(startPosition);
return countryCode == COUNTRY_CODE && providerCode == PROVIDER_CODE
&& userIdentifier == USER_ID && userDataTypeCode == USER_DATA_TYPE_CODE;
}
private CeaUtil() {} private CeaUtil() {}
} }
...@@ -20,6 +20,7 @@ import android.os.Handler; ...@@ -20,6 +20,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
...@@ -197,25 +198,17 @@ public final class Loader implements LoaderErrorThrower { ...@@ -197,25 +198,17 @@ public final class Loader implements LoaderErrorThrower {
* Releases the {@link Loader}. This method should be called when the {@link Loader} is no longer * Releases the {@link Loader}. This method should be called when the {@link Loader} is no longer
* required. * required.
* *
* @param callback A callback to be called when the release ends. Will be called synchronously * @param callback An optional callback to be called on the loading thread once the loader has
* from this method if no load is in progress, or asynchronously once the load has been * been released.
* canceled otherwise. May be null.
* @return True if {@code callback} was called synchronously. False if it will be called
* asynchronously or if {@code callback} is null.
*/ */
public boolean release(ReleaseCallback callback) { public void release(@Nullable ReleaseCallback callback) {
boolean callbackInvoked = false;
if (currentTask != null) { if (currentTask != null) {
currentTask.cancel(true); currentTask.cancel(true);
}
if (callback != null) { if (callback != null) {
downloadExecutorService.execute(new ReleaseTask(callback)); downloadExecutorService.execute(new ReleaseTask(callback));
} }
} else if (callback != null) {
callback.onLoaderReleased();
callbackInvoked = true;
}
downloadExecutorService.shutdown(); downloadExecutorService.shutdown();
return callbackInvoked;
} }
// LoaderErrorThrower implementation. // LoaderErrorThrower implementation.
...@@ -419,7 +412,7 @@ public final class Loader implements LoaderErrorThrower { ...@@ -419,7 +412,7 @@ public final class Loader implements LoaderErrorThrower {
} }
private static final class ReleaseTask extends Handler implements Runnable { private static final class ReleaseTask implements Runnable {
private final ReleaseCallback callback; private final ReleaseCallback callback;
...@@ -429,13 +422,6 @@ public final class Loader implements LoaderErrorThrower { ...@@ -429,13 +422,6 @@ public final class Loader implements LoaderErrorThrower {
@Override @Override
public void run() { public void run() {
if (getLooper().getThread().isAlive()) {
sendEmptyMessage(0);
}
}
@Override
public void handleMessage(Message msg) {
callback.onLoaderReleased(); callback.onLoaderReleased();
} }
......
...@@ -263,6 +263,40 @@ public final class ParsableBitArray { ...@@ -263,6 +263,40 @@ public final class ParsableBitArray {
assertValidOffset(); assertValidOffset();
} }
/**
* Overwrites {@code numBits} from this array using the {@code numBits} least significant bits
* from {@code value}. Bits are written in order from most significant to least significant. The
* read position is advanced by {@code numBits}.
*
* @param value The integer whose {@code numBits} least significant bits are written into {@link
* #data}.
* @param numBits The number of bits to write.
*/
public void putInt(int value, int numBits) {
int remainingBitsToRead = numBits;
if (numBits < 32) {
value &= (1 << numBits) - 1;
}
int firstByteReadSize = Math.min(8 - bitOffset, numBits);
int firstByteRightPaddingSize = 8 - bitOffset - firstByteReadSize;
int firstByteBitmask = (0xFF00 >> bitOffset) | ((1 << firstByteRightPaddingSize) - 1);
data[byteOffset] &= firstByteBitmask;
int firstByteInputBits = value >>> (numBits - firstByteReadSize);
data[byteOffset] |= firstByteInputBits << firstByteRightPaddingSize;
remainingBitsToRead -= firstByteReadSize;
int currentByteIndex = byteOffset + 1;
while (remainingBitsToRead > 8) {
data[currentByteIndex++] = (byte) (value >>> (remainingBitsToRead - 8));
remainingBitsToRead -= 8;
}
int lastByteRightPaddingSize = 8 - remainingBitsToRead;
data[currentByteIndex] &= (1 << lastByteRightPaddingSize) - 1;
int lastByteInput = value & ((1 << remainingBitsToRead) - 1);
data[currentByteIndex] |= lastByteInput << lastByteRightPaddingSize;
skipBits(numBits);
assertValidOffset();
}
private void assertValidOffset() { private void assertValidOffset() {
// It is fine for position to be at the end of the array, but no further. // It is fine for position to be at the end of the array, but no further.
Assertions.checkState(byteOffset >= 0 Assertions.checkState(byteOffset >= 0
......
...@@ -1334,7 +1334,11 @@ public final class Util { ...@@ -1334,7 +1334,11 @@ public final class Util {
if ("Sony".equals(Util.MANUFACTURER) && Util.MODEL.startsWith("BRAVIA") if ("Sony".equals(Util.MANUFACTURER) && Util.MODEL.startsWith("BRAVIA")
&& context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) { && context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
return new Point(3840, 2160); return new Point(3840, 2160);
} else if ("NVIDIA".equals(Util.MANUFACTURER) && Util.MODEL.contains("SHIELD")) { } else if (("NVIDIA".equals(Util.MANUFACTURER) && Util.MODEL.contains("SHIELD"))
|| ("philips".equals(Util.toLowerInvariant(Util.MANUFACTURER))
&& (Util.MODEL.startsWith("QM1")
|| Util.MODEL.equals("QV151E")
|| Util.MODEL.equals("TPM171E")))) {
// Attempt to read sys.display-size. // Attempt to read sys.display-size.
String sysDisplaySize = null; String sysDisplaySize = null;
try { try {
......
...@@ -1085,15 +1085,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -1085,15 +1085,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private static boolean codecNeedsSetOutputSurfaceWorkaround(String name) { private static boolean codecNeedsSetOutputSurfaceWorkaround(String name) {
// Work around https://github.com/google/ExoPlayer/issues/3236, // Work around https://github.com/google/ExoPlayer/issues/3236,
// https://github.com/google/ExoPlayer/issues/3355, // https://github.com/google/ExoPlayer/issues/3355,
// https://github.com/google/ExoPlayer/issues/3439 and // https://github.com/google/ExoPlayer/issues/3439,
// https://github.com/google/ExoPlayer/issues/3724. // https://github.com/google/ExoPlayer/issues/3724 and
return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) // https://github.com/google/ExoPlayer/issues/3835.
return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE)) // Nexus 7 (2013)
&& "OMX.qcom.video.decoder.avc".equals(name)) && "OMX.qcom.video.decoder.avc".equals(name))
|| (("tcl_eu".equals(Util.DEVICE) || (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV
|| "SVP-DTV15".equals(Util.DEVICE) || "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015
|| "BRAVIA_ATV2".equals(Util.DEVICE)) || "BRAVIA_ATV2".equals(Util.DEVICE) // Sony Bravia 4K GB
|| Util.DEVICE.startsWith("panell_") // Motorola Moto C Plus
|| "F3311".equals(Util.DEVICE) // Sony Xperia E5
|| "M5c".equals(Util.DEVICE) // Meizu M5C
|| "A7010a48".equals(Util.DEVICE)) // Lenovo K4 Note
&& "OMX.MTK.VIDEO.DECODER.AVC".equals(name)) && "OMX.MTK.VIDEO.DECODER.AVC".equals(name))
|| ("OMX.k3.video.decoder.avc".equals(name) && "ALE-L21".equals(Util.MODEL)); || (("ALE-L21".equals(Util.MODEL) // Huawei P8 Lite
|| "CAM-L21".equals(Util.MODEL)) // Huawei Y6II
&& "OMX.k3.video.decoder.avc".equals(name));
} }
/** /**
......
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.android.exoplayer2.core.test">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
</manifest>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment