From eb8c686243b2650aa3b05f91637f320a4d871382 Mon Sep 17 00:00:00 2001 From: hoangtc Date: Mon, 25 Jun 2018 09:51:47 -0700 Subject: [PATCH] Fix a bug with MetadataRetriever getMedatadata query. When getMedatadata from an ExtractorMediaPeriod, due to the MediaPeriod refreshing its Timeline twice, the MetadataRetriever may wrongly reset periodIndex of the current trackGroup to C.TIME_UNSET, which makes it unable to handle subsequent metadata queries. This CL changes the MetadataRetriever to keep track of periodUid of the current trackGroup instead of period index. PeriodUid is not changed on timeline refreshing, so is not prone the existing problem. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=201969350 --- library/core/build.gradle | 2 + .../src/test/assets/mp4/testvid_1022ms.mp4 | Bin 0 -> 45846 bytes testutils/build.gradle | 2 + .../MetadataRetrieverTestRunner.java | 493 ++++++++++++++++++ 4 files changed, 497 insertions(+) create mode 100644 library/core/src/test/assets/mp4/testvid_1022ms.mp4 create mode 100644 testutils/src/main/java/com/google/android/exoplayer2/MetadataRetrieverTestRunner.java diff --git a/library/core/build.gradle b/library/core/build.gradle index 00f5cd27eb..d45e405b41 100644 --- a/library/core/build.gradle +++ b/library/core/build.gradle @@ -72,6 +72,8 @@ dependencies { testImplementation 'org.robolectric:robolectric:' + robolectricVersion testImplementation 'com.google.auto.value:auto-value-annotations:' + autoValueVersion testAnnotationProcessor 'com.google.auto.value:auto-value:' + autoValueVersion + androidTestImplementation 'com.google.auto.value:auto-value-annotations:' + autoValueVersion + androidTestAnnotationProcessor 'com.google.auto.value:auto-value:' + autoValueVersion } ext { diff --git a/library/core/src/test/assets/mp4/testvid_1022ms.mp4 b/library/core/src/test/assets/mp4/testvid_1022ms.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..bbd2729c4dbe1f0b414fa0d0743ece531b8ab436 GIT binary patch literal 45846 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 literal 0 HcmV?d00001 diff --git a/testutils/build.gradle b/testutils/build.gradle index e4faea1ee8..2ef377ba5d 100644 --- a/testutils/build.gradle +++ b/testutils/build.gradle @@ -41,5 +41,7 @@ dependencies { api 'com.google.truth:truth:' + truthVersion implementation 'com.android.support:support-annotations:' + supportLibraryVersion implementation project(modulePrefix + 'library-core') + implementation 'com.google.auto.value:auto-value-annotations:' + autoValueVersion + annotationProcessor 'com.google.auto.value:auto-value:' + autoValueVersion testImplementation project(modulePrefix + 'testutils-robolectric') } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/MetadataRetrieverTestRunner.java b/testutils/src/main/java/com/google/android/exoplayer2/MetadataRetrieverTestRunner.java new file mode 100644 index 0000000000..152fcc6ee7 --- /dev/null +++ b/testutils/src/main/java/com/google/android/exoplayer2/MetadataRetrieverTestRunner.java @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.exoplayer2; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.support.annotation.Nullable; +import com.google.android.exoplayer2.Player.TimelineChangeReason; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.testutil.FakeRenderer; +import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.ClosedSource; +import com.google.android.exoplayer2.util.ConditionVariable; +import com.google.auto.value.AutoValue; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Helper class to run {@link MetadataRetriever} tests. + * + *

The tests will be run on a separate thread with a looper. + */ +@ClosedSource(reason = "Not ready yet") +/* package */ final class MetadataRetrieverTestRunner + implements MetadataRetriever.MediaSourceCallback, MetadataRetriever.MetadataCallback { + + /** + * Represents callback data for {@link + * MetadataRetriever.MediaSourceCallback#onTimelineUpdated(Timeline, Object, int)}. + */ + @AutoValue + public abstract static class PrepareCallbackData { + abstract @Nullable Timeline timeline(); + + abstract @Nullable Object manifest(); + + abstract @TimelineChangeReason int reason(); + + /** Creates a new {@link PrepareCallbackData}. */ + public static PrepareCallbackData prepareCallbackData( + @Nullable Timeline expectedTimeline, + @Nullable Object expectedManifest, + @TimelineChangeReason int expectedReason) { + return new AutoValue_MetadataRetrieverTestRunner_PrepareCallbackData( + expectedTimeline, expectedManifest, expectedReason); + } + } + + /** + * Represents callback data for {@link + * MetadataRetriever.MetadataCallback#onMetadataAvailable(TrackGroupArray, Timeline, int, int)}. + */ + @AutoValue + public abstract static class MetadataCallbackData { + abstract @Nullable TrackGroupArray trackGroupArray(); + + abstract @Nullable Timeline timeline(); + + abstract int windowIndex(); + + abstract int periodIndex(); + + /** Creates a new {@link MetadataCallbackData}. */ + public static MetadataCallbackData metadataCallbackData( + @Nullable TrackGroupArray expectedTrackGroupArray, + @Nullable Timeline expectedTimeline, + int expectedWindowIndex, + int expectedPeriodIndex) { + return new AutoValue_MetadataRetrieverTestRunner_MetadataCallbackData( + expectedTrackGroupArray, expectedTimeline, expectedWindowIndex, expectedPeriodIndex); + } + } + + /** Factory to create the {@link MetadataRetriever} under test. */ + /* package */ interface TestMetadataRetrieverFactory { + MetadataRetriever createMetadataRetriever( + Clock clock, Renderer[] renderers, Looper eventLooper); + } + + private static final TestMetadataRetrieverFactory DEFAULT_TEST_METADATA_RETRIEVER_FACTORY = + new TestMetadataRetrieverFactory() { + @Override + public MetadataRetriever createMetadataRetriever( + Clock clock, Renderer[] renderers, Looper eventLooper) { + return new MetadataRetrieverImpl(clock, renderers, eventLooper); + } + }; + + private static final Renderer[] FAKE_RENDERERS = new Renderer[] {new FakeRenderer()}; + private static final long DEFAULT_TIMEOUT_MS = 50_000; + + private final Handler handler; + private final HandlerThread testThread; + + private final List preparedCallbackData; + private final List metadataCallbackData; + private final List failedQueryExceptions; + + private MetadataRetriever metadataRetriever; + + /** + * Creates a new test runner, starts its test runner thread and creates a new {@link + * MetadataRetriever} under test using the default factory. + * + * @return The newly created test runner. + * @throws InterruptedException If the test thread gets interrupted while waiting for the {@link + * MetadataRetriever} under test being created. + */ + public static MetadataRetrieverTestRunner newTestRunner() throws InterruptedException { + return newTestRunner(DEFAULT_TEST_METADATA_RETRIEVER_FACTORY); + } + + /** + * Creates a new test runner, starts its test runner thread and creates a new {@link + * MetadataRetriever} under test using the given {@link TestMetadataRetrieverFactory}. + * + * @param metadataRetrieverFactory The factory used to create the {@link MetadataRetriever} under + * test. + * @return The newly created test runner. + * @throws InterruptedException If the test thread gets interrupted while waiting for the {@link + * MetadataRetriever} under test being created. + */ + /* package */ static MetadataRetrieverTestRunner newTestRunner( + TestMetadataRetrieverFactory metadataRetrieverFactory) throws InterruptedException { + MetadataRetrieverTestRunner metadataRetrieverTestRunner = new MetadataRetrieverTestRunner(); + metadataRetrieverTestRunner.startTestRunnerThreadBlocking(metadataRetrieverFactory); + return metadataRetrieverTestRunner; + } + + private MetadataRetrieverTestRunner() { + testThread = new HandlerThread("Test thread"); + testThread.start(); + handler = new Handler(testThread.getLooper()); + preparedCallbackData = new ArrayList<>(); + metadataCallbackData = new ArrayList<>(); + failedQueryExceptions = new ArrayList<>(); + } + + /** Returns the {@link MetadataRetriever} under-test. */ + public MetadataRetriever getMetadataRetriever() { + return metadataRetriever; + } + + /** + * Instructs the {@link MetadataRetriever} under test to prepare the given media source on the + * test runner thread, and return immediately. + * + * @param mediaSource The {@link MediaSource} to be prepared. + */ + public void prepareAsync(MediaSource mediaSource) { + handler.post( + new Runnable() { + @Override + public void run() { + metadataRetriever.prepare(mediaSource, MetadataRetrieverTestRunner.this); + } + }); + } + + /** + * Instructs the {@link MetadataRetriever} under test to prepare the given media source on the + * test runner thread, and wait until one of the callbacks from {@link + * MetadataRetriever.MediaSourceCallback} is called, or until the {@link #DEFAULT_TIMEOUT_MS} + * passed. + * + * @param mediaSource The {@link MediaSource} to be prepared. + * @throws TimeoutException If the test runner did not finish within the specified timeout. + * @throws InterruptedException If the test thread gets interrupted while waiting. + */ + public void prepareBlocking(MediaSource mediaSource) + throws InterruptedException, TimeoutException { + ConditionVariable callbackReceived = new ConditionVariable(); + handler.post( + new Runnable() { + @Override + public void run() { + metadataRetriever.prepare( + mediaSource, new UnblockingMediaSourceCallback(callbackReceived)); + } + }); + if (!callbackReceived.block(DEFAULT_TIMEOUT_MS)) { + throw new TimeoutException( + "Test metadata retriever timed out waiting for preparing media source."); + } + } + + /** + * Instructs the {@link MetadataRetriever} under test to call {@link + * MetadataRetriever#getMetadata(MetadataRetriever.MetadataCallback)} and returns immediately. + */ + public void getMetadataAsync() { + handler.post( + new Runnable() { + @Override + public void run() { + metadataRetriever.getMetadata(MetadataRetrieverTestRunner.this); + } + }); + } + + /** + * Instructs the {@link MetadataRetriever} under test to call {@link + * MetadataRetriever#getMetadata(long, MetadataRetriever.MetadataCallback)} and returns + * immediately. + */ + public void getMetadataAsync(long positionMs) { + handler.post( + new Runnable() { + @Override + public void run() { + metadataRetriever.getMetadata(positionMs, MetadataRetrieverTestRunner.this); + } + }); + } + + /** + * Instructs the {@link MetadataRetriever} under test to call {@link + * MetadataRetriever#getMetadata(MetadataRetriever.MetadataCallback)} on test runner thread, and + * wait until one of the callbacks from {@link MetadataRetriever.MetadataCallback} is called, or + * until the {@link #DEFAULT_TIMEOUT_MS} passed. + * + * @throws TimeoutException If the test runner did not finish within the specified timeout. + * @throws InterruptedException If the test thread gets interrupted while waiting. + */ + public void getMetadataBlocking() throws InterruptedException, TimeoutException { + getMetadataBlockingImpl(/* callWithParam= */ false, /* positionMs= */ 0); + } + + /** + * Instructs the {@link MetadataRetriever} under test to call {@link + * MetadataRetriever#getMetadata(long, MetadataRetriever.MetadataCallback)} on test runner thread, + * and wait until one of the callbacks from {@link MetadataRetriever.MetadataCallback}\ is called, + * or until the {@link #DEFAULT_TIMEOUT_MS} passed. + * + * @throws TimeoutException If the test runner did not finish within the specified timeout. + * @throws InterruptedException If the test thread gets interrupted while waiting. + */ + public void getMetadataBlocking(long positionMs) throws InterruptedException, TimeoutException { + getMetadataBlockingImpl(/* callWithParam= */ true, positionMs); + } + + /** + * Instructs the {@link MetadataRetriever} under test to call {@link + * MetadataRetriever#setWindowIndex(int)} on test runner thread, and wait until it's done, or + * until the {@link #DEFAULT_TIMEOUT_MS} passed. + * + * @throws InterruptedException If the test runner did not finish within the specified timeout. + */ + public void setWindowIndex(int windowIndex) throws InterruptedException { + runOnTestThreadBlocking( + new Runnable() { + @Override + public void run() { + metadataRetriever.setWindowIndex(windowIndex); + } + }); + } + + /** Releases the {@link MetadataRetriever} under test and stops the test thread. */ + public void release() throws InterruptedException { + runOnTestThreadBlocking( + new Runnable() { + @Override + public void run() { + metadataRetriever.release(); + } + }); + handler.removeCallbacksAndMessages(null); + testThread.quit(); + } + + // Assertions on retriever behavior. + + /** + * Asserts that the data reported by {@link + * MetadataRetriever.MediaSourceCallback#onTimelineUpdated(Timeline, Object, int)} are equal to + * the provided data. + * + * @param preparedCallbackData A list of expected {@link PrepareCallbackData}s. + */ + public void assertPrepareCallbackDataEqual(PrepareCallbackData... preparedCallbackData) { + assertThat(this.preparedCallbackData).containsExactlyElementsIn(preparedCallbackData).inOrder(); + } + + /** + * Asserts that the data reported by {@link + * MetadataRetriever.MetadataCallback#onMetadataAvailable(TrackGroupArray, Timeline, int, int)} + * are equal to the provided data. + * + * @param metadataCallbackData A list of expected {@link MetadataCallbackData}s. + */ + public void assertMetadataCallbackDataEqual(MetadataCallbackData... metadataCallbackData) { + assertThat(this.metadataCallbackData).containsExactlyElementsIn(metadataCallbackData).inOrder(); + } + + /** Asserts that no exception occurred during the test. */ + public void assertNoException() { + assertThat(this.failedQueryExceptions).isEmpty(); + } + + /** + * Returns list of {@link PrepareCallbackData} that were reported in {@link + * MetadataRetriever.MediaSourceCallback#onTimelineUpdated(Timeline, Object, int)} in order of + * occurrence. + */ + public List getPrepareCallbackData() { + return this.preparedCallbackData; + } + + /** + * Returns list of {@link MetadataCallbackData} that were reported in {@link + * MetadataRetriever.MetadataCallback#onMetadataAvailable(TrackGroupArray, Timeline, int, int)}} + * in order of occurrence. + */ + public List getMetadataCallbackData() { + return this.metadataCallbackData; + } + + /** + * Returns list of {@link Exception} that were reported in either {@link + * MetadataRetriever.MediaSourceCallback#onTimelineUnavailable(Exception)} and {@link + * MetadataRetriever.MetadataCallback#onMetadataUnavailable(Exception)} in order of occurrence. + */ + public List getFailedQueryExceptions() { + return this.failedQueryExceptions; + } + + /** + * Asserts that the {@link MetadataRetriever#getWindowDurationMs()} is equal to the given value. + */ + public void assertWindowDurationMs(long windowDurationMs) throws InterruptedException { + AtomicLong actualWindowDurationMs = new AtomicLong(); + runOnTestThreadBlocking( + new Runnable() { + @Override + public void run() { + actualWindowDurationMs.set(metadataRetriever.getWindowDurationMs()); + } + }); + assertThat(actualWindowDurationMs.get()).isEqualTo(windowDurationMs); + } + + // MetadataRetriever.MediaSourceCallback implementation. + + @Override + public void onTimelineUpdated(Timeline timeline, @Nullable Object manifest, int reason) { + preparedCallbackData.add(PrepareCallbackData.prepareCallbackData(timeline, manifest, reason)); + } + + @Override + public void onTimelineUnavailable(Exception exception) { + failedQueryExceptions.add(exception); + } + + // MetadataRetriever.MetadataCallback implementation. + + @Override + public void onMetadataAvailable( + TrackGroupArray trackGroupArray, Timeline timeline, int windowIndex, int periodIndex) { + metadataCallbackData.add( + MetadataCallbackData.metadataCallbackData( + trackGroupArray, timeline, windowIndex, periodIndex)); + } + + @Override + public void onMetadataUnavailable(Exception exception) { + failedQueryExceptions.add(exception); + } + + /** + * Starts the test runner on its own thread. This will trigger the creation of the {@link + * MetadataRetriever}. + * + * @param metadataRetrieverFactory The factory to create the {@link MetadataRetriever} under test. + */ + private void startTestRunnerThreadBlocking(TestMetadataRetrieverFactory metadataRetrieverFactory) + throws InterruptedException { + runOnTestThreadBlocking( + new Runnable() { + @Override + public void run() { + metadataRetriever = + metadataRetrieverFactory.createMetadataRetriever( + Clock.DEFAULT, FAKE_RENDERERS, Looper.myLooper()); + } + }); + } + + private void runOnTestThreadBlocking(Runnable runnable) throws InterruptedException { + ConditionVariable conditionVariable = new ConditionVariable(); + handler.post( + new Runnable() { + @Override + public void run() { + runnable.run(); + conditionVariable.open(); + } + }); + conditionVariable.block(DEFAULT_TIMEOUT_MS); + } + + private void getMetadataBlockingImpl(boolean callWithParam, long positionMs) + throws InterruptedException, TimeoutException { + ConditionVariable callbackReceived = new ConditionVariable(); + handler.post( + new Runnable() { + @Override + public void run() { + MetadataRetriever.MetadataCallback unblockingMetadataCallback = + new UnblockingMetadataCallback(callbackReceived); + if (callWithParam) { + metadataRetriever.getMetadata(positionMs, unblockingMetadataCallback); + } else { + metadataRetriever.getMetadata(unblockingMetadataCallback); + } + } + }); + if (!callbackReceived.block(DEFAULT_TIMEOUT_MS)) { + throw new TimeoutException( + "Test metadata retriever timed out waiting for get metadata callback."); + } + } + + /** + * A {@link MetadataRetriever.MediaSourceCallback} that will unblock a {@link ConditionVariable} + * when one of the callback is called. + */ + private class UnblockingMediaSourceCallback implements MetadataRetriever.MediaSourceCallback { + private final ConditionVariable blockedCondition; + + public UnblockingMediaSourceCallback(ConditionVariable blockedCondition) { + this.blockedCondition = blockedCondition; + } + + @Override + public void onTimelineUpdated(Timeline timeline, @Nullable Object manifest, int reason) { + MetadataRetrieverTestRunner.this.onTimelineUpdated(timeline, manifest, reason); + blockedCondition.open(); + } + + @Override + public void onTimelineUnavailable(Exception exception) { + MetadataRetrieverTestRunner.this.onTimelineUnavailable(exception); + blockedCondition.open(); + } + } + + /** + * A {@link MetadataRetriever.MetadataCallback} that will unblock a {@link ConditionVariable} when + * one of the callback is called. + */ + private final class UnblockingMetadataCallback implements MetadataRetriever.MetadataCallback { + private final ConditionVariable blockedCondition; + + private UnblockingMetadataCallback(ConditionVariable blockedCondition) { + this.blockedCondition = blockedCondition; + } + + @Override + public void onMetadataAvailable( + TrackGroupArray trackGroupArray, Timeline timeline, int windowIndex, int periodIndex) { + MetadataRetrieverTestRunner.this.onMetadataAvailable( + trackGroupArray, timeline, windowIndex, periodIndex); + blockedCondition.open(); + } + + @Override + public void onMetadataUnavailable(Exception exception) { + MetadataRetrieverTestRunner.this.onMetadataUnavailable(exception); + blockedCondition.open(); + } + } +}