Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 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; |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 20 | import android.annotation.NonNull; |
| 21 | import android.annotation.Nullable; |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 22 | import android.content.ContentResolver; |
| 23 | import android.content.Context; |
| 24 | import android.content.res.AssetFileDescriptor; |
| 25 | import android.graphics.Bitmap; |
| 26 | import android.net.Uri; |
| 27 | import android.os.IBinder; |
| 28 | |
| 29 | import java.io.FileDescriptor; |
| 30 | import java.io.FileInputStream; |
| 31 | import java.io.FileNotFoundException; |
| 32 | import java.io.IOException; |
| 33 | import java.lang.annotation.Retention; |
| 34 | import java.lang.annotation.RetentionPolicy; |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 35 | import java.util.List; |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 36 | import java.util.Map; |
| 37 | |
| 38 | /** |
| 39 | * MediaMetadataRetriever class provides a unified interface for retrieving |
| 40 | * frame and meta data from an input media file. |
| 41 | */ |
| 42 | public class MediaMetadataRetriever |
| 43 | { |
| 44 | static { |
| 45 | System.loadLibrary("media_jni"); |
| 46 | native_init(); |
| 47 | } |
| 48 | |
| 49 | // The field below is accessed by native methods |
| 50 | @SuppressWarnings("unused") |
| 51 | private long mNativeContext; |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 52 | |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 53 | private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF; |
| 54 | |
| 55 | public MediaMetadataRetriever() { |
| 56 | native_setup(); |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * Sets the data source (file pathname) to use. Call this |
| 61 | * method before the rest of the methods in this class. This method may be |
| 62 | * time-consuming. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 63 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 64 | * @param path The path of the input media file. |
| 65 | * @throws IllegalArgumentException If the path is invalid. |
| 66 | */ |
| 67 | public void setDataSource(String path) throws IllegalArgumentException { |
| 68 | if (path == null) { |
| 69 | throw new IllegalArgumentException(); |
| 70 | } |
| 71 | |
| 72 | try (FileInputStream is = new FileInputStream(path)) { |
| 73 | FileDescriptor fd = is.getFD(); |
| 74 | setDataSource(fd, 0, 0x7ffffffffffffffL); |
| 75 | } catch (FileNotFoundException fileEx) { |
| 76 | throw new IllegalArgumentException(); |
| 77 | } catch (IOException ioEx) { |
| 78 | throw new IllegalArgumentException(); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Sets the data source (URI) to use. Call this |
| 84 | * method before the rest of the methods in this class. This method may be |
| 85 | * time-consuming. |
| 86 | * |
| 87 | * @param uri The URI of the input media. |
| 88 | * @param headers the headers to be sent together with the request for the data |
| 89 | * @throws IllegalArgumentException If the URI is invalid. |
| 90 | */ |
| 91 | public void setDataSource(String uri, Map<String, String> headers) |
| 92 | throws IllegalArgumentException { |
| 93 | int i = 0; |
| 94 | String[] keys = new String[headers.size()]; |
| 95 | String[] values = new String[headers.size()]; |
| 96 | for (Map.Entry<String, String> entry: headers.entrySet()) { |
| 97 | keys[i] = entry.getKey(); |
| 98 | values[i] = entry.getValue(); |
| 99 | ++i; |
| 100 | } |
| 101 | |
| 102 | _setDataSource( |
| 103 | MediaHTTPService.createHttpServiceBinderIfNecessary(uri), |
| 104 | uri, |
| 105 | keys, |
| 106 | values); |
| 107 | } |
| 108 | |
| 109 | private native void _setDataSource( |
| 110 | IBinder httpServiceBinder, String uri, String[] keys, String[] values) |
| 111 | throws IllegalArgumentException; |
| 112 | |
| 113 | /** |
| 114 | * Sets the data source (FileDescriptor) to use. It is the caller's |
| 115 | * responsibility to close the file descriptor. It is safe to do so as soon |
| 116 | * as this call returns. Call this method before the rest of the methods in |
| 117 | * this class. This method may be time-consuming. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 118 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 119 | * @param fd the FileDescriptor for the file you want to play |
| 120 | * @param offset the offset into the file where the data to be played starts, |
| 121 | * in bytes. It must be non-negative |
| 122 | * @param length the length in bytes of the data to be played. It must be |
| 123 | * non-negative. |
| 124 | * @throws IllegalArgumentException if the arguments are invalid |
| 125 | */ |
| 126 | public native void setDataSource(FileDescriptor fd, long offset, long length) |
| 127 | throws IllegalArgumentException; |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 128 | |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 129 | /** |
| 130 | * Sets the data source (FileDescriptor) to use. It is the caller's |
| 131 | * responsibility to close the file descriptor. It is safe to do so as soon |
| 132 | * as this call returns. Call this method before the rest of the methods in |
| 133 | * this class. This method may be time-consuming. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 134 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 135 | * @param fd the FileDescriptor for the file you want to play |
| 136 | * @throws IllegalArgumentException if the FileDescriptor is invalid |
| 137 | */ |
| 138 | public void setDataSource(FileDescriptor fd) |
| 139 | throws IllegalArgumentException { |
| 140 | // intentionally less than LONG_MAX |
| 141 | setDataSource(fd, 0, 0x7ffffffffffffffL); |
| 142 | } |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 143 | |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 144 | /** |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 145 | * Sets the data source as a content Uri. Call this method before |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 146 | * the rest of the methods in this class. This method may be time-consuming. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 147 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 148 | * @param context the Context to use when resolving the Uri |
| 149 | * @param uri the Content URI of the data you want to play |
| 150 | * @throws IllegalArgumentException if the Uri is invalid |
| 151 | * @throws SecurityException if the Uri cannot be used due to lack of |
| 152 | * permission. |
| 153 | */ |
| 154 | public void setDataSource(Context context, Uri uri) |
| 155 | throws IllegalArgumentException, SecurityException { |
| 156 | if (uri == null) { |
| 157 | throw new IllegalArgumentException(); |
| 158 | } |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 159 | |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 160 | String scheme = uri.getScheme(); |
| 161 | if(scheme == null || scheme.equals("file")) { |
| 162 | setDataSource(uri.getPath()); |
| 163 | return; |
| 164 | } |
| 165 | |
| 166 | AssetFileDescriptor fd = null; |
| 167 | try { |
| 168 | ContentResolver resolver = context.getContentResolver(); |
| 169 | try { |
| 170 | fd = resolver.openAssetFileDescriptor(uri, "r"); |
| 171 | } catch(FileNotFoundException e) { |
| 172 | throw new IllegalArgumentException(); |
| 173 | } |
| 174 | if (fd == null) { |
| 175 | throw new IllegalArgumentException(); |
| 176 | } |
| 177 | FileDescriptor descriptor = fd.getFileDescriptor(); |
| 178 | if (!descriptor.valid()) { |
| 179 | throw new IllegalArgumentException(); |
| 180 | } |
| 181 | // Note: using getDeclaredLength so that our behavior is the same |
| 182 | // as previous versions when the content provider is returning |
| 183 | // a full file. |
| 184 | if (fd.getDeclaredLength() < 0) { |
| 185 | setDataSource(descriptor); |
| 186 | } else { |
| 187 | setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength()); |
| 188 | } |
| 189 | return; |
| 190 | } catch (SecurityException ex) { |
| 191 | } finally { |
| 192 | try { |
| 193 | if (fd != null) { |
| 194 | fd.close(); |
| 195 | } |
| 196 | } catch(IOException ioEx) { |
| 197 | } |
| 198 | } |
| 199 | setDataSource(uri.toString()); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Sets the data source (MediaDataSource) to use. |
| 204 | * |
| 205 | * @param dataSource the MediaDataSource for the media you want to play |
| 206 | */ |
| 207 | public void setDataSource(MediaDataSource dataSource) |
| 208 | throws IllegalArgumentException { |
| 209 | _setDataSource(dataSource); |
| 210 | } |
| 211 | |
| 212 | private native void _setDataSource(MediaDataSource dataSource) |
| 213 | throws IllegalArgumentException; |
| 214 | |
| 215 | /** |
| 216 | * Call this method after setDataSource(). This method retrieves the |
| 217 | * meta data value associated with the keyCode. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 218 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 219 | * The keyCode currently supported is listed below as METADATA_XXX |
| 220 | * constants. With any other value, it returns a null pointer. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 221 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 222 | * @param keyCode One of the constants listed below at the end of the class. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 223 | * @return The meta data value associate with the given keyCode on success; |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 224 | * null on failure. |
| 225 | */ |
| 226 | public native String extractMetadata(int keyCode); |
| 227 | |
| 228 | /** |
| 229 | * Call this method after setDataSource(). This method finds a |
| 230 | * representative frame close to the given time position by considering |
Justin Klaassen | 98fe781 | 2018-01-03 13:39:41 -0500 | [diff] [blame] | 231 | * the given option if possible, and returns it as a bitmap. |
| 232 | * |
| 233 | * <p>If you don't need a full-resolution |
| 234 | * frame (for example, because you need a thumbnail image), use |
| 235 | * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this |
| 236 | * method.</p> |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 237 | * |
| 238 | * @param timeUs The time position where the frame will be retrieved. |
| 239 | * When retrieving the frame at the given time position, there is no |
| 240 | * guarantee that the data source has a frame located at the position. |
| 241 | * When this happens, a frame nearby will be returned. If timeUs is |
| 242 | * negative, time position and option will ignored, and any frame |
| 243 | * that the implementation considers as representative may be returned. |
| 244 | * |
| 245 | * @param option a hint on how the frame is found. Use |
| 246 | * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame |
| 247 | * that has a timestamp earlier than or the same as timeUs. Use |
| 248 | * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame |
| 249 | * that has a timestamp later than or the same as timeUs. Use |
| 250 | * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame |
| 251 | * that has a timestamp closest to or the same as timeUs. Use |
| 252 | * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may |
| 253 | * or may not be a sync frame but is closest to or the same as timeUs. |
| 254 | * {@link #OPTION_CLOSEST} often has larger performance overhead compared |
| 255 | * to the other options if there is no sync frame located at timeUs. |
| 256 | * |
| 257 | * @return A Bitmap containing a representative video frame, which |
| 258 | * can be null, if such a frame cannot be retrieved. |
| 259 | */ |
| 260 | public Bitmap getFrameAtTime(long timeUs, @Option int option) { |
| 261 | if (option < OPTION_PREVIOUS_SYNC || |
| 262 | option > OPTION_CLOSEST) { |
| 263 | throw new IllegalArgumentException("Unsupported option: " + option); |
| 264 | } |
| 265 | |
| 266 | return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/); |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Retrieve a video frame near a given timestamp scaled to a desired size. |
| 271 | * Call this method after setDataSource(). This method finds a representative |
| 272 | * frame close to the given time position by considering the given option |
| 273 | * if possible, and returns it as a bitmap with same aspect ratio as the source |
| 274 | * while scaling it so that it fits into the desired size of dst_width by dst_height. |
| 275 | * This is useful for generating a thumbnail for an input data source or just to |
| 276 | * obtain a scaled frame at the given time position. |
| 277 | * |
| 278 | * @param timeUs The time position in microseconds where the frame will be retrieved. |
| 279 | * When retrieving the frame at the given time position, there is no |
| 280 | * guarantee that the data source has a frame located at the position. |
| 281 | * When this happens, a frame nearby will be returned. If timeUs is |
| 282 | * negative, time position and option will ignored, and any frame |
| 283 | * that the implementation considers as representative may be returned. |
| 284 | * |
| 285 | * @param option a hint on how the frame is found. Use |
| 286 | * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame |
| 287 | * that has a timestamp earlier than or the same as timeUs. Use |
| 288 | * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame |
| 289 | * that has a timestamp later than or the same as timeUs. Use |
| 290 | * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame |
| 291 | * that has a timestamp closest to or the same as timeUs. Use |
| 292 | * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may |
| 293 | * or may not be a sync frame but is closest to or the same as timeUs. |
| 294 | * {@link #OPTION_CLOSEST} often has larger performance overhead compared |
| 295 | * to the other options if there is no sync frame located at timeUs. |
| 296 | * |
| 297 | * @param dstWidth expected output bitmap width |
| 298 | * @param dstHeight expected output bitmap height |
| 299 | * @return A Bitmap of size not larger than dstWidth by dstHeight containing a |
| 300 | * scaled video frame, which can be null, if such a frame cannot be retrieved. |
| 301 | * @throws IllegalArgumentException if passed in invalid option or width by height |
| 302 | * is less than or equal to 0. |
| 303 | */ |
| 304 | public Bitmap getScaledFrameAtTime( |
| 305 | long timeUs, @Option int option, int dstWidth, int dstHeight) { |
| 306 | if (option < OPTION_PREVIOUS_SYNC || |
| 307 | option > OPTION_CLOSEST) { |
| 308 | throw new IllegalArgumentException("Unsupported option: " + option); |
| 309 | } |
| 310 | if (dstWidth <= 0) { |
| 311 | throw new IllegalArgumentException("Invalid width: " + dstWidth); |
| 312 | } |
| 313 | if (dstHeight <= 0) { |
| 314 | throw new IllegalArgumentException("Invalid height: " + dstHeight); |
| 315 | } |
| 316 | |
| 317 | return _getFrameAtTime(timeUs, option, dstWidth, dstHeight); |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Call this method after setDataSource(). This method finds a |
| 322 | * representative frame close to the given time position if possible, |
Justin Klaassen | 98fe781 | 2018-01-03 13:39:41 -0500 | [diff] [blame] | 323 | * and returns it as a bitmap. Call this method if one does not care |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 324 | * how the frame is found as long as it is close to the given time; |
| 325 | * otherwise, please call {@link #getFrameAtTime(long, int)}. |
| 326 | * |
Justin Klaassen | 98fe781 | 2018-01-03 13:39:41 -0500 | [diff] [blame] | 327 | * <p>If you don't need a full-resolution |
| 328 | * frame (for example, because you need a thumbnail image), use |
| 329 | * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this |
| 330 | * method.</p> |
| 331 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 332 | * @param timeUs The time position where the frame will be retrieved. |
| 333 | * When retrieving the frame at the given time position, there is no |
| 334 | * guarentee that the data source has a frame located at the position. |
| 335 | * When this happens, a frame nearby will be returned. If timeUs is |
| 336 | * negative, time position and option will ignored, and any frame |
| 337 | * that the implementation considers as representative may be returned. |
| 338 | * |
| 339 | * @return A Bitmap of size dst_widthxdst_height containing a representative |
| 340 | * video frame, which can be null, if such a frame cannot be retrieved. |
| 341 | * |
| 342 | * @see #getFrameAtTime(long, int) |
| 343 | */ |
| 344 | public Bitmap getFrameAtTime(long timeUs) { |
| 345 | return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC); |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Call this method after setDataSource(). This method finds a |
| 350 | * representative frame at any time position if possible, |
Justin Klaassen | 98fe781 | 2018-01-03 13:39:41 -0500 | [diff] [blame] | 351 | * and returns it as a bitmap. Call this method if one does not |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 352 | * care about where the frame is located; otherwise, please call |
| 353 | * {@link #getFrameAtTime(long)} or {@link #getFrameAtTime(long, int)} |
| 354 | * |
Justin Klaassen | 98fe781 | 2018-01-03 13:39:41 -0500 | [diff] [blame] | 355 | * <p>If you don't need a full-resolution |
| 356 | * frame (for example, because you need a thumbnail image), use |
| 357 | * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this |
| 358 | * method.</p> |
| 359 | * |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 360 | * @return A Bitmap containing a representative video frame, which |
| 361 | * can be null, if such a frame cannot be retrieved. |
| 362 | * |
| 363 | * @see #getFrameAtTime(long) |
| 364 | * @see #getFrameAtTime(long, int) |
| 365 | */ |
| 366 | public Bitmap getFrameAtTime() { |
| 367 | return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/); |
| 368 | } |
| 369 | |
| 370 | private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height); |
| 371 | |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 372 | public static final class BitmapParams { |
| 373 | private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888; |
| 374 | private Bitmap.Config outActualConfig = Bitmap.Config.ARGB_8888; |
| 375 | |
| 376 | /** |
| 377 | * Create a default BitmapParams object. By default, it uses {@link Bitmap.Config#ARGB_8888} |
| 378 | * as the preferred bitmap config. |
| 379 | */ |
| 380 | public BitmapParams() {} |
| 381 | |
| 382 | /** |
| 383 | * Set the preferred bitmap config for the decoder to decode into. |
| 384 | * |
| 385 | * If not set, or the request cannot be met, the decoder will output |
| 386 | * in {@link Bitmap.Config#ARGB_8888} config by default. |
| 387 | * |
| 388 | * After decode, the actual config used can be retrieved by {@link #getActualConfig()}. |
| 389 | * |
| 390 | * @param config the preferred bitmap config to use. |
| 391 | */ |
| 392 | public void setPreferredConfig(@NonNull Bitmap.Config config) { |
| 393 | if (config == null) { |
| 394 | throw new IllegalArgumentException("preferred config can't be null"); |
| 395 | } |
| 396 | inPreferredConfig = config; |
| 397 | } |
| 398 | |
| 399 | /** |
| 400 | * Retrieve the preferred bitmap config in the params. |
| 401 | * |
| 402 | * @return the preferred bitmap config. |
| 403 | */ |
| 404 | public @NonNull Bitmap.Config getPreferredConfig() { |
| 405 | return inPreferredConfig; |
| 406 | } |
| 407 | |
| 408 | /** |
| 409 | * Get the actual bitmap config used to decode the bitmap after the decoding. |
| 410 | * |
| 411 | * @return the actual bitmap config used. |
| 412 | */ |
| 413 | public @NonNull Bitmap.Config getActualConfig() { |
| 414 | return outActualConfig; |
| 415 | } |
| 416 | } |
| 417 | |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 418 | /** |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 419 | * This method retrieves a video frame by its index. It should only be called |
| 420 | * after {@link #setDataSource}. |
| 421 | * |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 422 | * After the bitmap is returned, you can query the actual parameters that were |
| 423 | * used to create the bitmap from the {@code BitmapParams} argument, for instance |
| 424 | * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. |
| 425 | * |
| 426 | * @param frameIndex 0-based index of the video frame. The frame index must be that of |
| 427 | * a valid frame. The total number of frames available for retrieval can be queried |
| 428 | * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. |
| 429 | * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). |
| 430 | * |
| 431 | * @throws IllegalStateException if the container doesn't contain video or image sequences. |
| 432 | * @throws IllegalArgumentException if the requested frame index does not exist. |
| 433 | * |
| 434 | * @return A Bitmap containing the requested video frame, or null if the retrieval fails. |
| 435 | * |
| 436 | * @see #getFrameAtIndex(int) |
| 437 | * @see #getFramesAtIndex(int, int, BitmapParams) |
| 438 | * @see #getFramesAtIndex(int, int) |
| 439 | */ |
| 440 | public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) { |
| 441 | List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params); |
| 442 | return bitmaps.get(0); |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that |
| 447 | * the default for {@link BitmapParams} will be used. |
| 448 | * |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 449 | * @param frameIndex 0-based index of the video frame. The frame index must be that of |
| 450 | * a valid frame. The total number of frames available for retrieval can be queried |
| 451 | * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. |
| 452 | * |
| 453 | * @throws IllegalStateException if the container doesn't contain video or image sequences. |
| 454 | * @throws IllegalArgumentException if the requested frame index does not exist. |
| 455 | * |
| 456 | * @return A Bitmap containing the requested video frame, or null if the retrieval fails. |
| 457 | * |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 458 | * @see #getFrameAtIndex(int, BitmapParams) |
| 459 | * @see #getFramesAtIndex(int, int, BitmapParams) |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 460 | * @see #getFramesAtIndex(int, int) |
| 461 | */ |
| 462 | public Bitmap getFrameAtIndex(int frameIndex) { |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 463 | List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1); |
| 464 | return bitmaps.get(0); |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | /** |
| 468 | * This method retrieves a consecutive set of video frames starting at the |
| 469 | * specified index. It should only be called after {@link #setDataSource}. |
| 470 | * |
| 471 | * If the caller intends to retrieve more than one consecutive video frames, |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 472 | * this method is preferred over {@link #getFrameAtIndex(int, BitmapParams)} for efficiency. |
| 473 | * |
| 474 | * After the bitmaps are returned, you can query the actual parameters that were |
| 475 | * used to create the bitmaps from the {@code BitmapParams} argument, for instance |
| 476 | * to query the bitmap config used for the bitmaps with {@link BitmapParams#getActualConfig}. |
| 477 | * |
| 478 | * @param frameIndex 0-based index of the first video frame to retrieve. The frame index |
| 479 | * must be that of a valid frame. The total number of frames available for retrieval |
| 480 | * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. |
| 481 | * @param numFrames number of consecutive video frames to retrieve. Must be a positive |
| 482 | * value. The stream must contain at least numFrames frames starting at frameIndex. |
| 483 | * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). |
| 484 | * |
| 485 | * @throws IllegalStateException if the container doesn't contain video or image sequences. |
| 486 | * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the |
| 487 | * stream doesn't contain at least numFrames starting at frameIndex. |
| 488 | |
| 489 | * @return An list of Bitmaps containing the requested video frames. The returned |
| 490 | * array could contain less frames than requested if the retrieval fails. |
| 491 | * |
| 492 | * @see #getFrameAtIndex(int, BitmapParams) |
| 493 | * @see #getFrameAtIndex(int) |
| 494 | * @see #getFramesAtIndex(int, int) |
| 495 | */ |
| 496 | public @NonNull List<Bitmap> getFramesAtIndex( |
| 497 | int frameIndex, int numFrames, @NonNull BitmapParams params) { |
| 498 | return getFramesAtIndexInternal(frameIndex, numFrames, params); |
| 499 | } |
| 500 | |
| 501 | /** |
| 502 | * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that |
| 503 | * the default for {@link BitmapParams} will be used. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 504 | * |
| 505 | * @param frameIndex 0-based index of the first video frame to retrieve. The frame index |
| 506 | * must be that of a valid frame. The total number of frames available for retrieval |
| 507 | * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. |
| 508 | * @param numFrames number of consecutive video frames to retrieve. Must be a positive |
| 509 | * value. The stream must contain at least numFrames frames starting at frameIndex. |
| 510 | * |
| 511 | * @throws IllegalStateException if the container doesn't contain video or image sequences. |
| 512 | * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the |
| 513 | * stream doesn't contain at least numFrames starting at frameIndex. |
| 514 | |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 515 | * @return An list of Bitmaps containing the requested video frames. The returned |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 516 | * array could contain less frames than requested if the retrieval fails. |
| 517 | * |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 518 | * @see #getFrameAtIndex(int, BitmapParams) |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 519 | * @see #getFrameAtIndex(int) |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 520 | * @see #getFramesAtIndex(int, int, BitmapParams) |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 521 | */ |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 522 | public @NonNull List<Bitmap> getFramesAtIndex(int frameIndex, int numFrames) { |
| 523 | return getFramesAtIndexInternal(frameIndex, numFrames, null); |
| 524 | } |
| 525 | |
| 526 | private @NonNull List<Bitmap> getFramesAtIndexInternal( |
| 527 | int frameIndex, int numFrames, @Nullable BitmapParams params) { |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 528 | if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { |
| 529 | throw new IllegalStateException("Does not contail video or image sequences"); |
| 530 | } |
| 531 | int frameCount = Integer.parseInt( |
| 532 | extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)); |
| 533 | if (frameIndex < 0 || numFrames < 1 |
| 534 | || frameIndex >= frameCount |
| 535 | || frameIndex > frameCount - numFrames) { |
| 536 | throw new IllegalArgumentException("Invalid frameIndex or numFrames: " |
| 537 | + frameIndex + ", " + numFrames); |
| 538 | } |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 539 | return _getFrameAtIndex(frameIndex, numFrames, params); |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 540 | } |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 541 | |
| 542 | private native @NonNull List<Bitmap> _getFrameAtIndex( |
| 543 | int frameIndex, int numFrames, @Nullable BitmapParams params); |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 544 | |
| 545 | /** |
| 546 | * This method retrieves a still image by its index. It should only be called |
| 547 | * after {@link #setDataSource}. |
| 548 | * |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 549 | * After the bitmap is returned, you can query the actual parameters that were |
| 550 | * used to create the bitmap from the {@code BitmapParams} argument, for instance |
| 551 | * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. |
| 552 | * |
| 553 | * @param imageIndex 0-based index of the image. |
| 554 | * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). |
| 555 | * |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 556 | * @throws IllegalStateException if the container doesn't contain still images. |
| 557 | * @throws IllegalArgumentException if the requested image does not exist. |
| 558 | * |
| 559 | * @return the requested still image, or null if the image cannot be retrieved. |
| 560 | * |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 561 | * @see #getImageAtIndex(int) |
| 562 | * @see #getPrimaryImage(BitmapParams) |
| 563 | * @see #getPrimaryImage() |
| 564 | */ |
| 565 | public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) { |
| 566 | return getImageAtIndexInternal(imageIndex, params); |
| 567 | } |
| 568 | |
| 569 | /** |
| 570 | * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that |
| 571 | * the default for {@link BitmapParams} will be used. |
| 572 | * |
| 573 | * @param imageIndex 0-based index of the image. |
| 574 | * |
| 575 | * @throws IllegalStateException if the container doesn't contain still images. |
| 576 | * @throws IllegalArgumentException if the requested image does not exist. |
| 577 | * |
| 578 | * @return the requested still image, or null if the image cannot be retrieved. |
| 579 | * |
| 580 | * @see #getImageAtIndex(int, BitmapParams) |
| 581 | * @see #getPrimaryImage(BitmapParams) |
| 582 | * @see #getPrimaryImage() |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 583 | */ |
| 584 | public Bitmap getImageAtIndex(int imageIndex) { |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 585 | return getImageAtIndexInternal(imageIndex, null); |
| 586 | } |
| 587 | |
| 588 | /** |
| 589 | * This method retrieves the primary image of the media content. It should only |
| 590 | * be called after {@link #setDataSource}. |
| 591 | * |
| 592 | * After the bitmap is returned, you can query the actual parameters that were |
| 593 | * used to create the bitmap from the {@code BitmapParams} argument, for instance |
| 594 | * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. |
| 595 | * |
| 596 | * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). |
| 597 | * |
| 598 | * @return the primary image, or null if it cannot be retrieved. |
| 599 | * |
| 600 | * @throws IllegalStateException if the container doesn't contain still images. |
| 601 | * |
| 602 | * @see #getImageAtIndex(int, BitmapParams) |
| 603 | * @see #getImageAtIndex(int) |
| 604 | * @see #getPrimaryImage() |
| 605 | */ |
| 606 | public Bitmap getPrimaryImage(@NonNull BitmapParams params) { |
| 607 | return getImageAtIndexInternal(-1, params); |
| 608 | } |
| 609 | |
| 610 | /** |
| 611 | * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that |
| 612 | * the default for {@link BitmapParams} will be used. |
| 613 | * |
| 614 | * @return the primary image, or null if it cannot be retrieved. |
| 615 | * |
| 616 | * @throws IllegalStateException if the container doesn't contain still images. |
| 617 | * |
| 618 | * @see #getImageAtIndex(int, BitmapParams) |
| 619 | * @see #getImageAtIndex(int) |
| 620 | * @see #getPrimaryImage(BitmapParams) |
| 621 | */ |
| 622 | public Bitmap getPrimaryImage() { |
| 623 | return getImageAtIndexInternal(-1, null); |
| 624 | } |
| 625 | |
| 626 | private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) { |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 627 | if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { |
| 628 | throw new IllegalStateException("Does not contail still images"); |
| 629 | } |
| 630 | |
| 631 | String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); |
| 632 | if (imageIndex >= Integer.parseInt(imageCount)) { |
| 633 | throw new IllegalArgumentException("Invalid image index: " + imageCount); |
| 634 | } |
| 635 | |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 636 | return _getImageAtIndex(imageIndex, params); |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 637 | } |
| 638 | |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 639 | private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params); |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 640 | |
| 641 | /** |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 642 | * Call this method after setDataSource(). This method finds the optional |
| 643 | * graphic or album/cover art associated associated with the data source. If |
| 644 | * there are more than one pictures, (any) one of them is returned. |
| 645 | * |
| 646 | * @return null if no such graphic is found. |
| 647 | */ |
| 648 | public byte[] getEmbeddedPicture() { |
| 649 | return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY); |
| 650 | } |
| 651 | |
| 652 | private native byte[] getEmbeddedPicture(int pictureType); |
| 653 | |
| 654 | /** |
| 655 | * Call it when one is done with the object. This method releases the memory |
| 656 | * allocated internally. |
| 657 | */ |
| 658 | public native void release(); |
| 659 | private native void native_setup(); |
| 660 | private static native void native_init(); |
| 661 | |
| 662 | private native final void native_finalize(); |
| 663 | |
| 664 | @Override |
| 665 | protected void finalize() throws Throwable { |
| 666 | try { |
| 667 | native_finalize(); |
| 668 | } finally { |
| 669 | super.finalize(); |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | /** |
| 674 | * Option used in method {@link #getFrameAtTime(long, int)} to get a |
| 675 | * frame at a specified location. |
| 676 | * |
| 677 | * @see #getFrameAtTime(long, int) |
| 678 | */ |
| 679 | /* Do not change these option values without updating their counterparts |
Justin Klaassen | 47ed54e | 2017-10-24 19:50:40 -0400 | [diff] [blame] | 680 | * in include/media/MediaSource.h! |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 681 | */ |
| 682 | /** |
| 683 | * This option is used with {@link #getFrameAtTime(long, int)} to retrieve |
| 684 | * a sync (or key) frame associated with a data source that is located |
| 685 | * right before or at the given time. |
| 686 | * |
| 687 | * @see #getFrameAtTime(long, int) |
| 688 | */ |
| 689 | public static final int OPTION_PREVIOUS_SYNC = 0x00; |
| 690 | /** |
| 691 | * This option is used with {@link #getFrameAtTime(long, int)} to retrieve |
| 692 | * a sync (or key) frame associated with a data source that is located |
| 693 | * right after or at the given time. |
| 694 | * |
| 695 | * @see #getFrameAtTime(long, int) |
| 696 | */ |
| 697 | public static final int OPTION_NEXT_SYNC = 0x01; |
| 698 | /** |
| 699 | * This option is used with {@link #getFrameAtTime(long, int)} to retrieve |
| 700 | * a sync (or key) frame associated with a data source that is located |
| 701 | * closest to (in time) or at the given time. |
| 702 | * |
| 703 | * @see #getFrameAtTime(long, int) |
| 704 | */ |
| 705 | public static final int OPTION_CLOSEST_SYNC = 0x02; |
| 706 | /** |
| 707 | * This option is used with {@link #getFrameAtTime(long, int)} to retrieve |
| 708 | * a frame (not necessarily a key frame) associated with a data source that |
| 709 | * is located closest to or at the given time. |
| 710 | * |
| 711 | * @see #getFrameAtTime(long, int) |
| 712 | */ |
| 713 | public static final int OPTION_CLOSEST = 0x03; |
| 714 | |
| 715 | /** @hide */ |
| 716 | @IntDef(flag = true, prefix = { "OPTION_" }, value = { |
| 717 | OPTION_PREVIOUS_SYNC, |
| 718 | OPTION_NEXT_SYNC, |
| 719 | OPTION_CLOSEST_SYNC, |
| 720 | OPTION_CLOSEST, |
| 721 | }) |
| 722 | @Retention(RetentionPolicy.SOURCE) |
| 723 | public @interface Option {} |
| 724 | |
| 725 | /* |
| 726 | * Do not change these metadata key values without updating their |
| 727 | * counterparts in include/media/mediametadataretriever.h! |
| 728 | */ |
| 729 | /** |
| 730 | * The metadata key to retrieve the numeric string describing the |
| 731 | * order of the audio data source on its original recording. |
| 732 | */ |
| 733 | public static final int METADATA_KEY_CD_TRACK_NUMBER = 0; |
| 734 | /** |
| 735 | * The metadata key to retrieve the information about the album title |
| 736 | * of the data source. |
| 737 | */ |
| 738 | public static final int METADATA_KEY_ALBUM = 1; |
| 739 | /** |
| 740 | * The metadata key to retrieve the information about the artist of |
| 741 | * the data source. |
| 742 | */ |
| 743 | public static final int METADATA_KEY_ARTIST = 2; |
| 744 | /** |
| 745 | * The metadata key to retrieve the information about the author of |
| 746 | * the data source. |
| 747 | */ |
| 748 | public static final int METADATA_KEY_AUTHOR = 3; |
| 749 | /** |
| 750 | * The metadata key to retrieve the information about the composer of |
| 751 | * the data source. |
| 752 | */ |
| 753 | public static final int METADATA_KEY_COMPOSER = 4; |
| 754 | /** |
| 755 | * The metadata key to retrieve the date when the data source was created |
| 756 | * or modified. |
| 757 | */ |
| 758 | public static final int METADATA_KEY_DATE = 5; |
| 759 | /** |
| 760 | * The metadata key to retrieve the content type or genre of the data |
| 761 | * source. |
| 762 | */ |
| 763 | public static final int METADATA_KEY_GENRE = 6; |
| 764 | /** |
| 765 | * The metadata key to retrieve the data source title. |
| 766 | */ |
| 767 | public static final int METADATA_KEY_TITLE = 7; |
| 768 | /** |
| 769 | * The metadata key to retrieve the year when the data source was created |
| 770 | * or modified. |
| 771 | */ |
| 772 | public static final int METADATA_KEY_YEAR = 8; |
| 773 | /** |
| 774 | * The metadata key to retrieve the playback duration of the data source. |
| 775 | */ |
| 776 | public static final int METADATA_KEY_DURATION = 9; |
| 777 | /** |
| 778 | * The metadata key to retrieve the number of tracks, such as audio, video, |
| 779 | * text, in the data source, such as a mp4 or 3gpp file. |
| 780 | */ |
| 781 | public static final int METADATA_KEY_NUM_TRACKS = 10; |
| 782 | /** |
| 783 | * The metadata key to retrieve the information of the writer (such as |
| 784 | * lyricist) of the data source. |
| 785 | */ |
| 786 | public static final int METADATA_KEY_WRITER = 11; |
| 787 | /** |
| 788 | * The metadata key to retrieve the mime type of the data source. Some |
| 789 | * example mime types include: "video/mp4", "audio/mp4", "audio/amr-wb", |
| 790 | * etc. |
| 791 | */ |
| 792 | public static final int METADATA_KEY_MIMETYPE = 12; |
| 793 | /** |
| 794 | * The metadata key to retrieve the information about the performers or |
| 795 | * artist associated with the data source. |
| 796 | */ |
| 797 | public static final int METADATA_KEY_ALBUMARTIST = 13; |
| 798 | /** |
| 799 | * The metadata key to retrieve the numberic string that describes which |
| 800 | * part of a set the audio data source comes from. |
| 801 | */ |
| 802 | public static final int METADATA_KEY_DISC_NUMBER = 14; |
| 803 | /** |
| 804 | * The metadata key to retrieve the music album compilation status. |
| 805 | */ |
| 806 | public static final int METADATA_KEY_COMPILATION = 15; |
| 807 | /** |
| 808 | * If this key exists the media contains audio content. |
| 809 | */ |
| 810 | public static final int METADATA_KEY_HAS_AUDIO = 16; |
| 811 | /** |
| 812 | * If this key exists the media contains video content. |
| 813 | */ |
| 814 | public static final int METADATA_KEY_HAS_VIDEO = 17; |
| 815 | /** |
| 816 | * If the media contains video, this key retrieves its width. |
| 817 | */ |
| 818 | public static final int METADATA_KEY_VIDEO_WIDTH = 18; |
| 819 | /** |
| 820 | * If the media contains video, this key retrieves its height. |
| 821 | */ |
| 822 | public static final int METADATA_KEY_VIDEO_HEIGHT = 19; |
| 823 | /** |
| 824 | * This key retrieves the average bitrate (in bits/sec), if available. |
| 825 | */ |
| 826 | public static final int METADATA_KEY_BITRATE = 20; |
| 827 | /** |
| 828 | * This key retrieves the language code of text tracks, if available. |
| 829 | * If multiple text tracks present, the return value will look like: |
| 830 | * "eng:chi" |
| 831 | * @hide |
| 832 | */ |
| 833 | public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES = 21; |
| 834 | /** |
| 835 | * If this key exists the media is drm-protected. |
| 836 | * @hide |
| 837 | */ |
| 838 | public static final int METADATA_KEY_IS_DRM = 22; |
| 839 | /** |
| 840 | * This key retrieves the location information, if available. |
| 841 | * The location should be specified according to ISO-6709 standard, under |
| 842 | * a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude |
| 843 | * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance. |
| 844 | */ |
| 845 | public static final int METADATA_KEY_LOCATION = 23; |
| 846 | /** |
| 847 | * This key retrieves the video rotation angle in degrees, if available. |
| 848 | * The video rotation angle may be 0, 90, 180, or 270 degrees. |
| 849 | */ |
| 850 | public static final int METADATA_KEY_VIDEO_ROTATION = 24; |
| 851 | /** |
| 852 | * This key retrieves the original capture framerate, if it's |
| 853 | * available. The capture framerate will be a floating point |
| 854 | * number. |
| 855 | */ |
| 856 | public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 857 | /** |
| 858 | * If this key exists the media contains still image content. |
| 859 | */ |
| 860 | public static final int METADATA_KEY_HAS_IMAGE = 26; |
| 861 | /** |
| 862 | * If the media contains still images, this key retrieves the number |
| 863 | * of still images. |
| 864 | */ |
| 865 | public static final int METADATA_KEY_IMAGE_COUNT = 27; |
| 866 | /** |
| 867 | * If the media contains still images, this key retrieves the image |
| 868 | * index of the primary image. |
| 869 | */ |
| 870 | public static final int METADATA_KEY_IMAGE_PRIMARY = 28; |
| 871 | /** |
| 872 | * If the media contains still images, this key retrieves the width |
| 873 | * of the primary image. |
| 874 | */ |
| 875 | public static final int METADATA_KEY_IMAGE_WIDTH = 29; |
| 876 | /** |
| 877 | * If the media contains still images, this key retrieves the height |
| 878 | * of the primary image. |
| 879 | */ |
| 880 | public static final int METADATA_KEY_IMAGE_HEIGHT = 30; |
| 881 | /** |
| 882 | * If the media contains still images, this key retrieves the rotation |
Justin Klaassen | 4d01eea | 2018-04-03 23:21:57 -0400 | [diff] [blame] | 883 | * angle (in degrees clockwise) of the primary image. The image rotation |
| 884 | * angle must be one of 0, 90, 180, or 270 degrees. |
Justin Klaassen | 6a65f2d | 2017-11-17 16:38:15 -0500 | [diff] [blame] | 885 | */ |
| 886 | public static final int METADATA_KEY_IMAGE_ROTATION = 31; |
| 887 | /** |
| 888 | * If the media contains video and this key exists, it retrieves the |
| 889 | * total number of frames in the video sequence. |
| 890 | */ |
| 891 | public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; |
| 892 | |
Justin Klaassen | b8042fc | 2018-04-15 00:41:15 -0400 | [diff] [blame] | 893 | /** |
| 894 | * @hide |
| 895 | */ |
| 896 | public static final int METADATA_KEY_EXIF_OFFSET = 33; |
| 897 | |
| 898 | /** |
| 899 | * @hide |
| 900 | */ |
| 901 | public static final int METADATA_KEY_EXIF_LENGTH = 34; |
Justin Klaassen | 10d07c8 | 2017-09-15 17:58:39 -0400 | [diff] [blame] | 902 | // Add more here... |
| 903 | } |