1/* 2 * Copyright (C) 2012 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.content.ContentResolver; 20import android.content.Context; 21import android.content.res.AssetFileDescriptor; 22import android.media.MediaCodec; 23import android.media.MediaFormat; 24import android.net.Uri; 25 26import java.io.FileDescriptor; 27import java.io.IOException; 28import java.nio.ByteBuffer; 29import java.nio.ByteOrder; 30import java.util.HashMap; 31import java.util.Map; 32import java.util.UUID; 33 34/** 35 * MediaExtractor facilitates extraction of demuxed, typically encoded, media data 36 * from a data source. 37 * <p>It is generally used like this: 38 * <pre> 39 * MediaExtractor extractor = new MediaExtractor(); 40 * extractor.setDataSource(...); 41 * int numTracks = extractor.getTrackCount(); 42 * for (int i = 0; i < numTracks; ++i) { 43 * MediaFormat format = extractor.getTrackFormat(i); 44 * String mime = format.getString(MediaFormat.KEY_MIME); 45 * if (weAreInterestedInThisTrack) { 46 * extractor.selectTrack(i); 47 * } 48 * } 49 * ByteBuffer inputBuffer = ByteBuffer.allocate(...) 50 * while (extractor.readSampleData(inputBuffer, ...) >= 0) { 51 * int trackIndex = extractor.getSampleTrackIndex(); 52 * long presentationTimeUs = extractor.getSampleTime(); 53 * ... 54 * extractor.advance(); 55 * } 56 * 57 * extractor.release(); 58 * extractor = null; 59 * </pre> 60 */ 61final public class MediaExtractor { 62 public MediaExtractor() { 63 native_setup(); 64 } 65 66 /** 67 * Sets the DataSource object to be used as the data source for this extractor 68 * {@hide} 69 */ 70 public native final void setDataSource(DataSource source) throws IOException; 71 72 /** 73 * Sets the data source as a content Uri. 74 * 75 * @param context the Context to use when resolving the Uri 76 * @param uri the Content URI of the data you want to extract from. 77 * @param headers the headers to be sent together with the request for the data 78 */ 79 public final void setDataSource( 80 Context context, Uri uri, Map<String, String> headers) 81 throws IOException { 82 String scheme = uri.getScheme(); 83 if(scheme == null || scheme.equals("file")) { 84 setDataSource(uri.getPath()); 85 return; 86 } 87 88 AssetFileDescriptor fd = null; 89 try { 90 ContentResolver resolver = context.getContentResolver(); 91 fd = resolver.openAssetFileDescriptor(uri, "r"); 92 if (fd == null) { 93 return; 94 } 95 // Note: using getDeclaredLength so that our behavior is the same 96 // as previous versions when the content provider is returning 97 // a full file. 98 if (fd.getDeclaredLength() < 0) { 99 setDataSource(fd.getFileDescriptor()); 100 } else { 101 setDataSource( 102 fd.getFileDescriptor(), 103 fd.getStartOffset(), 104 fd.getDeclaredLength()); 105 } 106 return; 107 } catch (SecurityException ex) { 108 } catch (IOException ex) { 109 } finally { 110 if (fd != null) { 111 fd.close(); 112 } 113 } 114 115 setDataSource(uri.toString(), headers); 116 } 117 118 /** 119 * Sets the data source (file-path or http URL) to use. 120 * 121 * @param path the path of the file, or the http URL 122 * @param headers the headers associated with the http request for the stream you want to play 123 */ 124 public final void setDataSource(String path, Map<String, String> headers) 125 throws IOException { 126 String[] keys = null; 127 String[] values = null; 128 129 if (headers != null) { 130 keys = new String[headers.size()]; 131 values = new String[headers.size()]; 132 133 int i = 0; 134 for (Map.Entry<String, String> entry: headers.entrySet()) { 135 keys[i] = entry.getKey(); 136 values[i] = entry.getValue(); 137 ++i; 138 } 139 } 140 setDataSource(path, keys, values); 141 } 142 143 private native final void setDataSource( 144 String path, String[] keys, String[] values) throws IOException; 145 146 /** 147 * Sets the data source (file-path or http URL) to use. 148 * 149 * @param path the path of the file, or the http URL of the stream 150 * 151 * <p>When <code>path</code> refers to a local file, the file may actually be opened by a 152 * process other than the calling application. This implies that the pathname 153 * should be an absolute path (as any other process runs with unspecified current working 154 * directory), and that the pathname should reference a world-readable file. 155 * As an alternative, the application could first open the file for reading, 156 * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. 157 */ 158 public final void setDataSource(String path) throws IOException { 159 setDataSource(path, null, null); 160 } 161 162 /** 163 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 164 * to close the file descriptor. It is safe to do so as soon as this call returns. 165 * 166 * @param fd the FileDescriptor for the file you want to extract from. 167 */ 168 public final void setDataSource(FileDescriptor fd) throws IOException { 169 setDataSource(fd, 0, 0x7ffffffffffffffL); 170 } 171 172 /** 173 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 174 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 175 * to close the file descriptor. It is safe to do so as soon as this call returns. 176 * 177 * @param fd the FileDescriptor for the file you want to extract from. 178 * @param offset the offset into the file where the data to be extracted starts, in bytes 179 * @param length the length in bytes of the data to be extracted 180 */ 181 public native final void setDataSource( 182 FileDescriptor fd, long offset, long length) throws IOException; 183 184 @Override 185 protected void finalize() { 186 native_finalize(); 187 } 188 189 /** 190 * Make sure you call this when you're done to free up any resources 191 * instead of relying on the garbage collector to do this for you at 192 * some point in the future. 193 */ 194 public native final void release(); 195 196 /** 197 * Count the number of tracks found in the data source. 198 */ 199 public native final int getTrackCount(); 200 201 /** 202 * Get the PSSH info if present. 203 * @return a map of uuid-to-bytes, with the uuid specifying 204 * the crypto scheme, and the bytes being the data specific to that scheme. 205 */ 206 public Map<UUID, byte[]> getPsshInfo() { 207 Map<UUID, byte[]> psshMap = null; 208 Map<String, Object> formatMap = getFileFormatNative(); 209 if (formatMap != null && formatMap.containsKey("pssh")) { 210 ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh"); 211 rawpssh.order(ByteOrder.nativeOrder()); 212 rawpssh.rewind(); 213 formatMap.remove("pssh"); 214 // parse the flat pssh bytebuffer into something more manageable 215 psshMap = new HashMap<UUID, byte[]>(); 216 while (rawpssh.remaining() > 0) { 217 rawpssh.order(ByteOrder.BIG_ENDIAN); 218 long msb = rawpssh.getLong(); 219 long lsb = rawpssh.getLong(); 220 UUID uuid = new UUID(msb, lsb); 221 rawpssh.order(ByteOrder.nativeOrder()); 222 int datalen = rawpssh.getInt(); 223 byte [] psshdata = new byte[datalen]; 224 rawpssh.get(psshdata); 225 psshMap.put(uuid, psshdata); 226 } 227 } 228 return psshMap; 229 } 230 231 private native Map<String, Object> getFileFormatNative(); 232 233 /** 234 * Get the track format at the specified index. 235 * More detail on the representation can be found at {@link android.media.MediaCodec} 236 */ 237 public MediaFormat getTrackFormat(int index) { 238 return new MediaFormat(getTrackFormatNative(index)); 239 } 240 241 private native Map<String, Object> getTrackFormatNative(int index); 242 243 /** 244 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 245 * {@link #getSampleTime} only retrieve information for the subset of tracks 246 * selected. 247 * Selecting the same track multiple times has no effect, the track is 248 * only selected once. 249 */ 250 public native void selectTrack(int index); 251 252 /** 253 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 254 * {@link #getSampleTime} only retrieve information for the subset of tracks 255 * selected. 256 */ 257 public native void unselectTrack(int index); 258 259 /** 260 * If possible, seek to a sync sample at or before the specified time 261 */ 262 public static final int SEEK_TO_PREVIOUS_SYNC = 0; 263 /** 264 * If possible, seek to a sync sample at or after the specified time 265 */ 266 public static final int SEEK_TO_NEXT_SYNC = 1; 267 /** 268 * If possible, seek to the sync sample closest to the specified time 269 */ 270 public static final int SEEK_TO_CLOSEST_SYNC = 2; 271 272 /** 273 * All selected tracks seek near the requested time according to the 274 * specified mode. 275 */ 276 public native void seekTo(long timeUs, int mode); 277 278 /** 279 * Advance to the next sample. Returns false if no more sample data 280 * is available (end of stream). 281 */ 282 public native boolean advance(); 283 284 /** 285 * Retrieve the current encoded sample and store it in the byte buffer 286 * starting at the given offset. Returns the sample size (or -1 if 287 * no more samples are available). 288 */ 289 public native int readSampleData(ByteBuffer byteBuf, int offset); 290 291 /** 292 * Returns the track index the current sample originates from (or -1 293 * if no more samples are available) 294 */ 295 public native int getSampleTrackIndex(); 296 297 /** 298 * Returns the current sample's presentation time in microseconds. 299 * or -1 if no more samples are available. 300 */ 301 public native long getSampleTime(); 302 303 // Keep these in sync with their equivalents in NuMediaExtractor.h 304 /** 305 * The sample is a sync sample 306 */ 307 public static final int SAMPLE_FLAG_SYNC = 1; 308 309 /** 310 * The sample is (at least partially) encrypted, see also the documentation 311 * for {@link android.media.MediaCodec#queueSecureInputBuffer} 312 */ 313 public static final int SAMPLE_FLAG_ENCRYPTED = 2; 314 315 /** 316 * Returns the current sample's flags. 317 */ 318 public native int getSampleFlags(); 319 320 /** 321 * If the sample flags indicate that the current sample is at least 322 * partially encrypted, this call returns relevant information about 323 * the structure of the sample data required for decryption. 324 * @param info The android.media.MediaCodec.CryptoInfo structure 325 * to be filled in. 326 * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED} 327 */ 328 public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info); 329 330 /** 331 * Returns an estimate of how much data is presently cached in memory 332 * expressed in microseconds. Returns -1 if that information is unavailable 333 * or not applicable (no cache). 334 */ 335 public native long getCachedDuration(); 336 337 /** 338 * Returns true iff we are caching data and the cache has reached the 339 * end of the data stream (for now, a future seek may of course restart 340 * the fetching of data). 341 * This API only returns a meaningful result if {@link #getCachedDuration} 342 * indicates the presence of a cache, i.e. does NOT return -1. 343 */ 344 public native boolean hasCacheReachedEndOfStream(); 345 346 private static native final void native_init(); 347 private native final void native_setup(); 348 private native final void native_finalize(); 349 350 static { 351 System.loadLibrary("media_jni"); 352 native_init(); 353 } 354 355 private int mNativeContext; 356} 357