diff options
| author | 2018-11-20 22:06:36 +0000 | |
|---|---|---|
| committer | 2018-11-20 22:06:36 +0000 | |
| commit | 0a91bc975e73fa1999a57a3ad419cb0099a2f584 (patch) | |
| tree | a6949841c81cc277f0879f9c9b85c3f3bc36cb50 | |
| parent | 197f2421103e46c78970a61b33ffe3e55b976c81 (diff) | |
| parent | 1bdf879d5ff031efc1d40647d6ae211c02a6478b (diff) | |
Merge "DataSourceDesc: refactor to base class and subclasses"
| -rw-r--r-- | media/java/android/media/CallbackDataSourceDesc.java | 113 | ||||
| -rw-r--r-- | media/java/android/media/DataSourceDesc.java | 388 | ||||
| -rw-r--r-- | media/java/android/media/FileDataSourceDesc.java | 186 | ||||
| -rw-r--r-- | media/java/android/media/MediaPlayer2.java | 60 | ||||
| -rw-r--r-- | media/java/android/media/UriDataSourceDesc.java | 228 |
5 files changed, 594 insertions, 381 deletions
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java new file mode 100644 index 000000000000..a7e168f60ae8 --- /dev/null +++ b/media/java/android/media/CallbackDataSourceDesc.java @@ -0,0 +1,113 @@ +/* + * Copyright 2018 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.NonNull; + +import com.android.internal.util.Preconditions; + +/** + * @hide + * Structure for file data source descriptor. + * + * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)} + * to set data source for playback. + * + * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}. + * + */ +public class CallbackDataSourceDesc extends DataSourceDesc { + private Media2DataSource mMedia2DataSource; + + private CallbackDataSourceDesc() { + } + + /** + * Return the Media2DataSource of this data source. + * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}. + * @return the Media2DataSource of this data source + */ + public Media2DataSource getMedia2DataSource() { + return mMedia2DataSource; + } + + /** + * Builder class for {@link CallbackDataSourceDesc} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link CallbackDataSourceDesc} to be used by a {@link MediaPlayer2} instance: + * + * <pre class="prettyprint"> + * CallbackDataSourceDesc newDSD = new CallbackDataSourceDesc.Builder() + * .setDataSource(media2DataSource) + * .setStartPosition(1000) + * .setEndPosition(15000) + * .build(); + * mediaplayer2.setDataSourceDesc(newDSD); + * </pre> + */ + public static class Builder extends BuilderBase<Builder> { + private Media2DataSource mMedia2DataSource; + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + super(); + } + + /** + * Constructs a new Builder from a given {@link CallbackDataSourceDesc} instance + * @param dsd the {@link CallbackDataSourceDesc} object whose data will be reused + * in the new Builder. + */ + public Builder(CallbackDataSourceDesc dsd) { + super(dsd); + if (dsd == null) { + return; // use default + } + mMedia2DataSource = dsd.mMedia2DataSource; + } + + /** + * Combines all of the fields that have been set and return a new + * {@link CallbackDataSourceDesc} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * + * @return a new {@link CallbackDataSourceDesc} object + */ + public @NonNull CallbackDataSourceDesc build() { + CallbackDataSourceDesc dsd = new CallbackDataSourceDesc(); + super.build(dsd); + dsd.mMedia2DataSource = mMedia2DataSource; + + return dsd; + } + + /** + * Sets the data source (Media2DataSource) to use. + * + * @param m2ds the Media2DataSource for the media to play + * @return the same Builder instance. + * @throws NullPointerException if m2ds is null. + */ + public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) { + Preconditions.checkNotNull(m2ds); + mMedia2DataSource = m2ds; + return this; + } + } +} diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java index afc99a08ce3c..aed3f84a9eb2 100644 --- a/media/java/android/media/DataSourceDesc.java +++ b/media/java/android/media/DataSourceDesc.java @@ -16,49 +16,21 @@ package android.media; -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; import com.android.internal.util.Preconditions; -import java.io.FileDescriptor; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.HttpCookie; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * @hide - * Structure for data source descriptor. + * Base class of data source descriptor. * * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} * to set data source for playback. * - * <p>Users should use {@link Builder} to change {@link DataSourceDesc}. + * <p>Users should use subclasses' builder to change {@link DataSourceDesc}. * */ -public final class DataSourceDesc { - /* No data source has been set yet */ - public static final int TYPE_NONE = 0; - /* data source is type of MediaDataSource */ - public static final int TYPE_CALLBACK = 1; - /* data source is type of FileDescriptor */ - public static final int TYPE_FD = 2; - /* data source is type of Uri */ - public static final int TYPE_URI = 3; - +public class DataSourceDesc { // intentionally less than long.MAX_VALUE public static final long LONG_MAX = 0x7ffffffffffffffL; @@ -66,25 +38,13 @@ public final class DataSourceDesc { public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000; public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000; - - private int mType = TYPE_NONE; - - private Media2DataSource mMedia2DataSource; - - private FileDescriptor mFD; - private long mFDOffset = 0; - private long mFDLength = LONG_MAX; - - private Uri mUri; - private Map<String, String> mUriHeader; - private List<HttpCookie> mUriCookies; - private Context mUriContext; + public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS; private String mMediaId; private long mStartPositionMs = 0; - private long mEndPositionMs = LONG_MAX_TIME_MS; + private long mEndPositionMs = POSITION_UNKNOWN; - private DataSourceDesc() { + DataSourceDesc() { } /** @@ -105,194 +65,69 @@ public final class DataSourceDesc { /** * Return the position in milliseconds at which the playback will end. - * -1 means ending at the end of source content. + * {@link #POSITION_UNKNOWN} means ending at the end of source content. * @return the position in milliseconds at which the playback will end */ public long getEndPosition() { return mEndPositionMs; } - /** - * Return the type of data source. - * @return the type of data source - */ - public int getType() { - return mType; - } - - /** - * Return the Media2DataSource of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}. - * @return the Media2DataSource of this data source - */ - public Media2DataSource getMedia2DataSource() { - return mMedia2DataSource; - } - - /** - * Return the FileDescriptor of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_FD}. - * @return the FileDescriptor of this data source - */ - public FileDescriptor getFileDescriptor() { - return mFD; - } - - /** - * Return the offset associated with the FileDescriptor of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_FD} and it has - * been set by the {@link Builder}. - * @return the offset associated with the FileDescriptor of this data source - */ - public long getFileDescriptorOffset() { - return mFDOffset; + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("DataSourceDesc{"); + sb.append("mMediaId=").append(mMediaId); + sb.append(", mStartPositionMs=").append(mStartPositionMs); + sb.append(", mEndPositionMs=").append(mEndPositionMs); + sb.append('}'); + return sb.toString(); } /** - * Return the content length associated with the FileDescriptor of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_FD}. - * -1 means same as the length of source content. - * @return the content length associated with the FileDescriptor of this data source + * Base class for Builders in the subclasses of {@link DataSourceDesc}. */ - public long getFileDescriptorLength() { - return mFDLength; - } - - /** - * Return the Uri of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. - * @return the Uri of this data source - */ - public Uri getUri() { - return mUri; - } - - /** - * Return the Uri headers of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. - * @return the Uri headers of this data source - */ - public Map<String, String> getUriHeaders() { - if (mUriHeader == null) { - return null; - } - return new HashMap<String, String>(mUriHeader); - } - - /** - * Return the Uri cookies of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. - * @return the Uri cookies of this data source - */ - public List<HttpCookie> getUriCookies() { - if (mUriCookies == null) { - return null; - } - return new ArrayList<HttpCookie>(mUriCookies); - } - - /** - * Return the Context used for resolving the Uri of this data source. - * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. - * @return the Context used for resolving the Uri of this data source - */ - public Context getUriContext() { - return mUriContext; - } - - /** - * Builder class for {@link DataSourceDesc} objects. - * <p> Here is an example where <code>Builder</code> is used to define the - * {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance: - * - * <pre class="prettyprint"> - * DataSourceDesc oldDSD = mediaplayer2.getDataSourceDesc(); - * DataSourceDesc newDSD = new DataSourceDesc.Builder(oldDSD) - * .setStartPosition(1000) - * .setEndPosition(15000) - * .build(); - * mediaplayer2.setDataSourceDesc(newDSD); - * </pre> - */ - public static class Builder { - private int mType = TYPE_NONE; - - private Media2DataSource mMedia2DataSource; - - private FileDescriptor mFD; - private long mFDOffset = 0; - private long mFDLength = LONG_MAX; - - private Uri mUri; - private Map<String, String> mUriHeader; - private List<HttpCookie> mUriCookies; - private Context mUriContext; - + protected static class BuilderBase<T extends BuilderBase> { private String mMediaId; private long mStartPositionMs = 0; - private long mEndPositionMs = LONG_MAX_TIME_MS; + private long mEndPositionMs = POSITION_UNKNOWN; /** - * Constructs a new Builder with the defaults. + * Constructs a new BuilderBase with the defaults. */ - public Builder() { + BuilderBase() { } /** - * Constructs a new Builder from a given {@link DataSourceDesc} instance + * Constructs a new BuilderBase from a given {@link DataSourceDesc} instance * @param dsd the {@link DataSourceDesc} object whose data will be reused - * in the new Builder. + * in the new BuilderBase. */ - public Builder(DataSourceDesc dsd) { - mType = dsd.mType; - mMedia2DataSource = dsd.mMedia2DataSource; - mFD = dsd.mFD; - mFDOffset = dsd.mFDOffset; - mFDLength = dsd.mFDLength; - mUri = dsd.mUri; - mUriHeader = dsd.mUriHeader; - mUriCookies = dsd.mUriCookies; - mUriContext = dsd.mUriContext; - + BuilderBase(DataSourceDesc dsd) { + if (dsd == null) { + return; + } mMediaId = dsd.mMediaId; mStartPositionMs = dsd.mStartPositionMs; mEndPositionMs = dsd.mEndPositionMs; } /** - * Combines all of the fields that have been set and return a new - * {@link DataSourceDesc} object. <code>IllegalStateException</code> will be - * thrown if there is conflict between fields. + * Sets all fields that have been set in the {@link DataSourceDesc} object. + * <code>IllegalStateException</code> will be thrown if there is conflict between fields. * - * @return a new {@link DataSourceDesc} object + * @param dsd an instance of subclass of {@link DataSourceDesc} whose data will be set + * @return the same instance of subclass of {@link DataSourceDesc} */ - public DataSourceDesc build() { - if (mType != TYPE_CALLBACK - && mType != TYPE_FD - && mType != TYPE_URI) { - throw new IllegalStateException("Illegal type: " + mType); - } + void build(@NonNull DataSourceDesc dsd) { + Preconditions.checkNotNull(dsd); + if (mStartPositionMs > mEndPositionMs) { throw new IllegalStateException("Illegal start/end position: " + mStartPositionMs + " : " + mEndPositionMs); } - DataSourceDesc dsd = new DataSourceDesc(); - dsd.mType = mType; - dsd.mMedia2DataSource = mMedia2DataSource; - dsd.mFD = mFD; - dsd.mFDOffset = mFDOffset; - dsd.mFDLength = mFDLength; - dsd.mUri = mUri; - dsd.mUriHeader = mUriHeader; - dsd.mUriCookies = mUriCookies; - dsd.mUriContext = mUriContext; - dsd.mMediaId = mMediaId; dsd.mStartPositionMs = mStartPositionMs; dsd.mEndPositionMs = mEndPositionMs; - - return dsd; } /** @@ -301,9 +136,9 @@ public final class DataSourceDesc { * @param mediaId the media Id of this data source * @return the same Builder instance. */ - public Builder setMediaId(String mediaId) { + public @NonNull T setMediaId(String mediaId) { mMediaId = mediaId; - return this; + return (T) this; } /** @@ -314,12 +149,12 @@ public final class DataSourceDesc { * @return the same Builder instance. * */ - public Builder setStartPosition(long position) { + public @NonNull T setStartPosition(long position) { if (position < 0) { position = 0; } mStartPositionMs = position; - return this; + return (T) this; } /** @@ -329,157 +164,12 @@ public final class DataSourceDesc { * @param position the end position in milliseconds at which the playback will end * @return the same Builder instance. */ - public Builder setEndPosition(long position) { + public @NonNull T setEndPosition(long position) { if (position < 0) { position = LONG_MAX_TIME_MS; } mEndPositionMs = position; - return this; - } - - /** - * Sets the data source (Media2DataSource) to use. - * - * @param m2ds the Media2DataSource for the media you want to play - * @return the same Builder instance. - * @throws NullPointerException if m2ds is null. - */ - public Builder setDataSource(Media2DataSource m2ds) { - Preconditions.checkNotNull(m2ds); - resetDataSource(); - mType = TYPE_CALLBACK; - mMedia2DataSource = m2ds; - return this; - } - - /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. - * - * @param fd the FileDescriptor for the file you want to play - * @return the same Builder instance. - * @throws NullPointerException if fd is null. - */ - public Builder setDataSource(FileDescriptor fd) { - Preconditions.checkNotNull(fd); - resetDataSource(); - mType = TYPE_FD; - mFD = fd; - return this; - } - - /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. - * - * Any negative number for offset is treated as 0. - * Any negative number for length is treated as maximum length of the data source. - * - * @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 - * @param length the length in bytes of the data to be played - * @return the same Builder instance. - * @throws NullPointerException if fd is null. - */ - public Builder setDataSource(FileDescriptor fd, long offset, long length) { - Preconditions.checkNotNull(fd); - if (offset < 0) { - offset = 0; - } - if (length < 0) { - length = LONG_MAX; - } - resetDataSource(); - mType = TYPE_FD; - mFD = fd; - mFDOffset = offset; - mFDLength = length; - return this; - } - - /** - * Sets the data source as a content Uri. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @return the same Builder instance. - * @throws NullPointerException if context or uri is null. - */ - public Builder setDataSource(@NonNull Context context, @NonNull Uri uri) { - Preconditions.checkNotNull(context, "context cannot be null"); - Preconditions.checkNotNull(uri, "uri cannot be null"); - resetDataSource(); - mType = TYPE_URI; - mUri = uri; - mUriContext = context; - return this; - } - - /** - * Sets the data source as a content Uri. - * - * To provide cookies for the subsequent HTTP requests, you can install your own default - * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you - * can use this API to pass the cookies as a list of HttpCookie. If the app has not - * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager - * and populates its CookieStore with the provided cookies when this data source is passed - * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler - * is required to be of CookieManager type such that {@link MediaPlayer2} can update the - * manager’s CookieStore. - * - * <p><strong>Note</strong> that the cross domain redirection is allowed by default, - * but that can be changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to - * disallow or allow cross domain redirection. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @param headers the headers to be sent together with the request for the data - * The headers must not include cookies. Instead, use the cookies param. - * @param cookies the cookies to be sent together with the request - * @return the same Builder instance. - * @throws NullPointerException if context or uri is null. - * @throws IllegalArgumentException if the cookie handler is not of CookieManager type - * when cookies are provided. - */ - public Builder setDataSource(@NonNull Context context, @NonNull Uri uri, - @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) { - Preconditions.checkNotNull(context, "context cannot be null"); - Preconditions.checkNotNull(uri); - if (cookies != null) { - CookieHandler cookieHandler = CookieHandler.getDefault(); - if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { - throw new IllegalArgumentException( - "The cookie handler has to be of CookieManager type " - + "when cookies are provided."); - } - } - - resetDataSource(); - mType = TYPE_URI; - mUri = uri; - if (headers != null) { - mUriHeader = new HashMap<String, String>(headers); - } - if (cookies != null) { - mUriCookies = new ArrayList<HttpCookie>(cookies); - } - mUriContext = context; - return this; - } - - private void resetDataSource() { - mType = TYPE_NONE; - mMedia2DataSource = null; - mFD = null; - mFDOffset = 0; - mFDLength = LONG_MAX; - mUri = null; - mUriHeader = null; - mUriCookies = null; - mUriContext = null; + return (T) this; } } } diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java new file mode 100644 index 000000000000..5d8ff4913fb5 --- /dev/null +++ b/media/java/android/media/FileDataSourceDesc.java @@ -0,0 +1,186 @@ +/* + * Copyright 2018 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.NonNull; + +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; + +/** + * @hide + * Structure for data source descriptor. + * + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} + * to set data source for playback. + * + * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}. + * + */ +public class FileDataSourceDesc extends DataSourceDesc { + /** + * Used when the length of file descriptor is unknown. + * + * @see #getLength() + */ + public static final long FD_LENGTH_UNKNOWN = LONG_MAX; + + private FileDescriptor mFD; + private long mOffset = 0; + private long mLength = FD_LENGTH_UNKNOWN; + + private FileDataSourceDesc() { + } + + /** + * Return the FileDescriptor of this data source. + * @return the FileDescriptor of this data source + */ + public FileDescriptor getFileDescriptor() { + return mFD; + } + + /** + * Return the offset associated with the FileDescriptor of this data source. + * It's meaningful only when it has been set by the {@link Builder}. + * @return the offset associated with the FileDescriptor of this data source + */ + public long getOffset() { + return mOffset; + } + + /** + * Return the content length associated with the FileDescriptor of this data source. + * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content. + * @return the content length associated with the FileDescriptor of this data source + */ + public long getLength() { + return mLength; + } + + /** + * Builder class for {@link FileDataSourceDesc} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link FileDataSourceDesc} to be used by a {@link MediaPlayer2} instance: + * + * <pre class="prettyprint"> + * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder() + * .setDataSource(fd, 0, srcLength) + * .setStartPosition(1000) + * .setEndPosition(15000) + * .build(); + * mediaplayer2.setDataSourceDesc(newDSD); + * </pre> + */ + public static class Builder extends BuilderBase<Builder> { + private FileDescriptor mFD; + private long mOffset = 0; + private long mLength = FD_LENGTH_UNKNOWN; + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + super(); + } + + /** + * Constructs a new Builder from a given {@link FileDataSourceDesc} instance + * @param dsd the {@link FileDataSourceDesc} object whose data will be reused + * in the new Builder. + */ + public Builder(FileDataSourceDesc dsd) { + super(dsd); + if (dsd == null) { + return; // use default + } + mFD = dsd.mFD; + mOffset = dsd.mOffset; + mLength = dsd.mLength; + } + + /** + * Combines all of the fields that have been set and return a new + * {@link FileDataSourceDesc} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * + * @return a new {@link FileDataSourceDesc} object + */ + public @NonNull FileDataSourceDesc build() { + FileDataSourceDesc dsd = new FileDataSourceDesc(); + super.build(dsd); + dsd.mFD = mFD; + dsd.mOffset = mOffset; + dsd.mLength = mLength; + + return dsd; + } + + /** + * Sets the data source (FileDescriptor) to use. The FileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility + * to close the file descriptor after the source has been used. + * + * @param fd the FileDescriptor for the file to play + * @return the same Builder instance. + * @throws NullPointerException if fd is null. + */ + public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) { + Preconditions.checkNotNull(fd); + resetDataSource(); + mFD = fd; + return this; + } + + /** + * Sets the data source (FileDescriptor) to use. The FileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility + * to close the file descriptor after the source has been used. + * + * Any negative number for offset is treated as 0. + * Any negative number for length is treated as maximum length of the data source. + * + * @param fd the FileDescriptor for the file to play + * @param offset the offset into the file where the data to be played starts, in bytes + * @param length the length in bytes of the data to be played + * @return the same Builder instance. + * @throws NullPointerException if fd is null. + */ + public @NonNull Builder setDataSource( + @NonNull FileDescriptor fd, long offset, long length) { + Preconditions.checkNotNull(fd); + if (offset < 0) { + offset = 0; + } + if (length < 0) { + length = FD_LENGTH_UNKNOWN; + } + resetDataSource(); + mFD = fd; + mOffset = offset; + mLength = length; + return this; + } + + private void resetDataSource() { + mFD = null; + mOffset = 0; + mLength = FD_LENGTH_UNKNOWN; + } + } +} diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index a80511a118cf..548dc881d733 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -790,38 +790,34 @@ public class MediaPlayer2 implements AutoCloseable throws IOException { checkArgument(dsd != null, "the DataSourceDesc cannot be null"); - switch (dsd.getType()) { - case DataSourceDesc.TYPE_CALLBACK: - handleDataSource(isCurrent, - srcId, - dsd.getMedia2DataSource(), - dsd.getStartPosition(), - dsd.getEndPosition()); - break; - - case DataSourceDesc.TYPE_FD: - handleDataSource(isCurrent, - srcId, - dsd.getFileDescriptor(), - dsd.getFileDescriptorOffset(), - dsd.getFileDescriptorLength(), - dsd.getStartPosition(), - dsd.getEndPosition()); - break; - - case DataSourceDesc.TYPE_URI: - handleDataSource(isCurrent, - srcId, - dsd.getUriContext(), - dsd.getUri(), - dsd.getUriHeaders(), - dsd.getUriCookies(), - dsd.getStartPosition(), - dsd.getEndPosition()); - break; - - default: - break; + if (dsd instanceof CallbackDataSourceDesc) { + CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd; + handleDataSource(isCurrent, + srcId, + cbDSD.getMedia2DataSource(), + cbDSD.getStartPosition(), + cbDSD.getEndPosition()); + } else if (dsd instanceof FileDataSourceDesc) { + FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd; + handleDataSource(isCurrent, + srcId, + fileDSD.getFileDescriptor(), + fileDSD.getOffset(), + fileDSD.getLength(), + fileDSD.getStartPosition(), + fileDSD.getEndPosition()); + } else if (dsd instanceof UriDataSourceDesc) { + UriDataSourceDesc uriDSD = (UriDataSourceDesc) dsd; + handleDataSource(isCurrent, + srcId, + uriDSD.getContext(), + uriDSD.getUri(), + uriDSD.getHeaders(), + uriDSD.getCookies(), + uriDSD.getStartPosition(), + uriDSD.getEndPosition()); + } else { + throw new IllegalArgumentException("Unsupported DataSourceDesc. " + dsd.toString()); } } diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java new file mode 100644 index 000000000000..e6f39e0bc8fb --- /dev/null +++ b/media/java/android/media/UriDataSourceDesc.java @@ -0,0 +1,228 @@ +/* + * Copyright 2018 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.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.net.Uri; + +import com.android.internal.util.Preconditions; + +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.HttpCookie; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @hide + * Structure for data source descriptor. + * + * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)} + * to set data source for playback. + * + * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}. + * + */ +public class UriDataSourceDesc extends DataSourceDesc { + private Uri mUri; + private Map<String, String> mHeader; + private List<HttpCookie> mCookies; + private Context mContext; + + private UriDataSourceDesc() { + } + + /** + * Return the Uri of this data source. + * @return the Uri of this data source + */ + public Uri getUri() { + return mUri; + } + + /** + * Return the Uri headers of this data source. + * @return the Uri headers of this data source + */ + public Map<String, String> getHeaders() { + if (mHeader == null) { + return null; + } + return new HashMap<String, String>(mHeader); + } + + /** + * Return the Uri cookies of this data source. + * @return the Uri cookies of this data source + */ + public List<HttpCookie> getCookies() { + if (mCookies == null) { + return null; + } + return new ArrayList<HttpCookie>(mCookies); + } + + /** + * Return the Context used for resolving the Uri of this data source. + * @return the Context used for resolving the Uri of this data source + */ + public Context getContext() { + return mContext; + } + + /** + * Builder class for {@link UriDataSourceDesc} objects. + * <p> Here is an example where <code>Builder</code> is used to define the + * {@link UriDataSourceDesc} to be used by a {@link MediaPlayer2} instance: + * + * <pre class="prettyprint"> + * UriDataSourceDesc newDSD = new UriDataSourceDesc.Builder() + * .setDataSource(context, uri, headers, cookies) + * .setStartPosition(1000) + * .setEndPosition(15000) + * .build(); + * mediaplayer2.setDataSourceDesc(newDSD); + * </pre> + */ + public static class Builder extends BuilderBase<Builder> { + private Uri mUri; + private Map<String, String> mHeader; + private List<HttpCookie> mCookies; + private Context mContext; + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + super(); + } + + /** + * Constructs a new Builder from a given {@link UriDataSourceDesc} instance + * @param dsd the {@link UriDataSourceDesc} object whose data will be reused + * in the new Builder. + */ + public Builder(UriDataSourceDesc dsd) { + super(dsd); + if (dsd == null) { + return; // use default + } + mUri = dsd.mUri; + mHeader = dsd.mHeader; + mCookies = dsd.mCookies; + mContext = dsd.mContext; + } + + /** + * Combines all of the fields that have been set and return a new + * {@link UriDataSourceDesc} object. <code>IllegalStateException</code> will be + * thrown if there is conflict between fields. + * + * @return a new {@link UriDataSourceDesc} object + */ + public @NonNull UriDataSourceDesc build() { + UriDataSourceDesc dsd = new UriDataSourceDesc(); + super.build(dsd); + dsd.mUri = mUri; + dsd.mHeader = mHeader; + dsd.mCookies = mCookies; + dsd.mContext = mContext; + + return dsd; + } + + /** + * Sets the data source as a content Uri. + * + * @param context the Context to use when resolving the Uri + * @param uri the Content URI of the data you want to play + * @return the same Builder instance. + * @throws NullPointerException if context or uri is null. + */ + public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) { + Preconditions.checkNotNull(context, "context cannot be null"); + Preconditions.checkNotNull(uri, "uri cannot be null"); + resetDataSource(); + mUri = uri; + mContext = context; + return this; + } + + /** + * Sets the data source as a content Uri. + * + * To provide cookies for the subsequent HTTP requests, you can install your own default + * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you + * can use this API to pass the cookies as a list of HttpCookie. If the app has not + * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager + * and populates its CookieStore with the provided cookies when this data source is passed + * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler + * is required to be of CookieManager type such that {@link MediaPlayer2} can update the + * manager’s CookieStore. + * + * <p><strong>Note</strong> that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. + * + * @param context the Context to use when resolving the Uri + * @param uri the Content URI of the data you want to play + * @param headers the headers to be sent together with the request for the data + * The headers must not include cookies. Instead, use the cookies param. + * @param cookies the cookies to be sent together with the request + * @return the same Builder instance. + * @throws NullPointerException if context or uri is null. + * @throws IllegalArgumentException if the cookie handler is not of CookieManager type + * when cookies are provided. + */ + public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri, + @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) { + Preconditions.checkNotNull(context, "context cannot be null"); + Preconditions.checkNotNull(uri); + if (cookies != null) { + CookieHandler cookieHandler = CookieHandler.getDefault(); + if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { + throw new IllegalArgumentException( + "The cookie handler has to be of CookieManager type " + + "when cookies are provided."); + } + } + + resetDataSource(); + mUri = uri; + if (headers != null) { + mHeader = new HashMap<String, String>(headers); + } + if (cookies != null) { + mCookies = new ArrayList<HttpCookie>(cookies); + } + mContext = context; + return this; + } + + private void resetDataSource() { + mUri = null; + mHeader = null; + mCookies = null; + mContext = null; + } + } +} |