MediaItem.java revision 05152ffd67a17491337236621aa5f7ef7c3b51db
1/* 2 * Copyright (C) 2010 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.videoeditor; 18 19import java.io.IOException; 20import java.util.ArrayList; 21import java.util.List; 22 23import android.graphics.Bitmap; 24 25/** 26 * This abstract class describes the base class for any MediaItem. Objects are 27 * defined with a file path as a source data. 28 * {@hide} 29s */ 30public abstract class MediaItem { 31 // A constant which can be used to specify the end of the file (instead of 32 // providing the actual duration of the media item). 33 public final static int END_OF_FILE = -1; 34 35 // Rendering modes 36 /** 37 * When using the RENDERING_MODE_BLACK_BORDER rendering mode video frames 38 * are resized by preserving the aspect ratio until the movie matches one of 39 * the dimensions of the output movie. The areas outside the resized video 40 * clip are rendered black. 41 */ 42 public static final int RENDERING_MODE_BLACK_BORDER = 0; 43 /** 44 * When using the RENDERING_MODE_STRETCH rendering mode video frames are 45 * stretched horizontally or vertically to match the current aspect ratio of 46 * the movie. 47 */ 48 public static final int RENDERING_MODE_STRETCH = 1; 49 50 51 // The unique id of the MediaItem 52 private final String mUniqueId; 53 54 // The name of the file associated with the MediaItem 55 protected final String mFilename; 56 57 // List of effects 58 private final List<Effect> mEffects; 59 60 // List of overlays 61 private final List<Overlay> mOverlays; 62 63 // The rendering mode 64 private int mRenderingMode; 65 66 // Beginning and end transitions 67 protected Transition mBeginTransition; 68 protected Transition mEndTransition; 69 70 /** 71 * Constructor 72 * 73 * @param mediaItemId The MediaItem id 74 * @param filename name of the media file. 75 * @param renderingMode The rendering mode 76 * 77 * @throws IOException if file is not found 78 * @throws IllegalArgumentException if a capability such as file format is not 79 * supported the exception object contains the unsupported 80 * capability 81 */ 82 protected MediaItem(String mediaItemId, String filename, int renderingMode) throws IOException { 83 mUniqueId = mediaItemId; 84 mFilename = filename; 85 mRenderingMode = renderingMode; 86 mEffects = new ArrayList<Effect>(); 87 mOverlays = new ArrayList<Overlay>(); 88 mBeginTransition = null; 89 mEndTransition = null; 90 } 91 92 /** 93 * @return The id of the media item 94 */ 95 public String getId() { 96 return mUniqueId; 97 } 98 99 /** 100 * @return The media source file name 101 */ 102 public String getFilename() { 103 return mFilename; 104 } 105 106 /** 107 * If aspect ratio of the MediaItem is different from the aspect ratio of 108 * the editor then this API controls the rendering mode. 109 * 110 * @param renderingMode rendering mode. It is one of: 111 * {@link #RENDERING_MODE_BLACK_BORDER}, 112 * {@link #RENDERING_MODE_STRETCH} 113 */ 114 public void setRenderingMode(int renderingMode) { 115 mRenderingMode = renderingMode; 116 if (mBeginTransition != null) { 117 mBeginTransition.invalidate(); 118 } 119 120 if (mEndTransition != null) { 121 mEndTransition.invalidate(); 122 } 123 } 124 125 /** 126 * @return The rendering mode 127 */ 128 public int getRenderingMode() { 129 return mRenderingMode; 130 } 131 132 /** 133 * @param transition The beginning transition 134 */ 135 void setBeginTransition(Transition transition) { 136 mBeginTransition = transition; 137 } 138 139 /** 140 * @return The begin transition 141 */ 142 public Transition getBeginTransition() { 143 return mBeginTransition; 144 } 145 146 /** 147 * @param transition The end transition 148 */ 149 void setEndTransition(Transition transition) { 150 mEndTransition = transition; 151 } 152 153 /** 154 * @return The end transition 155 */ 156 public Transition getEndTransition() { 157 return mEndTransition; 158 } 159 160 /** 161 * @return The duration of the media item 162 */ 163 public abstract long getDuration(); 164 165 /** 166 * @return The timeline duration. This is the actual duration in the 167 * timeline (trimmed duration) 168 */ 169 public abstract long getTimelineDuration(); 170 171 /** 172 * @return The source file type 173 */ 174 public abstract int getFileType(); 175 176 /** 177 * @return Get the native width of the media item 178 */ 179 public abstract int getWidth(); 180 181 /** 182 * @return Get the native height of the media item 183 */ 184 public abstract int getHeight(); 185 186 /** 187 * Get aspect ratio of the source media item. 188 * 189 * @return the aspect ratio as described in MediaProperties. 190 * MediaProperties.ASPECT_RATIO_UNDEFINED if aspect ratio is not 191 * supported as in MediaProperties 192 */ 193 public abstract int getAspectRatio(); 194 195 /** 196 * Add the specified effect to this media item. 197 * 198 * Note that certain types of effects cannot be applied to video and to 199 * image media items. For example in certain implementation a Ken Burns 200 * implementation cannot be applied to video media item. 201 * 202 * This method invalidates transition video clips if the 203 * effect overlaps with the beginning and/or the end transition. 204 * 205 * @param effect The effect to apply 206 * @throws IllegalStateException if a preview or an export is in progress 207 * @throws IllegalArgumentException if the effect start and/or duration are 208 * invalid or if the effect cannot be applied to this type of media 209 * item or if the effect id is not unique across all the Effects 210 * added. 211 */ 212 public void addEffect(Effect effect) { 213 if (mEffects.contains(effect)) { 214 throw new IllegalArgumentException("Effect already exists: " + effect.getId()); 215 } 216 217 if (effect.getStartTime() + effect.getDuration() > getDuration()) { 218 throw new IllegalArgumentException( 219 "Effect start time + effect duration > media clip duration"); 220 } 221 222 mEffects.add(effect); 223 invalidateTransitions(effect); 224 } 225 226 /** 227 * Remove the effect with the specified id. 228 * 229 * This method invalidates a transition video clip if the effect overlaps 230 * with a transition. 231 * 232 * @param effectId The id of the effect to be removed 233 * 234 * @return The effect that was removed 235 * @throws IllegalStateException if a preview or an export is in progress 236 */ 237 public Effect removeEffect(String effectId) { 238 for (Effect effect : mEffects) { 239 if (effect.getId().equals(effectId)) { 240 mEffects.remove(effect); 241 invalidateTransitions(effect); 242 return effect; 243 } 244 } 245 246 return null; 247 } 248 249 /** 250 * Find the effect with the specified id 251 * 252 * @param effectId The effect id 253 * 254 * @return The effect with the specified id (null if it does not exist) 255 */ 256 public Effect getEffect(String effectId) { 257 for (Effect effect : mEffects) { 258 if (effect.getId().equals(effectId)) { 259 return effect; 260 } 261 } 262 263 return null; 264 } 265 266 /** 267 * Get the list of effects. 268 * 269 * @return the effects list. If no effects exist an empty list will be returned. 270 */ 271 public List<Effect> getAllEffects() { 272 return mEffects; 273 } 274 275 /** 276 * Add an overlay to the storyboard. This method invalidates a transition 277 * video clip if the overlay overlaps with a transition. 278 * 279 * @param overlay The overlay to add 280 * @throws IllegalStateException if a preview or an export is in progress or 281 * if the overlay id is not unique across all the overlays 282 * added or if the bitmap is not specified or if the dimensions of 283 * the bitmap do not match the dimensions of the media item 284 */ 285 public void addOverlay(Overlay overlay) { 286 if (mOverlays.contains(overlay)) { 287 throw new IllegalArgumentException("Overlay already exists: " + overlay.getId()); 288 } 289 290 if (overlay.getStartTime() + overlay.getDuration() > getDuration()) { 291 throw new IllegalArgumentException( 292 "Overlay start time + overlay duration > media clip duration"); 293 } 294 295 if (overlay instanceof OverlayFrame) { 296 final OverlayFrame frame = (OverlayFrame)overlay; 297 final Bitmap bitmap = frame.getBitmap(); 298 if (bitmap == null) { 299 throw new IllegalArgumentException("Overlay bitmap not specified"); 300 } 301 302 // The dimensions of the overlay bitmap must be the same as the 303 // media item dimensions 304 if (bitmap.getWidth() != getWidth() || bitmap.getHeight() != getHeight()) { 305 throw new IllegalArgumentException( 306 "Bitmap dimensions must match media item dimensions"); 307 } 308 } else { 309 throw new IllegalArgumentException("Overlay not supported"); 310 } 311 312 mOverlays.add(overlay); 313 invalidateTransitions(overlay); 314 } 315 316 /** 317 * Remove the overlay with the specified id. 318 * 319 * This method invalidates a transition video clip if the overlay overlaps 320 * with a transition. 321 * 322 * @param overlayId The id of the overlay to be removed 323 * 324 * @return The overlay that was removed 325 * @throws IllegalStateException if a preview or an export is in progress 326 */ 327 public Overlay removeOverlay(String overlayId) { 328 for (Overlay overlay : mOverlays) { 329 if (overlay.getId().equals(overlayId)) { 330 mOverlays.remove(overlay); 331 if (overlay instanceof OverlayFrame) { 332 ((OverlayFrame)overlay).invalidate(); 333 } 334 invalidateTransitions(overlay); 335 return overlay; 336 } 337 } 338 339 return null; 340 } 341 342 /** 343 * Find the overlay with the specified id 344 * 345 * @param overlayId The overlay id 346 * 347 * @return The overlay with the specified id (null if it does not exist) 348 */ 349 public Overlay getOverlay(String overlayId) { 350 for (Overlay overlay : mOverlays) { 351 if (overlay.getId().equals(overlayId)) { 352 return overlay; 353 } 354 } 355 356 return null; 357 } 358 359 /** 360 * Get the list of overlays associated with this media item 361 * 362 * Note that if any overlay source files are not accessible anymore, 363 * this method will still provide the full list of overlays. 364 * 365 * @return The list of overlays. If no overlays exist an empty list will 366 * be returned. 367 */ 368 public List<Overlay> getAllOverlays() { 369 return mOverlays; 370 } 371 372 /** 373 * Create a thumbnail at specified time in a video stream in Bitmap format 374 * 375 * @param width width of the thumbnail in pixels 376 * @param height height of the thumbnail in pixels 377 * @param timeMs The time in the source video file at which the thumbnail is 378 * requested (even if trimmed). 379 * 380 * @return The thumbnail as a Bitmap. 381 * 382 * @throws IOException if a file error occurs 383 * @throws IllegalArgumentException if time is out of video duration 384 */ 385 public abstract Bitmap getThumbnail(int width, int height, long timeMs) throws IOException; 386 387 /** 388 * Get the array of Bitmap thumbnails between start and end. 389 * 390 * @param width width of the thumbnail in pixels 391 * @param height height of the thumbnail in pixels 392 * @param startMs The start of time range in milliseconds 393 * @param endMs The end of the time range in milliseconds 394 * @param thumbnailCount The thumbnail count 395 * 396 * @return The array of Bitmaps 397 * 398 * @throws IOException if a file error occurs 399 */ 400 public abstract Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs, 401 int thumbnailCount) throws IOException; 402 403 /* 404 * {@inheritDoc} 405 */ 406 @Override 407 public boolean equals(Object object) { 408 if (!(object instanceof MediaItem)) { 409 return false; 410 } 411 return mUniqueId.equals(((MediaItem)object).mUniqueId); 412 } 413 414 /* 415 * {@inheritDoc} 416 */ 417 @Override 418 public int hashCode() { 419 return mUniqueId.hashCode(); 420 } 421 422 /** 423 * Invalidate the start and end transitions if necessary 424 * 425 * @param effect The effect that was added or removed 426 */ 427 private void invalidateTransitions(Effect effect) { 428 // Check if the effect overlaps with the beginning and end transitions 429 if (mBeginTransition != null) { 430 if (effect.getStartTime() < mBeginTransition.getDuration()) { 431 mBeginTransition.invalidate(); 432 } 433 } 434 435 if (mEndTransition != null) { 436 if (effect.getStartTime() + effect.getDuration() > getDuration() 437 - mEndTransition.getDuration()) { 438 mEndTransition.invalidate(); 439 } 440 } 441 } 442 443 /** 444 * Invalidate the start and end transitions if necessary 445 * 446 * @param overlay The effect that was added or removed 447 */ 448 private void invalidateTransitions(Overlay overlay) { 449 // Check if the overlay overlaps with the beginning and end transitions 450 if (mBeginTransition != null) { 451 if (overlay.getStartTime() < mBeginTransition.getDuration()) { 452 mBeginTransition.invalidate(); 453 } 454 } 455 456 if (mEndTransition != null) { 457 if (overlay.getStartTime() + overlay.getDuration() > getDuration() 458 - mEndTransition.getDuration()) { 459 mEndTransition.invalidate(); 460 } 461 } 462 } 463} 464