diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-11-17 16:38:15 -0500 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-11-17 16:38:15 -0500 |
commit | 6a65f2da209bff03cb0eb6da309710ac6ee5026d (patch) | |
tree | 48e2090e716d4178378cb0599fc5d9cffbcf3f63 /android/media | |
parent | 46c77c203439b3b37c99d09e326df4b1fe08c10b (diff) | |
download | android-28-6a65f2da209bff03cb0eb6da309710ac6ee5026d.tar.gz |
Import Android SDK Platform P [4456821]
/google/data/ro/projects/android/fetch_artifact \
--bid 4456821 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4456821.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: I2d206b200d7952f899a5d1647ab532638cc8dd43
Diffstat (limited to 'android/media')
-rw-r--r-- | android/media/AudioAttributes.java | 1 | ||||
-rw-r--r-- | android/media/AudioManager.java | 26 | ||||
-rw-r--r-- | android/media/BufferingParams.java | 384 | ||||
-rw-r--r-- | android/media/ExifInterface.java | 83 | ||||
-rw-r--r-- | android/media/MediaDrm.java | 2 | ||||
-rw-r--r-- | android/media/MediaFormat.java | 90 | ||||
-rw-r--r-- | android/media/MediaMetadataRetriever.java | 162 | ||||
-rw-r--r-- | android/media/MediaPlayer.java | 180 |
8 files changed, 535 insertions, 393 deletions
diff --git a/android/media/AudioAttributes.java b/android/media/AudioAttributes.java index 20405d3b..7afe267f 100644 --- a/android/media/AudioAttributes.java +++ b/android/media/AudioAttributes.java @@ -244,6 +244,7 @@ public final class AudioAttributes implements Parcelable { SUPPRESSIBLE_USAGES.put(USAGE_GAME, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER); SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER); SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER); + SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER); } /** diff --git a/android/media/AudioManager.java b/android/media/AudioManager.java index dab7632a..58976ca0 100644 --- a/android/media/AudioManager.java +++ b/android/media/AudioManager.java @@ -43,17 +43,16 @@ import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; -import android.os.SystemClock; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; +import android.util.Slog; import android.view.KeyEvent; import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -1966,9 +1965,28 @@ public class AudioManager { */ private boolean querySoundEffectsEnabled(int user) { return Settings.System.getIntForUser(getContext().getContentResolver(), - Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0; + Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0 + && !areSystemSoundsZenModeBlocked(getContext()); } + private boolean areSystemSoundsZenModeBlocked(Context context) { + int zenMode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.ZEN_MODE, 0); + + switch (zenMode) { + case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS: + case Settings.Global.ZEN_MODE_ALARMS: + return true; + case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: + final NotificationManager noMan = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + return (noMan.getNotificationPolicy().priorityCategories + & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0; + case Settings.Global.ZEN_MODE_OFF: + default: + return false; + } + } /** * Load Sound effects. diff --git a/android/media/BufferingParams.java b/android/media/BufferingParams.java index 681271b1..521e8975 100644 --- a/android/media/BufferingParams.java +++ b/android/media/BufferingParams.java @@ -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, - * {@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>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 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 + * Return initial buffering mark in milliseconds. + * @return initial buffering mark in milliseconds */ - public int getInitialBufferingWatermarkKB() { - return mInitialWatermarkKB; + public int getInitialMarkMs() { + return mInitialMarkMs; } /** - * 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 + * Return the mark in milliseconds for resuming playback. + * @return the mark for resuming playback 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; + 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) - * .build(); + * .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); } } diff --git a/android/media/ExifInterface.java b/android/media/ExifInterface.java index ba41a7bd..91754162 100644 --- a/android/media/ExifInterface.java +++ b/android/media/ExifInterface.java @@ -2564,51 +2564,66 @@ public class ExifInterface { }); } + String hasImage = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE); String hasVideo = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO); - final String METADATA_HAS_VIDEO_VALUE_YES = "yes"; - if (METADATA_HAS_VIDEO_VALUE_YES.equals(hasVideo)) { - String width = retriever.extractMetadata( + String width = null; + String height = null; + String rotation = null; + final String METADATA_VALUE_YES = "yes"; + // If the file has both image and video, prefer image info over video info. + // App querying ExifInterface is most likely using the bitmap path which + // picks the image first. + if (METADATA_VALUE_YES.equals(hasImage)) { + width = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH); + height = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT); + rotation = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION); + } else if (METADATA_VALUE_YES.equals(hasVideo)) { + width = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); - String height = retriever.extractMetadata( + height = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + rotation = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); + } - if (width != null) { - mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, - ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder)); - } - - if (height != null) { - mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, - ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder)); - } + if (width != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, + ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder)); + } - String rotation = retriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); - if (rotation != null) { - int orientation = ExifInterface.ORIENTATION_NORMAL; + if (height != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, + ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder)); + } - // all rotation angles in CW - switch (Integer.parseInt(rotation)) { - case 90: - orientation = ExifInterface.ORIENTATION_ROTATE_90; - break; - case 180: - orientation = ExifInterface.ORIENTATION_ROTATE_180; - break; - case 270: - orientation = ExifInterface.ORIENTATION_ROTATE_270; - break; - } + if (rotation != null) { + int orientation = ExifInterface.ORIENTATION_NORMAL; - mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, - ExifAttribute.createUShort(orientation, mExifByteOrder)); + // all rotation angles in CW + switch (Integer.parseInt(rotation)) { + case 90: + orientation = ExifInterface.ORIENTATION_ROTATE_90; + break; + case 180: + orientation = ExifInterface.ORIENTATION_ROTATE_180; + break; + case 270: + orientation = ExifInterface.ORIENTATION_ROTATE_270; + break; } - if (DEBUG) { - Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation); - } + mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, + ExifAttribute.createUShort(orientation, mExifByteOrder)); + } + + if (DEBUG) { + Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation); } } finally { retriever.release(); diff --git a/android/media/MediaDrm.java b/android/media/MediaDrm.java index 88b1c5ff..1feea890 100644 --- a/android/media/MediaDrm.java +++ b/android/media/MediaDrm.java @@ -994,7 +994,6 @@ public final class MediaDrm { * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION}, * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS} */ - /* FIXME this throws IllegalStateException for invalid property names */ @NonNull public native String getPropertyString(@NonNull @StringProperty String propertyName); @@ -1002,7 +1001,6 @@ public final class MediaDrm { * Byte array property name: the device unique identifier is established during * device provisioning and provides a means of uniquely identifying each device. */ - /* FIXME this throws IllegalStateException for invalid property names */ public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; /** @hide */ diff --git a/android/media/MediaFormat.java b/android/media/MediaFormat.java index ed5f7d84..c475e122 100644 --- a/android/media/MediaFormat.java +++ b/android/media/MediaFormat.java @@ -96,6 +96,19 @@ import java.util.Map; * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr> * <tr><td>{@link #KEY_LANGUAGE}</td><td>String</td><td>The language of the content.</td></tr> * </table> + * + * Image formats have the following keys: + * <table> + * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr> + * <tr><td>{@link #KEY_WIDTH}</td><td>Integer</td><td></td></tr> + * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr> + * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user + * for encoders, readable in the output format of decoders</b></td></tr> + * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr> + * </table> */ public final class MediaFormat { public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; @@ -126,6 +139,35 @@ public final class MediaFormat { public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; /** + * MIME type for HEIF still image data encoded in HEVC. + * + * To decode such an image, {@link MediaCodec} decoder for + * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form + * the correct {@link #MediaFormat} based on additional information in + * the track format, and send it to {@link MediaCodec#configure}. + * + * The track's MediaFormat will come with {@link #KEY_WIDTH} and + * {@link #KEY_HEIGHT} keys, which describes the width and height + * of the image. If the image doesn't contain grid (i.e. none of + * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT}, + * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the + * track will contain a single sample of coded data for the entire image, + * and the image width and height should be used to set up the decoder. + * + * If the image does come with grid, each sample from the track will + * contain one tile in the grid, of which the size is described by + * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size + * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be + * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS} + * by {@link #KEY_GRID_COLS} samples in row-major, top-row first, + * left-to-right order. The output image should be reconstructed by + * first tiling the decoding results of the tiles in the correct order, + * then trimming (before rotation is applied) on the bottom and right + * side, if the tiled area is larger than the image width and height. + */ + public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; + + /** * MIME type for WebVTT subtitle data. */ public static final String MIMETYPE_TEXT_VTT = "text/vtt"; @@ -232,6 +274,54 @@ public final class MediaFormat { public static final String KEY_FRAME_RATE = "frame-rate"; /** + * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} + * track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_ROWS + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_WIDTH = "grid-width"; + + /** + * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} + * track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_ROWS + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_HEIGHT = "grid-height"; + + /** + * A key describing the number of grid rows in the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_ROWS = "grid-rows"; + + /** + * A key describing the number of grid columns in the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_ROWS + */ + public static final String KEY_GRID_COLS = "grid-cols"; + + /** * A key describing the raw audio sample encoding/format. * * <p>The associated value is an integer, using one of the diff --git a/android/media/MediaMetadataRetriever.java b/android/media/MediaMetadataRetriever.java index 760cc49b..0b864018 100644 --- a/android/media/MediaMetadataRetriever.java +++ b/android/media/MediaMetadataRetriever.java @@ -47,7 +47,7 @@ public class MediaMetadataRetriever // The field below is accessed by native methods @SuppressWarnings("unused") private long mNativeContext; - + private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF; public MediaMetadataRetriever() { @@ -58,7 +58,7 @@ public class MediaMetadataRetriever * Sets the data source (file pathname) to use. Call this * method before the rest of the methods in this class. This method may be * time-consuming. - * + * * @param path The path of the input media file. * @throws IllegalArgumentException If the path is invalid. */ @@ -113,7 +113,7 @@ public class MediaMetadataRetriever * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. - * + * * @param fd the FileDescriptor for the file you want to play * @param offset the offset into the file where the data to be played starts, * in bytes. It must be non-negative @@ -123,13 +123,13 @@ public class MediaMetadataRetriever */ public native void setDataSource(FileDescriptor fd, long offset, long length) throws IllegalArgumentException; - + /** * Sets the data source (FileDescriptor) to use. It is the caller's * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. - * + * * @param fd the FileDescriptor for the file you want to play * @throws IllegalArgumentException if the FileDescriptor is invalid */ @@ -138,11 +138,11 @@ public class MediaMetadataRetriever // intentionally less than LONG_MAX setDataSource(fd, 0, 0x7ffffffffffffffL); } - + /** - * Sets the data source as a content Uri. Call this method before + * Sets the data source as a content Uri. Call this method before * the rest of the methods in this class. This method may be time-consuming. - * + * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @throws IllegalArgumentException if the Uri is invalid @@ -154,7 +154,7 @@ public class MediaMetadataRetriever if (uri == null) { throw new IllegalArgumentException(); } - + String scheme = uri.getScheme(); if(scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); @@ -213,12 +213,12 @@ public class MediaMetadataRetriever /** * Call this method after setDataSource(). This method retrieves the * meta data value associated with the keyCode. - * + * * The keyCode currently supported is listed below as METADATA_XXX * constants. With any other value, it returns a null pointer. - * + * * @param keyCode One of the constants listed below at the end of the class. - * @return The meta data value associate with the given keyCode on success; + * @return The meta data value associate with the given keyCode on success; * null on failure. */ public native String extractMetadata(int keyCode); @@ -357,6 +357,109 @@ public class MediaMetadataRetriever private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height); /** + * This method retrieves a video frame by its index. It should only be called + * after {@link #setDataSource}. + * + * @param frameIndex 0-based index of the video frame. The frame index must be that of + * a valid frame. The total number of frames available for retrieval can be queried + * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. + * + * @throws IllegalStateException if the container doesn't contain video or image sequences. + * @throws IllegalArgumentException if the requested frame index does not exist. + * + * @return A Bitmap containing the requested video frame, or null if the retrieval fails. + * + * @see #getFramesAtIndex(int, int) + */ + public Bitmap getFrameAtIndex(int frameIndex) { + Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1); + if (bitmaps == null || bitmaps.length < 1) { + return null; + } + return bitmaps[0]; + } + + /** + * This method retrieves a consecutive set of video frames starting at the + * specified index. It should only be called after {@link #setDataSource}. + * + * If the caller intends to retrieve more than one consecutive video frames, + * this method is preferred over {@link #getFrameAtIndex(int)} for efficiency. + * + * @param frameIndex 0-based index of the first video frame to retrieve. The frame index + * must be that of a valid frame. The total number of frames available for retrieval + * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. + * @param numFrames number of consecutive video frames to retrieve. Must be a positive + * value. The stream must contain at least numFrames frames starting at frameIndex. + * + * @throws IllegalStateException if the container doesn't contain video or image sequences. + * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the + * stream doesn't contain at least numFrames starting at frameIndex. + + * @return An array of Bitmaps containing the requested video frames. The returned + * array could contain less frames than requested if the retrieval fails. + * + * @see #getFrameAtIndex(int) + */ + public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) { + if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { + throw new IllegalStateException("Does not contail video or image sequences"); + } + int frameCount = Integer.parseInt( + extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)); + if (frameIndex < 0 || numFrames < 1 + || frameIndex >= frameCount + || frameIndex > frameCount - numFrames) { + throw new IllegalArgumentException("Invalid frameIndex or numFrames: " + + frameIndex + ", " + numFrames); + } + return _getFrameAtIndex(frameIndex, numFrames); + } + private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames); + + /** + * This method retrieves a still image by its index. It should only be called + * after {@link #setDataSource}. + * + * @param imageIndex 0-based index of the image, with negative value indicating + * the primary image. + * @throws IllegalStateException if the container doesn't contain still images. + * @throws IllegalArgumentException if the requested image does not exist. + * + * @return the requested still image, or null if the image cannot be retrieved. + * + * @see #getPrimaryImage + */ + public Bitmap getImageAtIndex(int imageIndex) { + if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { + throw new IllegalStateException("Does not contail still images"); + } + + String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); + if (imageIndex >= Integer.parseInt(imageCount)) { + throw new IllegalArgumentException("Invalid image index: " + imageCount); + } + + return _getImageAtIndex(imageIndex); + } + + /** + * This method retrieves the primary image of the media content. It should only + * be called after {@link #setDataSource}. + * + * @return the primary image, or null if it cannot be retrieved. + * + * @throws IllegalStateException if the container doesn't contain still images. + * + * @see #getImageAtIndex(int) + */ + public Bitmap getPrimaryImage() { + return getImageAtIndex(-1); + } + + private native Bitmap _getImageAtIndex(int imageIndex); + + /** * Call this method after setDataSource(). This method finds the optional * graphic or album/cover art associated associated with the data source. If * there are more than one pictures, (any) one of them is returned. @@ -572,5 +675,40 @@ public class MediaMetadataRetriever * number. */ public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; + /** + * If this key exists the media contains still image content. + */ + public static final int METADATA_KEY_HAS_IMAGE = 26; + /** + * If the media contains still images, this key retrieves the number + * of still images. + */ + public static final int METADATA_KEY_IMAGE_COUNT = 27; + /** + * If the media contains still images, this key retrieves the image + * index of the primary image. + */ + public static final int METADATA_KEY_IMAGE_PRIMARY = 28; + /** + * If the media contains still images, this key retrieves the width + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_WIDTH = 29; + /** + * If the media contains still images, this key retrieves the height + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_HEIGHT = 30; + /** + * If the media contains still images, this key retrieves the rotation + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_ROTATION = 31; + /** + * If the media contains video and this key exists, it retrieves the + * total number of frames in the video sequence. + */ + public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; + // Add more here... } diff --git a/android/media/MediaPlayer.java b/android/media/MediaPlayer.java index 62757e2e..649c091b 100644 --- a/android/media/MediaPlayer.java +++ b/android/media/MediaPlayer.java @@ -43,6 +43,7 @@ import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.util.Pair; +import android.util.ArrayMap; import android.view.Surface; import android.view.SurfaceHolder; import android.widget.VideoView; @@ -58,6 +59,7 @@ import android.media.SubtitleData; import android.media.SubtitleTrack.RenderingWidget; import android.media.SyncParams; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import libcore.io.IoBridge; @@ -577,6 +579,7 @@ import java.util.Vector; public class MediaPlayer extends PlayerBase implements SubtitleController.Listener , VolumeAutomation + , AudioRouting { /** Constant to retrieve only the new metadata since the last @@ -1417,6 +1420,155 @@ public class MediaPlayer extends PlayerBase private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id); + //-------------------------------------------------------------------------- + // Explicit Routing + //-------------------- + private AudioDeviceInfo mPreferredDevice = null; + + /** + * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route + * the output from this MediaPlayer. + * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source. + * If deviceInfo is null, default routing is restored. + * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and + * does not correspond to a valid audio device. + */ + @Override + public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) { + if (deviceInfo != null && !deviceInfo.isSink()) { + return false; + } + int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; + boolean status = native_setOutputDevice(preferredDeviceId); + if (status == true) { + synchronized (this) { + mPreferredDevice = deviceInfo; + } + } + return status; + } + + /** + * Returns the selected output specified by {@link #setPreferredDevice}. Note that this + * is not guaranteed to correspond to the actual device being used for playback. + */ + @Override + public AudioDeviceInfo getPreferredDevice() { + synchronized (this) { + return mPreferredDevice; + } + } + + /** + * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer + * Note: The query is only valid if the MediaPlayer is currently playing. + * If the player is not playing, the returned device can be null or correspond to previously + * selected device when the player was last active. + */ + @Override + public AudioDeviceInfo getRoutedDevice() { + int deviceId = native_getRoutedDeviceId(); + if (deviceId == 0) { + return null; + } + AudioDeviceInfo[] devices = + AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS); + for (int i = 0; i < devices.length; i++) { + if (devices[i].getId() == deviceId) { + return devices[i]; + } + } + return null; + } + + /* + * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. + */ + private void enableNativeRoutingCallbacksLocked(boolean enabled) { + if (mRoutingChangeListeners.size() == 0) { + native_enableDeviceCallback(enabled); + } + } + + /** + * The list of AudioRouting.OnRoutingChangedListener interfaces added (with + * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)} + * by an app to receive (re)routing notifications. + */ + @GuardedBy("mRoutingChangeListeners") + private ArrayMap<AudioRouting.OnRoutingChangedListener, + NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); + + /** + * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing + * changes on this MediaPlayer. + * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive + * notifications of rerouting events. + * @param handler Specifies the {@link Handler} object for the thread on which to execute + * the callback. If <code>null</code>, the handler on the main looper will be used. + */ + @Override + public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, + Handler handler) { + synchronized (mRoutingChangeListeners) { + if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { + enableNativeRoutingCallbacksLocked(true); + mRoutingChangeListeners.put( + listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); + } + } + } + + /** + * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added + * to receive rerouting notifications. + * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface + * to remove. + */ + @Override + public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) { + synchronized (mRoutingChangeListeners) { + if (mRoutingChangeListeners.containsKey(listener)) { + mRoutingChangeListeners.remove(listener); + enableNativeRoutingCallbacksLocked(false); + } + } + } + + /** + * Helper class to handle the forwarding of native events to the appropriate listener + * (potentially) handled in a different thread + */ + private class NativeRoutingEventHandlerDelegate { + private MediaPlayer mMediaPlayer; + private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; + private Handler mHandler; + + NativeRoutingEventHandlerDelegate(final MediaPlayer mediaPlayer, + final AudioRouting.OnRoutingChangedListener listener, Handler handler) { + mMediaPlayer = mediaPlayer; + mOnRoutingChangedListener = listener; + mHandler = handler != null ? handler : mEventHandler; + } + + void notifyClient() { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mOnRoutingChangedListener != null) { + mOnRoutingChangedListener.onRoutingChanged(mMediaPlayer); + } + } + }); + } + } + } + + private native final boolean native_setOutputDevice(int deviceId); + private native final int native_getRoutedDeviceId(); + private native final void native_enableDeviceCallback(boolean enabled); + /** * Set the low-level power management behavior for this MediaPlayer. This * can be used when the MediaPlayer is not playing through a SurfaceHolder @@ -1546,21 +1698,9 @@ 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 @@ -1575,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. * @@ -3176,6 +3315,7 @@ public class MediaPlayer extends PlayerBase private static final int MEDIA_SUBTITLE_DATA = 201; private static final int MEDIA_META_DATA = 202; private static final int MEDIA_DRM_INFO = 210; + private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000; private TimeProvider mTimeProvider; @@ -3414,6 +3554,16 @@ public class MediaPlayer extends PlayerBase case MEDIA_NOP: // interface test message - ignore break; + case MEDIA_AUDIO_ROUTING_CHANGED: + AudioManager.resetAudioPortGeneration(); + synchronized (mRoutingChangeListeners) { + for (NativeRoutingEventHandlerDelegate delegate + : mRoutingChangeListeners.values()) { + delegate.notifyClient(); + } + } + return; + default: Log.e(TAG, "Unknown message type " + msg.what); return; |