Transition.java revision 9bcedf7cf3e9c981837f2d8ec98cd118efad3f01
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 17 18package android.media.videoeditor; 19 20import java.io.File; 21import java.util.ArrayList; 22import java.util.List; 23 24import android.media.videoeditor.MediaArtistNativeHelper.AlphaMagicSettings; 25import android.media.videoeditor.MediaArtistNativeHelper.AudioTransition; 26import android.media.videoeditor.MediaArtistNativeHelper.ClipSettings; 27import android.media.videoeditor.MediaArtistNativeHelper.EditSettings; 28import android.media.videoeditor.MediaArtistNativeHelper.EffectSettings; 29import android.media.videoeditor.MediaArtistNativeHelper.SlideTransitionSettings; 30import android.media.videoeditor.MediaArtistNativeHelper.TransitionSettings; 31import android.media.videoeditor.MediaArtistNativeHelper.VideoTransition; 32 33/** 34 * This class is super class for all transitions. Transitions (with the 35 * exception of TransitionAtStart and TransitioAtEnd) can only be inserted 36 * between media items. 37 * 38 * Adding a transition between MediaItems makes the 39 * duration of the storyboard shorter by the duration of the Transition itself. 40 * As a result, if the duration of the transition is larger than the smaller 41 * duration of the two MediaItems associated with the Transition, an exception 42 * will be thrown. 43 * 44 * During a transition, the audio track are cross-fading 45 * automatically. {@hide} 46 */ 47public abstract class Transition { 48 /** 49 * The transition behavior 50 */ 51 private static final int BEHAVIOR_MIN_VALUE = 0; 52 53 /** The transition starts slowly and speed up */ 54 public static final int BEHAVIOR_SPEED_UP = 0; 55 /** The transition start fast and speed down */ 56 public static final int BEHAVIOR_SPEED_DOWN = 1; 57 /** The transition speed is constant */ 58 public static final int BEHAVIOR_LINEAR = 2; 59 /** The transition starts fast and ends fast with a slow middle */ 60 public static final int BEHAVIOR_MIDDLE_SLOW = 3; 61 /** The transition starts slowly and ends slowly with a fast middle */ 62 public static final int BEHAVIOR_MIDDLE_FAST = 4; 63 64 private static final int BEHAVIOR_MAX_VALUE = 4; 65 66 /** 67 * The unique id of the transition 68 */ 69 private final String mUniqueId; 70 71 /** 72 * The transition is applied at the end of this media item 73 */ 74 private final MediaItem mAfterMediaItem; 75 /** 76 * The transition is applied at the beginning of this media item 77 */ 78 private final MediaItem mBeforeMediaItem; 79 80 /** 81 * The transition behavior 82 */ 83 protected final int mBehavior; 84 85 /** 86 * The transition duration 87 */ 88 protected long mDurationMs; 89 90 /** 91 * The transition filename 92 */ 93 protected String mFilename; 94 95 protected MediaArtistNativeHelper mNativeHelper; 96 /** 97 * An object of this type cannot be instantiated by using the default 98 * constructor 99 */ 100 @SuppressWarnings("unused") 101 private Transition() { 102 this(null, null, null, 0, 0); 103 } 104 105 /** 106 * Constructor 107 * 108 * @param transitionId The transition id 109 * @param afterMediaItem The transition is applied to the end of this 110 * media item 111 * @param beforeMediaItem The transition is applied to the beginning of 112 * this media item 113 * @param durationMs The duration of the transition in milliseconds 114 * @param behavior The transition behavior 115 */ 116 protected Transition(String transitionId, MediaItem afterMediaItem, 117 MediaItem beforeMediaItem,long durationMs, 118 int behavior) { 119 if (behavior < BEHAVIOR_MIN_VALUE || behavior > BEHAVIOR_MAX_VALUE) { 120 throw new IllegalArgumentException("Invalid behavior: " + behavior); 121 } 122 if ((afterMediaItem == null) && (beforeMediaItem == null)) { 123 throw new IllegalArgumentException("Null media items"); 124 } 125 mUniqueId = transitionId; 126 mAfterMediaItem = afterMediaItem; 127 mBeforeMediaItem = beforeMediaItem; 128 mDurationMs = durationMs; 129 mBehavior = behavior; 130 mNativeHelper = null; 131 if (durationMs > getMaximumDuration()) { 132 throw new IllegalArgumentException("The duration is too large"); 133 } 134 } 135 136 /** 137 * Get the ID of the transition. 138 * 139 * @return The ID of the transition 140 */ 141 public String getId() { 142 return mUniqueId; 143 } 144 145 /** 146 * Get the media item at the end of which the transition is applied. 147 * 148 * @return The media item at the end of which the transition is applied 149 */ 150 public MediaItem getAfterMediaItem() { 151 return mAfterMediaItem; 152 } 153 154 /** 155 * Get the media item at the beginning of which the transition is applied. 156 * 157 * @return The media item at the beginning of which the transition is 158 * applied 159 */ 160 public MediaItem getBeforeMediaItem() { 161 return mBeforeMediaItem; 162 } 163 164 /** 165 * Set the duration of the transition. 166 * 167 * @param durationMs the duration of the transition in milliseconds 168 */ 169 public void setDuration(long durationMs) { 170 if (durationMs > getMaximumDuration()) { 171 throw new IllegalArgumentException("The duration is too large"); 172 } 173 174 mDurationMs = durationMs; 175 invalidate(); 176 } 177 178 /** 179 * Get the duration of the transition. 180 * 181 * @return the duration of the transition in milliseconds 182 */ 183 public long getDuration() { 184 return mDurationMs; 185 } 186 187 /** 188 * The duration of a transition cannot be greater than half of the minimum 189 * duration of the bounding media items. 190 * 191 * @return The maximum duration of this transition 192 */ 193 public long getMaximumDuration() { 194 if (mAfterMediaItem == null) { 195 return mBeforeMediaItem.getTimelineDuration() / 2; 196 } else if (mBeforeMediaItem == null) { 197 return mAfterMediaItem.getTimelineDuration() / 2; 198 } else { 199 return (Math.min(mAfterMediaItem.getTimelineDuration(), 200 mBeforeMediaItem.getTimelineDuration()) / 2); 201 } 202 } 203 204 /** 205 * Get the behavior of the transition. 206 * 207 * @return The behavior 208 */ 209 public int getBehavior() { 210 return mBehavior; 211 } 212 213 /** 214 * Get the transition data. 215 * 216 * @return The transition data in TransitionSettings object 217 * {@link android.media.videoeditor.MediaArtistNativeHelper.TransitionSettings} 218 */ 219 TransitionSettings getTransitionSettings() { 220 TransitionAlpha transitionAlpha = null; 221 TransitionSliding transitionSliding = null; 222 TransitionCrossfade transitionCrossfade = null; 223 TransitionFadeBlack transitionFadeBlack = null; 224 TransitionSettings transitionSetting = null; 225 transitionSetting = new TransitionSettings(); 226 transitionSetting.duration = (int)getDuration(); 227 if (this instanceof TransitionAlpha) { 228 transitionAlpha = (TransitionAlpha)this; 229 transitionSetting.videoTransitionType = VideoTransition.ALPHA_MAGIC; 230 transitionSetting.audioTransitionType = AudioTransition.CROSS_FADE; 231 transitionSetting.transitionBehaviour = mNativeHelper 232 .getVideoTransitionBehaviour(transitionAlpha.getBehavior()); 233 transitionSetting.alphaSettings = new AlphaMagicSettings(); 234 transitionSetting.slideSettings = null; 235 transitionSetting.alphaSettings.file = transitionAlpha.getPNGMaskFilename(); 236 transitionSetting.alphaSettings.blendingPercent = transitionAlpha.getBlendingPercent(); 237 transitionSetting.alphaSettings.invertRotation = transitionAlpha.isInvert(); 238 transitionSetting.alphaSettings.rgbWidth = transitionAlpha.getRGBFileWidth(); 239 transitionSetting.alphaSettings.rgbHeight = transitionAlpha.getRGBFileHeight(); 240 241 } else if (this instanceof TransitionSliding) { 242 transitionSliding = (TransitionSliding)this; 243 transitionSetting.videoTransitionType = VideoTransition.SLIDE_TRANSITION; 244 transitionSetting.audioTransitionType = AudioTransition.CROSS_FADE; 245 transitionSetting.transitionBehaviour = mNativeHelper 246 .getVideoTransitionBehaviour(transitionSliding.getBehavior()); 247 transitionSetting.alphaSettings = null; 248 transitionSetting.slideSettings = new SlideTransitionSettings(); 249 transitionSetting.slideSettings.direction = mNativeHelper 250 .getSlideSettingsDirection(transitionSliding.getDirection()); 251 } else if (this instanceof TransitionCrossfade) { 252 transitionCrossfade = (TransitionCrossfade)this; 253 transitionSetting.videoTransitionType = VideoTransition.CROSS_FADE; 254 transitionSetting.audioTransitionType = AudioTransition.CROSS_FADE; 255 transitionSetting.transitionBehaviour = mNativeHelper 256 .getVideoTransitionBehaviour(transitionCrossfade.getBehavior()); 257 transitionSetting.alphaSettings = null; 258 transitionSetting.slideSettings = null; 259 } else if (this instanceof TransitionFadeBlack) { 260 transitionFadeBlack = (TransitionFadeBlack)this; 261 transitionSetting.videoTransitionType = VideoTransition.FADE_BLACK; 262 transitionSetting.audioTransitionType = AudioTransition.CROSS_FADE; 263 transitionSetting.transitionBehaviour = mNativeHelper 264 .getVideoTransitionBehaviour(transitionFadeBlack.getBehavior()); 265 transitionSetting.alphaSettings = null; 266 transitionSetting.slideSettings = null; 267 } 268 269 return transitionSetting; 270 } 271 272 /** 273 * Checks if the effect and overlay applied on a media item 274 * overlaps with the transition on media item. 275 * 276 * @param m The media item 277 * @param clipSettings The ClipSettings object 278 * @param clipNo The clip no.(out of the two media items 279 * associated with current transition)for which the effect 280 * clip should be generated 281 * @return List of effects that overlap with the transition 282 */ 283 284 List<EffectSettings> isEffectandOverlayOverlapping(MediaItem m, ClipSettings clipSettings, 285 int clipNo) { 286 List<Effect> effects; 287 List<Overlay> overlays; 288 List<EffectSettings> effectSettings = new ArrayList<EffectSettings>(); 289 EffectSettings tmpEffectSettings; 290 291 effects = m.getAllEffects(); 292 for (Effect effect : effects) { 293 if (effect instanceof EffectColor) { 294 tmpEffectSettings = mNativeHelper.getEffectSettings((EffectColor)effect); 295 mNativeHelper.adjustEffectsStartTimeAndDuration(tmpEffectSettings, 296 clipSettings.beginCutTime, clipSettings.endCutTime); 297 if (tmpEffectSettings.duration != 0) { 298 if (m instanceof MediaVideoItem) { 299 tmpEffectSettings.fiftiesFrameRate = mNativeHelper 300 .GetClosestVideoFrameRate(((MediaVideoItem)m).getFps()); 301 } 302 effectSettings.add(tmpEffectSettings); 303 } 304 } 305 } 306 overlays = m.getAllOverlays(); 307 for (Overlay overlay : overlays) { 308 tmpEffectSettings = mNativeHelper.getOverlaySettings((OverlayFrame)overlay); 309 mNativeHelper.adjustEffectsStartTimeAndDuration(tmpEffectSettings, 310 clipSettings.beginCutTime, clipSettings.endCutTime); 311 if (tmpEffectSettings.duration != 0) { 312 effectSettings.add(tmpEffectSettings); 313 } 314 } 315 return effectSettings; 316 } 317 318 /** 319 * Generate the video clip for the specified transition. This method may 320 * block for a significant amount of time. Before the method completes 321 * execution it sets the mFilename to the name of the newly generated 322 * transition video clip file. 323 */ 324 void generate() { 325 MediaItem m1 = this.getAfterMediaItem(); 326 MediaItem m2 = this.getBeforeMediaItem(); 327 ClipSettings clipSettings1 = new ClipSettings(); 328 ClipSettings clipSettings2 = new ClipSettings(); 329 TransitionSettings transitionSetting = null; 330 EditSettings editSettings = new EditSettings(); 331 List<EffectSettings> effectSettings_clip1; 332 List<EffectSettings> effectSettings_clip2; 333 334 String output = null; 335 String effectClip1 = null; 336 String effectClip2 = null; 337 338 if (mNativeHelper == null) { 339 if (m1 != null) 340 mNativeHelper = m1.getNativeContext(); 341 else if (m2 != null) 342 mNativeHelper = m2.getNativeContext(); 343 } 344 transitionSetting = getTransitionSettings(); 345 if (m1 != null && m2 != null) { 346 /* transition between media items */ 347 clipSettings1 = m1.getClipSettings(); 348 clipSettings2 = m2.getClipSettings(); 349 clipSettings1.beginCutTime = (int)(clipSettings1.endCutTime - 350 this.mDurationMs); 351 clipSettings2.endCutTime = (int)(clipSettings2.beginCutTime + 352 this.mDurationMs); 353 /* 354 * Check how many effects and overlays overlap with transition and 355 * generate effect clip first if there is any overlap 356 */ 357 effectSettings_clip1 = isEffectandOverlayOverlapping(m1, clipSettings1,1); 358 effectSettings_clip2 = isEffectandOverlayOverlapping(m2, clipSettings2,2); 359 for (int index = 0; index < effectSettings_clip2.size(); index++ ) { 360 effectSettings_clip2.get(index).startTime += this.mDurationMs; 361 } 362 editSettings.effectSettingsArray = 363 new EffectSettings[effectSettings_clip1.size() 364 + effectSettings_clip2.size()]; 365 int i=0,j=0; 366 while (i < effectSettings_clip1.size()) { 367 editSettings.effectSettingsArray[j] = effectSettings_clip1.get(i); 368 i++; 369 j++; 370 } 371 i=0; 372 while (i < effectSettings_clip2.size()) { 373 editSettings.effectSettingsArray[j] = effectSettings_clip2.get(i); 374 i++; 375 j++; 376 } 377 } else if (m1 == null && m2 != null) { 378 /* begin transition at first media item */ 379 m2.generateBlankFrame(clipSettings1); 380 clipSettings2 = m2.getClipSettings(); 381 clipSettings1.endCutTime = (int)(this.mDurationMs + 50); 382 clipSettings2.endCutTime = (int)(clipSettings2.beginCutTime + 383 this.mDurationMs); 384 /* 385 * Check how many effects and overlays overlap with transition and 386 * generate effect clip first if there is any overlap 387 */ 388 effectSettings_clip2 = isEffectandOverlayOverlapping(m2, clipSettings2,2); 389 for (int index = 0; index < effectSettings_clip2.size(); index++ ) { 390 effectSettings_clip2.get(index).startTime += this.mDurationMs; 391 } 392 editSettings.effectSettingsArray = new EffectSettings[effectSettings_clip2.size()]; 393 int i=0, j=0; 394 while (i < effectSettings_clip2.size()) { 395 editSettings.effectSettingsArray[j] = effectSettings_clip2.get(i); 396 i++; 397 j++; 398 } 399 } else if (m1 != null && m2 == null) { 400 /* end transition at last media item */ 401 clipSettings1 = m1.getClipSettings(); 402 m1.generateBlankFrame(clipSettings2); 403 clipSettings1.beginCutTime = (int)(clipSettings1.endCutTime - 404 this.mDurationMs); 405 clipSettings2.endCutTime = (int)(this.mDurationMs + 50); 406 /* 407 * Check how many effects and overlays overlap with transition and 408 * generate effect clip first if there is any overlap 409 */ 410 effectSettings_clip1 = isEffectandOverlayOverlapping(m1, clipSettings1,1); 411 editSettings.effectSettingsArray = new EffectSettings[effectSettings_clip1.size()]; 412 int i=0,j=0; 413 while (i < effectSettings_clip1.size()) { 414 editSettings.effectSettingsArray[j] = effectSettings_clip1.get(i); 415 i++; 416 j++; 417 } 418 } 419 420 editSettings.clipSettingsArray = new ClipSettings[2]; 421 editSettings.clipSettingsArray[0] = clipSettings1; 422 editSettings.clipSettingsArray[1] = clipSettings2; 423 editSettings.backgroundMusicSettings = null; 424 editSettings.transitionSettingsArray = new TransitionSettings[1]; 425 editSettings.transitionSettingsArray[0] = transitionSetting; 426 output = mNativeHelper.generateTransitionClip(editSettings, mUniqueId, 427 m1, m2,this); 428 setFilename(output); 429 } 430 431 432 /** 433 * Set the transition filename. 434 */ 435 void setFilename(String filename) { 436 mFilename = filename; 437 } 438 439 /** 440 * Get the transition filename. 441 */ 442 String getFilename() { 443 return mFilename; 444 } 445 446 /** 447 * Remove any resources associated with this transition 448 */ 449 void invalidate() { 450 if (mFilename != null) { 451 new File(mFilename).delete(); 452 mFilename = null; 453 } 454 } 455 456 /** 457 * Check if the transition is generated. 458 * 459 * @return true if the transition is generated 460 */ 461 boolean isGenerated() { 462 return (mFilename != null); 463 } 464 465 /* 466 * {@inheritDoc} 467 */ 468 @Override 469 public boolean equals(Object object) { 470 if (!(object instanceof Transition)) { 471 return false; 472 } 473 return mUniqueId.equals(((Transition)object).mUniqueId); 474 } 475 476 /* 477 * {@inheritDoc} 478 */ 479 @Override 480 public int hashCode() { 481 return mUniqueId.hashCode(); 482 } 483} 484