diff options
| author | 2017-01-25 21:53:29 +0000 | |
|---|---|---|
| committer | 2017-01-25 21:53:32 +0000 | |
| commit | 3416cc280034b4e617768ecb2d51f55fda7f576b (patch) | |
| tree | 6d2d7ea4f3c1d6b7cd077345116a356acfbb358d | |
| parent | 5b15e376783861d451d9d8c05628131d5c0871c2 (diff) | |
| parent | 23dfee521a7782bf901344beec6f94614e0b02b8 (diff) | |
Merge "mediaplayer: add buffering settings API"
| -rw-r--r-- | api/current.txt | 37 | ||||
| -rw-r--r-- | api/system-current.txt | 37 | ||||
| -rw-r--r-- | api/test-current.txt | 37 | ||||
| -rw-r--r-- | media/java/android/media/BufferingParams.aidl | 19 | ||||
| -rw-r--r-- | media/java/android/media/BufferingParams.java | 459 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer.java | 45 | ||||
| -rw-r--r-- | media/jni/android_media_BufferingParams.h | 111 | ||||
| -rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 66 |
8 files changed, 811 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt index 1228db83907c..85747c1dc91a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20900,6 +20900,40 @@ package android.media { method public default void onRoutingChanged(android.media.AudioRouting); } + public final class BufferingParams implements android.os.Parcelable { + method public int describeContents(); + method public int getInitialBufferingMode(); + method public int getInitialBufferingWatermarkKB(); + method public int getInitialBufferingWatermarkMs(); + method public int getRebufferingMode(); + method public int getRebufferingWatermarkHighKB(); + method public int getRebufferingWatermarkHighMs(); + method public int getRebufferingWatermarkLowKB(); + method public int getRebufferingWatermarkLowMs(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int BUFFERING_MODE_NONE = 0; // 0x0 + field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2 + field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1 + field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3 + field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR; + } + + public static class BufferingParams.Builder { + ctor public BufferingParams.Builder(); + ctor public BufferingParams.Builder(android.media.BufferingParams); + method public android.media.BufferingParams build(); + method public android.media.BufferingParams.Builder setInitialBufferingMode(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int); + method public android.media.BufferingParams.Builder setRebufferingMode(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -22072,7 +22106,9 @@ package android.media { method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int); method public void deselectTrack(int) throws java.lang.IllegalStateException; method public int getAudioSessionId(); + method public android.media.BufferingParams getBufferingParams(); method public int getCurrentPosition(); + method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22095,6 +22131,7 @@ package android.media { method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public deprecated void setAudioStreamType(int); method public void setAuxEffectSendLevel(float); + method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; diff --git a/api/system-current.txt b/api/system-current.txt index 4698a6810012..6a37e8289ac4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -22500,6 +22500,40 @@ package android.media { method public default void onRoutingChanged(android.media.AudioRouting); } + public final class BufferingParams implements android.os.Parcelable { + method public int describeContents(); + method public int getInitialBufferingMode(); + method public int getInitialBufferingWatermarkKB(); + method public int getInitialBufferingWatermarkMs(); + method public int getRebufferingMode(); + method public int getRebufferingWatermarkHighKB(); + method public int getRebufferingWatermarkHighMs(); + method public int getRebufferingWatermarkLowKB(); + method public int getRebufferingWatermarkLowMs(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int BUFFERING_MODE_NONE = 0; // 0x0 + field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2 + field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1 + field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3 + field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR; + } + + public static class BufferingParams.Builder { + ctor public BufferingParams.Builder(); + ctor public BufferingParams.Builder(android.media.BufferingParams); + method public android.media.BufferingParams build(); + method public android.media.BufferingParams.Builder setInitialBufferingMode(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int); + method public android.media.BufferingParams.Builder setRebufferingMode(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -23672,7 +23706,9 @@ package android.media { method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int); method public void deselectTrack(int) throws java.lang.IllegalStateException; method public int getAudioSessionId(); + method public android.media.BufferingParams getBufferingParams(); method public int getCurrentPosition(); + method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -23695,6 +23731,7 @@ package android.media { method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public deprecated void setAudioStreamType(int); method public void setAuxEffectSendLevel(float); + method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; diff --git a/api/test-current.txt b/api/test-current.txt index 15844cd01cd7..d67c02248f87 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -20990,6 +20990,40 @@ package android.media { method public default void onRoutingChanged(android.media.AudioRouting); } + public final class BufferingParams implements android.os.Parcelable { + method public int describeContents(); + method public int getInitialBufferingMode(); + method public int getInitialBufferingWatermarkKB(); + method public int getInitialBufferingWatermarkMs(); + method public int getRebufferingMode(); + method public int getRebufferingWatermarkHighKB(); + method public int getRebufferingWatermarkHighMs(); + method public int getRebufferingWatermarkLowKB(); + method public int getRebufferingWatermarkLowMs(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int BUFFERING_MODE_NONE = 0; // 0x0 + field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2 + field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1 + field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3 + field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR; + } + + public static class BufferingParams.Builder { + ctor public BufferingParams.Builder(); + ctor public BufferingParams.Builder(android.media.BufferingParams); + method public android.media.BufferingParams build(); + method public android.media.BufferingParams.Builder setInitialBufferingMode(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int); + method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int); + method public android.media.BufferingParams.Builder setRebufferingMode(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int); + method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -22162,7 +22196,9 @@ package android.media { method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int); method public void deselectTrack(int) throws java.lang.IllegalStateException; method public int getAudioSessionId(); + method public android.media.BufferingParams getBufferingParams(); method public int getCurrentPosition(); + method public android.media.BufferingParams getDefaultBufferingParams(); method public int getDuration(); method public android.media.PlaybackParams getPlaybackParams(); method public int getSelectedTrack(int) throws java.lang.IllegalStateException; @@ -22185,6 +22221,7 @@ package android.media { method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public deprecated void setAudioStreamType(int); method public void setAuxEffectSendLevel(float); + method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; diff --git a/media/java/android/media/BufferingParams.aidl b/media/java/android/media/BufferingParams.aidl new file mode 100644 index 000000000000..d156d44cd7df --- /dev/null +++ b/media/java/android/media/BufferingParams.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2017 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 android.media; + +parcelable BufferingParams; diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java new file mode 100644 index 000000000000..fdcd6baa7d18 --- /dev/null +++ b/media/java/android/media/BufferingParams.java @@ -0,0 +1,459 @@ +/* + * Copyright 2017 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 android.media; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Structure for source buffering management params. + * + * Used by {@link MediaPlayer#getDefaultBufferingParams()}, + * {@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. + * + * <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}. + * <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, + * {@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. + * <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> + * </ul> + * <p>Users should use {@link Builder} to change {@link BufferingParams}. + */ +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; + + // 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 mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK; + private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK; + private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK; + private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK; + + 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 + */ + public int getRebufferingWatermarkHighMs() { + return mRebufferingWatermarkHighMs; + } + + /** + * 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 + */ + 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; + } + + /** + * Builder class for {@link BufferingParams} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link BufferingParams} to be used by a {@link MediaPlayer} instance: + * + * <pre class="prettyprint"> + * BufferingParams myParams = mediaplayer.getDefaultBufferingParams(); + * myParams = new BufferingParams.Builder(myParams) + * .setInitialBufferingWatermarkMs(10000) + * .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; + + /** + * 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. + */ + public Builder() { + } + + /** + * Constructs a new Builder from a given {@link BufferingParams} instance + * @param bp the {@link BufferingParams} object whose data will be reused + * 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; + } + + /** + * Combines all of the fields that have been set and return a new + * {@link BufferingParams} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * @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.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}, + * @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); + } + 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}, + * @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; + 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(); + } + + public static final Parcelable.Creator<BufferingParams> CREATOR = + new Parcelable.Creator<BufferingParams>() { + @Override + public BufferingParams createFromParcel(Parcel in) { + return new BufferingParams(in); + } + + @Override + public BufferingParams[] newArray(int size) { + return new BufferingParams[size]; + } + }; + + + @Override + public int describeContents() { + return 0; + } + + @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); + } +} diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index e3a0f25d5ccd..4023400b71c4 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -45,6 +45,7 @@ import android.view.SurfaceHolder; import android.widget.VideoView; import android.graphics.SurfaceTexture; import android.media.AudioManager; +import android.media.BufferingParams; import android.media.MediaFormat; import android.media.MediaTimeProvider; import android.media.PlaybackParams; @@ -479,6 +480,11 @@ import java.lang.ref.WeakReference; * <td>{} </p></td> * <td>This method can be called in any state and calling it does not change * the object state. </p></td></tr> + * <tr><td>setBufferingParams</p></td> + * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}</p></td> + * <td>{Idle} </p></td> + * <td>This method does not change the object state. + * </p></td></tr> * <tr><td>setPlaybackParams</p></td> * <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td> * <td>{Idle, Stopped} </p></td> @@ -1390,6 +1396,45 @@ 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. + */ + @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. + * + * @return the current buffering management params used by the source component. + * @throws IllegalStateException if the internal player engine has not been + * initialized, or {@code setDataSource} has not been called. + */ + @NonNull + public native BufferingParams getBufferingParams(); + + /** + * Sets buffering management params. + * 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}. + * + * @param params the buffering management params. + * + * @throws IllegalStateException if the internal player engine has not been + * initialized or has been released, or {@code setDataSource} has not been called. + * @throws IllegalArgumentException if params is invalid or not supported. + */ + public native void setBufferingParams(@NonNull BufferingParams params); + + /** * Change playback speed of audio by resampling the audio. * <p> * Specifies resampling as audio mode for variable rate playback, i.e., diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h new file mode 100644 index 000000000000..24c51f5f00d4 --- /dev/null +++ b/media/jni/android_media_BufferingParams.h @@ -0,0 +1,111 @@ +/* + * Copyright 2017, 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. + */ + +#ifndef _ANDROID_MEDIA_BUFFERING_PARAMS_H_ +#define _ANDROID_MEDIA_BUFFERING_PARAMS_H_ + +#include <media/BufferingSettings.h> + +namespace android { + +// This entire class is inline +struct BufferingParams { + BufferingSettings settings; + + struct fields_t { + jclass clazz; + jmethodID constructID; + + jfieldID initial_buffering_mode; + jfieldID rebuffering_mode; + jfieldID initial_watermark_ms; + jfieldID initial_watermark_kb; + jfieldID rebuffering_watermark_low_ms; + jfieldID rebuffering_watermark_high_ms; + jfieldID rebuffering_watermark_low_kb; + jfieldID rebuffering_watermark_high_kb; + + void init(JNIEnv *env) { + jclass lclazz = env->FindClass("android/media/BufferingParams"); + if (lclazz == NULL) { + return; + } + + clazz = (jclass)env->NewGlobalRef(lclazz); + if (clazz == NULL) { + return; + } + + constructID = env->GetMethodID(clazz, "<init>", "()V"); + + initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I"); + rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I"); + initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I"); + initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I"); + rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I"); + rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I"); + rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I"); + rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I"); + + env->DeleteLocalRef(lclazz); + } + + void exit(JNIEnv *env) { + env->DeleteGlobalRef(clazz); + clazz = NULL; + } + }; + + void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) { + settings.mInitialBufferingMode = + (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode); + settings.mRebufferingMode = + (BufferingMode)env->GetIntField(params, fields.rebuffering_mode); + settings.mInitialWatermarkMs = + env->GetIntField(params, fields.initial_watermark_ms); + settings.mInitialWatermarkKB = + env->GetIntField(params, fields.initial_watermark_kb); + settings.mRebufferingWatermarkLowMs = + env->GetIntField(params, fields.rebuffering_watermark_low_ms); + settings.mRebufferingWatermarkHighMs = + env->GetIntField(params, fields.rebuffering_watermark_high_ms); + settings.mRebufferingWatermarkLowKB = + env->GetIntField(params, fields.rebuffering_watermark_low_kb); + settings.mRebufferingWatermarkHighKB = + env->GetIntField(params, fields.rebuffering_watermark_high_kb); + } + + jobject asJobject(JNIEnv *env, const fields_t& fields) { + jobject params = env->NewObject(fields.clazz, fields.constructID); + if (params == NULL) { + return NULL; + } + env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode); + env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode); + env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs); + env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB); + env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs); + env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs); + env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB); + env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB); + + return params; + } +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_BUFFERING_PARAMS_H_ diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index c52ed941af85..822505242426 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -37,6 +37,7 @@ #include "utils/Errors.h" // for status_t #include "utils/KeyedVector.h" #include "utils/String8.h" +#include "android_media_BufferingParams.h" #include "android_media_MediaDataSource.h" #include "android_media_PlaybackParams.h" #include "android_media_SyncParams.h" @@ -69,6 +70,7 @@ struct fields_t { }; static fields_t fields; +static BufferingParams::fields_t gBufferingParamsFields; static PlaybackParams::fields_t gPlaybackParamsFields; static SyncParams::fields_t gSyncParamsFields; @@ -343,6 +345,66 @@ 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) +{ + 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->getBufferingSettings(&settings), + "java/lang/IllegalStateException", "unexpected error"); + ALOGV("getBufferingSettings:{%s}", settings.toString().string()); + + return bp.asJobject(env, gBufferingParamsFields); +} + +static void +android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params) +{ + if (params == NULL) { + return; + } + + sp<MediaPlayer> mp = getMediaPlayer(env, thiz); + if (mp == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + BufferingParams bp; + bp.fillFromJobject(env, gBufferingParamsFields, params); + ALOGV("setBufferingParams:{%s}", bp.settings.toString().string()); + + process_media_player_call( + env, thiz, mp->setBufferingSettings(bp.settings), + "java/lang/IllegalStateException", "unexpected error"); +} + static void android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) { @@ -860,6 +922,7 @@ android_media_MediaPlayer_native_init(JNIEnv *env) env->DeleteLocalRef(clazz); + gBufferingParamsFields.init(env); gPlaybackParamsFields.init(env); gSyncParamsFields.init(env); } @@ -1046,6 +1109,9 @@ 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}, {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, {"_start", "()V", (void *)android_media_MediaPlayer_start}, |