diff options
author | Gil Dobjanschi <virgild@google.com> | 2010-09-22 20:03:58 -0700 |
---|---|---|
committer | Gil Dobjanschi <virgild@google.com> | 2010-09-22 20:12:07 -0700 |
commit | 21e9da6f446301756ddabbfb9d61155db5480366 (patch) | |
tree | 2aebb1e5a4cf823cc48f2440aacc462765760484 /media/java | |
parent | 0e1d6876f93332eb430d31d48f627ad812abb29d (diff) | |
download | frameworks_base-21e9da6f446301756ddabbfb9d61155db5480366.zip frameworks_base-21e9da6f446301756ddabbfb9d61155db5480366.tar.gz frameworks_base-21e9da6f446301756ddabbfb9d61155db5480366.tar.bz2 |
1. Added OverlayFrame constuctor
2. Invalidate transitions when trimming video
3. Remove image file when removing an OverlayFrame
4. Bug fixes in the VideoEditor implementation
Change-Id: I7bd3c888848edaf659a4faef14ad1c5a6603c3cc
Diffstat (limited to 'media/java')
6 files changed, 228 insertions, 110 deletions
diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java index 177b863..8c39577 100755 --- a/media/java/android/media/videoeditor/Effect.java +++ b/media/java/android/media/videoeditor/Effect.java @@ -84,7 +84,7 @@ public abstract class Effect { * Set start time of the effect. If a preview or export is in progress, then
* this change is effective for next preview or export session.
*
- * @param startTimeMs The start time of the effect relative to the begining
+ * @param startTimeMs The start time of the effect relative to the beginning
* of the media item in milliseconds
*/
public void setStartTime(long startTimeMs) {
diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java index 9e32744..e7be35d 100755 --- a/media/java/android/media/videoeditor/MediaItem.java +++ b/media/java/android/media/videoeditor/MediaItem.java @@ -64,8 +64,8 @@ public abstract class MediaItem { private int mRenderingMode;
// Beginning and end transitions
- private Transition mBeginTransition;
- private Transition mEndTransition;
+ protected Transition mBeginTransition;
+ protected Transition mEndTransition;
/**
* Constructor
@@ -113,6 +113,13 @@ public abstract class MediaItem { */
public void setRenderingMode(int renderingMode) {
mRenderingMode = renderingMode;
+ if (mBeginTransition != null) {
+ mBeginTransition.invalidate();
+ }
+
+ if (mEndTransition != null) {
+ mEndTransition.invalidate();
+ }
}
/**
@@ -271,7 +278,9 @@ public abstract class MediaItem { *
* @param overlay The overlay to add
* @throws IllegalStateException if a preview or an export is in progress or
- * if the overlay id is not unique across all the overlays added.
+ * if the overlay id is not unique across all the overlays
+ * added or if the bitmap is not specified or if the dimensions of
+ * the bitmap do not match the dimensions of the media item
*/
public void addOverlay(Overlay overlay) {
if (mOverlays.contains(overlay)) {
@@ -283,6 +292,23 @@ public abstract class MediaItem { "Overlay start time + overlay duration > media clip duration");
}
+ if (overlay instanceof OverlayFrame) {
+ final OverlayFrame frame = (OverlayFrame)overlay;
+ final Bitmap bitmap = frame.getBitmap();
+ if (bitmap == null) {
+ throw new IllegalArgumentException("Overlay bitmap not specified");
+ }
+
+ // The dimensions of the overlay bitmap must be the same as the
+ // media item dimensions
+ if (bitmap.getWidth() != getWidth() || bitmap.getHeight() != getHeight()) {
+ throw new IllegalArgumentException(
+ "Bitmap dimensions must match media item dimensions");
+ }
+ } else {
+ throw new IllegalArgumentException("Overlay not supported");
+ }
+
mOverlays.add(overlay);
invalidateTransitions(overlay);
}
@@ -302,6 +328,9 @@ public abstract class MediaItem { for (Overlay overlay : mOverlays) {
if (overlay.getId().equals(overlayId)) {
mOverlays.remove(overlay);
+ if (overlay instanceof OverlayFrame) {
+ ((OverlayFrame)overlay).invalidate();
+ }
invalidateTransitions(overlay);
return overlay;
}
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java index 87e9a22..af50b83 100755 --- a/media/java/android/media/videoeditor/MediaVideoItem.java +++ b/media/java/android/media/videoeditor/MediaVideoItem.java @@ -264,8 +264,19 @@ public class MediaVideoItem extends MediaItem { throw new IllegalArgumentException("Invalid end time");
}
- mBeginBoundaryTimeMs = beginMs;
- mEndBoundaryTimeMs = endMs;
+ if (beginMs != mBeginBoundaryTimeMs) {
+ mBeginBoundaryTimeMs = beginMs;
+ if (mBeginTransition != null) {
+ mBeginTransition.invalidate();
+ }
+ }
+
+ if (endMs == mEndBoundaryTimeMs) {
+ mEndBoundaryTimeMs = endMs;
+ if (mEndTransition != null) {
+ mEndTransition.invalidate();
+ }
+ }
// TODO: Validate/modify the start and the end time of effects and overlays
}
diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java index e5d9b81..3abac6c 100755 --- a/media/java/android/media/videoeditor/OverlayFrame.java +++ b/media/java/android/media/videoeditor/OverlayFrame.java @@ -16,16 +16,24 @@ package android.media.videoeditor;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap.CompressFormat;
+
/**
- * This class is used to overlay an image on top of a media item. This class
- * does not manage deletion of the overlay file so application may use
- * {@link #getFilename()} for this purpose.
+ * This class is used to overlay an image on top of a media item.
* {@hide}
*/
public class OverlayFrame extends Overlay {
// Instance variables
- private final String mFilename;
+ private final Bitmap mBitmap;
+ private String mFilename;
/**
* An object of this type cannot be instantiated by using the default
@@ -33,30 +41,90 @@ public class OverlayFrame extends Overlay { */
@SuppressWarnings("unused")
private OverlayFrame() {
- this(null, null, 0, 0);
+ this(null, (String)null, 0, 0);
}
/**
* Constructor for an OverlayFrame
*
* @param overlayId The overlay id
- * @param filename The file name that contains the overlay. Only PNG
- * supported.
+ * @param bitmap The bitmap to be used as an overlay. The size of the
+ * bitmap must equal to the size of the media item to which it is
+ * added. The bitmap is typically a decoded PNG file.
* @param startTimeMs The overlay start time in milliseconds
* @param durationMs The overlay duration in milliseconds
*
* @throws IllegalArgumentException if the file type is not PNG or the
* startTimeMs and durationMs are incorrect.
*/
- public OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {
+ public OverlayFrame(String overlayId, Bitmap bitmap, long startTimeMs,
+ long durationMs) {
+ super(overlayId, startTimeMs, durationMs);
+ mBitmap = bitmap;
+ mFilename = null;
+ }
+
+ /**
+ * Constructor for an OverlayFrame. This constructor can be used to
+ * restore the overlay after it was saved internally by the video editor.
+ *
+ * @param overlayId The overlay id
+ * @param filename The file name that contains the overlay.
+ * @param startTimeMs The overlay start time in milliseconds
+ * @param durationMs The overlay duration in milliseconds
+ *
+ * @throws IllegalArgumentException if the file type is not PNG or the
+ * startTimeMs and durationMs are incorrect.
+ */
+ OverlayFrame(String overlayId, String filename, long startTimeMs, long durationMs) {
super(overlayId, startTimeMs, durationMs);
mFilename = filename;
+ mBitmap = BitmapFactory.decodeFile(mFilename);
+ }
+
+ /**
+ * @return Get the overlay bitmap
+ */
+ public Bitmap getBitmap() {
+ return mBitmap;
}
/**
* Get the file name of this overlay
*/
- public String getFilename() {
+ String getFilename() {
return mFilename;
}
+
+ /**
+ * Save the overlay to the project folder
+ *
+ * @param editor The video editor
+ *
+ * @return
+ * @throws FileNotFoundException if the bitmap cannot be saved
+ * @throws IOException if the bitmap file cannot be saved
+ */
+ String save(VideoEditor editor) throws FileNotFoundException, IOException {
+ if (mFilename != null) {
+ return mFilename;
+ }
+
+ mFilename = editor.getPath() + "/" + getId() + ".png";
+ // Save the image to a local file
+ final FileOutputStream out = new FileOutputStream(mFilename);
+ mBitmap.compress(CompressFormat.PNG, 100, out);
+ out.flush();
+ out.close();
+ return mFilename;
+ }
+
+ /**
+ * Delete the overlay file
+ */
+ void invalidate() {
+ if (mFilename != null) {
+ new File(mFilename).delete();
+ }
+ }
}
diff --git a/media/java/android/media/videoeditor/TransitionAlpha.java b/media/java/android/media/videoeditor/TransitionAlpha.java index 0a4a12f..30e66fc 100755 --- a/media/java/android/media/videoeditor/TransitionAlpha.java +++ b/media/java/android/media/videoeditor/TransitionAlpha.java @@ -58,17 +58,19 @@ public class TransitionAlpha extends Transition { * Constructor
*
* @param transitionId The transition id
- * @param afterMediaItem The transition is applied to the end of this
- * media item
- * @param beforeMediaItem The transition is applied to the beginning of
- * this media item
+ * @param afterMediaItem The transition is applied to the end of this media
+ * item
+ * @param beforeMediaItem The transition is applied to the beginning of this
+ * media item
* @param durationMs duration of the transition in milliseconds
* @param behavior behavior is one of the behavior defined in Transition
* class
- * @param maskFilename JPEG file name
+ * @param maskFilename JPEG file name. The dimension of the image
+ * corresponds to 720p (16:9 aspect ratio). Mask files are
+ * shared between video editors and can be created in the
+ * projects folder (the parent folder for all projects).
* @param blendingPercent The blending percent applied
* @param invert true to invert the direction of the alpha blending
- *
* @throws IllegalArgumentException if behavior is not supported, or if
* direction are not supported.
*/
diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java index 14e2658..7226d5d 100644 --- a/media/java/android/media/videoeditor/VideoEditorTestImpl.java +++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java @@ -48,8 +48,8 @@ public class VideoEditorTestImpl implements VideoEditor { private static final String TAG_PROJECT = "project"; private static final String TAG_MEDIA_ITEMS = "media_items"; private static final String TAG_MEDIA_ITEM = "media_item"; - private static final String TAG_BEGIN_TRANSITION = "begin_transition"; - private static final String TAG_END_TRANSITION = "end_transition"; + private static final String TAG_TRANSITIONS = "transitions"; + private static final String TAG_TRANSITION = "transition"; private static final String ATTR_ID = "id"; private static final String ATTR_FILENAME = "filename"; private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem"; @@ -65,6 +65,8 @@ public class VideoEditorTestImpl implements VideoEditor { private static final String ATTR_BLENDING = "blending"; private static final String ATTR_INVERT = "invert"; private static final String ATTR_MASK = "mask"; + private static final String ATTR_BEFORE_MEDIA_ITEM_ID = "before_media_item"; + private static final String ATTR_AFTER_MEDIA_ITEM_ID = "after_media_item"; // Instance variables private long mDurationMs; @@ -400,6 +402,7 @@ public class VideoEditorTestImpl implements VideoEditor { } beforeMediaItem.setBeginTransition(transition); } + computeTimelineDuration(); } @@ -412,22 +415,25 @@ public class VideoEditorTestImpl implements VideoEditor { } final Transition transition = getTransition(transitionId); - if (transition != null) { - mTransitions.remove(transition); - transition.invalidate(); - computeTimelineDuration(); + if (transition == null) { + throw new IllegalStateException("Transition not found: " + transitionId); } - // Cross reference the transitions + // Remove the transition references final MediaItem afterMediaItem = transition.getAfterMediaItem(); if (afterMediaItem != null) { afterMediaItem.setEndTransition(null); } + final MediaItem beforeMediaItem = transition.getBeforeMediaItem(); if (beforeMediaItem != null) { beforeMediaItem.setBeginTransition(null); } + mTransitions.remove(transition); + transition.invalidate(); + computeTimelineDuration(); + return transition; } @@ -543,15 +549,14 @@ public class VideoEditorTestImpl implements VideoEditor { serializer.startTag("", TAG_PROJECT); serializer.attribute("", ATTR_ASPECT_RATIO, Integer.toString(mAspectRatio)); - boolean firstMediaItem = true; serializer.startTag("", TAG_MEDIA_ITEMS); for (MediaItem mediaItem : mMediaItems) { serializer.startTag("", TAG_MEDIA_ITEM); serializer.attribute("", ATTR_ID, mediaItem.getId()); serializer.attribute("", ATTR_TYPE, mediaItem.getClass().getSimpleName()); serializer.attribute("", ATTR_FILENAME, mediaItem.getFilename()); - serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString(mediaItem - .getRenderingMode())); + serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString( + mediaItem.getRenderingMode())); if (mediaItem instanceof MediaVideoItem) { final MediaVideoItem mvi = (MediaVideoItem)mediaItem; serializer @@ -559,54 +564,49 @@ public class VideoEditorTestImpl implements VideoEditor { serializer.attribute("", ATTR_END_TIME, Long.toString(mvi.getBoundaryEndTime())); serializer.attribute("", ATTR_VOLUME, Integer.toString(mvi.getVolume())); if (mvi.getAudioWaveformFilename() != null) { - serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME, mvi - .getAudioWaveformFilename()); + serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME, + mvi.getAudioWaveformFilename()); } } else if (mediaItem instanceof MediaImageItem) { serializer.attribute("", ATTR_DURATION, Long.toString(mediaItem.getDuration())); } - if (firstMediaItem) { - firstMediaItem = false; - final Transition beginTransition = mediaItem.getBeginTransition(); - if (beginTransition != null) { - serializer.startTag("", TAG_BEGIN_TRANSITION); - serializer.attribute("", ATTR_ID, beginTransition.getId()); - serializer.attribute("", ATTR_TYPE, beginTransition.getClass() - .getSimpleName()); - serializer.attribute("", ATTR_DURATION, Long.toString(beginTransition - .getDuration())); - serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(beginTransition - .getBehavior())); - serializer.endTag("", TAG_BEGIN_TRANSITION); - } + serializer.endTag("", TAG_MEDIA_ITEM); + } + serializer.endTag("", TAG_MEDIA_ITEMS); + + serializer.startTag("", TAG_TRANSITIONS); + + for (Transition transition : mTransitions) { + serializer.startTag("", TAG_TRANSITION); + serializer.attribute("", ATTR_ID, transition.getId()); + serializer.attribute("", ATTR_TYPE, transition.getClass().getSimpleName()); + serializer.attribute("", ATTR_DURATION, Long.toString(transition.getDuration())); + serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(transition.getBehavior())); + final MediaItem afterMediaItem = transition.getAfterMediaItem(); + if (afterMediaItem != null) { + serializer.attribute("", ATTR_AFTER_MEDIA_ITEM_ID, afterMediaItem.getId()); + } + + final MediaItem beforeMediaItem = transition.getBeforeMediaItem(); + if (beforeMediaItem != null) { + serializer.attribute("", ATTR_BEFORE_MEDIA_ITEM_ID, beforeMediaItem.getId()); } - final Transition endTransition = mediaItem.getEndTransition(); - if (endTransition != null) { - serializer.startTag("", TAG_END_TRANSITION); - serializer.attribute("", ATTR_ID, endTransition.getId()); - serializer.attribute("", ATTR_TYPE, endTransition.getClass().getSimpleName()); - serializer.attribute("", ATTR_DURATION, Long.toString(endTransition - .getDuration())); - serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(endTransition - .getBehavior())); - if (endTransition instanceof TransitionSliding) { - serializer.attribute("", ATTR_DIRECTION, Integer - .toString(((TransitionSliding)endTransition).getDirection())); - } else if (endTransition instanceof TransitionAlpha) { - TransitionAlpha ta = (TransitionAlpha)endTransition; - serializer.attribute("", ATTR_BLENDING, Integer.toString(ta - .getBlendingPercent())); - serializer.attribute("", ATTR_INVERT, Boolean.toString(ta.isInvert())); + if (transition instanceof TransitionSliding) { + serializer.attribute("", ATTR_DIRECTION, + Integer.toString(((TransitionSliding)transition).getDirection())); + } else if (transition instanceof TransitionAlpha) { + TransitionAlpha ta = (TransitionAlpha)transition; + serializer.attribute("", ATTR_BLENDING, Integer.toString(ta.getBlendingPercent())); + serializer.attribute("", ATTR_INVERT, Boolean.toString(ta.isInvert())); + if (ta.getMaskFilename() != null) { serializer.attribute("", ATTR_MASK, ta.getMaskFilename()); } - serializer.endTag("", TAG_END_TRANSITION); } - - serializer.endTag("", TAG_MEDIA_ITEM); + serializer.endTag("", TAG_TRANSITION); } - serializer.endTag("", TAG_MEDIA_ITEMS); + serializer.endTag("", TAG_TRANSITIONS); serializer.endTag("", TAG_PROJECT); serializer.endDocument(); @@ -628,8 +628,6 @@ public class VideoEditorTestImpl implements VideoEditor { parser.setInput(new FileInputStream(file), "UTF-8"); int eventType = parser.getEventType(); String name; - MediaItem currentMediaItem = null; - MediaItem previousMediaItem = null; while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: { @@ -644,6 +642,7 @@ public class VideoEditorTestImpl implements VideoEditor { final int renderingMode = Integer.parseInt(parser.getAttributeValue("", ATTR_RENDERING_MODE)); + MediaItem currentMediaItem; if (MediaImageItem.class.getSimpleName().equals(type)) { final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); @@ -671,29 +670,13 @@ public class VideoEditorTestImpl implements VideoEditor { } if (currentMediaItem != null) { - if (previousMediaItem != null) { - currentMediaItem.setBeginTransition(previousMediaItem - .getEndTransition()); - } mMediaItems.add(currentMediaItem); } - } else if (name.equals(TAG_BEGIN_TRANSITION)) { - final Transition transition = parseTransition(parser, currentMediaItem, - null); - currentMediaItem.setBeginTransition(transition); - } else if (name.equals(TAG_END_TRANSITION)) { - final Transition transition = parseTransition(parser, previousMediaItem, - currentMediaItem); - currentMediaItem.setEndTransition(transition); - } - break; - } - - case XmlPullParser.END_TAG: { - name = parser.getName(); - if (name.equals(TAG_MEDIA_ITEM)) { - previousMediaItem = currentMediaItem; - currentMediaItem = null; + } else if (name.equals(TAG_TRANSITION)) { + final Transition transition = parseTransition(parser); + if (transition != null) { + mTransitions.add(transition); + } } break; } @@ -712,47 +695,72 @@ public class VideoEditorTestImpl implements VideoEditor { * Parse the transition * * @param parser The parser - * @param afterMediaItem The transition is at the end of this media item - * @param beforeMediaItem The transition is at the beginning of this media - * item * @return The transition */ - private Transition parseTransition(XmlPullParser parser, MediaItem beforeMediaItem, - MediaItem afterMediaItem) { + private Transition parseTransition(XmlPullParser parser) { final String transitionId = parser.getAttributeValue("", ATTR_ID); final String type = parser.getAttributeValue("", ATTR_TYPE); final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); final int behavior = Integer.parseInt(parser.getAttributeValue("", ATTR_BEHAVIOR)); + + final String beforeMediaItemId = parser.getAttributeValue("", ATTR_BEFORE_MEDIA_ITEM_ID); + final MediaItem beforeMediaItem; + if (beforeMediaItemId != null) { + beforeMediaItem = getMediaItem(beforeMediaItemId); + } else { + beforeMediaItem = null; + } + + final String afterMediaItemId = parser.getAttributeValue("", ATTR_AFTER_MEDIA_ITEM_ID); + final MediaItem afterMediaItem; + if (afterMediaItemId != null) { + afterMediaItem = getMediaItem(afterMediaItemId); + } else { + afterMediaItem = null; + } + + final Transition transition; if (TransitionStartCurtainOpening.class.getSimpleName().equals(type)) { - return new TransitionStartCurtainOpening(transitionId, beforeMediaItem, durationMs, - behavior); + transition = new TransitionStartCurtainOpening(transitionId, beforeMediaItem, + durationMs, behavior); } else if (TransitionStartFadeFromBlack.class.getSimpleName().equals(type)) { - return new TransitionStartFadeFromBlack(transitionId, beforeMediaItem, durationMs, - behavior); + transition = new TransitionStartFadeFromBlack(transitionId, beforeMediaItem, + durationMs, behavior); } else if (TransitionAlpha.class.getSimpleName().equals(type)) { final int blending = Integer.parseInt(parser.getAttributeValue("", ATTR_BLENDING)); final String maskFilename = parser.getAttributeValue("", ATTR_MASK); final boolean invert = Boolean.getBoolean(parser.getAttributeValue("", ATTR_INVERT)); - return new TransitionAlpha(transitionId, afterMediaItem, beforeMediaItem, durationMs, - behavior, maskFilename, blending, invert); - } else if (TransitionAlpha.class.getSimpleName().equals(type)) { - return new TransitionCrossfade(transitionId, afterMediaItem, beforeMediaItem, + transition = new TransitionAlpha(transitionId, afterMediaItem, beforeMediaItem, + durationMs, behavior, maskFilename, blending, invert); + } else if (TransitionCrossfade.class.getSimpleName().equals(type)) { + transition = new TransitionCrossfade(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior); } else if (TransitionSliding.class.getSimpleName().equals(type)) { final int direction = Integer.parseInt(parser.getAttributeValue("", ATTR_DIRECTION)); - return new TransitionSliding(transitionId, afterMediaItem, beforeMediaItem, durationMs, - behavior, direction); + transition = new TransitionSliding(transitionId, afterMediaItem, beforeMediaItem, + durationMs, behavior, direction); } else if (TransitionFadeToBlack.class.getSimpleName().equals(type)) { - return new TransitionFadeToBlack(transitionId, afterMediaItem, beforeMediaItem, + transition = new TransitionFadeToBlack(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior); } else if (TransitionEndCurtainClosing.class.getSimpleName().equals(type)) { - return new TransitionEndCurtainClosing(transitionId, beforeMediaItem, durationMs, + transition = new TransitionEndCurtainClosing(transitionId, afterMediaItem, durationMs, behavior); } else if (TransitionEndFadeToBlack.class.getSimpleName().equals(type)) { - return new TransitionEndFadeToBlack(transitionId, beforeMediaItem, durationMs, behavior); + transition = new TransitionEndFadeToBlack(transitionId, afterMediaItem, durationMs, + behavior); + } else { + transition = null; } - return null; + if (beforeMediaItem != null) { + beforeMediaItem.setBeginTransition(transition); + } + + if (afterMediaItem != null) { + afterMediaItem.setEndTransition(transition); + } + + return transition; } public void cancelExport(String filename) { |