DataSourceDesc.java revision de0c3979ce4bdb9464645d6a898b8e1e042cf33f
1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.media; 18 19import android.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.Context; 23import android.content.res.AssetFileDescriptor; 24import android.net.Uri; 25import android.os.Parcel; 26import android.os.Parcelable; 27 28import com.android.internal.util.Preconditions; 29 30import java.io.FileDescriptor; 31import java.lang.annotation.Retention; 32import java.lang.annotation.RetentionPolicy; 33import java.net.CookieHandler; 34import java.net.CookieManager; 35import java.net.HttpCookie; 36 37import java.util.ArrayList; 38import java.util.HashMap; 39import java.util.List; 40import java.util.Map; 41 42/** 43 * Structure for data source descriptor. 44 * 45 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} 46 * to set data source for playback. 47 * 48 * <p>Users should use {@link Builder} to change {@link DataSourceDesc}. 49 * 50 */ 51public final class DataSourceDesc { 52 /* No data source has been set yet */ 53 public static final int TYPE_NONE = 0; 54 /* data source is type of MediaDataSource */ 55 public static final int TYPE_CALLBACK = 1; 56 /* data source is type of FileDescriptor */ 57 public static final int TYPE_FD = 2; 58 /* data source is type of Uri */ 59 public static final int TYPE_URI = 3; 60 61 // intentionally less than long.MAX_VALUE 62 public static final long LONG_MAX = 0x7ffffffffffffffL; 63 64 private int mType = TYPE_NONE; 65 66 private Media2DataSource mMedia2DataSource; 67 68 private FileDescriptor mFD; 69 private long mFDOffset = 0; 70 private long mFDLength = LONG_MAX; 71 72 private Uri mUri; 73 private Map<String, String> mUriHeader; 74 private List<HttpCookie> mUriCookies; 75 private Context mUriContext; 76 77 private long mId = 0; 78 private long mStartPositionMs = 0; 79 private long mEndPositionMs = LONG_MAX; 80 81 private DataSourceDesc() { 82 } 83 84 /** 85 * Return the Id of data source. 86 * @return the Id of data source 87 */ 88 public long getId() { 89 return mId; 90 } 91 92 /** 93 * Return the position in milliseconds at which the playback will start. 94 * @return the position in milliseconds at which the playback will start 95 */ 96 public long getStartPosition() { 97 return mStartPositionMs; 98 } 99 100 /** 101 * Return the position in milliseconds at which the playback will end. 102 * -1 means ending at the end of source content. 103 * @return the position in milliseconds at which the playback will end 104 */ 105 public long getEndPosition() { 106 return mEndPositionMs; 107 } 108 109 /** 110 * Return the type of data source. 111 * @return the type of data source 112 */ 113 public int getType() { 114 return mType; 115 } 116 117 /** 118 * Return the Media2DataSource of this data source. 119 * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}. 120 * @return the Media2DataSource of this data source 121 */ 122 public Media2DataSource getMedia2DataSource() { 123 return mMedia2DataSource; 124 } 125 126 /** 127 * Return the FileDescriptor of this data source. 128 * It's meaningful only when {@code getType} returns {@link #TYPE_FD}. 129 * @return the FileDescriptor of this data source 130 */ 131 public FileDescriptor getFileDescriptor() { 132 return mFD; 133 } 134 135 /** 136 * Return the offset associated with the FileDescriptor of this data source. 137 * It's meaningful only when {@code getType} returns {@link #TYPE_FD} and it has 138 * been set by the {@link Builder}. 139 * @return the offset associated with the FileDescriptor of this data source 140 */ 141 public long getFileDescriptorOffset() { 142 return mFDOffset; 143 } 144 145 /** 146 * Return the content length associated with the FileDescriptor of this data source. 147 * It's meaningful only when {@code getType} returns {@link #TYPE_FD}. 148 * -1 means same as the length of source content. 149 * @return the content length associated with the FileDescriptor of this data source 150 */ 151 public long getFileDescriptorLength() { 152 return mFDLength; 153 } 154 155 /** 156 * Return the Uri of this data source. 157 * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. 158 * @return the Uri of this data source 159 */ 160 public Uri getUri() { 161 return mUri; 162 } 163 164 /** 165 * Return the Uri headers of this data source. 166 * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. 167 * @return the Uri headers of this data source 168 */ 169 public Map<String, String> getUriHeaders() { 170 if (mUriHeader == null) { 171 return null; 172 } 173 return new HashMap<String, String>(mUriHeader); 174 } 175 176 /** 177 * Return the Uri cookies of this data source. 178 * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. 179 * @return the Uri cookies of this data source 180 */ 181 public List<HttpCookie> getUriCookies() { 182 if (mUriCookies == null) { 183 return null; 184 } 185 return new ArrayList<HttpCookie>(mUriCookies); 186 } 187 188 /** 189 * Return the Context used for resolving the Uri of this data source. 190 * It's meaningful only when {@code getType} returns {@link #TYPE_URI}. 191 * @return the Context used for resolving the Uri of this data source 192 */ 193 public Context getUriContext() { 194 return mUriContext; 195 } 196 197 /** 198 * Builder class for {@link DataSourceDesc} objects. 199 * <p> Here is an example where <code>Builder</code> is used to define the 200 * {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance: 201 * 202 * <pre class="prettyprint"> 203 * DataSourceDesc oldDSD = mediaplayer2.getDataSourceDesc(); 204 * DataSourceDesc newDSD = new DataSourceDesc.Builder(oldDSD) 205 * .setStartPosition(1000) 206 * .setEndPosition(15000) 207 * .build(); 208 * mediaplayer2.setDataSourceDesc(newDSD); 209 * </pre> 210 */ 211 public static class Builder { 212 private int mType = TYPE_NONE; 213 214 private Media2DataSource mMedia2DataSource; 215 216 private FileDescriptor mFD; 217 private long mFDOffset = 0; 218 private long mFDLength = LONG_MAX; 219 220 private Uri mUri; 221 private Map<String, String> mUriHeader; 222 private List<HttpCookie> mUriCookies; 223 private Context mUriContext; 224 225 private long mId = 0; 226 private long mStartPositionMs = 0; 227 private long mEndPositionMs = LONG_MAX; 228 229 /** 230 * Constructs a new Builder with the defaults. 231 */ 232 public Builder() { 233 } 234 235 /** 236 * Constructs a new Builder from a given {@link DataSourceDesc} instance 237 * @param dsd the {@link DataSourceDesc} object whose data will be reused 238 * in the new Builder. 239 */ 240 public Builder(DataSourceDesc dsd) { 241 mType = dsd.mType; 242 mMedia2DataSource = dsd.mMedia2DataSource; 243 mFD = dsd.mFD; 244 mFDOffset = dsd.mFDOffset; 245 mFDLength = dsd.mFDLength; 246 mUri = dsd.mUri; 247 mUriHeader = dsd.mUriHeader; 248 mUriCookies = dsd.mUriCookies; 249 mUriContext = dsd.mUriContext; 250 251 mId = dsd.mId; 252 mStartPositionMs = dsd.mStartPositionMs; 253 mEndPositionMs = dsd.mEndPositionMs; 254 } 255 256 /** 257 * Combines all of the fields that have been set and return a new 258 * {@link DataSourceDesc} object. <code>IllegalStateException</code> will be 259 * thrown if there is conflict between fields. 260 * 261 * @return a new {@link DataSourceDesc} object 262 */ 263 public DataSourceDesc build() { 264 if (mType != TYPE_CALLBACK 265 && mType != TYPE_FD 266 && mType != TYPE_URI) { 267 throw new IllegalStateException("Illegal type: " + mType); 268 } 269 if (mStartPositionMs > mEndPositionMs) { 270 throw new IllegalStateException("Illegal start/end position: " 271 + mStartPositionMs + " : " + mEndPositionMs); 272 } 273 274 DataSourceDesc dsd = new DataSourceDesc(); 275 dsd.mType = mType; 276 dsd.mMedia2DataSource = mMedia2DataSource; 277 dsd.mFD = mFD; 278 dsd.mFDOffset = mFDOffset; 279 dsd.mFDLength = mFDLength; 280 dsd.mUri = mUri; 281 dsd.mUriHeader = mUriHeader; 282 dsd.mUriCookies = mUriCookies; 283 dsd.mUriContext = mUriContext; 284 285 dsd.mId = mId; 286 dsd.mStartPositionMs = mStartPositionMs; 287 dsd.mEndPositionMs = mEndPositionMs; 288 289 return dsd; 290 } 291 292 /** 293 * Sets the Id of this data source. 294 * 295 * @param id the Id of this data source 296 * @return the same Builder instance. 297 */ 298 public Builder setId(long id) { 299 mId = id; 300 return this; 301 } 302 303 /** 304 * Sets the start position in milliseconds at which the playback will start. 305 * Any negative number is treated as 0. 306 * 307 * @param position the start position in milliseconds at which the playback will start 308 * @return the same Builder instance. 309 * 310 */ 311 public Builder setStartPosition(long position) { 312 if (position < 0) { 313 position = 0; 314 } 315 mStartPositionMs = position; 316 return this; 317 } 318 319 /** 320 * Sets the end position in milliseconds at which the playback will end. 321 * Any negative number is treated as maximum length of the data source. 322 * 323 * @param position the end position in milliseconds at which the playback will end 324 * @return the same Builder instance. 325 */ 326 public Builder setEndPosition(long position) { 327 if (position < 0) { 328 position = LONG_MAX; 329 } 330 mEndPositionMs = position; 331 return this; 332 } 333 334 /** 335 * Sets the data source (Media2DataSource) to use. 336 * 337 * @param m2ds the Media2DataSource for the media you want to play 338 * @return the same Builder instance. 339 * @throws NullPointerException if m2ds is null. 340 */ 341 public Builder setDataSource(Media2DataSource m2ds) { 342 Preconditions.checkNotNull(m2ds); 343 resetDataSource(); 344 mType = TYPE_CALLBACK; 345 mMedia2DataSource = m2ds; 346 return this; 347 } 348 349 /** 350 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 351 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 352 * to close the file descriptor after the source has been used. 353 * 354 * @param fd the FileDescriptor for the file you want to play 355 * @return the same Builder instance. 356 * @throws NullPointerException if fd is null. 357 */ 358 public Builder setDataSource(FileDescriptor fd) { 359 Preconditions.checkNotNull(fd); 360 resetDataSource(); 361 mType = TYPE_FD; 362 mFD = fd; 363 return this; 364 } 365 366 /** 367 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 368 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 369 * to close the file descriptor after the source has been used. 370 * 371 * Any negative number for offset is treated as 0. 372 * Any negative number for length is treated as maximum length of the data source. 373 * 374 * @param fd the FileDescriptor for the file you want to play 375 * @param offset the offset into the file where the data to be played starts, in bytes 376 * @param length the length in bytes of the data to be played 377 * @return the same Builder instance. 378 * @throws NullPointerException if fd is null. 379 */ 380 public Builder setDataSource(FileDescriptor fd, long offset, long length) { 381 Preconditions.checkNotNull(fd); 382 if (offset < 0) { 383 offset = 0; 384 } 385 if (length < 0) { 386 length = LONG_MAX; 387 } 388 resetDataSource(); 389 mType = TYPE_FD; 390 mFD = fd; 391 mFDOffset = offset; 392 mFDLength = length; 393 return this; 394 } 395 396 /** 397 * Sets the data source as a content Uri. 398 * 399 * @param context the Context to use when resolving the Uri 400 * @param uri the Content URI of the data you want to play 401 * @return the same Builder instance. 402 * @throws NullPointerException if context or uri is null. 403 */ 404 public Builder setDataSource(@NonNull Context context, @NonNull Uri uri) { 405 Preconditions.checkNotNull(context, "context cannot be null"); 406 Preconditions.checkNotNull(uri, "uri cannot be null"); 407 resetDataSource(); 408 mType = TYPE_URI; 409 mUri = uri; 410 mUriContext = context; 411 return this; 412 } 413 414 /** 415 * Sets the data source as a content Uri. 416 * 417 * To provide cookies for the subsequent HTTP requests, you can install your own default 418 * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you 419 * can use this API to pass the cookies as a list of HttpCookie. If the app has not 420 * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager 421 * and populates its CookieStore with the provided cookies when this data source is passed 422 * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler 423 * is required to be of CookieManager type such that {@link MediaPlayer2} can update the 424 * manager’s CookieStore. 425 * 426 * <p><strong>Note</strong> that the cross domain redirection is allowed by default, 427 * but that can be changed with key/value pairs through the headers parameter with 428 * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to 429 * disallow or allow cross domain redirection. 430 * 431 * @param context the Context to use when resolving the Uri 432 * @param uri the Content URI of the data you want to play 433 * @param headers the headers to be sent together with the request for the data 434 * The headers must not include cookies. Instead, use the cookies param. 435 * @param cookies the cookies to be sent together with the request 436 * @return the same Builder instance. 437 * @throws NullPointerException if context or uri is null. 438 * @throws IllegalArgumentException if the cookie handler is not of CookieManager type 439 * when cookies are provided. 440 */ 441 public Builder setDataSource(@NonNull Context context, @NonNull Uri uri, 442 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) { 443 Preconditions.checkNotNull(context, "context cannot be null"); 444 Preconditions.checkNotNull(uri); 445 if (cookies != null) { 446 CookieHandler cookieHandler = CookieHandler.getDefault(); 447 if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { 448 throw new IllegalArgumentException( 449 "The cookie handler has to be of CookieManager type " 450 + "when cookies are provided."); 451 } 452 } 453 454 resetDataSource(); 455 mType = TYPE_URI; 456 mUri = uri; 457 if (headers != null) { 458 mUriHeader = new HashMap<String, String>(headers); 459 } 460 if (cookies != null) { 461 mUriCookies = new ArrayList<HttpCookie>(cookies); 462 } 463 mUriContext = context; 464 return this; 465 } 466 467 private void resetDataSource() { 468 mType = TYPE_NONE; 469 mMedia2DataSource = null; 470 mFD = null; 471 mFDOffset = 0; 472 mFDLength = LONG_MAX; 473 mUri = null; 474 mUriHeader = null; 475 mUriCookies = null; 476 mUriContext = null; 477 } 478 } 479} 480