MediaExtractor.java revision e20a6d5c479909f37af748a81a6e5a5deb7b6e2c
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); 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 String[] keys = null; 126 String[] values = null; 127 128 if (headers != null) { 129 keys = new String[headers.size()]; 130 values = new String[headers.size()]; 131 132 int i = 0; 133 for (Map.Entry<String, String> entry: headers.entrySet()) { 134 keys[i] = entry.getKey(); 135 values[i] = entry.getValue(); 136 ++i; 137 } 138 } 139 setDataSource(path, keys, values); 140 } 141 142 private native final void setDataSource( 143 String path, String[] keys, String[] values); 144 145 /** 146 * Sets the data source (file-path or http URL) to use. 147 * 148 * @param path the path of the file, or the http URL of the stream 149 * 150 * <p>When <code>path</code> refers to a local file, the file may actually be opened by a 151 * process other than the calling application. This implies that the pathname 152 * should be an absolute path (as any other process runs with unspecified current working 153 * directory), and that the pathname should reference a world-readable file. 154 * As an alternative, the application could first open the file for reading, 155 * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. 156 */ 157 public final void setDataSource(String path) { 158 setDataSource(path, null, null); 159 } 160 161 /** 162 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 163 * to close the file descriptor. It is safe to do so as soon as this call returns. 164 * 165 * @param fd the FileDescriptor for the file you want to extract from. 166 */ 167 public final void setDataSource(FileDescriptor fd) { 168 setDataSource(fd, 0, 0x7ffffffffffffffL); 169 } 170 171 /** 172 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 173 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 174 * to close the file descriptor. It is safe to do so as soon as this call returns. 175 * 176 * @param fd the FileDescriptor for the file you want to extract from. 177 * @param offset the offset into the file where the data to be extracted starts, in bytes 178 * @param length the length in bytes of the data to be extracted 179 */ 180 public native final void setDataSource( 181 FileDescriptor fd, long offset, long length); 182 183 @Override 184 protected void finalize() { 185 native_finalize(); 186 } 187 188 /** 189 * Make sure you call this when you're done to free up any resources 190 * instead of relying on the garbage collector to do this for you at 191 * some point in the future. 192 */ 193 public native final void release(); 194 195 /** 196 * Count the number of tracks found in the data source. 197 */ 198 public native final int getTrackCount(); 199 200 /** 201 * Get the PSSH info if present. This returns a map of uuid-to-bytes, with the uuid specifying 202 * the crypto scheme, and the bytes being the data specific to that scheme. 203 * {@hide} 204 */ 205 public Map<UUID, byte[]> getPsshInfo() { 206 Map<UUID, byte[]> psshMap = null; 207 Map<String, Object> formatMap = getFileFormatNative(); 208 if (formatMap != null && formatMap.containsKey("pssh")) { 209 ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh"); 210 rawpssh.order(ByteOrder.nativeOrder()); 211 rawpssh.rewind(); 212 formatMap.remove("pssh"); 213 // parse the flat pssh bytebuffer into something more manageable 214 psshMap = new HashMap<UUID, byte[]>(); 215 while (rawpssh.remaining() > 0) { 216 rawpssh.order(ByteOrder.BIG_ENDIAN); 217 long msb = rawpssh.getLong(); 218 long lsb = rawpssh.getLong(); 219 UUID uuid = new UUID(msb, lsb); 220 rawpssh.order(ByteOrder.nativeOrder()); 221 int datalen = rawpssh.getInt(); 222 byte [] psshdata = new byte[datalen]; 223 rawpssh.get(psshdata); 224 psshMap.put(uuid, psshdata); 225 } 226 } 227 return psshMap; 228 } 229 230 private native Map<String, Object> getFileFormatNative(); 231 232 /** 233 * Get the track format at the specified index. 234 * More detail on the representation can be found at {@link android.media.MediaCodec} 235 */ 236 public MediaFormat getTrackFormat(int index) { 237 return new MediaFormat(getTrackFormatNative(index)); 238 } 239 240 private native Map<String, Object> getTrackFormatNative(int index); 241 242 /** 243 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 244 * {@link #getSampleTime} only retrieve information for the subset of tracks 245 * selected. 246 * Selecting the same track multiple times has no effect, the track is 247 * only selected once. 248 */ 249 public native void selectTrack(int index); 250 251 /** 252 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 253 * {@link #getSampleTime} only retrieve information for the subset of tracks 254 * selected. 255 */ 256 public native void unselectTrack(int index); 257 258 /** 259 * If possible, seek to a sync sample at or before the specified time 260 */ 261 public static final int SEEK_TO_PREVIOUS_SYNC = 0; 262 /** 263 * If possible, seek to a sync sample at or after the specified time 264 */ 265 public static final int SEEK_TO_NEXT_SYNC = 1; 266 /** 267 * If possible, seek to the sync sample closest to the specified time 268 */ 269 public static final int SEEK_TO_CLOSEST_SYNC = 2; 270 271 /** 272 * All selected tracks seek near the requested time according to the 273 * specified mode. 274 */ 275 public native void seekTo(long timeUs, int mode); 276 277 /** 278 * Advance to the next sample. Returns false if no more sample data 279 * is available (end of stream). 280 */ 281 public native boolean advance(); 282 283 /** 284 * Retrieve the current encoded sample and store it in the byte buffer 285 * starting at the given offset. Returns the sample size (or -1 if 286 * no more samples are available). 287 */ 288 public native int readSampleData(ByteBuffer byteBuf, int offset); 289 290 /** 291 * Returns the track index the current sample originates from (or -1 292 * if no more samples are available) 293 */ 294 public native int getSampleTrackIndex(); 295 296 /** 297 * Returns the current sample's presentation time in microseconds. 298 * or -1 if no more samples are available. 299 */ 300 public native long getSampleTime(); 301 302 // Keep these in sync with their equivalents in NuMediaExtractor.h 303 /** 304 * The sample is a sync sample 305 */ 306 public static final int SAMPLE_FLAG_SYNC = 1; 307 308 /** 309 * The sample is (at least partially) encrypted, see also the documentation 310 * for {@link android.media.MediaCodec#queueSecureInputBuffer} 311 */ 312 public static final int SAMPLE_FLAG_ENCRYPTED = 2; 313 314 /** 315 * Returns the current sample's flags. 316 */ 317 public native int getSampleFlags(); 318 319 /** 320 * If the sample flags indicate that the current sample is at least 321 * partially encrypted, this call returns relevant information about 322 * the structure of the sample data required for decryption. 323 * @param info The android.media.MediaCodec.CryptoInfo structure 324 * to be filled in. 325 * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED} 326 */ 327 public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info); 328 329 /** 330 * Returns an estimate of how much data is presently cached in memory 331 * expressed in microseconds. Returns -1 if that information is unavailable 332 * or not applicable (no cache). 333 */ 334 public native long getCachedDuration(); 335 336 /** 337 * Returns true iff we are caching data and the cache has reached the 338 * end of the data stream (for now, a future seek may of course restart 339 * the fetching of data). 340 * This API only returns a meaningful result if {@link #getCachedDuration} 341 * indicates the presence of a cache, i.e. does NOT return -1. 342 */ 343 public native boolean hasCacheReachedEndOfStream(); 344 345 private static native final void native_init(); 346 private native final void native_setup(); 347 private native final void native_finalize(); 348 349 static { 350 System.loadLibrary("media_jni"); 351 native_init(); 352 } 353 354 private int mNativeContext; 355} 356