Consolidate builder classes for DataSourceDesc

Test: MediaPlayer2Test MediaPlayer2DrmTest
Bug: 128461237
Change-Id: I86a3d43c2ceccf97b64018156aacba6c8b744203
This commit is contained in:
Dongwon Kang
2019-03-20 20:19:58 -07:00
parent 02e487e715
commit 7941a46f80
7 changed files with 292 additions and 433 deletions

View File

@@ -23706,17 +23706,6 @@ package android.media {
method public void onTearDown(@NonNull android.media.AudioTrack);
}
public class CallbackDataSourceDesc extends android.media.DataSourceDesc {
method @NonNull public android.media.DataSourceCallback getDataSourceCallback();
}
public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.CallbackDataSourceDesc.Builder> {
ctor public CallbackDataSourceDesc.Builder();
ctor public CallbackDataSourceDesc.Builder(@Nullable android.media.CallbackDataSourceDesc);
method @NonNull public android.media.CallbackDataSourceDesc build();
method @NonNull public android.media.CallbackDataSourceDesc.Builder setDataSource(@NonNull android.media.DataSourceCallback);
}
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -23783,10 +23772,18 @@ package android.media {
field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL
}
protected static class DataSourceDesc.BuilderBase<T extends android.media.DataSourceDesc.BuilderBase> {
method @NonNull public T setEndPosition(long);
method @NonNull public T setMediaId(@Nullable String);
method @NonNull public T setStartPosition(long);
public static final class DataSourceDesc.Builder {
ctor public DataSourceDesc.Builder();
ctor public DataSourceDesc.Builder(@Nullable android.media.DataSourceDesc);
method @NonNull public android.media.DataSourceDesc build();
method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.net.Uri);
method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.net.Uri, @Nullable java.util.Map<java.lang.String,java.lang.String>, @Nullable java.util.List<java.net.HttpCookie>);
method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor, long, long);
method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.media.DataSourceCallback);
method @NonNull public android.media.DataSourceDesc.Builder setEndPosition(long);
method @NonNull public android.media.DataSourceDesc.Builder setMediaId(@Nullable String);
method @NonNull public android.media.DataSourceDesc.Builder setStartPosition(long);
}
public final class DeniedByServerException extends android.media.MediaDrmException {
@@ -23991,21 +23988,6 @@ package android.media {
field public static final int EULER_Z = 2; // 0x2
}
public class FileDataSourceDesc extends android.media.DataSourceDesc {
method public long getLength();
method public long getOffset();
method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
}
public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.FileDataSourceDesc.Builder> {
ctor public FileDataSourceDesc.Builder();
ctor public FileDataSourceDesc.Builder(@Nullable android.media.FileDataSourceDesc);
method @NonNull public android.media.FileDataSourceDesc build();
method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor, long, long);
}
public abstract class Image implements java.lang.AutoCloseable {
method public abstract void close();
method public android.graphics.Rect getCropRect();
@@ -26639,21 +26621,6 @@ package android.media {
ctor public UnsupportedSchemeException(String);
}
public class UriDataSourceDesc extends android.media.DataSourceDesc {
method @NonNull public android.content.Context getContext();
method @Nullable public java.util.List<java.net.HttpCookie> getCookies();
method @Nullable public java.util.Map<java.lang.String,java.lang.String> getHeaders();
method @NonNull public android.net.Uri getUri();
}
public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.UriDataSourceDesc.Builder> {
ctor public UriDataSourceDesc.Builder();
ctor public UriDataSourceDesc.Builder(@Nullable android.media.UriDataSourceDesc);
method @NonNull public android.media.UriDataSourceDesc build();
method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable java.util.Map<java.lang.String,java.lang.String>, @Nullable java.util.List<java.net.HttpCookie>);
}
public interface VolumeAutomation {
method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
}

View File

@@ -1077,6 +1077,17 @@ package android.media {
method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
}
public class CallbackDataSourceDesc extends android.media.DataSourceDesc {
method @NonNull public android.media.DataSourceCallback getDataSourceCallback();
}
public class FileDataSourceDesc extends android.media.DataSourceDesc {
method public long getLength();
method public long getOffset();
method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
}
public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size);
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size);
@@ -1101,6 +1112,12 @@ package android.media {
method public android.media.PlaybackParams setAudioStretchMode(int);
}
public class UriDataSourceDesc extends android.media.DataSourceDesc {
method @Nullable public java.util.List<java.net.HttpCookie> getCookies();
method @Nullable public java.util.Map<java.lang.String,java.lang.String> getHeaders();
method @NonNull public android.net.Uri getUri();
}
public static final class VolumeShaper.Configuration.Builder {
method @NonNull public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
}

View File

@@ -17,7 +17,7 @@
package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
/**
* Structure of data source descriptor for sources using callback.
@@ -26,12 +26,16 @@ import android.annotation.Nullable;
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
*
* @hide
*/
@TestApi
public class CallbackDataSourceDesc extends DataSourceDesc {
private DataSourceCallback mDataSourceCallback;
private CallbackDataSourceDesc() {
CallbackDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
DataSourceCallback dataSourceCallback) {
super(mediaId, startPositionMs, endPositionMs);
mDataSourceCallback = dataSourceCallback;
}
/**
@@ -41,75 +45,4 @@ public class CallbackDataSourceDesc extends DataSourceDesc {
public @NonNull DataSourceCallback getDataSourceCallback() {
return mDataSourceCallback;
}
/**
* 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 DataSourceCallback mDataSourceCallback;
/**
* 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(@Nullable CallbackDataSourceDesc dsd) {
super(dsd);
if (dsd == null) {
return; // use default
}
mDataSourceCallback = dsd.mDataSourceCallback;
}
/**
* 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() {
if (mDataSourceCallback == null) {
throw new IllegalStateException(
"DataSourceCallback should not be null");
}
CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
super.build(dsd);
dsd.mDataSourceCallback = mDataSourceCallback;
return dsd;
}
/**
* Sets the data source (DataSourceCallback) to use.
*
* @param dscb the DataSourceCallback for the media to play
* @return the same Builder instance.
* @throws NullPointerException if dscb is null.
*/
public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
mDataSourceCallback = dscb;
return this;
}
}
}

View File

@@ -18,15 +18,22 @@ package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
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;
/**
* Base class of data source descriptor.
* Data source descriptor.
*
* Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
*
*/
public class DataSourceDesc {
// intentionally less than long.MAX_VALUE
@@ -45,7 +52,10 @@ public class DataSourceDesc {
private long mStartPositionMs = 0;
private long mEndPositionMs = POSITION_UNKNOWN;
DataSourceDesc() {
DataSourceDesc(String mediaId, long startPositionMs, long endPositionMs) {
mMediaId = mediaId;
mStartPositionMs = startPositionMs;
mEndPositionMs = endPositionMs;
}
/**
@@ -97,17 +107,48 @@ public class DataSourceDesc {
}
/**
* Base class for Builders in the subclasses of {@link DataSourceDesc}.
* Builder for {@link DataSourceDesc}.
* <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 newDSD = new DataSourceDesc.Builder()
* .setDataSource(context, uri, headers, cookies)
* .setStartPosition(1000)
* .setEndPosition(15000)
* .build();
* mediaplayer2.setDataSourceDesc(newDSD);
* </pre>
*/
protected static class BuilderBase<T extends BuilderBase> {
public static final class Builder {
private static final int SOURCE_TYPE_UNKNOWN = 0;
private static final int SOURCE_TYPE_URI = 1;
private static final int SOURCE_TYPE_FILE = 2;
private static final int SOURCE_TYPE_CALLBACK = 3;
private int mSourceType = SOURCE_TYPE_UNKNOWN;
private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = POSITION_UNKNOWN;
// For UriDataSourceDesc
private Uri mUri;
private Map<String, String> mHeader;
private List<HttpCookie> mCookies;
// For FileDataSourceDesc
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FileDataSourceDesc.FD_LENGTH_UNKNOWN;
// For CallbackDataSourceDesc
private DataSourceCallback mDataSourceCallback;
/**
* Constructs a new BuilderBase with the defaults.
*/
BuilderBase() {
public Builder() {
}
/**
@@ -115,33 +156,61 @@ public class DataSourceDesc {
* @param dsd the {@link DataSourceDesc} object whose data will be reused
* in the new BuilderBase.
*/
BuilderBase(DataSourceDesc dsd) {
public Builder(@Nullable DataSourceDesc dsd) {
if (dsd == null) {
return;
}
mMediaId = dsd.mMediaId;
mStartPositionMs = dsd.mStartPositionMs;
mEndPositionMs = dsd.mEndPositionMs;
if (dsd instanceof FileDataSourceDesc) {
mSourceType = SOURCE_TYPE_FILE;
mPFD = ((FileDataSourceDesc) dsd).getParcelFileDescriptor();
mOffset = ((FileDataSourceDesc) dsd).getOffset();
mLength = ((FileDataSourceDesc) dsd).getLength();
} else if (dsd instanceof UriDataSourceDesc) {
mSourceType = SOURCE_TYPE_URI;
mUri = ((UriDataSourceDesc) dsd).getUri();
mHeader = ((UriDataSourceDesc) dsd).getHeaders();
mCookies = ((UriDataSourceDesc) dsd).getCookies();
} else if (dsd instanceof CallbackDataSourceDesc) {
mSourceType = SOURCE_TYPE_CALLBACK;
mDataSourceCallback = ((CallbackDataSourceDesc) dsd).getDataSourceCallback();
} else {
throw new IllegalStateException("Unknown source type:" + mSourceType);
}
}
/**
* Sets all fields that have been set in the {@link DataSourceDesc} object.
* <code>IllegalStateException</code> will be thrown if there is conflict between fields.
*
* @param dsd an instance of subclass of {@link DataSourceDesc} whose data will be set
* @return the same instance of subclass of {@link DataSourceDesc}
* @return {@link DataSourceDesc}
*/
void build(@NonNull DataSourceDesc dsd) {
Media2Utils.checkArgument(dsd != null, "dsd cannot be null.");
@NonNull
public DataSourceDesc build() {
if (mSourceType == SOURCE_TYPE_UNKNOWN) {
throw new IllegalStateException("Source is not set.");
}
if (mStartPositionMs > mEndPositionMs) {
throw new IllegalStateException("Illegal start/end position: "
+ mStartPositionMs + " : " + mEndPositionMs);
}
dsd.mMediaId = mMediaId;
dsd.mStartPositionMs = mStartPositionMs;
dsd.mEndPositionMs = mEndPositionMs;
DataSourceDesc desc;
if (mSourceType == SOURCE_TYPE_FILE) {
desc = new FileDataSourceDesc(
mMediaId, mStartPositionMs, mEndPositionMs, mPFD, mOffset, mLength);
} else if (mSourceType == SOURCE_TYPE_URI) {
desc = new UriDataSourceDesc(
mMediaId, mStartPositionMs, mEndPositionMs, mUri, mHeader, mCookies);
} else if (mSourceType == SOURCE_TYPE_CALLBACK) {
desc = new CallbackDataSourceDesc(
mMediaId, mStartPositionMs, mEndPositionMs, mDataSourceCallback);
} else {
throw new IllegalStateException("Unknown source type:" + mSourceType);
}
return desc;
}
/**
@@ -150,9 +219,10 @@ public class DataSourceDesc {
* @param mediaId the media Id of this data source
* @return the same Builder instance.
*/
public @NonNull T setMediaId(@Nullable String mediaId) {
@NonNull
public Builder setMediaId(@Nullable String mediaId) {
mMediaId = mediaId;
return (T) this;
return this;
}
/**
@@ -163,12 +233,13 @@ public class DataSourceDesc {
* @return the same Builder instance.
*
*/
public @NonNull T setStartPosition(long position) {
@NonNull
public Builder setStartPosition(long position) {
if (position < 0) {
position = 0;
}
mStartPositionMs = position;
return (T) this;
return this;
}
/**
@@ -179,12 +250,155 @@ public class DataSourceDesc {
* @param position the end position in milliseconds at which the playback will end
* @return the same Builder instance.
*/
public @NonNull T setEndPosition(long position) {
@NonNull
public Builder setEndPosition(long position) {
if (position < 0) {
position = LONG_MAX_TIME_MS;
}
mEndPositionMs = position;
return (T) this;
return this;
}
/**
* Sets the data source as a content 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.
*/
@NonNull
public Builder setDataSource(@NonNull Uri uri) {
setSourceType(SOURCE_TYPE_URI);
Media2Utils.checkArgument(uri != null, "uri cannot be null");
mUri = uri;
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
* managers 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 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.
*/
@NonNull
public Builder setDataSource(@NonNull Uri uri, @Nullable Map<String, String> headers,
@Nullable List<HttpCookie> cookies) {
setSourceType(SOURCE_TYPE_URI);
Media2Utils.checkArgument(uri != null, "uri cannot be null");
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.");
}
}
mUri = uri;
if (headers != null) {
mHeader = new HashMap<String, String>(headers);
}
if (cookies != null) {
mCookies = new ArrayList<HttpCookie>(cookies);
}
return this;
}
/**
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
* {@link MediaPlayer2#setDataSource},
* {@link MediaPlayer2#setNextDataSource} or
* {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* @param pfd the ParcelFileDescriptor for the file to play
* @return the same Builder instance.
* @throws NullPointerException if pfd is null.
*/
@NonNull
public Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
setSourceType(SOURCE_TYPE_FILE);
Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
mPFD = pfd;
return this;
}
/**
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
* {@link MediaPlayer2#setDataSource},
* {@link MediaPlayer2#setNextDataSource} or
* {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* Any negative number for offset is treated as 0.
* Any negative number for length is treated as maximum length of the data source.
*
* @param pfd the ParcelFileDescriptor 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 pfd is null.
*/
@NonNull
public Builder setDataSource(
@NonNull ParcelFileDescriptor pfd, long offset, long length) {
setSourceType(SOURCE_TYPE_FILE);
Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
if (offset < 0) {
offset = 0;
}
if (length < 0) {
length = FileDataSourceDesc.FD_LENGTH_UNKNOWN;
}
mPFD = pfd;
mOffset = offset;
mLength = length;
return this;
}
/**
* Sets the data source (DataSourceCallback) to use.
*
* @param dscb the DataSourceCallback for the media to play
* @return the same Builder instance.
* @throws NullPointerException if dscb is null.
*/
public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
setSourceType(SOURCE_TYPE_CALLBACK);
Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
mDataSourceCallback = dscb;
return this;
}
private void setSourceType(int type) {
if (mSourceType != SOURCE_TYPE_UNKNOWN) {
throw new IllegalStateException("Source is already set. type=" + mSourceType);
}
mSourceType = type;
}
}
}

View File

@@ -17,7 +17,7 @@
package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -30,8 +30,9 @@ import java.io.IOException;
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
*
* @hide
*/
@TestApi
public class FileDataSourceDesc extends DataSourceDesc {
private static final String TAG = "FileDataSourceDesc";
@@ -48,8 +49,12 @@ public class FileDataSourceDesc extends DataSourceDesc {
private int mCount = 0;
private boolean mClosed = false;
private FileDataSourceDesc() {
super();
FileDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
ParcelFileDescriptor pfd, long offset, long length) {
super(mediaId, startPositionMs, endPositionMs);
mPFD = pfd;
mOffset = offset;
mLength = length;
}
/**
@@ -128,133 +133,4 @@ public class FileDataSourceDesc extends DataSourceDesc {
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(pfd, 0, srcLength)
* .setStartPosition(1000)
* .setEndPosition(15000)
* .build();
* mediaplayer2.setDataSourceDesc(newDSD);
* </pre>
*/
public static class Builder extends BuilderBase<Builder> {
private ParcelFileDescriptor mPFD;
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(@Nullable FileDataSourceDesc dsd) {
super(dsd);
if (dsd == null) {
return; // use default
}
mPFD = dsd.mPFD;
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() {
if (mPFD == null) {
throw new IllegalStateException(
"underline ParcelFileDescriptor should not be null");
}
try {
mPFD.getFd();
} catch (IllegalStateException e) {
throw new IllegalStateException("ParcelFileDescriptor has been closed");
}
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
dsd.mPFD = mPFD;
dsd.mOffset = mOffset;
dsd.mLength = mLength;
return dsd;
}
/**
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
* {@link MediaPlayer2#setDataSource},
* {@link MediaPlayer2#setNextDataSource} or
* {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* @param pfd the ParcelFileDescriptor for the file to play
* @return the same Builder instance.
* @throws NullPointerException if pfd is null.
*/
public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
resetDataSource();
mPFD = pfd;
return this;
}
/**
* Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
* created by this builder is passed to {@link MediaPlayer2} via
* {@link MediaPlayer2#setDataSource},
* {@link MediaPlayer2#setNextDataSource} or
* {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
* close the ParcelFileDescriptor.
*
* Any negative number for offset is treated as 0.
* Any negative number for length is treated as maximum length of the data source.
*
* @param pfd the ParcelFileDescriptor 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 pfd is null.
*/
public @NonNull Builder setDataSource(
@NonNull ParcelFileDescriptor pfd, long offset, long length) {
Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
if (offset < 0) {
offset = 0;
}
if (length < 0) {
length = FD_LENGTH_UNKNOWN;
}
resetDataSource();
mPFD = pfd;
mOffset = offset;
mLength = length;
return this;
}
private void resetDataSource() {
mPFD = null;
mOffset = 0;
mLength = FD_LENGTH_UNKNOWN;
}
}
}

View File

@@ -898,7 +898,7 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting {
UriDataSourceDesc uriDSD = (UriDataSourceDesc) dsd;
handleDataSource(isCurrent,
srcId,
uriDSD.getContext(),
mContext,
uriDSD.getUri(),
uriDSD.getHeaders(),
uriDSD.getCookies(),

View File

@@ -18,11 +18,9 @@ package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.annotation.TestApi;
import android.net.Uri;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,15 +34,20 @@ import java.util.Map;
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
*
* @hide
*/
@TestApi
public class UriDataSourceDesc extends DataSourceDesc {
private Uri mUri;
private Map<String, String> mHeader;
private List<HttpCookie> mCookies;
private Context mContext;
private UriDataSourceDesc() {
UriDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
Uri uri, Map<String, String> header, List<HttpCookie> cookies) {
super(mediaId, startPositionMs, endPositionMs);
mUri = uri;
mHeader = header;
mCookies = cookies;
}
/**
@@ -76,155 +79,4 @@ public class UriDataSourceDesc extends DataSourceDesc {
}
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 @NonNull 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(@Nullable 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() {
if (mUri == null || mContext == null) {
throw new IllegalStateException(
"Uri and Context should not be null");
}
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) {
Media2Utils.checkArgument(context != null, "context cannot be null");
Media2Utils.checkArgument(uri != null, "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
* managers 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) {
Media2Utils.checkArgument(context != null, "context cannot be null");
Media2Utils.checkArgument(uri != null, "uri cannot be null");
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;
}
}
}