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