Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56f7c90c authored by Robert Shih's avatar Robert Shih
Browse files

MediaPlayer2Impl: pack source info into subclass

Bug: 109928575
Test: android.media.cts.MediaPlayer2Test
Change-Id: Ia478b828f4fa11502f7693a530a367ff769d2881
parent 45ac8e40
Loading
Loading
Loading
Loading
+123 −102
Original line number Diff line number Diff line
@@ -57,15 +57,20 @@ import java.net.URL;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @hide
@@ -95,17 +100,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {

    private final Object mSrcLock = new Object();
    //--- guarded by |mSrcLock| start
    private long mSrcIdGenerator = 0;
    private DataSourceDesc mCurrentDSD;
    private long mCurrentSrcId = mSrcIdGenerator++;
    private List<DataSourceDesc> mNextDSDs;
    private long mNextSrcId = mSrcIdGenerator++;
    private int mNextSourceState = NEXT_SOURCE_STATE_INIT;
    private boolean mNextSourcePlayPending = false;
    private SourceInfo mCurrentSourceInfo;
    private final Queue<SourceInfo> mNextSourceInfos = new ConcurrentLinkedQueue<>();
    //--- guarded by |mSrcLock| end
    private final AtomicLong mSrcIdGenerator = new AtomicLong(0);

    private AtomicInteger mBufferedPercentageCurrent = new AtomicInteger(0);
    private AtomicInteger mBufferedPercentageNext = new AtomicInteger(0);
    private volatile float mVolume = 1.0f;
    private VideoSize mVideoSize = new VideoSize(0, 0);

@@ -221,7 +220,15 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
    @Override
    public long getBufferedPosition() {
        // Use cached buffered percent for now.
        return getDuration() * mBufferedPercentageCurrent.get() / 100;
        int bufferedPercentage;
        synchronized (mSrcLock) {
            if (mCurrentSourceInfo == null) {
                bufferedPercentage = 0;
            } else {
                bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
            }
        }
        return getDuration() * bufferedPercentage / 100;
    }

    @Override
@@ -263,9 +270,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                }

                synchronized (mSrcLock) {
                    mCurrentDSD = dsd;
                    mCurrentSrcId = mSrcIdGenerator++;
                    handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
                    mCurrentSourceInfo = new SourceInfo(dsd);
                    handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
                }
            }
        });
@@ -278,10 +284,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
            void process() {
                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                synchronized (mSrcLock) {
                    mNextDSDs = new ArrayList<DataSourceDesc>(1);
                    mNextDSDs.add(dsd);
                    mNextSrcId = mSrcIdGenerator++;
                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
                    mNextSourceInfos.clear();
                    mNextSourceInfos.add(new SourceInfo(dsd));
                }
                prepareNextDataSource();
            }
@@ -304,9 +308,10 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                }

                synchronized (mSrcLock) {
                    mNextDSDs = new ArrayList(dsds);
                    mNextSrcId = mSrcIdGenerator++;
                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
                    mNextSourceInfos.clear();
                    for (DataSourceDesc dsd : dsds) {
                        mNextSourceInfos.add(new SourceInfo(dsd));
                    }
                }
                prepareNextDataSource();
            }
@@ -318,22 +323,15 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
        return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
            @Override
            void process() {
                synchronized (mSrcLock) {
                    if (mNextDSDs != null) {
                        mNextDSDs.clear();
                        mNextDSDs = null;
                    }
                    mNextSrcId = mSrcIdGenerator++;
                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
                }
                mNextSourceInfos.clear();
            }
        });
    }

    @Override
    public @NonNull DataSourceDesc getCurrentDataSource() {
    public DataSourceDesc getCurrentDataSource() {
        synchronized (mSrcLock) {
            return mCurrentDSD;
            return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
        }
    }

@@ -702,34 +700,29 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
        }

        boolean hasNextDSD;
        synchronized (mSrcLock) {
            hasNextDSD = (mNextDSDs != null && !mNextDSDs.isEmpty());
        }

        int state = getState();
        synchronized (mSrcLock) {
            hasNextDSD = !mNextSourceInfos.isEmpty();
            if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
                // Current source has not been prepared yet.
                return hasNextDSD;
            }

        synchronized (mSrcLock) {
            if (!hasNextDSD || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
            SourceInfo nextSource = mNextSourceInfos.peek();
            if (!hasNextDSD || nextSource.mStateAsNextSource != NEXT_SOURCE_STATE_INIT) {
                // There is no next source or it's in preparing or prepared state.
                return hasNextDSD;
            }

            try {
                mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
                handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
                nextSource.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARING;
                handleDataSource(false /* isCurrent */, nextSource.mDSD, nextSource.mId);
            } catch (Exception e) {
                Message msg = mTaskHandler.obtainMessage(
                        MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
                mTaskHandler.handleMessage(msg, mNextSrcId);
                mTaskHandler.handleMessage(msg, nextSource.mId);

                mNextDSDs.remove(0);
                // make a new SrcId to obsolete notification for previous one.
                mNextSrcId = mSrcIdGenerator++;
                mNextSourceState = NEXT_SOURCE_STATE_INIT;
                mNextSourceInfos.poll();
                return prepareNextDataSource();
            }
        }
@@ -744,19 +737,14 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {

        boolean hasNextDSD = false;
        synchronized (mSrcLock) {
            if (mNextDSDs != null && !mNextDSDs.isEmpty()) {
            if (!mNextSourceInfos.isEmpty()) {
                hasNextDSD = true;
                if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
                SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
                    // Switch to next source only when it has been prepared.
                    mCurrentDSD = mNextDSDs.get(0);
                    mCurrentSrcId = mNextSrcId;
                    mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
                    mNextDSDs.remove(0);
                    mNextSrcId = mSrcIdGenerator++;  // make it different from |mCurrentSrcId|
                    mBufferedPercentageNext.set(0);
                    mNextSourceState = NEXT_SOURCE_STATE_INIT;

                    long srcId = mCurrentSrcId;
                    mCurrentSourceInfo = mNextSourceInfos.poll();

                    long srcId = mCurrentSourceInfo.mId;
                    try {
                        nativePlayNextDataSource(srcId);
                    } catch (Exception e) {
@@ -771,9 +759,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {

                        // Now a new current src is playing.
                        // Wait for MEDIA_INFO_DATA_SOURCE_START to prepare next source.
                        mNextSourcePlayPending = false;
                    }
                } else if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
                } else if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_INIT) {
                    hasNextDSD = prepareNextDataSource();
                }
            }
@@ -1180,12 +1167,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
            mDrmEventCallbackRecords.clear();
        }
        synchronized (mSrcLock) {
            if (mNextDSDs != null) {
                mNextDSDs.clear();
                mNextDSDs = null;
            }
            mNextSrcId = mSrcIdGenerator++;
            mNextSourceState = NEXT_SOURCE_STATE_INIT;
            mCurrentSourceInfo = null;
            mNextSourceInfos.clear();
        }

        stayAwake(false);
@@ -1637,20 +1620,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
            final int what = msg.arg1;
            final int extra = msg.arg2;

            final DataSourceDesc dsd;
            boolean isCurrentSrcId = false;
            boolean isNextSrcId = false;
            synchronized (mSrcLock) {
                if (srcId == mCurrentSrcId) {
                    dsd = mCurrentDSD;
                    isCurrentSrcId = true;
                } else if (mNextDSDs != null && !mNextDSDs.isEmpty() && srcId == mNextSrcId) {
                    dsd = mNextDSDs.get(0);
                    isNextSrcId = true;
                } else {
            final SourceInfo sourceInfo = getSourceInfoById(srcId);
            if (sourceInfo == null) {
                return;
            }
            }
            final DataSourceDesc dsd = sourceInfo.mDSD;

            switch(msg.what) {
                case MEDIA_PREPARED:
@@ -1666,14 +1640,16 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                    }

                    synchronized (mSrcLock) {
                        SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                        Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
                                + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
                                + ", curSrc=" + mCurrentSourceInfo
                                + ", nextSrc=" + nextSourceInfo);

                        if (isCurrentSrcId) {
                        if (isCurrentSource(srcId)) {
                            prepareNextDataSource();
                        } else if (isNextSrcId) {
                            mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
                            if (mNextSourcePlayPending) {
                        } else if (isNextSource(srcId)) {
                            nextSourceInfo.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARED;
                            if (nextSourceInfo.mPlayPendingAsNextSource) {
                                playNextDataSource();
                            }
                        }
@@ -1726,7 +1702,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {

                case MEDIA_PLAYBACK_COMPLETE:
                {
                    if (isCurrentSrcId) {
                    if (isCurrentSource(srcId)) {
                        sendEvent(new EventNotifier() {
                            @Override
                            public void notify(EventCallback callback) {
@@ -1737,11 +1713,13 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                        stayAwake(false);

                        synchronized (mSrcLock) {
                            mNextSourcePlayPending = true;

                            SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                            if (nextSourceInfo != null) {
                                nextSourceInfo.mPlayPendingAsNextSource = true;
                            }
                            Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
                                    + ", currentSrcId=" + mCurrentSrcId
                                    + ", nextSrcId=" + mNextSrcId);
                                    + ", curSrc=" + mCurrentSourceInfo
                                    + ", nextSrc=" + nextSourceInfo);
                        }

                        playNextDataSource();
@@ -1772,13 +1750,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                        }
                    });

                    synchronized (mSrcLock) {
                        if (isCurrentSrcId) {
                            mBufferedPercentageCurrent.set(percent);
                        } else if (isNextSrcId) {
                            mBufferedPercentageNext.set(percent);
                        }
                    SourceInfo src = getSourceInfoById(srcId);
                    if (src != null) {
                        src.mBufferedPercentage.set(percent);
                    }

                    return;
                }

@@ -1850,7 +1826,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                    });

                    if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
                        if (isCurrentSrcId) {
                        if (isCurrentSource(srcId)) {
                            prepareNextDataSource();
                        }
                    }
@@ -1965,6 +1941,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                }
            }
        }

    }

    /*
@@ -2241,7 +2218,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                        @Override
                        public void notify(DrmEventCallback callback) {
                            callback.onDrmPrepared(
                                    MediaPlayer2Impl.this, mCurrentDSD, prepareDrmStatus);
                                    MediaPlayer2Impl.this, getCurrentDataSource(), prepareDrmStatus);
                        }
                    });

@@ -2307,7 +2284,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {

        // call the callback outside the lock
        if (mOnDrmConfigHelper != null)  {
            mOnDrmConfigHelper.onDrmConfig(this, mCurrentDSD);
            mOnDrmConfigHelper.onDrmConfig(this, getCurrentDataSource());
        }

        synchronized (mDrmLock) {
@@ -2956,7 +2933,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
                @Override
                public void notify(DrmEventCallback callback) {
                    callback.onDrmPrepared(
                            mediaPlayer, mCurrentDSD, status);
                            mediaPlayer, getCurrentDataSource(), status);
                }
            });

@@ -3223,9 +3200,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
            } catch (Exception e) {
                status = CALL_STATUS_ERROR_UNKNOWN;
            }
            synchronized (mSrcLock) {
                mDSD = mCurrentDSD;
            }
            mDSD = getCurrentDataSource();

            // TODO: Make native implementations asynchronous and let them send notifications.
            if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
@@ -3262,4 +3237,50 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
            super(detailMessage);
        }
    };

    private final class SourceInfo {
        final DataSourceDesc mDSD;
        final long mId = mSrcIdGenerator.getAndIncrement();
        AtomicInteger mBufferedPercentage = new AtomicInteger(0);

        // m*AsNextSource (below) only applies to pending data sources in the playlist;
        // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
        // are undefined.
        int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
        boolean mPlayPendingAsNextSource = false;

        SourceInfo(DataSourceDesc dsd) {
            this.mDSD = dsd;
        }

        @Override
        public String toString() {
            return String.format("%s(%d)", SourceInfo.class.getName(), mId);
        }

    }

    private SourceInfo getSourceInfoById(long srcId) {
        synchronized (mSrcLock) {
            if (isCurrentSource(srcId)) {
                return mCurrentSourceInfo;
            }
            if (isNextSource(srcId)) {
                return mNextSourceInfos.peek();
            }
        }
        return null;
    }

    private boolean isCurrentSource(long srcId) {
        synchronized (mSrcLock) {
            return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
        }
    }

    private boolean isNextSource(long srcId) {
        SourceInfo nextSourceInfo = mNextSourceInfos.peek();
        return nextSourceInfo != null && nextSourceInfo.mId == srcId;
    }

}