Commit 90ee038f by Daniel Thompson Committed by GitHub

Merge branch 'dev-v2' into dev-v2

parents cb46e875 8ffd40ab
Showing with 1019 additions and 658 deletions
......@@ -69,6 +69,7 @@
([#3622](https://github.com/google/ExoPlayer/issues/3622)).
* Use long for media sequence numbers
([#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.
* Audio:
* Support TrueHD passthrough for rechunked samples in Matroska files
......@@ -76,8 +77,12 @@
* Support resampling 24-bit and 32-bit integer to 32-bit float for high
resolution output in `DefaultAudioSink`
([#3635](https://github.com/google/ExoPlayer/pull/3635)).
* Captions: Initial support for PGS subtitles
([#3008](https://github.com/google/ExoPlayer/issues/3008)).
* Captions:
* Initial support for PGS subtitles
([#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
cache after deciding to bypass cache.
* IMA extension:
......@@ -85,17 +90,36 @@
([#3584](https://github.com/google/ExoPlayer/issues/3584)).
* Work around loadAd not being called beore the LOADED AdEvent arrives
([#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
the IMA demo app
([#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.
* Fix ANR issue on Huawei P8 Lite
([#3724](https://github.com/google/ExoPlayer/issues/3724)).
* Fix ANR issue on the Huawei P8 Lite, Huawei Y6II, Moto C+, Meizu M5C,
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
DynamicConcatenatingMediaSource
([#3796](https://github.com/google/ExoPlayer/issues/3796)).
* Open source DownloadService, DownloadManager and related classes
([#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 ###
......
......@@ -34,11 +34,14 @@ public class FlacExtractorTest extends InstrumentationTestCase {
}
public void testSample() throws Exception {
ExtractorAsserts.assertBehavior(new ExtractorFactory() {
@Override
public Extractor create() {
return new FlacExtractor();
}
}, "bear.flac", getInstrumentation());
ExtractorAsserts.assertBehavior(
new ExtractorFactory() {
@Override
public Extractor create() {
return new FlacExtractor();
}
},
"bear.flac",
getInstrumentation().getContext());
}
}
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Herhaal alles"</string>
<string name="exo_media_action_repeat_off_description">"Herhaal niks"</string>
<string name="exo_media_action_repeat_one_description">"Herhaal een"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"ሁሉንም ድገም"</string>
<string name="exo_media_action_repeat_off_description">"ምንም አትድገም"</string>
<string name="exo_media_action_repeat_one_description">"አንዱን ድገም"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"تكرار الكل"</string>
<string name="exo_media_action_repeat_off_description">"عدم التكرار"</string>
<string name="exo_media_action_repeat_one_description">"تكرار مقطع واحد"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi sve"</string>
<string name="exo_media_action_repeat_off_description">"Ne ponavljaj nijednu"</string>
<string name="exo_media_action_repeat_one_description">"Ponovi jednu"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Повтаряне на всички"</string>
<string name="exo_media_action_repeat_off_description">"Без повтаряне"</string>
<string name="exo_media_action_repeat_one_description">"Повтаряне на един елемент"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeteix-ho tot"</string>
<string name="exo_media_action_repeat_off_description">"No en repeteixis cap"</string>
<string name="exo_media_action_repeat_one_description">"Repeteix-ne un"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Opakovat vše"</string>
<string name="exo_media_action_repeat_off_description">"Neopakovat"</string>
<string name="exo_media_action_repeat_one_description">"Opakovat jednu položku"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Gentag alle"</string>
<string name="exo_media_action_repeat_off_description">"Gentag ingen"</string>
<string name="exo_media_action_repeat_one_description">"Gentag en"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Alle wiederholen"</string>
<string name="exo_media_action_repeat_off_description">"Keinen Titel wiederholen"</string>
<string name="exo_media_action_repeat_one_description">"Einen Titel wiederholen"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Επανάληψη όλων"</string>
<string name="exo_media_action_repeat_off_description">"Καμία επανάληψη"</string>
<string name="exo_media_action_repeat_one_description">"Επανάληψη ενός στοιχείου"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string>
<string name="exo_media_action_repeat_off_description">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description">"Repeat one"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string>
<string name="exo_media_action_repeat_off_description">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description">"Repeat one"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repeat all"</string>
<string name="exo_media_action_repeat_off_description">"Repeat none"</string>
<string name="exo_media_action_repeat_one_description">"Repeat one"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir todo"</string>
<string name="exo_media_action_repeat_off_description">"No repetir"</string>
<string name="exo_media_action_repeat_one_description">"Repetir uno"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir todo"</string>
<string name="exo_media_action_repeat_off_description">"No repetir"</string>
<string name="exo_media_action_repeat_one_description">"Repetir uno"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"تکرار همه"</string>
<string name="exo_media_action_repeat_off_description">"تکرار هیچ‌کدام"</string>
<string name="exo_media_action_repeat_one_description">"یک‌بار تکرار"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Toista kaikki"</string>
<string name="exo_media_action_repeat_off_description">"Toista ei mitään"</string>
<string name="exo_media_action_repeat_one_description">"Toista yksi"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Tout lire en boucle"</string>
<string name="exo_media_action_repeat_off_description">"Aucune répétition"</string>
<string name="exo_media_action_repeat_one_description">"Répéter un élément"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Tout lire en boucle"</string>
<string name="exo_media_action_repeat_off_description">"Ne rien lire en boucle"</string>
<string name="exo_media_action_repeat_one_description">"Lire en boucle un élément"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"सभी को दोहराएं"</string>
<string name="exo_media_action_repeat_off_description">"कुछ भी न दोहराएं"</string>
<string name="exo_media_action_repeat_one_description">"एक दोहराएं"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi sve"</string>
<string name="exo_media_action_repeat_off_description">"Bez ponavljanja"</string>
<string name="exo_media_action_repeat_one_description">"Ponovi jedno"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Összes ismétlése"</string>
<string name="exo_media_action_repeat_off_description">"Nincs ismétlés"</string>
<string name="exo_media_action_repeat_one_description">"Egy ismétlése"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ulangi Semua"</string>
<string name="exo_media_action_repeat_off_description">"Jangan Ulangi"</string>
<string name="exo_media_action_repeat_one_description">"Ulangi Satu"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ripeti tutti"</string>
<string name="exo_media_action_repeat_off_description">"Non ripetere nessuno"</string>
<string name="exo_media_action_repeat_one_description">"Ripeti uno"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"חזור על הכל"</string>
<string name="exo_media_action_repeat_off_description">"אל תחזור על כלום"</string>
<string name="exo_media_action_repeat_one_description">"חזור על פריט אחד"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"全曲を繰り返し"</string>
<string name="exo_media_action_repeat_off_description">"繰り返しなし"</string>
<string name="exo_media_action_repeat_one_description">"1曲を繰り返し"</string>
-->
<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">"1 曲をリピート"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"全曲をリピート"</string>
</resources>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"전체 반복"</string>
<string name="exo_media_action_repeat_off_description">"반복 안함"</string>
<string name="exo_media_action_repeat_one_description">"한 항목 반복"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Kartoti viską"</string>
<string name="exo_media_action_repeat_off_description">"Nekartoti nieko"</string>
<string name="exo_media_action_repeat_one_description">"Kartoti vieną"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Atkārtot visu"</string>
<string name="exo_media_action_repeat_off_description">"Neatkārtot nevienu"</string>
<string name="exo_media_action_repeat_one_description">"Atkārtot vienu"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Gjenta alle"</string>
<string name="exo_media_action_repeat_off_description">"Ikke gjenta noen"</string>
<string name="exo_media_action_repeat_one_description">"Gjenta én"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Alles herhalen"</string>
<string name="exo_media_action_repeat_off_description">"Niet herhalen"</string>
<string name="exo_media_action_repeat_one_description">"Eén herhalen"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Powtórz wszystkie"</string>
<string name="exo_media_action_repeat_off_description">"Nie powtarzaj"</string>
<string name="exo_media_action_repeat_one_description">"Powtórz jeden"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir tudo"</string>
<string name="exo_media_action_repeat_off_description">"Não repetir"</string>
<string name="exo_media_action_repeat_one_description">"Repetir um"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetir tudo"</string>
<string name="exo_media_action_repeat_off_description">"Não repetir"</string>
<string name="exo_media_action_repeat_one_description">"Repetir uma"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Repetați toate"</string>
<string name="exo_media_action_repeat_off_description">"Repetați niciuna"</string>
<string name="exo_media_action_repeat_one_description">"Repetați unul"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Повторять все"</string>
<string name="exo_media_action_repeat_off_description">"Не повторять"</string>
<string name="exo_media_action_repeat_one_description">"Повторять один элемент"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Opakovať všetko"</string>
<string name="exo_media_action_repeat_off_description">"Neopakovať"</string>
<string name="exo_media_action_repeat_one_description">"Opakovať jednu položku"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ponovi vse"</string>
<string name="exo_media_action_repeat_off_description">"Ne ponovi"</string>
<string name="exo_media_action_repeat_one_description">"Ponovi eno"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,6 +12,11 @@
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.
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Upprepa alla"</string>
<string name="exo_media_action_repeat_off_description">"Upprepa inga"</string>
<string name="exo_media_action_repeat_one_description">"Upprepa en"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Rudia zote"</string>
<string name="exo_media_action_repeat_off_description">"Usirudie Yoyote"</string>
<string name="exo_media_action_repeat_one_description">"Rudia Moja"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"เล่นซ้ำทั้งหมด"</string>
<string name="exo_media_action_repeat_off_description">"ไม่เล่นซ้ำ"</string>
<string name="exo_media_action_repeat_one_description">"เล่นซ้ำรายการเดียว"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Ulitin Lahat"</string>
<string name="exo_media_action_repeat_off_description">"Walang Uulitin"</string>
<string name="exo_media_action_repeat_one_description">"Ulitin ang Isa"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Tümünü Tekrarla"</string>
<string name="exo_media_action_repeat_off_description">"Hiçbirini Tekrarlama"</string>
<string name="exo_media_action_repeat_one_description">"Birini Tekrarla"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Повторити все"</string>
<string name="exo_media_action_repeat_off_description">"Не повторювати"</string>
<string name="exo_media_action_repeat_one_description">"Повторити один елемент"</string>
-->
<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">"Повторити 1"</string>
<string name="exo_media_action_repeat_all_description" msgid="92377890871273452">"Повторити всі"</string>
</resources>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Lặp lại tất cả"</string>
<string name="exo_media_action_repeat_off_description">"Không lặp lại"</string>
<string name="exo_media_action_repeat_one_description">"Lặp lại một mục"</string>
-->
<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">"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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"重复播放全部"</string>
<string name="exo_media_action_repeat_off_description">"不重复播放"</string>
<string name="exo_media_action_repeat_one_description">"重复播放单个视频"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"重複播放所有媒體項目"</string>
<string name="exo_media_action_repeat_off_description">"不重複播放任何媒體項目"</string>
<string name="exo_media_action_repeat_one_description">"重複播放一個媒體項目"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"重複播放所有媒體項目"</string>
<string name="exo_media_action_repeat_off_description">"不重複播放"</string>
<string name="exo_media_action_repeat_one_description">"重複播放單一媒體項目"</string>
-->
<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>
<?xml version="1.0"?>
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
......@@ -13,9 +12,11 @@
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.
-->
<resources>
<string name="exo_media_action_repeat_all_description">"Phinda konke"</string>
<string name="exo_media_action_repeat_off_description">"Ungaphindi lutho"</string>
<string name="exo_media_action_repeat_one_description">"Phida okukodwa"</string>
-->
<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">"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>
......@@ -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.DownloadState;
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.upstream.DummyDataSource;
import com.google.android.exoplayer2.upstream.cache.Cache;
......@@ -53,12 +54,13 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private DownloadManager downloadManager;
private File actionFile;
private TestDownloadListener testDownloadListener;
private DummyMainThread dummyMainThread;
@Override
public void setUp() throws Exception {
super.setUp();
MockitoUtil.setUpMockito(this);
dummyMainThread = new DummyMainThread();
actionFile = Util.createTempFile(getInstrumentation().getContext(), "ExoPlayerTest");
testDownloadListener = new TestDownloadListener();
setUpDownloadManager(100);
......@@ -68,6 +70,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
public void tearDown() throws Exception {
releaseDownloadManager();
actionFile.delete();
dummyMainThread.release();
super.tearDown();
}
......@@ -76,7 +79,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
releaseDownloadManager();
}
try {
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -98,7 +101,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
private void releaseDownloadManager() throws Exception {
try {
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -345,7 +348,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
remove2Action.post().assertStarted();
download2Action.post().assertDoesNotStart();
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -368,7 +371,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// New download actions can be added but they don't start.
download3Action.post().assertDoesNotStart();
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -393,7 +396,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download3Action doesn't start as DM was configured to run two downloads in parallel.
download3Action.post().assertDoesNotStart();
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -404,7 +407,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
// download1Action doesn't stop yet as it ignores interrupts.
download2Action.assertStopped();
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......@@ -462,6 +465,10 @@ public class DownloadManagerTest extends InstrumentationTestCase {
return new FakeDownloadAction(mediaId, true);
}
private void runOnMainThread(final Runnable r) throws Throwable {
dummyMainThread.runOnMainThread(r);
}
private static final class TestDownloadListener implements DownloadListener {
private ConditionVariable downloadFinishedCondition;
......@@ -544,7 +551,7 @@ public class DownloadManagerTest extends InstrumentationTestCase {
}
private FakeDownloadAction post() throws Throwable {
runTestOnUiThread(
runOnMainThread(
new Runnable() {
@Override
public void run() {
......
......@@ -41,7 +41,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
this(
timeline,
/* manifest= */ null,
new MediaPeriodId(0),
new MediaPeriodId(/* periodIndex= */ 0),
startPositionUs,
/* contentPositionUs =*/ C.TIME_UNSET,
Player.STATE_IDLE,
......
......@@ -413,53 +413,30 @@ public abstract class Timeline {
* @return Whether the ad group at index {@code adGroupIndex} has been played.
*/
public boolean hasPlayedAdGroup(int adGroupIndex) {
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
return adGroup.getFirstAdIndexToPlay() == adGroup.count;
return !adPlaybackState.adGroups[adGroupIndex].hasUnplayedAds();
}
/**
* 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
* played, or if there is no such ad group.
* 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) {
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs;
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;
return adPlaybackState.getAdGroupIndexForPositionUs(positionUs);
}
/**
* Returns the index of the next unplayed ad group after {@code positionUs}. Returns
* {@link C#INDEX_UNSET} if there is no such ad group.
* 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) {
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs;
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;
return adPlaybackState.getAdGroupIndexAfterPositionUs(positionUs);
}
/**
......
......@@ -20,12 +20,22 @@ import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Utility methods for parsing DTS frames.
*/
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.
*/
......@@ -46,6 +56,20 @@ public final class DtsUtil {
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
* subsections 5.3/5.4.
*
......@@ -57,8 +81,8 @@ public final class DtsUtil {
*/
public static Format parseDtsFormat(byte[] frame, String trackId, String language,
DrmInitData drmInitData) {
ParsableBitArray frameBits = new ParsableBitArray(frame);
frameBits.skipBits(4 * 8 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
ParsableBitArray frameBits = getNormalizedFrameHeader(frame);
frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
int amode = frameBits.readBits(6);
int channelCount = CHANNELS_BY_AMODE[amode];
int sfreq = frameBits.readBits(4);
......@@ -79,8 +103,21 @@ public final class DtsUtil {
* @return The number of audio samples represented by the frame.
*/
public static int parseDtsAudioSampleCount(byte[] data) {
// See ETSI TS 102 114 subsection 5.4.1.
int nblks = ((data[4] & 0x01) << 6) | ((data[5] & 0xFC) >> 2);
int nblks;
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;
}
......@@ -94,8 +131,21 @@ public final class DtsUtil {
public static int parseDtsAudioSampleCount(ByteBuffer buffer) {
// See ETSI TS 102 114 subsection 5.4.1.
int position = buffer.position();
int nblks = ((buffer.get(position + 4) & 0x01) << 6)
| ((buffer.get(position + 5) & 0xFC) >> 2);
int nblks;
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;
}
......@@ -106,9 +156,59 @@ public final class DtsUtil {
* @return The frame's size in bytes.
*/
public static int getDtsFrameSize(byte[] data) {
return (((data[5] & 0x02) << 12)
| ((data[6] & 0xFF) << 4)
| ((data[7] & 0xF0) >> 4)) + 1;
int fsize;
boolean uses14BitPerWord = false;
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() {}
......
......@@ -32,9 +32,7 @@ public final class DtsReader implements ElementaryStreamReader {
private static final int STATE_READING_HEADER = 1;
private static final int STATE_READING_SAMPLE = 2;
private static final int HEADER_SIZE = 15;
private static final int SYNC_VALUE = 0x7FFE8001;
private static final int SYNC_VALUE_SIZE = 4;
private static final int HEADER_SIZE = 18;
private final ParsableByteArray headerScratchBytes;
private final String language;
......@@ -63,10 +61,6 @@ public final class DtsReader implements ElementaryStreamReader {
*/
public DtsReader(String language) {
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;
this.language = language;
}
......@@ -96,7 +90,6 @@ public final class DtsReader implements ElementaryStreamReader {
switch (state) {
case STATE_FINDING_SYNC:
if (skipToNextSync(data)) {
bytesRead = SYNC_VALUE_SIZE;
state = STATE_READING_HEADER;
}
break;
......@@ -154,7 +147,12 @@ public final class DtsReader implements ElementaryStreamReader {
while (pesBuffer.bytesLeft() > 0) {
syncBytes <<= 8;
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;
return true;
}
......
......@@ -405,14 +405,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset);
String value;
int valueStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (valueStartIndex < data.length) {
int valueEndIndex = indexOfEos(data, valueStartIndex, encoding);
value = new String(data, valueStartIndex, valueEndIndex - valueStartIndex, charset);
} else {
value = "";
}
int valueEndIndex = indexOfEos(data, valueStartIndex, encoding);
String value = decodeStringIfValid(data, valueStartIndex, valueEndIndex, charset);
return new TextInformationFrame("TXXX", description, value);
}
......@@ -452,14 +447,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset);
String url;
int urlStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (urlStartIndex < data.length) {
int urlEndIndex = indexOfZeroByte(data, urlStartIndex);
url = new String(data, urlStartIndex, urlEndIndex - urlStartIndex, "ISO-8859-1");
} else {
url = "";
}
int urlEndIndex = indexOfZeroByte(data, urlStartIndex);
String url = decodeStringIfValid(data, urlStartIndex, urlEndIndex, "ISO-8859-1");
return new UrlLinkFrame("WXXX", description, url);
}
......@@ -502,13 +492,12 @@ public final class Id3Decoder implements MetadataDecoder {
int filenameStartIndex = mimeTypeEndIndex + 1;
int filenameEndIndex = indexOfEos(data, filenameStartIndex, encoding);
String filename = new String(data, filenameStartIndex, filenameEndIndex - filenameStartIndex,
charset);
String filename = decodeStringIfValid(data, filenameStartIndex, filenameEndIndex, charset);
int descriptionStartIndex = filenameEndIndex + delimiterLength(encoding);
int descriptionEndIndex = indexOfEos(data, descriptionStartIndex, encoding);
String description = new String(data, descriptionStartIndex,
descriptionEndIndex - descriptionStartIndex, charset);
String description =
decodeStringIfValid(data, descriptionStartIndex, descriptionEndIndex, charset);
int objectDataStartIndex = descriptionEndIndex + delimiterLength(encoding);
byte[] objectData = copyOfRangeIfValid(data, objectDataStartIndex, data.length);
......@@ -573,14 +562,9 @@ public final class Id3Decoder implements MetadataDecoder {
int descriptionEndIndex = indexOfEos(data, 0, encoding);
String description = new String(data, 0, descriptionEndIndex, charset);
String text;
int textStartIndex = descriptionEndIndex + delimiterLength(encoding);
if (textStartIndex < data.length) {
int textEndIndex = indexOfEos(data, textStartIndex, encoding);
text = new String(data, textStartIndex, textEndIndex - textStartIndex, charset);
} else {
text = "";
}
int textEndIndex = indexOfEos(data, textStartIndex, encoding);
String text = decodeStringIfValid(data, textStartIndex, textEndIndex, charset);
return new CommentFrame(language, description, text);
}
......@@ -760,6 +744,25 @@ public final class Id3Decoder implements MetadataDecoder {
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 final int majorVersion;
......
......@@ -115,7 +115,12 @@ public final class DownloadManager {
tasks = 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.start();
......
......@@ -40,16 +40,11 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
public DownloadAction readFromStream(int version, DataInputStream input) throws IOException {
Uri manifestUri = Uri.parse(input.readUTF());
String data = input.readUTF();
boolean removeAction = input.readBoolean();
int keyCount = input.readInt();
boolean removeAction = keyCount == -1;
K[] keys;
if (removeAction) {
keys = null;
} else {
keys = createKeyArray(keyCount);
for (int i = 0; i < keyCount; i++) {
keys[i] = readKey(input);
}
K[] keys = createKeyArray(keyCount);
for (int i = 0; i < keyCount; i++) {
keys[i] = readKey(input);
}
return createDownloadAction(manifestUri, removeAction, data, keys);
}
......@@ -74,12 +69,15 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
* @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 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
* downloaded. If {@code removeAction} is true, this is ignored.
* @param keys Keys of representations to be downloaded. If empty, all representations are
* downloaded. If {@code removeAction} is true, {@code keys} should be an empty array.
*/
protected SegmentDownloadAction(Uri manifestUri, boolean removeAction, String data, K[] keys) {
super(data);
Assertions.checkArgument(!removeAction || keys == null || keys.length == 0);
Assertions.checkNotNull(keys);
if (removeAction) {
Assertions.checkArgument(keys.length == 0);
}
this.manifestUri = manifestUri;
this.keys = keys;
this.removeAction = removeAction;
......@@ -94,13 +92,10 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
public final void writeToStream(DataOutputStream output) throws IOException {
output.writeUTF(manifestUri.toString());
output.writeUTF(getData());
if (isRemoveAction()) {
output.writeInt(-1);
} else {
output.writeInt(keys.length);
for (K key : keys) {
writeKey(output, key);
}
output.writeBoolean(removeAction);
output.writeInt(keys.length);
for (K key : keys) {
writeKey(output, key);
}
}
......@@ -124,11 +119,9 @@ public abstract class SegmentDownloadAction<K> extends DownloadAction {
}
SegmentDownloadAction<?> that = (SegmentDownloadAction<?>) o;
return manifestUri.equals(that.manifestUri)
&& (keys == null || keys.length == 0
? (that.keys == null || that.keys.length == 0)
: (that.keys != null
&& that.keys.length == keys.length
&& Arrays.asList(keys).containsAll(Arrays.asList(that.keys))));
&& removeAction == that.removeAction
&& keys.length == that.keys.length
&& Arrays.asList(keys).containsAll(Arrays.asList(that.keys));
}
@Override
......
......@@ -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
* 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) {
this.keys = (keys != null && keys.length > 0) ? keys.clone() : null;
this.keys = keys.length > 0 ? keys.clone() : null;
resetCounters();
}
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.trackselection.TrackSelection;
......@@ -29,6 +30,15 @@ import java.io.IOException;
*/
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;
private final MediaPeriodId id;
......@@ -37,7 +47,16 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
private MediaPeriod mediaPeriod;
private Callback callback;
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) {
this.id = id;
this.allocator = allocator;
......@@ -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
* prepares it if {@link #prepare(Callback, long)} has been called. Call {@link #releasePeriod()}
* to release the period.
......@@ -76,10 +106,20 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override
public void maybeThrowPrepareError() throws IOException {
if (mediaPeriod != null) {
mediaPeriod.maybeThrowPrepareError();
} else {
mediaSource.maybeThrowSourceInfoRefreshError();
try {
if (mediaPeriod != null) {
mediaPeriod.maybeThrowPrepareError();
} else {
mediaSource.maybeThrowSourceInfoRefreshError();
}
} catch (final IOException e) {
if (listener == null) {
throw e;
}
if (!notifiedPrepareError) {
notifiedPrepareError = true;
listener.onPrepareError(e);
}
}
}
......
......@@ -780,7 +780,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
@Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
timeline.getPeriod(periodIndex, period, setIds);
if (period.uid == replacedId) {
if (Util.areEqual(period.uid, replacedId)) {
period.uid = DUMMY_ID;
}
return period;
......@@ -788,7 +788,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
@Override
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;
}
public void release() {
boolean releasedSynchronously = loader.release(this);
if (prepared && !releasedSynchronously) {
if (prepared) {
// 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.
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.discardToEnd();
}
}
loader.release(this);
handler.removeCallbacksAndMessages(null);
released = true;
}
@Override
public void onLoaderReleased() {
extractorHolder.release();
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.reset();
}
extractorHolder.release();
}
@Override
......
......@@ -64,12 +64,6 @@ public interface MediaSource {
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.
*/
public final int periodIndex;
......@@ -87,12 +81,31 @@ public interface MediaSource {
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.
*
* @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) {
this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET);
public MediaPeriodId(int periodIndex, long windowSequenceNumber) {
this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber);
}
/**
......@@ -102,19 +115,24 @@ public interface MediaSource {
* @param periodIndex The index of the timeline period that contains the ad group.
* @param adGroupIndex The index of 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.adGroupIndex = adGroupIndex;
this.adIndexInAdGroup = adIndexInAdGroup;
this.windowSequenceNumber = windowSequenceNumber;
}
/**
* Returns a copy of this period identifier but with {@code newPeriodIndex} as its period index.
*/
public MediaPeriodId copyWithPeriodIndex(int newPeriodIndex) {
return periodIndex == newPeriodIndex ? this
: new MediaPeriodId(newPeriodIndex, adGroupIndex, adIndexInAdGroup);
return periodIndex == newPeriodIndex
? this
: new MediaPeriodId(newPeriodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
}
/**
......@@ -134,8 +152,10 @@ public interface MediaSource {
}
MediaPeriodId periodId = (MediaPeriodId) obj;
return periodIndex == periodId.periodIndex && adGroupIndex == periodId.adGroupIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup;
return periodIndex == periodId.periodIndex
&& adGroupIndex == periodId.adGroupIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup
&& windowSequenceNumber == periodId.windowSequenceNumber;
}
@Override
......@@ -144,6 +164,7 @@ public interface MediaSource {
result = 31 * result + periodIndex;
result = 31 * result + adGroupIndex;
result = 31 * result + adIndexInAdGroup;
result = 31 * result + (int) windowSequenceNumber;
return result;
}
......
......@@ -91,6 +91,11 @@ public final class AdPlaybackState {
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
* if this instance's ad count has not yet been specified.
......@@ -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}.
* The ad count must be greater than zero.
*/
......
......@@ -103,4 +103,13 @@ public interface AdsLoader {
*/
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> {
}
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
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);
if (mediaPeriods == null) {
deferredMediaPeriod.createPeriod();
......@@ -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
private final BaseMediaChunkOutput mediaChunkOutput;
private Format primaryDownstreamTrackFormat;
private ReleaseCallback<T> releaseCallback;
private @Nullable ReleaseCallback<T> releaseCallback;
private long pendingResetPositionUs;
private long lastSeekPositionUs;
/* package */ long decodeOnlyUntilPositionUs;
......@@ -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
* {@link #release()} can be used to release this stream.
*
* @param callback A callback to be called when the release ends. Will be called synchronously
* from this method if no load is in progress, or asynchronously once the load has been
* canceled otherwise.
* @param callback An optional callback to be called on the loading thread once the loader has
* been released.
*/
public void release(@Nullable ReleaseCallback<T> callback) {
this.releaseCallback = callback;
boolean releasedSynchronously = loader.release(this);
if (!releasedSynchronously) {
// Discard as much as we can synchronously.
primarySampleQueue.discardToEnd();
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.discardToEnd();
}
// Discard as much as we can synchronously.
primarySampleQueue.discardToEnd();
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.discardToEnd();
}
loader.release(this);
}
@Override
......
......@@ -15,15 +15,11 @@
*/
package com.google.android.exoplayer2.text;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
/**
* A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}.
*/
public final class SubtitleInputBuffer extends DecoderInputBuffer
implements Comparable<SubtitleInputBuffer> {
/** A {@link DecoderInputBuffer} for a {@link SubtitleDecoder}. */
public class SubtitleInputBuffer extends DecoderInputBuffer {
/**
* 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
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 @@
*/
package com.google.android.exoplayer2.text.cea;
import android.support.annotation.NonNull;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.Subtitle;
......@@ -34,21 +35,22 @@ import java.util.PriorityQueue;
private static final int NUM_INPUT_BUFFERS = 10;
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 PriorityQueue<SubtitleInputBuffer> queuedInputBuffers;
private final PriorityQueue<CeaInputBuffer> queuedInputBuffers;
private SubtitleInputBuffer dequeuedInputBuffer;
private CeaInputBuffer dequeuedInputBuffer;
private long playbackPositionUs;
private long queuedInputBufferCount;
public CeaDecoder() {
availableInputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
availableInputBuffers.add(new SubtitleInputBuffer());
availableInputBuffers.add(new CeaInputBuffer());
}
availableOutputBuffers = new LinkedList<>();
for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) {
availableOutputBuffers.add(new CeaOutputBuffer(this));
availableOutputBuffers.add(new CeaOutputBuffer());
}
queuedInputBuffers = new PriorityQueue<>();
}
......@@ -77,9 +79,10 @@ import java.util.PriorityQueue;
if (inputBuffer.isDecodeOnly()) {
// We can drop this buffer early (i.e. before it would be decoded) as the CEA formats allow
// for decoding to begin mid-stream.
releaseInputBuffer(inputBuffer);
releaseInputBuffer(dequeuedInputBuffer);
} else {
queuedInputBuffers.add(inputBuffer);
dequeuedInputBuffer.queuedInputBufferCount = queuedInputBufferCount++;
queuedInputBuffers.add(dequeuedInputBuffer);
}
dequeuedInputBuffer = null;
}
......@@ -94,7 +97,7 @@ import java.util.PriorityQueue;
// be deferred until they would be applicable
while (!queuedInputBuffers.isEmpty()
&& 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
// return immediately with an output buffer propagating that
......@@ -126,7 +129,7 @@ import java.util.PriorityQueue;
return null;
}
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) {
private void releaseInputBuffer(CeaInputBuffer inputBuffer) {
inputBuffer.clear();
availableInputBuffers.add(inputBuffer);
}
......@@ -138,6 +141,7 @@ import java.util.PriorityQueue;
@Override
public void flush() {
queuedInputBufferCount = 0;
playbackPositionUs = 0;
while (!queuedInputBuffers.isEmpty()) {
releaseInputBuffer(queuedInputBuffers.poll());
......@@ -169,4 +173,32 @@ import java.util.PriorityQueue;
*/
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;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
/**
* Utility methods for handling CEA-608/708 messages.
*/
/** Utility methods for handling CEA-608/708 messages. Defined in A/53 Part 4:2009. */
public final class CeaUtil {
private static final String TAG = "CeaUtil";
private static final int PAYLOAD_TYPE_CC = 4;
private static final int COUNTRY_CODE = 0xB5;
private static final int PROVIDER_CODE = 0x31;
private static final int USER_ID = 0x47413934; // "GA94"
private static final int PROVIDER_CODE_ATSC = 0x31;
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;
/**
......@@ -46,33 +47,49 @@ public final class CeaUtil {
while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) {
int payloadType = readNon255TerminatedValue(seiBuffer);
int payloadSize = readNon255TerminatedValue(seiBuffer);
int nextPayloadPosition = seiBuffer.getPosition() + payloadSize;
// Process the payload.
if (payloadSize == -1 || payloadSize > seiBuffer.bytesLeft()) {
// This might occur if we're trying to read an encrypted SEI NAL unit.
Log.w(TAG, "Skipping remainder of malformed SEI NAL unit.");
seiBuffer.setPosition(seiBuffer.limit());
} else if (isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) {
// Ignore country_code (1) + provider_code (2) + user_identifier (4)
// + user_data_type_code (1).
seiBuffer.skipBytes(8);
// Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1).
int ccCount = seiBuffer.readUnsignedByte() & 0x1F;
// Ignore em_data (1)
seiBuffer.skipBytes(1);
// Each data packet consists of 24 bits: marker bits (5) + cc_valid (1) + cc_type (2)
// + cc_data_1 (8) + cc_data_2 (8).
int sampleLength = ccCount * 3;
int sampleStartPosition = seiBuffer.getPosition();
for (TrackOutput output : outputs) {
seiBuffer.setPosition(sampleStartPosition);
output.sampleData(seiBuffer, sampleLength);
output.sampleMetadata(presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null);
nextPayloadPosition = seiBuffer.limit();
} else if (payloadType == PAYLOAD_TYPE_CC && payloadSize >= 8) {
int countryCode = seiBuffer.readUnsignedByte();
int providerCode = seiBuffer.readUnsignedShort();
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).
int ccCount = seiBuffer.readUnsignedByte() & 0x1F;
// Ignore em_data (1)
seiBuffer.skipBytes(1);
// Each data packet consists of 24 bits: marker bits (5) + cc_valid (1) + cc_type (2)
// + cc_data_1 (8) + cc_data_2 (8).
int sampleLength = ccCount * 3;
int sampleStartPosition = seiBuffer.getPosition();
for (TrackOutput output : outputs) {
seiBuffer.setPosition(sampleStartPosition);
output.sampleData(seiBuffer, sampleLength);
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 {
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() {}
}
......@@ -20,6 +20,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.TraceUtil;
......@@ -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
* required.
*
* @param callback A callback to be called when the release ends. Will be called synchronously
* from this method if no load is in progress, or asynchronously once the load has been
* 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.
* @param callback An optional callback to be called on the loading thread once the loader has
* been released.
*/
public boolean release(ReleaseCallback callback) {
boolean callbackInvoked = false;
public void release(@Nullable ReleaseCallback callback) {
if (currentTask != null) {
currentTask.cancel(true);
if (callback != null) {
downloadExecutorService.execute(new ReleaseTask(callback));
}
} else if (callback != null) {
callback.onLoaderReleased();
callbackInvoked = true;
}
if (callback != null) {
downloadExecutorService.execute(new ReleaseTask(callback));
}
downloadExecutorService.shutdown();
return callbackInvoked;
}
// LoaderErrorThrower implementation.
......@@ -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;
......@@ -429,13 +422,6 @@ public final class Loader implements LoaderErrorThrower {
@Override
public void run() {
if (getLooper().getThread().isAlive()) {
sendEmptyMessage(0);
}
}
@Override
public void handleMessage(Message msg) {
callback.onLoaderReleased();
}
......
......@@ -263,6 +263,40 @@ public final class ParsableBitArray {
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() {
// It is fine for position to be at the end of the array, but no further.
Assertions.checkState(byteOffset >= 0
......
......@@ -1334,7 +1334,11 @@ public final class Util {
if ("Sony".equals(Util.MANUFACTURER) && Util.MODEL.startsWith("BRAVIA")
&& context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
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.
String sysDisplaySize = null;
try {
......
......@@ -1085,15 +1085,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private static boolean codecNeedsSetOutputSurfaceWorkaround(String name) {
// Work around https://github.com/google/ExoPlayer/issues/3236,
// https://github.com/google/ExoPlayer/issues/3355,
// https://github.com/google/ExoPlayer/issues/3439 and
// https://github.com/google/ExoPlayer/issues/3724.
return (("deb".equals(Util.DEVICE) || "flo".equals(Util.DEVICE))
// https://github.com/google/ExoPlayer/issues/3439,
// https://github.com/google/ExoPlayer/issues/3724 and
// 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))
|| (("tcl_eu".equals(Util.DEVICE)
|| "SVP-DTV15".equals(Util.DEVICE)
|| "BRAVIA_ATV2".equals(Util.DEVICE))
|| (("tcl_eu".equals(Util.DEVICE) // TCL Percee TV
|| "SVP-DTV15".equals(Util.DEVICE) // Sony Bravia 4K 2015
|| "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.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