MediaVideoItem.java revision f1d8f2a140bf5c03c8e3b0a4baab8a3a68fb9f1d
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.lang.ref.SoftReference; 21 22import android.graphics.Bitmap; 23import android.view.SurfaceHolder; 24 25/** 26 * This class represents a video clip item on the storyboard 27 * {@hide} 28 */ 29public class MediaVideoItem extends MediaItem { 30 // Instance variables 31 private final int mWidth; 32 private final int mHeight; 33 private final int mAspectRatio; 34 private final int mFileType; 35 private final int mVideoType; 36 private final int mVideoProfile; 37 private final int mVideoBitrate; 38 private final long mDurationMs; 39 private final int mAudioBitrate; 40 private final int mFps; 41 private final int mAudioType; 42 private final int mAudioChannels; 43 private final int mAudioSamplingFrequency; 44 45 private long mBeginBoundaryTimeMs; 46 private long mEndBoundaryTimeMs; 47 private int mVolumePercentage; 48 private boolean mMuted; 49 private String mAudioWaveformFilename; 50 // The audio waveform data 51 private SoftReference<WaveformData> mWaveformData; 52 53 /** 54 * An object of this type cannot be instantiated with a default constructor 55 */ 56 @SuppressWarnings("unused") 57 private MediaVideoItem() throws IOException { 58 this(null, null, null, RENDERING_MODE_BLACK_BORDER); 59 } 60 61 /** 62 * Constructor 63 * 64 * @param editor The video editor reference 65 * @param mediaItemId The MediaItem id 66 * @param filename The image file name 67 * @param renderingMode The rendering mode 68 * 69 * @throws IOException if the file cannot be opened for reading 70 */ 71 public MediaVideoItem(VideoEditor editor, String mediaItemId, String filename, 72 int renderingMode) 73 throws IOException { 74 this(editor, mediaItemId, filename, renderingMode, 0, END_OF_FILE, 100, false, null); 75 } 76 77 /** 78 * Constructor 79 * 80 * @param editor The video editor reference 81 * @param mediaItemId The MediaItem id 82 * @param filename The image file name 83 * @param renderingMode The rendering mode 84 * @param beginMs Start time in milliseconds. Set to 0 to extract from the 85 * beginning 86 * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to 87 * extract until the end 88 * @param volumePercent in %/. 100% means no change; 50% means half value, 200% 89 * means double, 0% means silent. 90 * @param muted true if the audio is muted 91 * @param audioWaveformFilename The name of the audio waveform file 92 * 93 * @throws IOException if the file cannot be opened for reading 94 */ 95 MediaVideoItem(VideoEditor editor, String mediaItemId, String filename, int renderingMode, 96 long beginMs, long endMs, int volumePercent, boolean muted, 97 String audioWaveformFilename) throws IOException { 98 super(editor, mediaItemId, filename, renderingMode); 99 // TODO: Set these variables correctly 100 mWidth = 1080; 101 mHeight = 720; 102 mAspectRatio = MediaProperties.ASPECT_RATIO_3_2; 103 mFileType = MediaProperties.FILE_MP4; 104 mVideoType = MediaProperties.VCODEC_H264BP; 105 // Do we have predefined values for this variable? 106 mVideoProfile = 0; 107 // Can video and audio duration be different? 108 mDurationMs = 10000; 109 mVideoBitrate = 800000; 110 mAudioBitrate = 30000; 111 mFps = 30; 112 mAudioType = MediaProperties.ACODEC_AAC_LC; 113 mAudioChannels = 2; 114 mAudioSamplingFrequency = 16000; 115 116 mBeginBoundaryTimeMs = beginMs; 117 mEndBoundaryTimeMs = endMs == END_OF_FILE ? mDurationMs : endMs; 118 mVolumePercentage = volumePercent; 119 mMuted = muted; 120 mAudioWaveformFilename = audioWaveformFilename; 121 if (audioWaveformFilename != null) { 122 mWaveformData = 123 new SoftReference<WaveformData>(new WaveformData(audioWaveformFilename)); 124 } else { 125 mWaveformData = null; 126 } 127 } 128 129 /** 130 * Sets the start and end marks for trimming a video media item. 131 * This method will adjust the duration of bounding transitions, effects 132 * and overlays if the current duration of the transactions become greater 133 * than the maximum allowable duration. 134 * 135 * @param beginMs Start time in milliseconds. Set to 0 to extract from the 136 * beginning 137 * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to 138 * extract until the end 139 * 140 * @throws IllegalArgumentException if the start time is greater or equal than 141 * end time, the end time is beyond the file duration, the start time 142 * is negative 143 */ 144 public void setExtractBoundaries(long beginMs, long endMs) { 145 if (beginMs > mDurationMs) { 146 throw new IllegalArgumentException("Invalid start time"); 147 } 148 if (endMs > mDurationMs) { 149 throw new IllegalArgumentException("Invalid end time"); 150 } 151 152 if (beginMs != mBeginBoundaryTimeMs) { 153 if (mBeginTransition != null) { 154 mBeginTransition.invalidate(); 155 } 156 } 157 158 if (endMs != mEndBoundaryTimeMs) { 159 if (mEndTransition != null) { 160 mEndTransition.invalidate(); 161 } 162 } 163 164 mBeginBoundaryTimeMs = beginMs; 165 mEndBoundaryTimeMs = endMs; 166 167 adjustTransitions(); 168 169 // Note that the start and duration of any effects and overlays are 170 // not adjusted nor are they automatically removed if they fall 171 // outside the new boundaries. 172 } 173 174 /** 175 * @return The boundary begin time 176 */ 177 public long getBoundaryBeginTime() { 178 return mBeginBoundaryTimeMs; 179 } 180 181 /** 182 * @return The boundary end time 183 */ 184 public long getBoundaryEndTime() { 185 return mEndBoundaryTimeMs; 186 } 187 188 /* 189 * {@inheritDoc} 190 */ 191 @Override 192 public void addEffect(Effect effect) { 193 if (effect instanceof EffectKenBurns) { 194 throw new IllegalArgumentException("Ken Burns effects cannot be applied to MediaVideoItem"); 195 } 196 super.addEffect(effect); 197 } 198 199 /* 200 * {@inheritDoc} 201 */ 202 @Override 203 public Bitmap getThumbnail(int width, int height, long timeMs) { 204 return null; 205 } 206 207 /* 208 * {@inheritDoc} 209 */ 210 @Override 211 public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs, 212 int thumbnailCount) throws IOException { 213 return null; 214 } 215 216 /* 217 * {@inheritDoc} 218 */ 219 @Override 220 void invalidateTransitions(long startTimeMs, long durationMs) { 221 // Check if the item overlaps with the beginning and end transitions 222 if (mBeginTransition != null) { 223 if (isOverlapping(startTimeMs, durationMs, 224 mBeginBoundaryTimeMs, mBeginTransition.getDuration())) { 225 mBeginTransition.invalidate(); 226 } 227 } 228 229 if (mEndTransition != null) { 230 final long transitionDurationMs = mEndTransition.getDuration(); 231 if (isOverlapping(startTimeMs, durationMs, 232 mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs)) { 233 mEndTransition.invalidate(); 234 } 235 } 236 } 237 238 /* 239 * {@inheritDoc} 240 */ 241 @Override 242 void invalidateTransitions(long oldStartTimeMs, long oldDurationMs, long newStartTimeMs, 243 long newDurationMs) { 244 // Check if the item overlaps with the beginning and end transitions 245 if (mBeginTransition != null) { 246 final long transitionDurationMs = mBeginTransition.getDuration(); 247 // If the start time has changed and if the old or the new item 248 // overlaps with the begin transition, invalidate the transition. 249 if (oldStartTimeMs != newStartTimeMs && 250 (isOverlapping(oldStartTimeMs, oldDurationMs, 251 mBeginBoundaryTimeMs, transitionDurationMs) || 252 isOverlapping(newStartTimeMs, newDurationMs, 253 mBeginBoundaryTimeMs, transitionDurationMs))) { 254 mBeginTransition.invalidate(); 255 } 256 } 257 258 if (mEndTransition != null) { 259 final long transitionDurationMs = mEndTransition.getDuration(); 260 // If the start time + duration has changed and if the old or the new 261 // item overlaps the end transition, invalidate the transition/ 262 if (oldStartTimeMs + oldDurationMs != newStartTimeMs + newDurationMs && 263 (isOverlapping(oldStartTimeMs, oldDurationMs, 264 mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs) || 265 isOverlapping(newStartTimeMs, newDurationMs, 266 mEndBoundaryTimeMs - transitionDurationMs, transitionDurationMs))) { 267 mEndTransition.invalidate(); 268 } 269 } 270 } 271 272 /* 273 * {@inheritDoc} 274 */ 275 @Override 276 public int getAspectRatio() { 277 return mAspectRatio; 278 } 279 280 /* 281 * {@inheritDoc} 282 */ 283 @Override 284 public int getFileType() { 285 return mFileType; 286 } 287 288 /* 289 * {@inheritDoc} 290 */ 291 @Override 292 public int getWidth() { 293 return mWidth; 294 } 295 296 /* 297 * {@inheritDoc} 298 */ 299 @Override 300 public int getHeight() { 301 return mHeight; 302 } 303 304 /* 305 * {@inheritDoc} 306 */ 307 @Override 308 public long getDuration() { 309 return mDurationMs; 310 } 311 312 /* 313 * {@inheritDoc} 314 */ 315 @Override 316 public long getTimelineDuration() { 317 return mEndBoundaryTimeMs - mBeginBoundaryTimeMs; 318 } 319 320 /** 321 * Render a frame according to the playback (in the native aspect ratio) for 322 * the specified media item. All effects and overlays applied to the media 323 * item are ignored. The extract boundaries are also ignored. This method 324 * can be used to playback frames when implementing trimming functionality. 325 * 326 * @param surfaceHolder SurfaceHolder used by the application 327 * @param timeMs time corresponding to the frame to display (relative to the 328 * the beginning of the media item). 329 * @return The accurate time stamp of the frame that is rendered . 330 * @throws IllegalStateException if a playback, preview or an export is 331 * already in progress 332 * @throws IllegalArgumentException if time is negative or greater than the 333 * media item duration 334 */ 335 public long renderFrame(SurfaceHolder surfaceHolder, long timeMs) { 336 return timeMs; 337 } 338 339 /** 340 * This API allows to generate a file containing the sample volume levels of 341 * the Audio track of this media item. This function may take significant 342 * time and is blocking. The file can be retrieved using 343 * getAudioWaveformFilename(). 344 * 345 * @param listener The progress listener 346 * 347 * @throws IOException if the output file cannot be created 348 * @throws IllegalArgumentException if the mediaItem does not have a valid 349 * Audio track 350 */ 351 public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener) 352 throws IOException { 353 // TODO: Set mAudioWaveformFilename at the end once the export is complete 354 mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename)); 355 } 356 357 /** 358 * Get the audio waveform file name if {@link #extractAudioWaveform()} was 359 * successful. The file format is as following: 360 * <ul> 361 * <li>first 4 bytes provide the number of samples for each value, as big-endian signed</li> 362 * <li>4 following bytes is the total number of values in the file, as big-endian signed</li> 363 * <li>all values follow as bytes Name is unique.</li> 364 *</ul> 365 * @return the name of the file, null if the file has not been computed or 366 * if there is no Audio track in the mediaItem 367 */ 368 String getAudioWaveformFilename() { 369 return mAudioWaveformFilename; 370 } 371 372 /** 373 * @return The waveform data 374 */ 375 public WaveformData getWaveformData() { 376 if (mWaveformData == null) { 377 return null; 378 } 379 380 WaveformData waveformData = mWaveformData.get(); 381 if (waveformData != null) { 382 return waveformData; 383 } else if (mAudioWaveformFilename != null) { 384 waveformData = new WaveformData(mAudioWaveformFilename); 385 mWaveformData = new SoftReference<WaveformData>(waveformData); 386 return waveformData; 387 } else { 388 return null; 389 } 390 } 391 392 /** 393 * Set volume of the Audio track of this mediaItem 394 * 395 * @param volumePercent in %/. 100% means no change; 50% means half value, 200% 396 * means double, 0% means silent. 397 * @throws UsupportedOperationException if volume value is not supported 398 */ 399 public void setVolume(int volumePercent) { 400 mVolumePercentage = volumePercent; 401 } 402 403 /** 404 * Get the volume value of the audio track as percentage. Call of this 405 * method before calling setVolume will always return 100% 406 * 407 * @return the volume in percentage 408 */ 409 public int getVolume() { 410 return mVolumePercentage; 411 } 412 413 /** 414 * @param muted true to mute the media item 415 */ 416 public void setMute(boolean muted) { 417 mMuted = muted; 418 } 419 420 /** 421 * @return true if the media item is muted 422 */ 423 public boolean isMuted() { 424 return mMuted; 425 } 426 427 /** 428 * @return The video type 429 */ 430 public int getVideoType() { 431 return mVideoType; 432 } 433 434 /** 435 * @return The video profile 436 */ 437 public int getVideoProfile() { 438 return mVideoProfile; 439 } 440 441 /** 442 * @return The video bitrate 443 */ 444 public int getVideoBitrate() { 445 return mVideoBitrate; 446 } 447 448 /** 449 * @return The audio bitrate 450 */ 451 public int getAudioBitrate() { 452 return mAudioBitrate; 453 } 454 455 /** 456 * @return The number of frames per second 457 */ 458 public int getFps() { 459 return mFps; 460 } 461 462 /** 463 * @return The audio codec 464 */ 465 public int getAudioType() { 466 return mAudioType; 467 } 468 469 /** 470 * @return The number of audio channels 471 */ 472 public int getAudioChannels() { 473 return mAudioChannels; 474 } 475 476 /** 477 * @return The audio sample frequency 478 */ 479 public int getAudioSamplingFrequency() { 480 return mAudioSamplingFrequency; 481 } 482} 483