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