Files
frameworks_base/media/apex/java/android/media/DataSourceDesc.java
Andrew Lewis 8ebf45ad34 Hide MediaPlayer2 and related classes
Test: compiles locally
Bug: 130420617
Change-Id: Ic8d838ce8ae850272941815de425991a3a87754e
2019-04-12 19:00:07 +01:00

385 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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.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;
/**
* Data source descriptor.
*
* Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* @hide
*/
public class DataSourceDesc {
// intentionally less than long.MAX_VALUE
static final long LONG_MAX = 0x7ffffffffffffffL;
// keep consistent with native code
public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
/**
* @hide
*/
public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;
private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = POSITION_UNKNOWN;
DataSourceDesc(String mediaId, long startPositionMs, long endPositionMs) {
mMediaId = mediaId;
mStartPositionMs = startPositionMs;
mEndPositionMs = endPositionMs;
}
/**
* Releases the resources held by this {@code DataSourceDesc} object.
*/
void close() {
}
// Have to declare protected for finalize() since it is protected
// in the base class Object.
@Override
protected void finalize() throws Throwable {
close();
}
/**
* Return the media Id of data source.
* @return the media Id of data source
*/
public @Nullable String getMediaId() {
return mMediaId;
}
/**
* Return the position in milliseconds at which the playback will start.
* @return the position in milliseconds at which the playback will start
*/
public long getStartPosition() {
return mStartPositionMs;
}
/**
* Return the position in milliseconds at which the playback will end.
* {@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;
}
@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();
}
/**
* 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>
*/
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 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;
/**
* Constructs a new BuilderBase with the defaults.
*/
public Builder() {
}
/**
* Constructs a new BuilderBase from a given {@link DataSourceDesc} instance
* @param dsd the {@link DataSourceDesc} object whose data will be reused
* in the new BuilderBase.
*/
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 {
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.
*
* @return {@link DataSourceDesc}
*/
@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);
}
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 {
throw new IllegalStateException("Unknown source type:" + mSourceType);
}
return desc;
}
/**
* Sets the media Id of this data source.
*
* @param mediaId the media Id of this data source
* @return the same Builder instance.
*/
@NonNull
public Builder setMediaId(@Nullable String mediaId) {
mMediaId = mediaId;
return this;
}
/**
* Sets the start position in milliseconds at which the playback will start.
* Any negative number is treated as 0.
*
* @param position the start position in milliseconds at which the playback will start
* @return the same Builder instance.
*
*/
@NonNull
public Builder setStartPosition(long position) {
if (position < 0) {
position = 0;
}
mStartPositionMs = position;
return this;
}
/**
* Sets the end position in milliseconds at which the playback will end.
* Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS}
* of the data source
*
* @param position the end position in milliseconds at which the playback will end
* @return the same Builder instance.
*/
@NonNull
public Builder setEndPosition(long position) {
if (position < 0) {
position = LONG_MAX_TIME_MS;
}
mEndPositionMs = position;
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
* 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 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 DataSourceDesc}
* 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 DataSourceDesc}
* 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);
if (pfd == null) {
throw new NullPointerException("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;
}
private void setSourceType(int type) {
if (mSourceType != SOURCE_TYPE_UNKNOWN) {
throw new IllegalStateException("Source is already set. type=" + mSourceType);
}
mSourceType = type;
}
}
}