From adf77a01e8142a5abb496997beddead7b48f43b7 Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 2 Mar 2018 18:10:49 -0800 Subject: [PATCH] Make MP2.prepare|play handled with task queue Test: plays video Bug: 63934228 Change-Id: Iecf8b2079e87241365ab6b7b7b0a5a669c948427 --- .../java/android/media/MediaPlayer2Impl.java | 101 +++++++++++++++++- media/jni/android_media_MediaPlayer2.cpp | 2 +- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 2d92d64e6a963..7c114df660374 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -72,6 +72,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; @@ -131,6 +132,14 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private ProvisioningThread mDrmProvisioningThread; //--- guarded by |mDrmLock| end + private HandlerThread mHandlerThread; + private final Handler mTaskHandler; + private final Object mTaskLock = new Object(); + @GuardedBy("mTaskLock") + private final List mPendingTasks = new LinkedList<>(); + @GuardedBy("mTaskLock") + private Task mCurrentTask; + /** * Default constructor. *

When done with the MediaPlayer2Impl, you should call {@link #close()}, @@ -147,6 +156,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { mEventHandler = null; } + mHandlerThread = new HandlerThread("MediaPlayer2TaskThread"); + mHandlerThread.start(); + looper = mHandlerThread.getLooper(); + mTaskHandler = new Handler(looper); + mTimeProvider = new TimeProvider(this); mOpenSubtitleSources = new Vector(); mGuard.open("close"); @@ -197,8 +211,18 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { */ @Override public void play() { - stayAwake(true); - _start(); + synchronized (mTaskLock) { + mPendingTasks.add(new Task(MEDIA_CALL_PLAY, false) { + @Override + int process() { + stayAwake(true); + _start(); + // TODO: define public constants for return value (status). + return 0; + } + }); + processPendingTask_l(); + } } private native void _start() throws IllegalStateException; @@ -214,7 +238,21 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { * @throws IllegalStateException if it is called in an invalid state */ @Override - public native void prepare(); + public void prepare() { + synchronized (mTaskLock) { + mPendingTasks.add(new Task(MEDIA_CALL_PREPARE, true) { + @Override + int process() { + _prepare(); + // TODO: define public constants for return value (status). + return 0; + } + }); + processPendingTask_l(); + } + } + + public native void _prepare(); /** * Pauses playback. Call play() to resume. @@ -691,6 +729,18 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { public void clearPendingCommands() { } + @GuardedBy("mTaskLock") + private void processPendingTask_l() { + if (mCurrentTask != null) { + return; + } + if (!mPendingTasks.isEmpty()) { + Task task = mPendingTasks.remove(0); + mCurrentTask = task; + mTaskHandler.post(task); + } + } + private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId) throws IOException { Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null"); @@ -2521,6 +2571,10 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { synchronized (mEventCbLock) { mEventCallbackRecords.clear(); } + if (mHandlerThread != null) { + mHandlerThread.quitSafely(); + mHandlerThread = null; + } if (mTimeProvider != null) { mTimeProvider.close(); mTimeProvider = null; @@ -2634,6 +2688,13 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } } + synchronized (mTaskLock) { + if (mCurrentTask.mMediaCallType == MEDIA_CALL_PREPARE + && mCurrentTask.mNeedToWaitForEventToComplete) { + mCurrentTask = null; + processPendingTask_l(); + } + } return; } @@ -4502,4 +4563,38 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } } + + private abstract class Task implements Runnable { + private final int mMediaCallType; + private final boolean mNeedToWaitForEventToComplete; + + public Task (int mediaCallType, boolean needToWaitForEventToComplete) { + mMediaCallType = mediaCallType; + mNeedToWaitForEventToComplete = needToWaitForEventToComplete; + } + + abstract int process(); + + @Override + public void run() { + int status = process(); + + if (!mNeedToWaitForEventToComplete) { + final DataSourceDesc dsd; + synchronized (mSrcLock) { + dsd = mCurrentDSD; + } + synchronized (mEventCbLock) { + for (Pair cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onCallComplete( + MediaPlayer2Impl.this, dsd, mMediaCallType, status)); + } + } + synchronized (mTaskLock) { + mCurrentTask = null; + processPendingTask_l(); + } + } + } + }; } diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index af78777a900b7..918b82b065703 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -1490,7 +1490,7 @@ static const JNINativeMethod gMethods[] = { {"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface}, {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams}, {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams}, - {"prepare", "()V", (void *)android_media_MediaPlayer2_prepare}, + {"_prepare", "()V", (void *)android_media_MediaPlayer2_prepare}, {"_start", "()V", (void *)android_media_MediaPlayer2_start}, {"_stop", "()V", (void *)android_media_MediaPlayer2_stop}, {"native_getMediaPlayer2State", "()I", (void *)android_media_MediaPlayer2_getMediaPlayer2State},