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

Commit 49ca7252 authored by Wei Jia's avatar Wei Jia
Browse files

MediaPlayer: simplify buffering control.

Test: pass cts tests
Bug: 36280094
Change-Id: If1e2dac511d038b88f7e867eb7ff8b345cb04478
parent e33bc3b9
Loading
Loading
Loading
Loading
+58 −326
Original line number Diff line number Diff line
@@ -26,170 +26,68 @@ import java.lang.annotation.RetentionPolicy;
/**
 * Structure for source buffering management params.
 *
 * Used by {@link MediaPlayer#getDefaultBufferingParams()},
 * {@link MediaPlayer#getBufferingParams()} and
 * Used by {@link MediaPlayer#getBufferingParams()} and
 * {@link MediaPlayer#setBufferingParams(BufferingParams)}
 * to control source buffering behavior.
 *
 * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
 * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
 * is playing back source). {@link BufferingParams} includes mode and corresponding
 * watermarks for each stage of source buffering. The watermarks could be either size
 * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
 * is playing back source). {@link BufferingParams} includes corresponding marks for each
 * stage of source buffering. The marks are time based (in milliseconds).
 *
 * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
 * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
 * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
 * {@link MediaPlayer} source component has default buffering modes which can be queried
 * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
 * Users should always use those default modes or their downsized version when trying to
 * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
 * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
 * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
 * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
 * <p>{@link MediaPlayer} source component has default marks which can be queried by
 * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
 * {@link MediaPlayer#setBufferingParams()}.
 * <ul>
 * <li><strong>initial buffering stage:</strong> has one watermark which is used when
 * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
 * <li><strong>initial buffering:</strong> initialMarkMs is used when
 * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
 * {@link MediaPlayer} is prepared. </li>
 * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
 * used when {@link MediaPlayer} is playing back content.
 * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
 * {@link MediaPlayer} is playing back content.
 * <ul>
 * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
 * buffering. Buffering will resume when cache runs below some limit which could be low
 * watermark or some intermediate value decided by the source component.</li>
 * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
 * playback. Playback will resume when cached data amount exceeds high watermark
 * or reaches end of stream.</li>
 * </ul>
 * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
 * to pause playback if cached data amount runs low. This internal mark varies based on
 * type of data source. </li>
 * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
 * resume playback if it has been paused due to low cached data amount. The internal mark
 * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
 * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
 * when to pause rebuffering. Apparently, this internal mark shall be no less than
 * resumePlaybackMarkMs. </li>
 * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
 * when to resume buffering. This internal mark varies based on type of data source. This
 * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
 * </li>
 * </ul> </li>
 * </ul>
 * <p>Users should use {@link Builder} to change {@link BufferingParams}.
 * @hide
 */
public final class BufferingParams implements Parcelable {
    /**
     * This mode indicates that source buffering is not supported.
     */
    public static final int BUFFERING_MODE_NONE = 0;
    /**
     * This mode indicates that only time based source buffering is supported. This means
     * the watermark(s) are time based.
     */
    public static final int BUFFERING_MODE_TIME_ONLY = 1;
    /**
     * This mode indicates that only size based source buffering is supported. This means
     * the watermark(s) are size based.
     */
    public static final int BUFFERING_MODE_SIZE_ONLY = 2;
    /**
     * This mode indicates that both time and size based source buffering are supported,
     * and time based calculation precedes size based. Size based calculation will be used
     * only when time information is not available from the source.
     */
    public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;

    /** @hide */
    @IntDef(
        value = {
                BUFFERING_MODE_NONE,
                BUFFERING_MODE_TIME_ONLY,
                BUFFERING_MODE_SIZE_ONLY,
                BUFFERING_MODE_TIME_THEN_SIZE,
        }
    )
    @Retention(RetentionPolicy.SOURCE)
    public @interface BufferingMode {}

    private static final int BUFFERING_NO_WATERMARK = -1;
    private static final int BUFFERING_NO_MARK = -1;

    // params
    private int mInitialBufferingMode = BUFFERING_MODE_NONE;
    private int mRebufferingMode = BUFFERING_MODE_NONE;

    private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
    private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
    private int mInitialMarkMs = BUFFERING_NO_MARK;

    private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
    private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
    private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
    private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
    private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;

    private BufferingParams() {
    }

    /**
     * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
     * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
     */
    public int getInitialBufferingMode() {
        return mInitialBufferingMode;
    }

    /**
     * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
     * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
     */
    public int getRebufferingMode() {
        return mRebufferingMode;
    }

    /**
     * Return the time based initial buffering watermark in milliseconds.
     * It is meaningful only when initial buffering mode obatined from
     * {@link #getInitialBufferingMode()} is time based.
     * @return time based initial buffering watermark in milliseconds
     */
    public int getInitialBufferingWatermarkMs() {
        return mInitialWatermarkMs;
    }

    /**
     * Return the size based initial buffering watermark in kilobytes.
     * It is meaningful only when initial buffering mode obatined from
     * {@link #getInitialBufferingMode()} is size based.
     * @return size based initial buffering watermark in kilobytes
     */
    public int getInitialBufferingWatermarkKB() {
        return mInitialWatermarkKB;
    }

    /**
     * Return the time based low watermark in milliseconds for rebuffering.
     * It is meaningful only when rebuffering mode obatined from
     * {@link #getRebufferingMode()} is time based.
     * @return time based low watermark for rebuffering in milliseconds
     */
    public int getRebufferingWatermarkLowMs() {
        return mRebufferingWatermarkLowMs;
    }

    /**
     * Return the time based high watermark in milliseconds for rebuffering.
     * It is meaningful only when rebuffering mode obatined from
     * {@link #getRebufferingMode()} is time based.
     * @return time based high watermark for rebuffering in milliseconds
     * Return initial buffering mark in milliseconds.
     * @return initial buffering mark in milliseconds
     */
    public int getRebufferingWatermarkHighMs() {
        return mRebufferingWatermarkHighMs;
    public int getInitialMarkMs() {
        return mInitialMarkMs;
    }

    /**
     * Return the size based low watermark in kilobytes for rebuffering.
     * It is meaningful only when rebuffering mode obatined from
     * {@link #getRebufferingMode()} is size based.
     * @return size based low watermark for rebuffering in kilobytes
     * Return the mark in milliseconds for resuming playback.
     * @return the mark for resuming playback in milliseconds
     */
    public int getRebufferingWatermarkLowKB() {
        return mRebufferingWatermarkLowKB;
    }

    /**
     * Return the size based high watermark in kilobytes for rebuffering.
     * It is meaningful only when rebuffering mode obatined from
     * {@link #getRebufferingMode()} is size based.
     * @return size based high watermark for rebuffering in kilobytes
     */
    public int getRebufferingWatermarkHighKB() {
        return mRebufferingWatermarkHighKB;
    public int getResumePlaybackMarkMs() {
        return mResumePlaybackMarkMs;
    }

    /**
@@ -200,27 +98,19 @@ public final class BufferingParams implements Parcelable {
     * <pre class="prettyprint">
     * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
     * myParams = new BufferingParams.Builder(myParams)
     *             .setInitialBufferingWatermarkMs(10000)
     *         .setInitialMarkMs(10000)
     *         .setResumePlaybackMarkMs(15000)
     *         .build();
     * mediaplayer.setBufferingParams(myParams);
     * </pre>
     */
    public static class Builder {
        private int mInitialBufferingMode = BUFFERING_MODE_NONE;
        private int mRebufferingMode = BUFFERING_MODE_NONE;

        private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
        private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;

        private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
        private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
        private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
        private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
        private int mInitialMarkMs = BUFFERING_NO_MARK;
        private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;

        /**
         * Constructs a new Builder with the defaults.
         * By default, both initial buffering mode and rebuffering mode are
         * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
         * By default, all marks are -1.
         */
        public Builder() {
        }
@@ -231,16 +121,8 @@ public final class BufferingParams implements Parcelable {
         * in the new Builder.
         */
        public Builder(BufferingParams bp) {
            mInitialBufferingMode = bp.mInitialBufferingMode;
            mRebufferingMode = bp.mRebufferingMode;

            mInitialWatermarkMs = bp.mInitialWatermarkMs;
            mInitialWatermarkKB = bp.mInitialWatermarkKB;

            mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
            mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
            mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
            mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
            mInitialMarkMs = bp.mInitialMarkMs;
            mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
        }

        /**
@@ -250,179 +132,37 @@ public final class BufferingParams implements Parcelable {
         * @return a new {@link BufferingParams} object
         */
        public BufferingParams build() {
            if (isTimeBasedMode(mRebufferingMode)
                    && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
                throw new IllegalStateException("Illegal watermark:"
                        + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
            }
            if (isSizeBasedMode(mRebufferingMode)
                    && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
                throw new IllegalStateException("Illegal watermark:"
                        + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
            }

            BufferingParams bp = new BufferingParams();
            bp.mInitialBufferingMode = mInitialBufferingMode;
            bp.mRebufferingMode = mRebufferingMode;

            bp.mInitialWatermarkMs = mInitialWatermarkMs;
            bp.mInitialWatermarkKB = mInitialWatermarkKB;
            bp.mInitialMarkMs = mInitialMarkMs;
            bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;

            bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
            bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
            bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
            bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
            return bp;
        }

        private boolean isTimeBasedMode(int mode) {
            return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
        }

        private boolean isSizeBasedMode(int mode) {
            return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
        }

        /**
         * Sets the initial buffering mode.
         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
         * Sets the time based mark in milliseconds for initial buffering.
         * @param markMs time based mark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setInitialBufferingMode(@BufferingMode int mode) {
            switch (mode) {
                case BUFFERING_MODE_NONE:
                case BUFFERING_MODE_TIME_ONLY:
                case BUFFERING_MODE_SIZE_ONLY:
                case BUFFERING_MODE_TIME_THEN_SIZE:
                     mInitialBufferingMode = mode;
                     break;
                default:
                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
            }
        public Builder setInitialMarkMs(int markMs) {
            mInitialMarkMs = markMs;
            return this;
        }

        /**
         * Sets the rebuffering mode.
         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
         * Sets the time based mark in milliseconds for resuming playback.
         * @param markMs time based mark in milliseconds for resuming playback
         * @return the same Builder instance.
         */
        public Builder setRebufferingMode(@BufferingMode int mode) {
            switch (mode) {
                case BUFFERING_MODE_NONE:
                case BUFFERING_MODE_TIME_ONLY:
                case BUFFERING_MODE_SIZE_ONLY:
                case BUFFERING_MODE_TIME_THEN_SIZE:
                     mRebufferingMode = mode;
                     break;
                default:
                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
            }
            return this;
        }

        /**
         * Sets the time based watermark in milliseconds for initial buffering.
         * @param watermarkMs time based watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
            mInitialWatermarkMs = watermarkMs;
            return this;
        }

        /**
         * Sets the size based watermark in kilobytes for initial buffering.
         * @param watermarkKB size based watermark in kilobytes
         * @return the same Builder instance.
         */
        public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
            mInitialWatermarkKB = watermarkKB;
            return this;
        }

        /**
         * Sets the time based low watermark in milliseconds for rebuffering.
         * @param watermarkMs time based low watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
            mRebufferingWatermarkLowMs = watermarkMs;
            return this;
        }

        /**
         * Sets the time based high watermark in milliseconds for rebuffering.
         * @param watermarkMs time based high watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
            mRebufferingWatermarkHighMs = watermarkMs;
            return this;
        }

        /**
         * Sets the size based low watermark in milliseconds for rebuffering.
         * @param watermarkKB size based low watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
            mRebufferingWatermarkLowKB = watermarkKB;
            return this;
        }

        /**
         * Sets the size based high watermark in milliseconds for rebuffering.
         * @param watermarkKB size based high watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
            mRebufferingWatermarkHighKB = watermarkKB;
            return this;
        }

        /**
         * Sets the time based low and high watermarks in milliseconds for rebuffering.
         * @param lowWatermarkMs time based low watermark in milliseconds
         * @param highWatermarkMs time based high watermark in milliseconds
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
            mRebufferingWatermarkLowMs = lowWatermarkMs;
            mRebufferingWatermarkHighMs = highWatermarkMs;
            return this;
        }

        /**
         * Sets the size based low and high watermarks in kilobytes for rebuffering.
         * @param lowWatermarkKB size based low watermark in kilobytes
         * @param highWatermarkKB size based high watermark in kilobytes
         * @return the same Builder instance.
         */
        public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
            mRebufferingWatermarkLowKB = lowWatermarkKB;
            mRebufferingWatermarkHighKB = highWatermarkKB;
        public Builder setResumePlaybackMarkMs(int markMs) {
            mResumePlaybackMarkMs = markMs;
            return this;
        }
    }

    private BufferingParams(Parcel in) {
        mInitialBufferingMode = in.readInt();
        mRebufferingMode = in.readInt();

        mInitialWatermarkMs = in.readInt();
        mInitialWatermarkKB = in.readInt();

        mRebufferingWatermarkLowMs = in.readInt();
        mRebufferingWatermarkHighMs = in.readInt();
        mRebufferingWatermarkLowKB = in.readInt();
        mRebufferingWatermarkHighKB = in.readInt();
        mInitialMarkMs = in.readInt();
        mResumePlaybackMarkMs = in.readInt();
    }

    public static final Parcelable.Creator<BufferingParams> CREATOR =
@@ -446,15 +186,7 @@ public final class BufferingParams implements Parcelable {

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mInitialBufferingMode);
        dest.writeInt(mRebufferingMode);

        dest.writeInt(mInitialWatermarkMs);
        dest.writeInt(mInitialWatermarkKB);

        dest.writeInt(mRebufferingWatermarkLowMs);
        dest.writeInt(mRebufferingWatermarkHighMs);
        dest.writeInt(mRebufferingWatermarkLowKB);
        dest.writeInt(mRebufferingWatermarkHighKB);
        dest.writeInt(mInitialMarkMs);
        dest.writeInt(mResumePlaybackMarkMs);
    }
}
+2 −15
Original line number Diff line number Diff line
@@ -1697,22 +1697,10 @@ public class MediaPlayer extends PlayerBase
     */
    public native boolean isPlaying();

    /**
     * Gets the default buffering management params.
     * Calling it only after {@code setDataSource} has been called.
     * Each type of data source might have different set of default params.
     *
     * @return the default buffering management params supported by the source component.
     * @throws IllegalStateException if the internal player engine has not been
     * initialized, or {@code setDataSource} has not been called.
     * @hide
     */
    @NonNull
    public native BufferingParams getDefaultBufferingParams();

    /**
     * Gets the current buffering management params used by the source component.
     * Calling it only after {@code setDataSource} has been called.
     * Each type of data source might have different set of default params.
     *
     * @return the current buffering management params used by the source component.
     * @throws IllegalStateException if the internal player engine has not been
@@ -1727,8 +1715,7 @@ public class MediaPlayer extends PlayerBase
     * The object sets its internal BufferingParams to the input, except that the input is
     * invalid or not supported.
     * Call it only after {@code setDataSource} has been called.
     * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
     * or its downsized version as described in {@link BufferingParams}.
     * The input is a hint to MediaPlayer.
     *
     * @param params the buffering management params.
     *
+10 −40

File changed.

Preview size limit exceeded, changes collapsed.

+0 −20
Original line number Diff line number Diff line
@@ -370,25 +370,6 @@ android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsu
    setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}

static jobject
android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return NULL;
    }

    BufferingParams bp;
    BufferingSettings &settings = bp.settings;
    process_media_player_call(
            env, thiz, mp->getDefaultBufferingSettings(&settings),
            "java/lang/IllegalStateException", "unexpected error");
    ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());

    return bp.asJobject(env, gBufferingParamsFields);
}

static jobject
android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
{
@@ -1436,7 +1417,6 @@ static const JNINativeMethod gMethods[] = {
    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
    {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
    {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},