/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.videoeditor; import java.io.IOException; import java.util.List; import java.util.concurrent.CancellationException; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.view.SurfaceHolder; /** * This is the interface implemented by classes which provide video editing * functionality. The VideoEditor implementation class manages all input and * output files. Unless specifically mentioned, methods are blocking. A typical * editing session may consist of the following sequence of operations: * * * The internal VideoEditor state consists of the following elements: * * {@hide} */ public interface VideoEditor { /** * The file name of the project thumbnail */ public static final String THUMBNAIL_FILENAME = "thumbnail.jpg"; /** * Use this value instead of the specific end of the storyboard timeline * value. */ public final static int DURATION_OF_STORYBOARD = -1; /** * Maximum supported file size */ public static final long MAX_SUPPORTED_FILE_SIZE = 2147483648L; /** * This listener interface is used by the VideoEditor to emit preview * progress notifications. This callback should be invoked after the number * of frames specified by * {@link #startPreview(SurfaceHolder surfaceHolder, long fromMs, * int callbackAfterFrameCount, PreviewProgressListener listener)} */ public interface PreviewProgressListener { /** * This method notifies the listener of the current time position while * previewing a project. * * @param videoEditor The VideoEditor instance * @param timeMs The current preview position (expressed in milliseconds * since the beginning of the storyboard timeline). * @param overlayData The overlay data (null if the overlay data * is unchanged) */ public void onProgress(VideoEditor videoEditor, long timeMs, OverlayData overlayData); /** * This method notifies the listener when the preview is started * previewing a project. * * @param videoEditor The VideoEditor instance */ public void onStart(VideoEditor videoEditor); /** * This method notifies the listener when the preview is stopped * previewing a project. * * @param videoEditor The VideoEditor instance */ public void onStop(VideoEditor videoEditor); /** * This method notifies the listener when error has occurred during * previewing a project. * * @param videoEditor The VideoEditor instance * @param error The error that has occurred * FIXME: We should pass well-defined error code to the application; * but for now, we just pass whatever error code reported by the native layer. */ public void onError(VideoEditor videoEditor, int error); } /** * This listener interface is used by the VideoEditor to emit export status * notifications. * {@link #export(String filename, ExportProgressListener listener, * int height, int bitrate)} */ public interface ExportProgressListener { /** * This method notifies the listener of the progress status of a export * operation. * * @param videoEditor The VideoEditor instance * @param filename The name of the file which is in the process of being * exported. * @param progress The progress in %. At the beginning of the export, * this value is set to 0; at the end, the value is set to 100. */ public void onProgress(VideoEditor videoEditor, String filename, int progress); } public interface MediaProcessingProgressListener { /** * Values used for the action parameter */ public static final int ACTION_ENCODE = 1; public static final int ACTION_DECODE = 2; /** * This method notifies the listener of the progress status of * processing a media object such as a Transition, AudioTrack & Kenburns * This method may be called maximum 100 times for one operation. * * @param object The object that is being processed such as a Transition * or AudioTrack * @param action The type of processing being performed * @param progress The progress in %. At the beginning of the operation, * this value is set to 0; at the end, the value is set to 100. */ public void onProgress(Object item, int action, int progress); } /** * The overlay data */ public static final class OverlayData { // Instance variables private Bitmap mOverlayBitmap; private int mRenderingMode; private boolean mClear; private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG); /** * Default constructor */ public OverlayData() { mOverlayBitmap = null; mRenderingMode = MediaArtistNativeHelper.MediaRendering.BLACK_BORDERS; mClear = false; } /** * Releases the bitmap */ public void release() { if (mOverlayBitmap != null) { mOverlayBitmap.recycle(); mOverlayBitmap = null; } } /** * Check if the overlay needs to be rendered * * @return true if rendering is needed */ public boolean needsRendering() { return (mClear || mOverlayBitmap != null); } /** * Store the overlay data * * @param overlayBitmap The overlay bitmap * @param renderingMode The rendering mode */ void set(Bitmap overlayBitmap, int renderingMode) { mOverlayBitmap = overlayBitmap; mRenderingMode = renderingMode; mClear = false; } /** * Clear the overlay */ void setClear() { mClear = true; } /** * Render the overlay by either clearing it or by * rendering the overlay bitmap with the specified * rendering mode * * @param destBitmap The destination bitmap */ public void renderOverlay(Bitmap destBitmap) { if (mClear) { destBitmap.eraseColor(Color.TRANSPARENT); } else if (mOverlayBitmap != null) { final Canvas overlayCanvas = new Canvas(destBitmap); final Rect destRect; final Rect srcRect; switch (mRenderingMode) { case MediaArtistNativeHelper.MediaRendering.RESIZING: { destRect = new Rect(0, 0, overlayCanvas.getWidth(), overlayCanvas.getHeight()); srcRect = new Rect(0, 0, mOverlayBitmap.getWidth(), mOverlayBitmap.getHeight()); break; } case MediaArtistNativeHelper.MediaRendering.BLACK_BORDERS: { int left, right, top, bottom; float aROverlayImage, aRCanvas; aROverlayImage = (float)(mOverlayBitmap.getWidth()) / (float)(mOverlayBitmap.getHeight()); aRCanvas = (float)(overlayCanvas.getWidth()) / (float)(overlayCanvas.getHeight()); if (aROverlayImage > aRCanvas) { int newHeight = ((overlayCanvas.getWidth() * mOverlayBitmap.getHeight()) / mOverlayBitmap.getWidth()); left = 0; top = (overlayCanvas.getHeight() - newHeight) / 2; right = overlayCanvas.getWidth(); bottom = top + newHeight; } else { int newWidth = ((overlayCanvas.getHeight() * mOverlayBitmap.getWidth()) / mOverlayBitmap.getHeight()); left = (overlayCanvas.getWidth() - newWidth) / 2; top = 0; right = left + newWidth; bottom = overlayCanvas.getHeight(); } destRect = new Rect(left, top, right, bottom); srcRect = new Rect(0, 0, mOverlayBitmap.getWidth(), mOverlayBitmap.getHeight()); break; } case MediaArtistNativeHelper.MediaRendering.CROPPING: { // Calculate the source rect int left, right, top, bottom; float aROverlayImage, aRCanvas; aROverlayImage = (float)(mOverlayBitmap.getWidth()) / (float)(mOverlayBitmap.getHeight()); aRCanvas = (float)(overlayCanvas.getWidth()) / (float)(overlayCanvas.getHeight()); if (aROverlayImage < aRCanvas) { int newHeight = ((mOverlayBitmap.getWidth() * overlayCanvas.getHeight()) / overlayCanvas.getWidth()); left = 0; top = (mOverlayBitmap.getHeight() - newHeight) / 2; right = mOverlayBitmap.getWidth(); bottom = top + newHeight; } else { int newWidth = ((mOverlayBitmap.getHeight() * overlayCanvas.getWidth()) / overlayCanvas.getHeight()); left = (mOverlayBitmap.getWidth() - newWidth) / 2; top = 0; right = left + newWidth; bottom = mOverlayBitmap.getHeight(); } srcRect = new Rect(left, top, right, bottom); destRect = new Rect(0, 0, overlayCanvas.getWidth(), overlayCanvas.getHeight()); break; } default: { throw new IllegalStateException("Rendering mode: " + mRenderingMode); } } destBitmap.eraseColor(Color.TRANSPARENT); overlayCanvas.drawBitmap(mOverlayBitmap, srcRect, destRect, sResizePaint); mOverlayBitmap.recycle(); } } } /** * @return The path where the VideoEditor stores all files related to the * project */ public String getPath(); /** * This method releases all in-memory resources used by the VideoEditor * instance. All pending operations such as preview, export and extract * audio waveform must be canceled. */ public void release(); /** * Persist the current internal state of VideoEditor to the project path. * The VideoEditor state may be restored by invoking the * {@link VideoEditorFactory#load(String)} method. This method does not * release the internal in-memory state of the VideoEditor. To release * the in-memory state of the VideoEditor the {@link #release()} method * must be invoked. * * Pending transition generations must be allowed to complete before the * state is saved. * Pending audio waveform generations must be allowed to complete. * Pending export operations must be allowed to continue. * * @throws IOException if the internal state cannot be saved to project file */ public void save() throws IOException; /** * Create the output movie based on all media items added and the applied * storyboard items. This method can take a long time to execute and is * blocking. The application will receive progress notifications via the * ExportProgressListener. Specific implementations may not support multiple * simultaneous export operations. Note that invoking methods which would * change the contents of the output movie throw an IllegalStateException * while an export operation is pending. * * The audio and video codecs are automatically selected by the underlying * implementation. * * @param filename The output file name (including the full path) * @param height The height of the output video file. The supported values * for height are described in the MediaProperties class, for * example: HEIGHT_480. The width will be automatically computed * according to the aspect ratio provided by * {@link #setAspectRatio(int)} * @param bitrate The bitrate of the output video file. This is approximate * value for the output movie. Supported bitrate values are * described in the MediaProperties class for example: BITRATE_384K * @param listener The listener for progress notifications. Use null if * export progress notifications are not needed. * * @throws IllegalArgumentException if height or bitrate are not supported * or if the audio or video codecs are not supported * @throws IOException if output file cannot be created * @throws IllegalStateException if a preview or an export is in progress or * if no MediaItem has been added * @throws CancellationException if export is canceled by calling * {@link #cancelExport()} * @throws UnsupportOperationException if multiple simultaneous export() are * not allowed */ public void export(String filename, int height, int bitrate, ExportProgressListener listener) throws IOException; /** * Create the output movie based on all media items added and the applied * storyboard items. This method can take a long time to execute and is * blocking. The application will receive progress notifications via the * ExportProgressListener. Specific implementations may not support multiple * simultaneous export operations. Note that invoking methods which would * change the contents of the output movie throw an IllegalStateException * while an export operation is pending. * * @param filename The output file name (including the full path) * @param height The height of the output video file. The supported values * for height are described in the MediaProperties class, for * example: HEIGHT_480. The width will be automatically computed * according to the aspect ratio provided by * {@link #setAspectRatio(int)} * @param bitrate The bitrate of the output video file. This is approximate * value for the output movie. Supported bitrate values are * described in the MediaProperties class for example: BITRATE_384K * @param audioCodec The audio codec to be used for the export. The audio * codec values are defined in the MediaProperties class (e.g. * ACODEC_AAC_LC). Note that not all audio codec types are * supported for export purposes. * @param videoCodec The video codec to be used for the export. The video * codec values are defined in the MediaProperties class (e.g. * VCODEC_H264). Note that not all video codec types are * supported for export purposes. * @param listener The listener for progress notifications. Use null if * export progress notifications are not needed. * * @throws IllegalArgumentException if height or bitrate are not supported * or if the audio or video codecs are not supported * @throws IOException if output file cannot be created * @throws IllegalStateException if a preview or an export is in progress or * if no MediaItem has been added * @throws CancellationException if export is cancelled by calling * {@link #cancelExport()} * @throws UnsupportOperationException if multiple simultaneous export() are * not allowed */ public void export(String filename, int height, int bitrate, int audioCodec, int videoCodec, ExportProgressListener listener) throws IOException; /** * Cancel the running export operation. This method blocks until the export * is cancelled and the exported file (if any) is deleted. If the export * completed by the time this method is invoked, the export file will be * deleted. * * @param filename The filename which identifies the export operation to be * canceled. **/ public void cancelExport(String filename); /** * Add a media item at the end of the storyboard. * * @param mediaItem The media item object to add * * @throws IllegalStateException if a preview or an export is in progress or * if the media item id is not unique across all the media items * added. */ public void addMediaItem(MediaItem mediaItem); /** * Insert a media item after the media item with the specified id. * * @param mediaItem The media item object to insert * @param afterMediaItemId Insert the mediaItem after the media item * identified by this id. If this parameter is null, the media * item is inserted at the beginning of the timeline. * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if media item with the specified id does * not exist (null is a valid value) or if the media item id is * not unique across all the media items added. */ public void insertMediaItem(MediaItem mediaItem, String afterMediaItemId); /** * Move a media item after the media item with the specified id. * * Note: The project thumbnail is regenerated if the media item is or * becomes the first media item in the storyboard timeline. * * @param mediaItemId The id of the media item to move * @param afterMediaItemId Move the media item identified by mediaItemId * after the media item identified by this parameter. If this * parameter is null, the media item is moved at the beginning of * the timeline. * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if one of media item ids is invalid * (null is a valid value) */ public void moveMediaItem(String mediaItemId, String afterMediaItemId); /** * Remove the media item with the specified id. If there are transitions * before or after this media item, then this/these transition(s) are * removed from the storyboard. If the extraction of the audio waveform is * in progress, the extraction is canceled and the file is deleted. * * Effects and overlays associated with the media item will also be removed. * * Note: The project thumbnail is regenerated if the media item which is * removed is the first media item in the storyboard or if the media item is * the only one in the storyboard. If the media item is the only one in the * storyboard, the project thumbnail will be set to a black frame and the * aspect ratio will revert to the default aspect ratio and this method is * equivalent to removeAllMediaItems() in this case. * * @param mediaItemId The unique id of the media item to be removed * * @return The media item that was removed * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if media item with the specified id does * not exist */ public MediaItem removeMediaItem(String mediaItemId); /** * Remove all media items in the storyboard. All effects, overlays and all * transitions are also removed. * * Note: The project thumbnail will be set to a black frame and the aspect * ratio will revert to the default aspect ratio. * * @throws IllegalStateException if a preview or an export is in progress */ public void removeAllMediaItems(); /** * Get the list of media items in the order in which it they appear in the * storyboard timeline. * * Note that if any media item source files are no longer * accessible, this method will still provide the full list of media items. * * @return The list of media items. If no media item exist an empty list * will be returned. */ public List getAllMediaItems(); /** * Find the media item with the specified id * * @param mediaItemId The media item id * * @return The media item with the specified id (null if it does not exist) */ public MediaItem getMediaItem(String mediaItemId); /** * Add a transition between the media items specified by the transition. * If a transition existed at the same position it is invalidated and then * the transition is replaced. Note that the new transition video clip is * not automatically generated by this method. The * {@link Transition#generate()} method must be invoked to generate * the transition video clip. * * Note that the TransitionAtEnd and TransitionAtStart are special kinds * that can not be applied between two media items. * * A crossfade audio transition will be automatically applied regardless of * the video transition. * * @param transition The transition to apply * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if the transition duration is larger * than the smallest duration of the two media item files or if * the two media items specified in the transition are not * adjacent */ public void addTransition(Transition transition); /** * Remove the transition with the specified id. * * @param transitionId The id of the transition to be removed * * @return The transition that was removed * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if transition with the specified id does * not exist */ public Transition removeTransition(String transitionId); /** * Get the list of transitions * * @return The list of transitions. If no transitions exist an empty list * will be returned. */ public List getAllTransitions(); /** * Find the transition with the specified transition id. * * @param transitionId The transition id * * @return The transition */ public Transition getTransition(String transitionId); /** * Add the specified AudioTrack to the storyboard. Note: Specific * implementations may support a limited number of audio tracks (e.g. only * one audio track) * * @param audioTrack The AudioTrack to add * * @throws UnsupportedOperationException if the implementation supports a * limited number of audio tracks. * @throws IllegalArgumentException if media item is not unique across all * the audio tracks already added. */ public void addAudioTrack(AudioTrack audioTrack); /** * Insert an audio track after the audio track with the specified id. Use * addAudioTrack to add an audio track at the end of the storyboard * timeline. * * @param audioTrack The audio track object to insert * @param afterAudioTrackId Insert the audio track after the audio track * identified by this parameter. If this parameter is null the * audio track is added at the beginning of the timeline. * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if media item with the specified id does * not exist (null is a valid value). if media item is not unique * across all the audio tracks already added. * @throws UnsupportedOperationException if the implementation supports a * limited number of audio tracks */ public void insertAudioTrack(AudioTrack audioTrack, String afterAudioTrackId); /** * Move an AudioTrack after the AudioTrack with the specified id. * * @param audioTrackId The id of the AudioTrack to move * @param afterAudioTrackId Move the AudioTrack identified by audioTrackId * after the AudioTrack identified by this parameter. If this * parameter is null the audio track is added at the beginning of * the timeline. * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if one of media item ids is invalid * (null is a valid value) */ public void moveAudioTrack(String audioTrackId, String afterAudioTrackId); /** * Remove the audio track with the specified id. If the extraction of the * audio waveform is in progress, the extraction is canceled and the file is * deleted. * * @param audioTrackId The id of the audio track to be removed * * @return The audio track that was removed * @throws IllegalStateException if a preview or an export is in progress */ public AudioTrack removeAudioTrack(String audioTrackId); /** * Get the list of AudioTracks in order in which they appear in the * storyboard. * * Note that if any AudioTrack source files are not accessible anymore, * this method will still provide the full list of audio tracks. * * @return The list of AudioTracks. If no audio tracks exist an empty list * will be returned. */ public List getAllAudioTracks(); /** * Find the AudioTrack with the specified id * * @param audioTrackId The AudioTrack id * * @return The AudioTrack with the specified id (null if it does not exist) */ public AudioTrack getAudioTrack(String audioTrackId); /** * Set the aspect ratio used in the preview and the export movie. * * The default aspect ratio is ASPECTRATIO_16_9 (16:9). * * @param aspectRatio to apply. If aspectRatio is the same as the current * aspect ratio, then this function just returns. The supported * aspect ratio are defined in the MediaProperties class for * example: ASPECTRATIO_16_9 * * @throws IllegalStateException if a preview or an export is in progress * @throws IllegalArgumentException if aspect ratio is not supported */ public void setAspectRatio(int aspectRatio); /** * Get current aspect ratio. * * @return The aspect ratio as described in MediaProperties */ public int getAspectRatio(); /** * Get the preview (and output movie) duration. * * @return The duration of the preview (and output movie) */ public long getDuration(); /** * Render a frame according to the preview aspect ratio and activating all * storyboard items relative to the specified time. * * @param surfaceHolder SurfaceHolder used by the application * @param timeMs time corresponding to the frame to display * @param overlayData The overlay data * * @return The accurate time stamp of the frame that is rendered. * * @throws IllegalStateException if a preview or an export is already in * progress * @throws IllegalArgumentException if time is negative or beyond the * preview duration */ public long renderPreviewFrame(SurfaceHolder surfaceHolder, long timeMs, OverlayData overlayData); /** * This method must be called after any changes made to the storyboard * and before startPreview is called. Note that this method may block for an * extensive period of time. */ public void generatePreview(MediaProcessingProgressListener listener); /** * Start the preview of all the storyboard items applied on all MediaItems * This method does not block (does not wait for the preview to complete). * The PreviewProgressListener allows to track the progress at the time * interval determined by the callbackAfterFrameCount parameter. The * SurfaceHolder has to be created and ready for use before calling this * method. The method is a no-op if there are no MediaItems in the * storyboard. * * @param surfaceHolder SurfaceHolder where the preview is rendered. * @param fromMs The time (relative to the timeline) at which the preview * will start * @param toMs The time (relative to the timeline) at which the preview will * stop. Use -1 to play to the end of the timeline * @param loop true if the preview should be looped once it reaches the end * @param callbackAfterFrameCount The listener interface should be invoked * after the number of frames specified by this parameter. * @param listener The listener which will be notified of the preview * progress * * @throws IllegalArgumentException if fromMs is beyond the preview duration * @throws IllegalStateException if a preview or an export is already in * progress */ public void startPreview(SurfaceHolder surfaceHolder, long fromMs, long toMs, boolean loop,int callbackAfterFrameCount, PreviewProgressListener listener); /** * Stop the current preview. This method blocks until ongoing preview is * stopped. Ignored if there is no preview running. * * @return The accurate current time when stop is effective expressed in * milliseconds */ public long stopPreview(); /** * Clears the preview surface * * @param surfaceHolder SurfaceHolder where the preview is rendered * and needs to be cleared. */ public void clearSurface(SurfaceHolder surfaceHolder); }