| /* |
| * 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 com.android.videoeditor.service; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.BlockingQueue; |
| import java.util.concurrent.LinkedBlockingQueue; |
| |
| import android.app.Service; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.graphics.Bitmap; |
| import android.graphics.Rect; |
| import android.media.videoeditor.AudioTrack; |
| import android.media.videoeditor.Effect; |
| import android.media.videoeditor.EffectColor; |
| import android.media.videoeditor.EffectKenBurns; |
| import android.media.videoeditor.ExtractAudioWaveformProgressListener; |
| import android.media.videoeditor.MediaImageItem; |
| import android.media.videoeditor.MediaItem; |
| import android.media.videoeditor.MediaProperties; |
| import android.media.videoeditor.MediaVideoItem; |
| import android.media.videoeditor.Overlay; |
| import android.media.videoeditor.OverlayFrame; |
| import android.media.videoeditor.Transition; |
| import android.media.videoeditor.TransitionAlpha; |
| import android.media.videoeditor.TransitionCrossfade; |
| import android.media.videoeditor.TransitionFadeBlack; |
| import android.media.videoeditor.TransitionSliding; |
| import android.media.videoeditor.VideoEditor; |
| import android.media.videoeditor.VideoEditorFactory; |
| import android.media.videoeditor.WaveformData; |
| import android.media.videoeditor.MediaItem.GetThumbnailListCallback; |
| import android.media.videoeditor.VideoEditor.ExportProgressListener; |
| import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.provider.MediaStore; |
| import android.provider.MediaStore.Audio; |
| import android.provider.MediaStore.Images; |
| import android.provider.MediaStore.Video; |
| import android.util.Log; |
| |
| import com.android.videoeditor.R; |
| import com.android.videoeditor.util.FileUtils; |
| import com.android.videoeditor.util.ImageUtils; |
| import com.android.videoeditor.util.MediaItemUtils; |
| import com.android.videoeditor.util.StringUtils; |
| |
| /** |
| * VideoEditor service API |
| */ |
| public class ApiService extends Service { |
| // Logging |
| private static final String TAG = "VEApiService"; |
| |
| // Additional updates |
| public static final int ACTION_UPDATE_FRAME = MediaProcessingProgressListener.ACTION_DECODE + 100; |
| public static final int ACTION_NO_FRAME_UPDATE = MediaProcessingProgressListener.ACTION_DECODE + 101; |
| |
| // Parameters |
| private static final String PARAM_OP = "op"; |
| private static final String PARAM_REQUEST_ID = "rid"; |
| private static final String PARAM_PROJECT_PATH = "project"; |
| private static final String PARAM_FILENAME = "filename"; |
| private static final String PARAM_STORYBOARD_ITEM_ID = "item_id"; |
| private static final String PARAM_RELATIVE_STORYBOARD_ITEM_ID = "r_item_id"; |
| private static final String PARAM_PROGRESS_VALUE = "prog_value"; |
| private static final String PARAM_EXCEPTION = "ex"; |
| private static final String PARAM_START_TIME = "s_time"; |
| private static final String PARAM_END_TIME = "e_time"; |
| private static final String PARAM_DURATION = "duration"; |
| private static final String PARAM_WIDTH = "width"; |
| private static final String PARAM_HEIGHT = "height"; |
| private static final String PARAM_BITRATE = "bitrate"; |
| private static final String PARAM_MEDIA_ITEM_RENDERING_MODE = "rm"; |
| private static final String PARAM_MEDIA_ITEM_START_RECT = "start_rect"; |
| private static final String PARAM_MEDIA_ITEM_END_RECT = "end_rect"; |
| private static final String PARAM_EFFECT_TYPE = "e_type"; |
| private static final String PARAM_EFFECT_PARAM = "e_param"; |
| private static final String PARAM_TRANSITION_BEHAVIOR = "behavior"; |
| private static final String PARAM_TRANSITION_MASK = "t_mask"; |
| private static final String PARAM_TRANSITION_BLENDING = "t_blending"; |
| private static final String PARAM_TRANSITION_INVERT = "t_invert"; |
| private static final String PARAM_TRANSITION_DIRECTION = "t_dir"; |
| private static final String PARAM_INTENT = "req_intent"; |
| private static final String PARAM_PROJECT_NAME = "name"; |
| private static final String PARAM_MOVIES_FILENAMES = "movies"; |
| private static final String PARAM_PHOTOS_FILENAMES = "images"; |
| private static final String PARAM_ASPECT_RATIO = "aspect_ratio"; |
| private static final String PARAM_BEGIN_BOUNDARY = "b_boundary"; |
| private static final String PARAM_END_BOUNDARY = "e_boundary"; |
| private static final String PARAM_ATTRIBUTES = "attributes"; |
| private static final String PARAM_VOLUME = "volume"; |
| private static final String PARAM_LOOP = "loop"; |
| private static final String PARAM_MUTE = "mute"; |
| private static final String PARAM_DUCK = "duck"; |
| private static final String PARAM_MOVIE_URI = "uri"; |
| private static final String PARAM_THEME = "theme"; |
| private static final String PARAM_ACTION = "action"; |
| private static final String PARAM_COUNT = "count"; |
| private static final String PARAM_TOKEN = "token"; |
| private static final String PARAM_INDICES = "indices"; |
| private static final String PARAM_CANCELLED = "cancelled"; |
| |
| // Operations |
| private static final int OP_VIDEO_EDITOR_CREATE = 1; |
| private static final int OP_VIDEO_EDITOR_LOAD = 2; |
| private static final int OP_VIDEO_EDITOR_SAVE = 3; |
| private static final int OP_VIDEO_EDITOR_EXPORT = 4; |
| private static final int OP_VIDEO_EDITOR_CANCEL_EXPORT = 5; |
| private static final int OP_VIDEO_EDITOR_EXPORT_STATUS = 6; |
| private static final int OP_VIDEO_EDITOR_RELEASE = 8; |
| private static final int OP_VIDEO_EDITOR_DELETE = 9; |
| private static final int OP_VIDEO_EDITOR_SET_ASPECT_RATIO = 10; |
| private static final int OP_VIDEO_EDITOR_APPLY_THEME = 11; |
| private static final int OP_VIDEO_EDITOR_GENERATE_PREVIEW_PROGRESS = 12; |
| private static final int OP_VIDEO_EDITOR_LOAD_PROJECTS = 13; |
| |
| private static final int OP_MEDIA_ITEM_ADD_VIDEO_URI = 100; |
| private static final int OP_MEDIA_ITEM_ADD_IMAGE_URI = 101; |
| private static final int OP_MEDIA_ITEM_MOVE = 102; |
| private static final int OP_MEDIA_ITEM_REMOVE = 103; |
| private static final int OP_MEDIA_ITEM_SET_RENDERING_MODE = 104; |
| private static final int OP_MEDIA_ITEM_SET_DURATION = 105; |
| private static final int OP_MEDIA_ITEM_SET_BOUNDARIES = 106; |
| private static final int OP_MEDIA_ITEM_SET_VOLUME = 107; |
| private static final int OP_MEDIA_ITEM_SET_MUTE = 108; |
| private static final int OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM = 109; |
| private static final int OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM_STATUS = 110; |
| private static final int OP_MEDIA_ITEM_GET_THUMBNAILS = 112; |
| private static final int OP_MEDIA_ITEM_LOAD = 113; |
| private static final int OP_MEDIA_ITEM_LOAD_STATUS = 114; |
| |
| private static final int OP_EFFECT_ADD_COLOR = 200; |
| private static final int OP_EFFECT_ADD_IMAGE_KEN_BURNS = 201; |
| private static final int OP_EFFECT_REMOVE = 202; |
| |
| private static final int OP_TRANSITION_INSERT_ALPHA = 300; |
| private static final int OP_TRANSITION_INSERT_CROSSFADE = 301; |
| private static final int OP_TRANSITION_INSERT_FADE_BLACK = 302; |
| private static final int OP_TRANSITION_INSERT_SLIDING = 303; |
| private static final int OP_TRANSITION_REMOVE = 304; |
| private static final int OP_TRANSITION_SET_DURATION = 305; |
| private static final int OP_TRANSITION_GET_THUMBNAIL = 306; |
| |
| private static final int OP_OVERLAY_ADD = 400; |
| private static final int OP_OVERLAY_REMOVE = 401; |
| private static final int OP_OVERLAY_SET_START_TIME = 402; |
| private static final int OP_OVERLAY_SET_DURATION = 403; |
| private static final int OP_OVERLAY_SET_ATTRIBUTES = 404; |
| |
| private static final int OP_AUDIO_TRACK_ADD = 500; |
| private static final int OP_AUDIO_TRACK_REMOVE = 501; |
| private static final int OP_AUDIO_TRACK_SET_VOLUME = 502; |
| private static final int OP_AUDIO_TRACK_SET_MUTE = 503; |
| private static final int OP_AUDIO_TRACK_SET_BOUNDARIES = 505; |
| private static final int OP_AUDIO_TRACK_SET_LOOP = 506; |
| private static final int OP_AUDIO_TRACK_SET_DUCK = 507; |
| private static final int OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM = 508; |
| private static final int OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM_STATUS = 509; |
| |
| private static final int DUCK_THRESHOLD = 20; |
| private static final int DUCK_TRACK_VOLUME = 65; |
| // The default audio track volume |
| private static final int DEFAULT_AUDIO_TRACK_VOLUME = 50; |
| |
| // Static member variables |
| private static final Map<String, Intent> mPendingIntents = new HashMap<String, Intent>(); |
| private static final List<ApiServiceListener> mListeners = new ArrayList<ApiServiceListener>(); |
| private static final IntentPool mIntentPool = new IntentPool(8); |
| private static VideoEditorProject mVideoProject; |
| private static VideoEditor mVideoEditor; |
| private static ServiceMediaProcessingProgressListener mGeneratePreviewListener; |
| private static volatile boolean mExportCancelled; |
| |
| private IntentProcessor mVideoThread; |
| private IntentProcessor mAudioThread; |
| private IntentProcessor mThumbnailThread; |
| private Handler mHandler; |
| |
| private final Runnable mStopRunnable = new Runnable() { |
| @Override |
| public void run() { |
| if (mPendingIntents.size() == 0) { |
| logd("Stop runnable: Stopping service"); |
| stopSelf(); |
| } |
| } |
| }; |
| |
| /** |
| * Generate preview listener |
| */ |
| private final class ServiceMediaProcessingProgressListener |
| implements VideoEditor.MediaProcessingProgressListener { |
| // Instance variables |
| private final String mProjectPath; |
| |
| /** |
| * Constructor |
| * |
| * @param projectPath The project path |
| */ |
| public ServiceMediaProcessingProgressListener(String projectPath) { |
| mProjectPath = projectPath; |
| } |
| |
| @Override |
| public void onProgress(Object item, int action, int progress) { |
| final Intent intent = mIntentPool.get(); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_GENERATE_PREVIEW_PROGRESS); |
| intent.putExtra(PARAM_PROJECT_PATH, mProjectPath); |
| intent.putExtra(PARAM_ACTION, action); |
| intent.putExtra(PARAM_PROGRESS_VALUE, progress); |
| |
| if (item == null) { // Last callback uses null |
| } else if (item instanceof MediaItem) { |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, ((MediaItem)item).getId()); |
| intent.putExtra(PARAM_ATTRIBUTES, MediaItem.class.getCanonicalName()); |
| } else if (item instanceof Transition) { |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, ((Transition)item).getId()); |
| intent.putExtra(PARAM_ATTRIBUTES, Transition.class.getCanonicalName()); |
| } else if (item instanceof AudioTrack) { |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, ((AudioTrack)item).getId()); |
| intent.putExtra(PARAM_ATTRIBUTES, AudioTrack.class.getCanonicalName()); |
| } else { |
| Log.w(TAG, "Unsupported storyboard item type: " + item.getClass()); |
| return; |
| } |
| |
| completeRequest(intent, null, null, null, null, true); |
| } |
| } |
| |
| /** |
| * @return A unique id |
| */ |
| public static String generateId() { |
| return StringUtils.randomString(6); |
| } |
| |
| /** |
| * Register a listener |
| * |
| * @param listener The listener |
| */ |
| public static void registerListener(ApiServiceListener listener) { |
| mListeners.add(listener); |
| } |
| |
| /** |
| * Unregister a listener |
| * |
| * @param listener The listener |
| */ |
| public static void unregisterListener(ApiServiceListener listener) { |
| mListeners.remove(listener); |
| } |
| |
| /** |
| * Load the projects |
| * |
| * @param context The context |
| */ |
| public static void loadProjects(Context context) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_LOAD_PROJECTS); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Create a new VideoEditor project |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param projectName The project name |
| * @param movies The array of movie file names to add to the newly |
| * created project |
| * @param photos The array of photo file names to add to the newly |
| * created project |
| * @param themeType The theme type |
| */ |
| public static void createVideoEditor(Context context, String projectPath, String projectName, |
| String[] movies, String[] photos, String themeType) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_CREATE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_PROJECT_NAME, projectName); |
| intent.putExtra(PARAM_MOVIES_FILENAMES, movies); |
| intent.putExtra(PARAM_PHOTOS_FILENAMES, photos); |
| intent.putExtra(PARAM_THEME, themeType); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Create a new VideoEditor project |
| * |
| * @param context The context |
| * @param projectPath The project path |
| */ |
| public static void loadVideoEditor(Context context, String projectPath) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_LOAD); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Export the VideoEditor movie |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param filename The export filename |
| * @param height The output movie height |
| * @param bitrate The output movie bitrate |
| */ |
| public static void exportVideoEditor(Context context, String projectPath, String filename, |
| int height, int bitrate) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_EXPORT); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_FILENAME, filename); |
| intent.putExtra(PARAM_HEIGHT, height); |
| intent.putExtra(PARAM_BITRATE, bitrate); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Check if export is pending |
| * |
| * @param projectPath The project path |
| * @param filename The export filename |
| * |
| * @return true if the export is pending |
| */ |
| public static boolean isVideoEditorExportPending(String projectPath, String filename) { |
| for (Intent intent : mPendingIntents.values()) { |
| final int op = intent.getIntExtra(PARAM_OP, -1); |
| if (op == OP_VIDEO_EDITOR_EXPORT) { |
| String pp = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (pp.equals(projectPath)) { |
| String fn = intent.getStringExtra(PARAM_FILENAME); |
| if (fn.equals(filename)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Cancel the export of the specified VideoEditor movie |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param filename The export filename |
| */ |
| public static void cancelExportVideoEditor(Context context, String projectPath, |
| String filename) { |
| mExportCancelled = true; |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_CANCEL_EXPORT); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_FILENAME, filename); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Change the aspect ratio |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param aspectRatio The aspect ratio |
| */ |
| public static void setAspectRatio(Context context, String projectPath, int aspectRatio) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_SET_ASPECT_RATIO); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_ASPECT_RATIO, aspectRatio); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Apply a theme |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param theme The theme |
| */ |
| public static void applyTheme(Context context, String projectPath, String theme) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_APPLY_THEME); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_THEME, theme); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Checks if the service is busy modifying the timeline. While |
| * the video editor is busy the application should not attempt |
| * to preview the movie. |
| * |
| * @param projectPath The project path |
| * |
| * @return {@code true} if the video editor is modifying the timeline |
| */ |
| public static boolean isProjectBeingEdited(String projectPath) { |
| for (Intent intent : mPendingIntents.values()) { |
| final int op = intent.getIntExtra(PARAM_OP, -1); |
| switch (op) { |
| // When these operations are pending the video editor is not busy. |
| case OP_VIDEO_EDITOR_LOAD_PROJECTS: |
| case OP_VIDEO_EDITOR_SAVE: |
| case OP_MEDIA_ITEM_SET_VOLUME: |
| case OP_MEDIA_ITEM_SET_MUTE: |
| case OP_MEDIA_ITEM_GET_THUMBNAILS: |
| case OP_MEDIA_ITEM_LOAD: |
| case OP_TRANSITION_GET_THUMBNAIL: |
| case OP_AUDIO_TRACK_SET_VOLUME: |
| case OP_AUDIO_TRACK_SET_MUTE: { |
| break; |
| } |
| |
| default: { |
| final String pp = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (pp != null && pp.equals(projectPath)) { |
| return true; |
| } |
| break; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Save the VideoEditor project |
| * |
| * @param context The context |
| * @param projectPath The project path |
| */ |
| public static void saveVideoEditor(Context context, String projectPath) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_SAVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Release the VideoEditor project |
| * |
| * @param context The context |
| * @param projectPath The project path |
| */ |
| public static void releaseVideoEditor(Context context, String projectPath) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_RELEASE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Delete the project specified by the project path |
| * |
| * @param context The context |
| * @param projectPath The project path |
| */ |
| public static void deleteProject(Context context, String projectPath) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_DELETE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Add a new video media item after the specified media item id |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The mediaItem id |
| * @param afterMediaItemId The id of the media item preceding the media item |
| * @param uri The media item URI |
| * @param renderingMode The rendering mode |
| * @param themeId The theme id |
| */ |
| public static void addMediaItemVideoUri(Context context, String projectPath, |
| String mediaItemId, String afterMediaItemId, Uri uri, int renderingMode, |
| String themeId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_ADD_VIDEO_URI); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_FILENAME, uri); |
| intent.putExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, renderingMode); |
| intent.putExtra(PARAM_THEME, themeId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Add a new image media item after the specified media item id |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The mediaItem id |
| * @param afterMediaItemId The id of the media item preceding the media item |
| * @param uri The media item URI |
| * @param renderingMode The rendering mode |
| * @param durationMs The duration of the item (for images only, ignored for videos) |
| * @param themeId The theme id |
| */ |
| public static void addMediaItemImageUri(Context context, String projectPath, |
| String mediaItemId, String afterMediaItemId, Uri uri, int renderingMode, |
| long durationMs, String themeId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_ADD_IMAGE_URI); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_FILENAME, uri); |
| intent.putExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, renderingMode); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_THEME, themeId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Download or make a copy of an image from the specified URI |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param uri The media item URI |
| * @param mimeType The MIME type |
| */ |
| public static void loadMediaItem(Context context, String projectPath, Uri uri, |
| String mimeType) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_LOAD); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_FILENAME, uri); |
| intent.putExtra(PARAM_ATTRIBUTES, mimeType); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Move a media item after the specified media id |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item to move |
| * @param afterMediaItemId The id of the relative media item |
| * @param themeId The theme id |
| */ |
| public static void moveMediaItem(Context context, String projectPath, |
| String mediaItemId, String afterMediaItemId, String themeId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_MOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_THEME, themeId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Remove a media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item to remove |
| * @param themeId The theme id |
| */ |
| public static void removeMediaItem(Context context, String projectPath, String mediaItemId, |
| String themeId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_REMOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_THEME, themeId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the rendering mode for a media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| */ |
| public static void setMediaItemRenderingMode(Context context, String projectPath, |
| String mediaItemId, int renderingMode) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_SET_RENDERING_MODE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, renderingMode); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Get the thumbnails of the specified size |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| * @param width The width |
| * @param height The height |
| * @param startMs The start time in milliseconds |
| * @param endMs The end time in milliseconds |
| * @param count The number of thumbnails |
| */ |
| public static void getMediaItemThumbnails(Context context, |
| String projectPath, String mediaItemId, int width, int height, |
| long startMs, long endMs, int count, int token, int[] indices) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_GET_THUMBNAILS); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_WIDTH, width); |
| intent.putExtra(PARAM_HEIGHT, height); |
| intent.putExtra(PARAM_START_TIME, startMs); |
| intent.putExtra(PARAM_END_TIME, endMs); |
| intent.putExtra(PARAM_COUNT, count); |
| intent.putExtra(PARAM_TOKEN, token); |
| intent.putExtra(PARAM_INDICES, indices); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the media item duration |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| * @param durationMs The media item duration |
| */ |
| public static void setMediaItemDuration(Context context, String projectPath, |
| String mediaItemId, long durationMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_SET_DURATION); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the media item boundaries |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| * @param beginBoundaryMs The media item begin boundary |
| * @param endBoundaryMs The media item end boundary |
| */ |
| public static void setMediaItemBoundaries(Context context, String projectPath, |
| String mediaItemId, long beginBoundaryMs, long endBoundaryMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_SET_BOUNDARIES); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_BEGIN_BOUNDARY, beginBoundaryMs); |
| intent.putExtra(PARAM_END_BOUNDARY, endBoundaryMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the media item volume (MediaVideoItem only) |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| * @param volumePercentage The volume |
| */ |
| public static void setMediaItemVolume(Context context, String projectPath, |
| String mediaItemId, int volumePercentage) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_SET_VOLUME); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_VOLUME, volumePercentage); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Mute/unmute the media item (MediaVideoItem only) |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| * @param muted true to mute the media item |
| */ |
| public static void setMediaItemMute(Context context, String projectPath, String mediaItemId, |
| boolean muted) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_SET_MUTE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_MUTE, muted); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Extract the media item audio waveform |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The id of the media item |
| */ |
| public static void extractMediaItemAudioWaveform(Context context, String projectPath, |
| String mediaItemId) { |
| if (isMediaItemAudioWaveformPending(projectPath, mediaItemId)) { |
| return; |
| } |
| |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItemId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Check if extract audio waveform is pending for the specified MediaItem |
| * |
| * @param projectPath The project path |
| * @param mediaItemId The MediaItem id |
| * |
| * @return true if the extract audio waveform is pending |
| */ |
| public static boolean isMediaItemAudioWaveformPending(String projectPath, String mediaItemId) { |
| for (Intent intent : mPendingIntents.values()) { |
| int op = intent.getIntExtra(PARAM_OP, -1); |
| if (op == OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM) { |
| String pp = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (pp.equals(projectPath)) { |
| String mid = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| if (mid.equals(mediaItemId)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Insert an alpha transition after the specified media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param afterMediaItemId Insert the transition after the media item with this id |
| * @param transitionId The transition id |
| * @param durationMs The duration in milliseconds |
| * @param behavior The transition behavior |
| * @param maskRawResourceId The mask raw resource id |
| * @param blending The transition blending |
| * @param invert The transition invert |
| */ |
| public static void insertAlphaTransition(Context context, String projectPath, |
| String afterMediaItemId, String transitionId, long durationMs, int behavior, |
| int maskRawResourceId, int blending, boolean invert) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_INSERT_ALPHA); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_TRANSITION_BEHAVIOR, behavior); |
| intent.putExtra(PARAM_TRANSITION_MASK, maskRawResourceId); |
| intent.putExtra(PARAM_TRANSITION_BLENDING, blending); |
| intent.putExtra(PARAM_TRANSITION_INVERT, invert); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Insert an crossfade transition after the specified media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param afterMediaItemId Insert the transition after the media item with this id |
| * @param transitionId The transition id |
| * @param durationMs The duration in milliseconds |
| * @param behavior The transition behavior |
| */ |
| public static void insertCrossfadeTransition(Context context, String projectPath, |
| String afterMediaItemId, String transitionId, long durationMs, int behavior) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_INSERT_CROSSFADE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_TRANSITION_BEHAVIOR, behavior); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Insert a fade-to-black transition after the specified media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param afterMediaItemId Insert the transition after the media item with this id |
| * @param transitionId The transition id |
| * @param durationMs The duration in milliseconds |
| * @param behavior The transition behavior |
| */ |
| public static void insertFadeBlackTransition(Context context, String projectPath, |
| String afterMediaItemId, String transitionId, long durationMs, int behavior) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_INSERT_FADE_BLACK); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_TRANSITION_BEHAVIOR, behavior); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Insert a sliding transition after the specified media item |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param afterMediaItemId Insert the transition after the media item with this id |
| * @param transitionId The transition id |
| * @param durationMs The duration in milliseconds |
| * @param behavior The transition behavior |
| * @param direction The slide direction |
| */ |
| public static void insertSlidingTransition(Context context, String projectPath, |
| String afterMediaItemId, String transitionId, long durationMs, int behavior, |
| int direction) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_INSERT_SLIDING); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, afterMediaItemId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_TRANSITION_BEHAVIOR, behavior); |
| intent.putExtra(PARAM_TRANSITION_DIRECTION, direction); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Remove a transition |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param transitionId The id of the transition to remove |
| */ |
| public static void removeTransition(Context context, String projectPath, String transitionId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_REMOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set a transition duration |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param transitionId The id of the transition |
| * @param durationMs The transition duration |
| */ |
| public static void setTransitionDuration(Context context, String projectPath, |
| String transitionId, long durationMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_SET_DURATION); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Get the thumbnail of the specified height |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param transitionId The id of the transition |
| * @param height The height |
| */ |
| public static void getTransitionThumbnails(Context context, String projectPath, |
| String transitionId, int height) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_TRANSITION_GET_THUMBNAIL); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, transitionId); |
| intent.putExtra(PARAM_HEIGHT, height); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Check if the transition thumbnailing is in progress |
| * |
| * @param projectPath The project path |
| * @param transitionId The id of the transition |
| * |
| * @return true if the transition thumbnailing is in progress |
| */ |
| public static boolean isTransitionThumbnailsPending(String projectPath, String transitionId) { |
| for (Intent intent : mPendingIntents.values()) { |
| int op = intent.getIntExtra(PARAM_OP, -1); |
| if (op == OP_TRANSITION_GET_THUMBNAIL) { |
| String pp = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (pp.equals(projectPath)) { |
| String mid = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| if (mid.equals(transitionId)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Add a color effect |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param effectId The effect id |
| * @param startTimeMs The start time in milliseconds |
| * @param durationMs The duration in milliseconds |
| * @param type The effect type |
| * @param param The effect param (if any) |
| */ |
| public static void addEffectColor(Context context, String projectPath, String mediaItemId, |
| String effectId, long startTimeMs, long durationMs, int type, int param) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_EFFECT_ADD_COLOR); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, effectId); |
| intent.putExtra(PARAM_START_TIME, startTimeMs); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_EFFECT_TYPE, type); |
| intent.putExtra(PARAM_EFFECT_PARAM, param); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Add a Ken Burns effect |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param effectId The effect id |
| * @param startTimeMs The start time |
| * @param durationMs The duration of the item |
| * @param startRect The start rectangle for the Ken Burns effect |
| * @param endRect The end rectangle for the Ken Burns effect |
| */ |
| public static void addEffectKenBurns(Context context, String projectPath, |
| String mediaItemId, String effectId, long startTimeMs, long durationMs, |
| Rect startRect, Rect endRect) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_EFFECT_ADD_IMAGE_KEN_BURNS); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, effectId); |
| intent.putExtra(PARAM_START_TIME, startTimeMs); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_MEDIA_ITEM_START_RECT, startRect); |
| intent.putExtra(PARAM_MEDIA_ITEM_END_RECT, endRect); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Remove an effect |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param effectId The id of the effect to remove |
| */ |
| public static void removeEffect(Context context, String projectPath, String mediaItemId, |
| String effectId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_EFFECT_REMOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, effectId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Add an overlay |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param userAttributes The overlay user attributes |
| * @param startTimeMs The start time in milliseconds |
| * @param durationMs The duration in milliseconds |
| */ |
| public static void addOverlay(Context context, String projectPath, String mediaItemId, |
| String overlayId, Bundle userAttributes, long startTimeMs, long durationMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_OVERLAY_ADD); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, overlayId); |
| intent.putExtra(PARAM_START_TIME, startTimeMs); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| intent.putExtra(PARAM_ATTRIBUTES, userAttributes); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Remove an overlay |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param overlayId The id of the overlay to remove |
| */ |
| public static void removeOverlay(Context context, String projectPath, String mediaItemId, |
| String overlayId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_OVERLAY_REMOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, overlayId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the start time of an overlay |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param overlayId The id of the overlay |
| * @param startTimeMs The start time in milliseconds |
| */ |
| public static void setOverlayStartTime(Context context, String projectPath, String mediaItemId, |
| String overlayId, long startTimeMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_OVERLAY_SET_START_TIME); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, overlayId); |
| intent.putExtra(PARAM_START_TIME, startTimeMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the duration of an overlay |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param overlayId The id of the overlay |
| * @param durationMs The duration in milliseconds |
| */ |
| public static void setOverlayDuration(Context context, String projectPath, String mediaItemId, |
| String overlayId, long durationMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_OVERLAY_SET_DURATION); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, overlayId); |
| intent.putExtra(PARAM_DURATION, durationMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the user attributes of an overlay |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param mediaItemId The media item id |
| * @param overlayId The id of the overlay |
| * @param userAttributes The user attributes |
| */ |
| public static void setOverlayUserAttributes(Context context, String projectPath, |
| String mediaItemId, String overlayId, Bundle userAttributes) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_OVERLAY_SET_ATTRIBUTES); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID, mediaItemId); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, overlayId); |
| intent.putExtra(PARAM_ATTRIBUTES, userAttributes); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Add an audio track |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param id The audio track id |
| * @param uri The audio track URI |
| * @param loop true to loop the audio track |
| */ |
| public static void addAudioTrack(Context context, String projectPath, String id, Uri uri, |
| boolean loop) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_ADD); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, id); |
| intent.putExtra(PARAM_FILENAME, uri); |
| intent.putExtra(PARAM_LOOP, loop); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Remove an audio track from the storyboard timeline |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track to remove |
| */ |
| public static void removeAudioTrack(Context context, String projectPath, String audioTrackId) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_REMOVE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the audio track boundaries |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| * @param beginBoundaryMs The audio track begin boundary |
| * @param endBoundaryMs The audio track end boundary |
| */ |
| public static void setAudioTrackBoundaries(Context context, String projectPath, |
| String audioTrackId, long beginBoundaryMs, long endBoundaryMs) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_SET_BOUNDARIES); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| intent.putExtra(PARAM_BEGIN_BOUNDARY, beginBoundaryMs); |
| intent.putExtra(PARAM_END_BOUNDARY, endBoundaryMs); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the loop flag for an audio track |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| * @param loop true to loop audio |
| */ |
| public static void setAudioTrackLoop(Context context, String projectPath, String audioTrackId, |
| boolean loop) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_SET_LOOP); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| intent.putExtra(PARAM_LOOP, loop); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the duck flag for an audio track |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| * @param duck true to enable ducking |
| */ |
| public static void setAudioTrackDuck(Context context, String projectPath, String audioTrackId, |
| boolean duck) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_SET_DUCK); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| intent.putExtra(PARAM_DUCK, duck); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Set the audio track volume |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| * @param volumePercentage The audio track volume (in percentage) |
| */ |
| public static void setAudioTrackVolume(Context context, String projectPath, |
| String audioTrackId, int volumePercentage) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_SET_VOLUME); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| intent.putExtra(PARAM_VOLUME, volumePercentage); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Mute/unmute the audio track |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| * @param muted true to mute the audio track |
| */ |
| public static void setAudioTrackMute(Context context, String projectPath, String audioTrackId, |
| boolean muted) { |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_SET_MUTE); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| intent.putExtra(PARAM_MUTE, muted); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Extract the audio track audio waveform |
| * |
| * @param context The context |
| * @param projectPath The project path |
| * @param audioTrackId The id of the audio track |
| */ |
| public static void extractAudioTrackAudioWaveform(Context context, String projectPath, |
| String audioTrackId) { |
| if (isAudioTrackAudioWaveformPending(projectPath, audioTrackId)) { |
| return; |
| } |
| final Intent intent = mIntentPool.get(context, ApiService.class); |
| intent.putExtra(PARAM_OP, OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM); |
| intent.putExtra(PARAM_PROJECT_PATH, projectPath); |
| intent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrackId); |
| |
| startCommand(context, intent); |
| } |
| |
| /** |
| * Check if extract audio waveform is pending for the specified audio track |
| * |
| * @param projectPath The project path |
| * @param audioTrackId The audio track id |
| * |
| * @return true if the extract audio waveform is pending |
| */ |
| public static boolean isAudioTrackAudioWaveformPending(String projectPath, |
| String audioTrackId) { |
| for (Intent intent : mPendingIntents.values()) { |
| int op = intent.getIntExtra(PARAM_OP, -1); |
| if (op == OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM) { |
| String pp = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (pp.equals(projectPath)) { |
| String mid = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| if (mid.equals(audioTrackId)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Start the service (if it is not running) with the specified Intent |
| * |
| * @param context The context |
| * @param intent The intent |
| * |
| * @return The request id of the pending request |
| */ |
| private static String startCommand(Context context, Intent intent) { |
| final String requestId = StringUtils.randomString(8); |
| intent.putExtra(PARAM_REQUEST_ID, requestId); |
| mPendingIntents.put(requestId, intent); |
| |
| context.startService(intent); |
| |
| final String projectPath = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (projectPath != null) { |
| final boolean projectEdited = isProjectBeingEdited(projectPath); |
| if (projectEdited) { |
| for (ApiServiceListener listener : mListeners) { |
| listener.onProjectEditState(projectPath, projectEdited); |
| } |
| } |
| } |
| |
| return requestId; |
| } |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| mHandler = new Handler(Looper.getMainLooper()); |
| |
| mVideoThread = new IntentProcessor("VideoServiceThread"); |
| mVideoThread.start(); |
| |
| mAudioThread = new IntentProcessor("AudioServiceThread"); |
| mAudioThread.start(); |
| |
| mThumbnailThread = new IntentProcessor("ThumbnailServiceThread"); |
| mThumbnailThread.start(); |
| } |
| |
| @Override |
| public int onStartCommand(final Intent intent, int flags, int startId) { |
| final int op = intent.getIntExtra(PARAM_OP, -1); |
| switch(op) { |
| case OP_VIDEO_EDITOR_LOAD_PROJECTS: |
| case OP_VIDEO_EDITOR_CREATE: |
| case OP_VIDEO_EDITOR_LOAD: |
| case OP_VIDEO_EDITOR_SAVE: |
| case OP_VIDEO_EDITOR_RELEASE: |
| case OP_VIDEO_EDITOR_DELETE: |
| case OP_VIDEO_EDITOR_SET_ASPECT_RATIO: |
| case OP_VIDEO_EDITOR_APPLY_THEME: |
| case OP_VIDEO_EDITOR_EXPORT: |
| case OP_VIDEO_EDITOR_CANCEL_EXPORT: |
| case OP_VIDEO_EDITOR_EXPORT_STATUS: |
| |
| case OP_MEDIA_ITEM_ADD_VIDEO_URI: |
| case OP_MEDIA_ITEM_ADD_IMAGE_URI: |
| case OP_MEDIA_ITEM_MOVE: |
| case OP_MEDIA_ITEM_REMOVE: |
| case OP_MEDIA_ITEM_SET_RENDERING_MODE: |
| case OP_MEDIA_ITEM_SET_DURATION: |
| case OP_MEDIA_ITEM_SET_BOUNDARIES: |
| case OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM: |
| case OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM_STATUS: |
| case OP_MEDIA_ITEM_LOAD: |
| case OP_MEDIA_ITEM_LOAD_STATUS: |
| |
| case OP_EFFECT_ADD_COLOR: |
| case OP_EFFECT_ADD_IMAGE_KEN_BURNS: |
| case OP_EFFECT_REMOVE: |
| |
| case OP_TRANSITION_INSERT_ALPHA: |
| case OP_TRANSITION_INSERT_CROSSFADE: |
| case OP_TRANSITION_INSERT_FADE_BLACK: |
| case OP_TRANSITION_INSERT_SLIDING: |
| case OP_TRANSITION_REMOVE: |
| case OP_TRANSITION_SET_DURATION: |
| |
| case OP_OVERLAY_ADD: |
| case OP_OVERLAY_REMOVE: |
| case OP_OVERLAY_SET_START_TIME: |
| case OP_OVERLAY_SET_DURATION: |
| case OP_OVERLAY_SET_ATTRIBUTES: |
| |
| case OP_AUDIO_TRACK_ADD: |
| case OP_AUDIO_TRACK_REMOVE: |
| case OP_AUDIO_TRACK_SET_BOUNDARIES: |
| case OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM: |
| case OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM_STATUS: { |
| mVideoThread.submit(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_GET_THUMBNAIL: { |
| mThumbnailThread.submit(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_GET_THUMBNAILS: { |
| final String projectPath = intent.getStringExtra(PARAM_PROJECT_PATH); |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final int token = intent.getIntExtra(PARAM_TOKEN, 0); |
| // Cancel any pending thumbnail request for the same media item |
| // but with a different token |
| Iterator<Intent> intentQueueIterator = mThumbnailThread.getIntentQueueIterator(); |
| while (intentQueueIterator.hasNext()) { |
| Intent qIntent = intentQueueIterator.next(); |
| int opi = qIntent.getIntExtra(PARAM_OP, -1); |
| String pp = qIntent.getStringExtra(PARAM_PROJECT_PATH); |
| String mid = qIntent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| int tk = qIntent.getIntExtra(PARAM_TOKEN, 0); |
| if (opi == op && pp.equals(projectPath) && mid.equals(mediaItemId) |
| && tk != token) { |
| boolean canceled = mThumbnailThread.cancel(qIntent); |
| if (canceled) { |
| logd("Canceled operation: " + op + " for media item" + mediaItemId); |
| mPendingIntents.remove(qIntent.getStringExtra(PARAM_REQUEST_ID)); |
| mIntentPool.put(qIntent); |
| } |
| break; |
| } |
| } |
| mThumbnailThread.submit(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_VOLUME: |
| case OP_MEDIA_ITEM_SET_MUTE: |
| |
| case OP_AUDIO_TRACK_SET_VOLUME: |
| case OP_AUDIO_TRACK_SET_MUTE: |
| case OP_AUDIO_TRACK_SET_LOOP: |
| case OP_AUDIO_TRACK_SET_DUCK: { |
| mAudioThread.submit(intent); |
| break; |
| } |
| |
| default: { |
| Log.e(TAG, "No thread assigned: " + op); |
| break; |
| } |
| } |
| |
| return START_NOT_STICKY; |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| |
| if (mThumbnailThread != null) { |
| mThumbnailThread.quit(); |
| mThumbnailThread = null; |
| } |
| |
| if (mAudioThread != null) { |
| mAudioThread.quit(); |
| mAudioThread = null; |
| } |
| |
| if (mVideoThread != null) { |
| mVideoThread.quit(); |
| mVideoThread = null; |
| } |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return null; |
| } |
| |
| /** |
| * Process the intent |
| * |
| * @param intent The intent |
| */ |
| public void processIntent(final Intent intent) { |
| final int op = intent.getIntExtra(PARAM_OP, -1); |
| VideoEditor videoEditor = null; |
| try { |
| final String projectPath = intent.getStringExtra(PARAM_PROJECT_PATH); |
| // Check if the project path matches the current VideoEditor project |
| switch (op) { |
| case OP_VIDEO_EDITOR_LOAD_PROJECTS: |
| case OP_VIDEO_EDITOR_CREATE: |
| case OP_VIDEO_EDITOR_LOAD: |
| case OP_VIDEO_EDITOR_DELETE: { |
| break; |
| } |
| |
| default: { |
| videoEditor = getVideoEditor(projectPath); |
| if (videoEditor == null) { |
| throw new IllegalArgumentException("Invalid project path: " |
| + projectPath + " for operation: " + op); |
| } |
| break; |
| } |
| } |
| |
| switch (op) { |
| case OP_VIDEO_EDITOR_LOAD_PROJECTS: { |
| logd("OP_LOAD_PROJECTS"); |
| final List<VideoEditorProject> projects = new ArrayList<VideoEditorProject>(); |
| final File dir = FileUtils.getProjectsRootDir(getApplicationContext()); |
| if (dir != null) { |
| // Collect valid projects (project with valid metadata). |
| final File[] files = dir.listFiles(); |
| if (files != null) { |
| for (int i = 0; i < files.length; i++) { |
| if (files[i].isDirectory()) { |
| final String pp = files[i].getAbsolutePath(); |
| try { |
| projects.add(VideoEditorProject.fromXml(null, pp)); |
| } catch (FileNotFoundException ex) { |
| Log.w(TAG, "processIntent: Project file not found: " + pp); |
| FileUtils.deleteDir(new File(pp)); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| } |
| |
| if (projects.size() > 0) { |
| // Sort the projects in order of "last saved" |
| Collections.sort(projects, new Comparator<VideoEditorProject>() { |
| @Override |
| public int compare(VideoEditorProject object1, |
| VideoEditorProject object2) { |
| if (object1.getLastSaved() > object2.getLastSaved()) { |
| return -1; |
| } else if (object1.getLastSaved() == object2.getLastSaved()) { |
| return 0; |
| } else { |
| return 1; |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| completeRequest(intent, videoEditor, null, projects, null, true); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_CREATE: { |
| logd("OP_VIDEO_EDITOR_CREATE: " + projectPath); |
| |
| try { |
| // Release the current video editor if any |
| releaseEditor(); |
| |
| videoEditor = VideoEditorFactory.create(projectPath); |
| |
| // Add the movies to the timeline |
| final String[] movies = intent.getStringArrayExtra(PARAM_MOVIES_FILENAMES); |
| for (int i = 0; i < movies.length; i++) { |
| final MediaItem mediaItem = new MediaVideoItem(videoEditor, |
| generateId(), movies[i], |
| MediaItem.RENDERING_MODE_BLACK_BORDER); |
| videoEditor.addMediaItem(mediaItem); |
| } |
| |
| // Add the photos to the timeline |
| final String[] photos = intent.getStringArrayExtra(PARAM_PHOTOS_FILENAMES); |
| for (int i = 0; i < photos.length; i++) { |
| final MediaItem mediaItem = new MediaImageItem(videoEditor, |
| generateId(), photos[i], |
| MediaItemUtils.getDefaultImageDuration(), |
| MediaItem.RENDERING_MODE_BLACK_BORDER); |
| videoEditor.addMediaItem(mediaItem); |
| } |
| |
| // Create the project |
| final String projectName = intent.getStringExtra(PARAM_PROJECT_NAME); |
| final String themeId = intent.getStringExtra(PARAM_THEME); |
| if (themeId != null) { |
| applyThemeToMovie(videoEditor, themeId); |
| } |
| |
| // Set the aspect ratio to the aspect ratio of the first item |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| if (mediaItems.size() > 0) { |
| videoEditor.setAspectRatio(mediaItems.get(0).getAspectRatio()); |
| } |
| |
| // Create the video editor project |
| final VideoEditorProject videoProject = new VideoEditorProject( |
| videoEditor, projectPath, projectName, System.currentTimeMillis(), |
| 0, 0, VideoEditorProject.DEFAULT_ZOOM_LEVEL, null, themeId, null); |
| videoProject.setMediaItems(copyMediaItems( |
| videoEditor.getAllMediaItems())); |
| videoProject.setAudioTracks(copyAudioTracks( |
| videoEditor.getAllAudioTracks())); |
| |
| // Make this project the current project |
| mVideoEditor = videoEditor; |
| mGeneratePreviewListener = new ServiceMediaProcessingProgressListener( |
| projectPath); |
| |
| completeRequest(intent, videoEditor, null, videoProject, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| } catch (Exception ex) { |
| if (videoEditor != null) { |
| videoEditor.release(); |
| videoEditor = null; |
| } |
| throw ex; |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_LOAD: { |
| videoEditor = releaseEditorNot(projectPath); |
| |
| if (videoEditor == null) { // The old project was released. |
| logd("OP_VIDEO_EDITOR_LOAD: Loading: " + projectPath); |
| try { |
| // Load the project |
| videoEditor = VideoEditorFactory.load(projectPath, false); |
| |
| // Load the video editor project |
| final VideoEditorProject videoProject = VideoEditorProject.fromXml( |
| videoEditor, projectPath); |
| videoProject.setMediaItems(copyMediaItems( |
| videoEditor.getAllMediaItems())); |
| videoProject.setAudioTracks(copyAudioTracks( |
| videoEditor.getAllAudioTracks())); |
| // Make this the current project |
| mVideoEditor = videoEditor; |
| mGeneratePreviewListener = new ServiceMediaProcessingProgressListener( |
| projectPath); |
| |
| completeRequest(intent, videoEditor, null, videoProject, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| } catch (Exception ex) { |
| if (videoEditor != null) { |
| videoEditor.release(); |
| videoEditor = null; |
| } |
| throw ex; |
| } |
| } else { // The project is already loaded. |
| logd("OP_VIDEO_EDITOR_LOAD: Was already loaded: " + projectPath); |
| completeRequest(intent, videoEditor, null, null, null, true); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_SET_ASPECT_RATIO: { |
| logd("OP_VIDEO_EDITOR_SET_ASPECT_RATIO"); |
| |
| videoEditor.setAspectRatio(intent.getIntExtra(PARAM_ASPECT_RATIO, |
| MediaProperties.ASPECT_RATIO_UNDEFINED)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_APPLY_THEME: { |
| logd("OP_VIDEO_EDITOR_APPLY_THEME"); |
| |
| // Apply the theme |
| applyThemeToMovie(videoEditor, intent.getStringExtra(PARAM_THEME)); |
| |
| final List<MovieMediaItem> mediaItems = |
| copyMediaItems(videoEditor.getAllMediaItems()); |
| final List<MovieAudioTrack> audioTracks = |
| copyAudioTracks(videoEditor.getAllAudioTracks()); |
| |
| completeRequest(intent, videoEditor, null, mediaItems, audioTracks, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_EXPORT: { |
| logd("OP_VIDEO_EDITOR_EXPORT"); |
| exportMovie(videoEditor, intent); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_CANCEL_EXPORT: { |
| logd("OP_VIDEO_EDITOR_CANCEL_EXPORT"); |
| videoEditor.cancelExport(intent.getStringExtra(PARAM_FILENAME)); |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_EXPORT_STATUS: { |
| logd("OP_VIDEO_EDITOR_EXPORT_STATUS"); |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_SAVE: { |
| logd("OP_VIDEO_EDITOR_SAVE: " + projectPath); |
| videoEditor.save(); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.saveToXml(); |
| } |
| |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_RELEASE: { |
| logd("OP_VIDEO_EDITOR_RELEASE: " + projectPath); |
| releaseEditor(projectPath); |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_DELETE: { |
| logd("OP_VIDEO_EDITOR_DELETE: " + projectPath); |
| releaseEditor(projectPath); |
| // Delete all the files and the project folder. |
| FileUtils.deleteDir(new File(projectPath)); |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_ADD_VIDEO_URI: { |
| logd("OP_MEDIA_ITEM_ADD_VIDEO_URI: " + |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| final Uri data = intent.getParcelableExtra(PARAM_FILENAME); |
| String filename = null; |
| // Get the filename |
| Cursor cursor = null; |
| try { |
| cursor = getContentResolver().query(data, |
| new String[] {Video.Media.DATA}, null, null, null); |
| if (cursor.moveToFirst()) { |
| filename = cursor.getString(0); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| if (filename == null) { |
| throw new IllegalArgumentException("Media file not found: " + data); |
| } |
| |
| final MediaItem mediaItem = new MediaVideoItem(videoEditor, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| filename, |
| intent.getIntExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, 0)); |
| |
| videoEditor.insertMediaItem(mediaItem, |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| |
| // If this is the first media item, change the aspect ratio |
| final Integer aspectRatio; |
| if (videoEditor.getAllMediaItems().size() == 1) { |
| videoEditor.setAspectRatio(mediaItem.getAspectRatio()); |
| aspectRatio = videoEditor.getAspectRatio(); |
| } else { |
| aspectRatio = null; |
| } |
| |
| // Apply the theme if any |
| final String themeId = intent.getStringExtra(PARAM_THEME); |
| if (themeId != null) { |
| applyThemeToMediaItem(videoEditor, themeId, mediaItem); |
| } |
| |
| completeRequest(intent, videoEditor, null, new MovieMediaItem(mediaItem), |
| aspectRatio, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_ADD_IMAGE_URI: { |
| logd("OP_MEDIA_ITEM_ADD_IMAGE_URI: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final Uri data = intent.getParcelableExtra(PARAM_FILENAME); |
| String filename = null; |
| // Get the filename |
| Cursor cursor = null; |
| try { |
| cursor = getContentResolver().query(data, |
| new String[] {Images.Media.DATA, Images.Media.MIME_TYPE}, |
| null, null, null); |
| if (cursor.moveToFirst()) { |
| filename = cursor.getString(0); |
| final String mimeType = cursor.getString(1); |
| if ("image/jpeg".equals(mimeType)) { |
| try { |
| final File outputFile = new File(projectPath, |
| "gallery_image_" + generateId() + ".jpg"); |
| if (ImageUtils.transformJpeg(filename, outputFile)) { |
| filename = outputFile.getAbsolutePath(); |
| } |
| } catch (Exception ex) { |
| // Ignore the exception and continue |
| Log.w(TAG, "Could not transform JPEG: " + filename, ex); |
| } |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| if (filename == null) { |
| throw new IllegalArgumentException("Media file not found: " + data); |
| } |
| |
| final MediaItem mediaItem = new MediaImageItem(videoEditor, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| filename, |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, 0)); |
| |
| videoEditor.insertMediaItem(mediaItem, |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| |
| // If this is the first media item, change the aspect ratio |
| final Integer aspectRatio; |
| if (videoEditor.getAllMediaItems().size() == 1) { |
| videoEditor.setAspectRatio(mediaItem.getAspectRatio()); |
| aspectRatio = videoEditor.getAspectRatio(); |
| } else { |
| aspectRatio = null; |
| } |
| |
| // Apply the theme if any |
| final String themeId = intent.getStringExtra(PARAM_THEME); |
| if (themeId != null) { |
| applyThemeToMediaItem(videoEditor, themeId, mediaItem); |
| } |
| |
| completeRequest(intent, videoEditor, null, new MovieMediaItem(mediaItem), |
| aspectRatio, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_LOAD: { |
| final Uri data = intent.getParcelableExtra(PARAM_FILENAME); |
| logd("OP_MEDIA_ITEM_LOAD: " + data); |
| final Intent requestIntent = intent; |
| new Thread() { |
| @Override |
| public void run() { |
| InputStream is = null; |
| FileOutputStream fos = null; |
| final File file = new File(projectPath, "download_" + generateId()); |
| |
| final Intent statusIntent = mIntentPool.get(); |
| statusIntent.putExtra(PARAM_OP, OP_MEDIA_ITEM_LOAD_STATUS); |
| statusIntent.putExtra(PARAM_PROJECT_PATH, |
| requestIntent.getStringExtra(PARAM_PROJECT_PATH)); |
| statusIntent.putExtra(PARAM_INTENT, requestIntent); |
| try { |
| is = getContentResolver().openInputStream(data); |
| // Save the input stream to a file |
| fos = new FileOutputStream(file); |
| final byte[] readBuffer = new byte[2048]; |
| int readBytes; |
| while ((readBytes = is.read(readBuffer)) >= 0) { |
| fos.write(readBuffer, 0, readBytes); |
| } |
| } catch (Exception ex) { |
| Log.e(TAG, "Cannot open input stream for: " + data); |
| statusIntent.putExtra(PARAM_EXCEPTION, ex); |
| file.delete(); |
| } finally { |
| if (is != null) { |
| try { |
| is.close(); |
| } catch (IOException ex) { |
| Log.e(TAG, "Cannot close input stream for: " + data); |
| } |
| } |
| |
| if (fos != null) { |
| try { |
| fos.flush(); |
| fos.close(); |
| } catch (IOException ex) { |
| Log.e(TAG, "Cannot close output stream for: " + data); |
| } |
| } |
| } |
| |
| if (!statusIntent.hasExtra(PARAM_EXCEPTION)) { |
| final String filename = file.getAbsolutePath(); |
| try { |
| final String mimeType = getContentResolver().getType(data); |
| if ("image/jpeg".equals(mimeType)) { |
| final File outputFile = new File(projectPath, |
| "download_" + generateId() + ".jpg"); |
| if (ImageUtils.transformJpeg(filename, outputFile)) { |
| // Delete the downloaded file |
| file.delete(); |
| statusIntent.putExtra(PARAM_FILENAME, |
| outputFile.getAbsolutePath()); |
| } else { |
| statusIntent.putExtra(PARAM_FILENAME, filename); |
| } |
| } else { |
| statusIntent.putExtra(PARAM_FILENAME, filename); |
| } |
| } catch (Exception ex) { |
| // Ignore the exception and continue |
| Log.w(TAG, "Could not transform JPEG: " + filename, ex); |
| statusIntent.putExtra(PARAM_FILENAME, filename); |
| } |
| } |
| |
| mVideoThread.submit(statusIntent); |
| } |
| }.start(); |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_LOAD_STATUS: { |
| final Intent originalIntent = (Intent)intent.getParcelableExtra(PARAM_INTENT); |
| if (intent.hasExtra(PARAM_EXCEPTION)) { // |
| final Exception exception = |
| (Exception)intent.getSerializableExtra(PARAM_EXCEPTION); |
| completeRequest(intent, videoEditor, exception, null, originalIntent, |
| true); |
| } else { |
| completeRequest(intent, videoEditor, null, |
| intent.getStringExtra(PARAM_FILENAME), originalIntent, true); |
| } |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_MOVE: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_MOVE: " + mediaItemId); |
| |
| // Determine the position of the media item we are moving |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| final int mediaItemsCount = mediaItems.size(); |
| int movedItemPosition = -1; |
| MediaItem movedMediaItem = null; |
| for (int i = 0; i < mediaItemsCount; i++) { |
| final MediaItem mi = mediaItems.get(i); |
| if (mi.getId().equals(mediaItemId)) { |
| movedMediaItem = mi; |
| movedItemPosition = i; |
| break; |
| } |
| } |
| |
| if (movedItemPosition == -1) { |
| throw new IllegalArgumentException("Moved MediaItem not found: " + |
| mediaItemId); |
| } |
| |
| final Transition beginTransition = movedMediaItem.getBeginTransition(); |
| final Transition endTransition = movedMediaItem.getEndTransition(); |
| |
| final String afterMediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| videoEditor.moveMediaItem(mediaItemId, afterMediaItemId); |
| |
| // Apply the theme if any |
| final String themeId = intent.getStringExtra(PARAM_THEME); |
| if (themeId != null) { |
| // Apply the theme at the removed position |
| applyThemeAfterMove(videoEditor, themeId, movedMediaItem, |
| movedItemPosition, beginTransition, endTransition); |
| } |
| |
| final List<MovieMediaItem> mediaItemsCopy = copyMediaItems(mediaItems); |
| completeRequest(intent, videoEditor, null, mediaItemsCopy, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_REMOVE: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_REMOVE: " + mediaItemId); |
| |
| // Determine the position of the media item we are removing |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| final int mediaItemsCount = mediaItems.size(); |
| int removedItemPosition = -1; |
| MediaItem removedMediaItem = null; |
| for (int i = 0; i < mediaItemsCount; i++) { |
| if (mediaItems.get(i).getId().equals(mediaItemId)) { |
| removedMediaItem = mediaItems.get(i); |
| removedItemPosition = i; |
| break; |
| } |
| } |
| |
| if (removedMediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| |
| final Transition beginTransition = removedMediaItem.getBeginTransition(); |
| final Transition endTransition = removedMediaItem.getEndTransition(); |
| |
| videoEditor.removeMediaItem(mediaItemId); |
| |
| // Apply the theme if any |
| MovieTransition movieTransition = null; |
| final String themeId = intent.getStringExtra(PARAM_THEME); |
| if (themeId != null && mediaItems.size() > 0) { |
| final Transition transition = applyThemeAfterRemove(videoEditor, themeId, |
| removedItemPosition, beginTransition, endTransition); |
| if (transition != null) { |
| movieTransition = new MovieTransition(transition); |
| } |
| } |
| |
| completeRequest(intent, videoEditor, null, movieTransition, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_RENDERING_MODE: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_SET_RENDERING_MODE: " + mediaItemId); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| mediaItem.setRenderingMode(intent.getIntExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, |
| MediaItem.RENDERING_MODE_BLACK_BORDER)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_DURATION: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_SET_DURATION: " + mediaItemId); |
| |
| final MediaImageItem mediaItem = |
| (MediaImageItem)videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| |
| final long durationMs = intent.getLongExtra(PARAM_DURATION, 0); |
| mediaItem.setDuration(durationMs); |
| // Adjust all effects to the new duration |
| final List<Effect> effects = mediaItem.getAllEffects(); |
| for (Effect effect : effects) { |
| effect.setDuration(durationMs); |
| } |
| |
| completeRequest(intent, videoEditor, null, new MovieMediaItem(mediaItem), null, |
| false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_BOUNDARIES: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MediaVideoItem mediaItem = |
| (MediaVideoItem)videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| |
| mediaItem.setExtractBoundaries(intent.getLongExtra(PARAM_BEGIN_BOUNDARY, 0), |
| intent.getLongExtra(PARAM_END_BOUNDARY, 0)); |
| |
| final List<Overlay> overlays = mediaItem.getAllOverlays(); |
| if (overlays.size() > 0) { |
| // Adjust the overlay |
| final Overlay overlay = overlays.get(0); |
| if (overlay.getStartTime() < mediaItem.getBoundaryBeginTime()) { |
| overlay.setStartTime(mediaItem.getBoundaryBeginTime()); |
| overlay.setDuration(Math.min(overlay.getDuration(), |
| mediaItem.getTimelineDuration())); |
| } else if (overlay.getStartTime() + overlay.getDuration() > |
| mediaItem.getBoundaryEndTime()) { |
| overlay.setStartTime(Math.max(mediaItem.getBoundaryBeginTime(), |
| mediaItem.getBoundaryEndTime() - overlay.getDuration())); |
| overlay.setDuration(mediaItem.getBoundaryEndTime() - |
| overlay.getStartTime()); |
| } |
| } |
| |
| completeRequest(intent, videoEditor, null, new MovieMediaItem(mediaItem), null, |
| false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_GET_THUMBNAILS: { |
| // Note that this command is executed in the thumbnail thread |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_GET_THUMBNAILS: " + mediaItemId); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| |
| final VideoEditor ve = videoEditor; // Just to make it "final" |
| mediaItem.getThumbnailList( |
| intent.getIntExtra(PARAM_WIDTH, 0), |
| intent.getIntExtra(PARAM_HEIGHT, 0), |
| intent.getLongExtra(PARAM_START_TIME, 0), |
| intent.getLongExtra(PARAM_END_TIME, 0), |
| intent.getIntExtra(PARAM_COUNT, 0), |
| intent.getIntArrayExtra(PARAM_INDICES), |
| new GetThumbnailListCallback() { |
| public void onThumbnail(Bitmap bitmap, int index) { |
| completeRequest( |
| intent, ve, null, bitmap, |
| Integer.valueOf(index), false); |
| } |
| } |
| ); |
| |
| completeRequest(intent, videoEditor, null, null, null, true); |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_VOLUME: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_SET_VOLUME: " + mediaItemId); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem != null && mediaItem instanceof MediaVideoItem) { |
| ((MediaVideoItem)mediaItem).setVolume(intent.getIntExtra(PARAM_VOLUME, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| } else { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_MUTE: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_SET_MUTE: " + mediaItemId); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem != null && mediaItem instanceof MediaVideoItem) { |
| ((MediaVideoItem)mediaItem).setMute(intent.getBooleanExtra(PARAM_MUTE, |
| false)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| } else { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM: { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM: " + mediaItemId); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem(mediaItemId); |
| if (mediaItem != null && mediaItem instanceof MediaVideoItem) { |
| final MediaVideoItem movieMediaItem = ((MediaVideoItem)mediaItem); |
| final WaveformData waveformData = movieMediaItem.getWaveformData(); |
| if (waveformData == null) { |
| extractMediaItemAudioWaveform(intent, videoEditor, movieMediaItem); |
| completeRequest(intent, videoEditor, null, |
| movieMediaItem.getWaveformData(), null, true); |
| } else { |
| completeRequest(intent, videoEditor, null, waveformData, null, true); |
| } |
| } else { |
| throw new IllegalArgumentException("MediaItem not found: " + mediaItemId); |
| } |
| break; |
| } |
| |
| case OP_TRANSITION_INSERT_ALPHA: { |
| logd("OP_TRANSITION_INSERT_ALPHA: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final MediaItem afterMediaItem; |
| if (afterMediaItemId != null) { |
| afterMediaItem = videoEditor.getMediaItem(afterMediaItemId); |
| } else { |
| afterMediaItem = null; |
| } |
| |
| final int maskRawResourceId = intent.getIntExtra(PARAM_TRANSITION_MASK, |
| R.raw.mask_contour); |
| |
| final MediaItem beforeMediaItem = nextMediaItem(videoEditor, afterMediaItemId); |
| final Transition transition = new TransitionAlpha( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| afterMediaItem, beforeMediaItem, |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_TRANSITION_BEHAVIOR, |
| Transition.BEHAVIOR_LINEAR), |
| FileUtils.getMaskFilename(getApplicationContext(), |
| maskRawResourceId), |
| intent.getIntExtra(PARAM_TRANSITION_BLENDING, 100), |
| intent.getBooleanExtra(PARAM_TRANSITION_INVERT, false)); |
| videoEditor.addTransition(transition); |
| |
| completeRequest(intent, videoEditor, null, transition, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_INSERT_CROSSFADE: { |
| logd("OP_TRANSITION_INSERT_CROSSFADE: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final MediaItem afterMediaItem; |
| if (afterMediaItemId != null) { |
| afterMediaItem = videoEditor.getMediaItem(afterMediaItemId); |
| } else { |
| afterMediaItem = null; |
| } |
| |
| final MediaItem beforeMediaItem = nextMediaItem(videoEditor, afterMediaItemId); |
| final Transition transition = new TransitionCrossfade( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| afterMediaItem, beforeMediaItem, |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_TRANSITION_BEHAVIOR, |
| Transition.BEHAVIOR_LINEAR)); |
| videoEditor.addTransition(transition); |
| |
| completeRequest(intent, videoEditor, null, transition, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_INSERT_FADE_BLACK: { |
| logd("OP_TRANSITION_INSERT_FADE_TO_BLACK: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final MediaItem afterMediaItem; |
| if (afterMediaItemId != null) { |
| afterMediaItem = videoEditor.getMediaItem(afterMediaItemId); |
| } else { |
| afterMediaItem = null; |
| } |
| |
| final MediaItem beforeMediaItem = nextMediaItem(videoEditor, afterMediaItemId); |
| final Transition transition = new TransitionFadeBlack( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| afterMediaItem, beforeMediaItem, |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_TRANSITION_BEHAVIOR, |
| Transition.BEHAVIOR_LINEAR)); |
| videoEditor.addTransition(transition); |
| |
| completeRequest(intent, videoEditor, null, transition, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_INSERT_SLIDING: { |
| logd("OP_TRANSITION_INSERT_SLIDING: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final MediaItem afterMediaItem; |
| if (afterMediaItemId != null) { |
| afterMediaItem = videoEditor.getMediaItem(afterMediaItemId); |
| } else { |
| afterMediaItem = null; |
| } |
| |
| final MediaItem beforeMediaItem = nextMediaItem(videoEditor, afterMediaItemId); |
| final Transition transition = new TransitionSliding( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| afterMediaItem, beforeMediaItem, |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_TRANSITION_BEHAVIOR, |
| Transition.BEHAVIOR_LINEAR), |
| intent.getIntExtra(PARAM_TRANSITION_DIRECTION, |
| TransitionSliding.DIRECTION_RIGHT_OUT_LEFT_IN)); |
| videoEditor.addTransition(transition); |
| |
| completeRequest(intent, videoEditor, null, transition, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_REMOVE: { |
| logd("OP_TRANSITION_REMOVE: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| videoEditor.removeTransition(intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_SET_DURATION: { |
| final String transitionId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_TRANSITION_SET_DURATION: " + transitionId); |
| |
| final Transition transition = videoEditor.getTransition(transitionId); |
| if (transition == null) { |
| throw new IllegalArgumentException("Transition not found: " + |
| transitionId); |
| } |
| transition.setDuration(intent.getLongExtra(PARAM_DURATION, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_TRANSITION_GET_THUMBNAIL: { |
| final String transitionId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_TRANSITION_GET_THUMBNAIL: " + transitionId); |
| |
| final Transition transition = videoEditor.getTransition(transitionId); |
| if (transition == null) { |
| throw new IllegalArgumentException("Transition not found: " + |
| transitionId); |
| } |
| |
| final int height = intent.getIntExtra(PARAM_HEIGHT, 0); |
| final MediaItem afterMediaItem = transition.getAfterMediaItem(); |
| final Bitmap[] thumbnails = new Bitmap[2]; |
| if (afterMediaItem != null) { |
| thumbnails[0] = afterMediaItem.getThumbnail( |
| (afterMediaItem.getWidth() * height) / afterMediaItem.getHeight(), |
| height, afterMediaItem.getTimelineDuration()); |
| } else { |
| thumbnails[0] = null; |
| } |
| |
| final MediaItem beforeMediaItem = transition.getBeforeMediaItem(); |
| if (beforeMediaItem != null) { |
| thumbnails[1] = beforeMediaItem.getThumbnail( |
| (beforeMediaItem.getWidth() * height) / beforeMediaItem.getHeight(), |
| height, 0); |
| } else { |
| thumbnails[1] = null; |
| } |
| |
| completeRequest(intent, videoEditor, null, thumbnails, null, true); |
| break; |
| } |
| |
| case OP_EFFECT_ADD_COLOR: { |
| logd("OP_EFFECT_ADD_COLOR: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| // Remove any existing effect |
| final List<Effect> effects = mediaItem.getAllEffects(); |
| for (Effect effect : effects) { |
| mediaItem.removeEffect(effect.getId()); |
| } |
| |
| final Effect effect = new EffectColor(mediaItem, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| intent.getLongExtra(PARAM_START_TIME, -1), |
| intent.getLongExtra(PARAM_DURATION, 0), |
| intent.getIntExtra(PARAM_EFFECT_TYPE, -1), |
| intent.getIntExtra(PARAM_EFFECT_PARAM, -1)); |
| mediaItem.addEffect(effect); |
| |
| completeRequest(intent, videoEditor, null, new MovieEffect(effect), null, |
| false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_EFFECT_ADD_IMAGE_KEN_BURNS: { |
| logd("OP_EFFECT_ADD_IMAGE_KEN_BURNS: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| // Remove any existing effect |
| final List<Effect> effects = mediaItem.getAllEffects(); |
| for (Effect effect : effects) { |
| mediaItem.removeEffect(effect.getId()); |
| } |
| |
| final Effect effect = new EffectKenBurns(mediaItem, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| (Rect)intent.getParcelableExtra(PARAM_MEDIA_ITEM_START_RECT), |
| (Rect)intent.getParcelableExtra(PARAM_MEDIA_ITEM_END_RECT), |
| intent.getLongExtra(PARAM_START_TIME, 0), |
| intent.getLongExtra(PARAM_DURATION, 0)); |
| mediaItem.addEffect(effect); |
| |
| completeRequest(intent, videoEditor, null, new MovieEffect(effect), null, |
| false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_EFFECT_REMOVE: { |
| logd("OP_EFFECT_REMOVE: " + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| mediaItem.removeEffect(intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_OVERLAY_ADD: { |
| logd("OP_OVERLAY_ADD: " + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| // Remove any existing overlays |
| final List<Overlay> overlays = mediaItem.getAllOverlays(); |
| for (Overlay overlay : overlays) { |
| mediaItem.removeOverlay(overlay.getId()); |
| } |
| |
| final int scaledWidth, scaledHeight; |
| if (mediaItem instanceof MediaVideoItem) { |
| scaledWidth = ((MediaVideoItem)mediaItem).getWidth(); |
| scaledHeight = ((MediaVideoItem)mediaItem).getHeight(); |
| } else { |
| scaledWidth = ((MediaImageItem)mediaItem).getScaledWidth(); |
| scaledHeight = ((MediaImageItem)mediaItem).getScaledHeight(); |
| } |
| |
| final Bundle userAttributes = intent.getBundleExtra(PARAM_ATTRIBUTES); |
| |
| final int overlayType = MovieOverlay.getType(userAttributes); |
| final String title = MovieOverlay.getTitle(userAttributes); |
| final String subTitle = MovieOverlay.getSubtitle(userAttributes); |
| |
| final OverlayFrame overlay = new OverlayFrame(mediaItem, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| ImageUtils.buildOverlayBitmap(getApplicationContext(), null, |
| overlayType, title, subTitle, scaledWidth, scaledHeight), |
| intent.getLongExtra(PARAM_START_TIME, -1), |
| intent.getLongExtra(PARAM_DURATION, 0)); |
| |
| // Set the user attributes |
| for (String name : userAttributes.keySet()) { |
| if (MovieOverlay.getAttributeType(name).equals(Integer.class)) { |
| overlay.setUserAttribute(name, |
| Integer.toString(userAttributes.getInt(name))); |
| } else { // Strings |
| overlay.setUserAttribute(name, userAttributes.getString(name)); |
| } |
| } |
| mediaItem.addOverlay(overlay); |
| |
| completeRequest(intent, videoEditor, null, new MovieOverlay(overlay), null, |
| false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_OVERLAY_REMOVE: { |
| logd("OP_OVERLAY_REMOVE: " + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| mediaItem.removeOverlay(intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_OVERLAY_SET_START_TIME: { |
| logd("OP_OVERLAY_SET_START_TIME: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| final Overlay overlay = mediaItem.getOverlay( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| if (overlay == null) { |
| throw new IllegalArgumentException("Overlay not found: " + |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| } |
| |
| overlay.setStartTime(intent.getLongExtra(PARAM_START_TIME, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_OVERLAY_SET_DURATION: { |
| logd("OP_OVERLAY_SET_DURATION: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| final Overlay overlay = mediaItem.getOverlay( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| if (overlay == null) { |
| throw new IllegalArgumentException("Overlay not found: " + |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| } |
| |
| overlay.setDuration(intent.getLongExtra(PARAM_DURATION, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_OVERLAY_SET_ATTRIBUTES: { |
| logd("OP_OVERLAY_SET_ATTRIBUTES: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final MediaItem mediaItem = videoEditor.getMediaItem( |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| if (mediaItem == null) { |
| throw new IllegalArgumentException("MediaItem not found: " + |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID)); |
| } |
| |
| final Overlay overlay = mediaItem.getOverlay( |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| if (overlay == null) { |
| throw new IllegalArgumentException("Overlay not found: " + |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| } |
| |
| final int scaledWidth, scaledHeight; |
| if (mediaItem instanceof MediaVideoItem) { |
| scaledWidth = ((MediaVideoItem)mediaItem).getWidth(); |
| scaledHeight = ((MediaVideoItem)mediaItem).getHeight(); |
| } else { |
| scaledWidth = ((MediaImageItem)mediaItem).getScaledWidth(); |
| scaledHeight = ((MediaImageItem)mediaItem).getScaledHeight(); |
| } |
| |
| final Bundle userAttributes = intent.getBundleExtra(PARAM_ATTRIBUTES); |
| final int overlayType = MovieOverlay.getType(userAttributes); |
| final String title = MovieOverlay.getTitle(userAttributes); |
| final String subTitle = MovieOverlay.getSubtitle(userAttributes); |
| |
| ((OverlayFrame)overlay).setBitmap( |
| ImageUtils.buildOverlayBitmap(getApplicationContext(), null, |
| overlayType, title, subTitle, scaledWidth, scaledHeight)); |
| |
| for (String name : userAttributes.keySet()) { |
| if (MovieOverlay.getAttributeType(name).equals(Integer.class)) { |
| overlay.setUserAttribute(name, |
| Integer.toString(userAttributes.getInt(name))); |
| } else { // Strings |
| overlay.setUserAttribute(name, userAttributes.getString(name)); |
| } |
| } |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, true); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_ADD: { |
| logd("OP_AUDIO_TRACK_ADD: " + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| final Uri data = intent.getParcelableExtra(PARAM_FILENAME); |
| String filename = null; |
| // Get the filename |
| Cursor cursor = null; |
| try { |
| cursor = getContentResolver().query(data, |
| new String[] {Audio.Media.DATA}, null, null, null); |
| if (cursor.moveToFirst()) { |
| filename = cursor.getString(0); |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| if (filename == null) { |
| throw new IllegalArgumentException("Media file not found: " + data); |
| } |
| |
| final AudioTrack audioTrack = new AudioTrack(videoEditor, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), filename); |
| audioTrack.enableDucking(DUCK_THRESHOLD, DUCK_TRACK_VOLUME); |
| audioTrack.setVolume(DEFAULT_AUDIO_TRACK_VOLUME); |
| if (intent.getBooleanExtra(PARAM_LOOP, false)) { |
| audioTrack.enableLoop(); |
| } else { |
| audioTrack.disableLoop(); |
| } |
| |
| videoEditor.addAudioTrack(audioTrack); |
| |
| completeRequest(intent, videoEditor, null, new MovieAudioTrack(audioTrack), |
| null, false); |
| // This is needed to decode the audio file into a PCM file |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_REMOVE: { |
| logd("OP_AUDIO_TRACK_REMOVE: " |
| + intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| videoEditor.removeAudioTrack(intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_BOUNDARIES: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_SET_BOUNDARIES: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| audioTrack.setExtractBoundaries(intent.getLongExtra(PARAM_BEGIN_BOUNDARY, 0), |
| intent.getLongExtra(PARAM_END_BOUNDARY, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_LOOP: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_SET_LOOP: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| if (intent.getBooleanExtra(PARAM_LOOP, false)) { |
| audioTrack.enableLoop(); |
| } else { |
| audioTrack.disableLoop(); |
| } |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_DUCK: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_SET_DUCK: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| if (intent.getBooleanExtra(PARAM_DUCK, false)) { |
| audioTrack.enableDucking(DUCK_THRESHOLD, DUCK_TRACK_VOLUME); |
| } else { |
| audioTrack.disableDucking(); |
| } |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_VOLUME: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_SET_VOLUME: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| audioTrack.setVolume(intent.getIntExtra(PARAM_VOLUME, 0)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_MUTE: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_SET_MUTE: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| audioTrack.setMute(intent.getBooleanExtra(PARAM_MUTE, false)); |
| |
| completeRequest(intent, videoEditor, null, null, null, false); |
| generatePreview(videoEditor, false); |
| completeRequest(intent); |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM: { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| logd("OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM: " + audioTrackId); |
| |
| final AudioTrack audioTrack = videoEditor.getAudioTrack(audioTrackId); |
| if (audioTrack == null) { |
| throw new IllegalArgumentException("AudioTrack not found: " + |
| audioTrackId); |
| } |
| |
| final WaveformData waveformData = audioTrack.getWaveformData(); |
| if (waveformData == null) { |
| extractAudioTrackAudioWaveform(intent, videoEditor, audioTrack); |
| completeRequest(intent, videoEditor, null, audioTrack.getWaveformData(), |
| null, true); |
| } else { |
| completeRequest(intent, videoEditor, null, waveformData, null, true); |
| } |
| break; |
| } |
| |
| default: { |
| throw new IllegalArgumentException("Unhandled operation: " + op); |
| } |
| } |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| completeRequest(intent, videoEditor, ex, null, null, true); |
| } |
| } |
| |
| /** |
| * Complete the request |
| * |
| * @param intent The intent |
| * @param videoEditor The video editor |
| * @param exception The exception |
| * @param result The result object |
| * @param extraResult The extra result object |
| * @param finalize true if the request should be finalized |
| */ |
| private void completeRequest(final Intent intent, final VideoEditor videoEditor, |
| final Exception exception, final Object result, final Object extraResult, |
| final boolean finalize) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| onIntentProcessed(intent, videoEditor, result, extraResult, exception, finalize); |
| } |
| }); |
| } |
| |
| /** |
| * Complete the request |
| * |
| * @param intent The intent |
| */ |
| private void completeRequest(final Intent intent) { |
| mHandler.post (new Runnable() { |
| @Override |
| public void run() { |
| finalizeRequest(intent); |
| mIntentPool.put(intent); |
| } |
| }); |
| } |
| |
| /** |
| * Callback called after the specified intent is processed. |
| * |
| * @param intent The intent |
| * @param videoEditor The VideoEditor on which the operation was performed |
| * @param result The result object |
| * @param extraResult The extra result object |
| * @param ex The exception |
| * @param finalize true if the request should be finalized |
| */ |
| @SuppressWarnings("unchecked") |
| public void onIntentProcessed(final Intent intent, VideoEditor videoEditor, |
| Object result, Object extraResult, Exception ex, boolean finalize) { |
| |
| final String projectPath = intent.getStringExtra(PARAM_PROJECT_PATH); |
| final int op = intent.getIntExtra(PARAM_OP, -1); |
| switch (op) { |
| case OP_VIDEO_EDITOR_LOAD_PROJECTS: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final List<VideoEditorProject> projects = (List<VideoEditorProject>)result; |
| for (ApiServiceListener listener : mListeners) { |
| listener.onProjectsLoaded(projects, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_CREATE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| // Release the old project |
| if (mVideoProject != null) { |
| mVideoProject.release(); |
| mVideoProject = null; |
| } |
| |
| if (ex != null) { |
| FileUtils.deleteDir(new File(projectPath)); |
| } else { |
| mVideoProject = (VideoEditorProject)result; |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorCreated(projectPath, mVideoProject, |
| videoEditor != null ? videoEditor.getAllMediaItems() : null, |
| videoEditor != null ? videoEditor.getAllAudioTracks() : null, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_LOAD: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| if (result != null) { // A new project was created |
| if (mVideoProject != null) { |
| mVideoProject.release(); |
| mVideoProject = null; |
| } |
| mVideoProject = (VideoEditorProject)result; |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorLoaded(projectPath, mVideoProject, |
| ex == null ? videoEditor.getAllMediaItems() : null, |
| ex == null ? videoEditor.getAllAudioTracks() : null, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_SET_ASPECT_RATIO: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final int aspectRatio = intent.getIntExtra(PARAM_ASPECT_RATIO, |
| MediaProperties.ASPECT_RATIO_UNDEFINED); |
| if (ex == null) { |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.setAspectRatio(aspectRatio); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorAspectRatioSet(projectPath, aspectRatio, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_APPLY_THEME: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String theme = intent.getStringExtra(PARAM_THEME); |
| if (ex == null) { |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.setTheme(theme); |
| videoProject.setMediaItems((List<MovieMediaItem>)result); |
| videoProject.setAudioTracks((List<MovieAudioTrack>)extraResult); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorThemeApplied(projectPath, theme, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_GENERATE_PREVIEW_PROGRESS: { |
| final String className = intent.getStringExtra(PARAM_ATTRIBUTES); |
| final String itemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final int action = intent.getIntExtra(PARAM_ACTION, |
| VideoEditor.MediaProcessingProgressListener.ACTION_DECODE); |
| final int progress = intent.getIntExtra(PARAM_PROGRESS_VALUE, 0); |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorGeneratePreviewProgress(projectPath, className, itemId, |
| action, progress); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_EXPORT: { |
| // The finalizeRequest() call and listener callbacks are done in |
| // OP_VIDEO_EDITOR_EXPORT_STATUS intent handling (where we are |
| // called originalIntent). |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_CANCEL_EXPORT: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorExportCanceled(projectPath, |
| intent.getStringExtra(PARAM_FILENAME)); |
| } |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_EXPORT_STATUS: { |
| // This operation is for the service internal use only |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String filename = intent.getStringExtra(PARAM_FILENAME); |
| if (intent.hasExtra(PARAM_EXCEPTION)) { // Complete |
| final Intent originalIntent = (Intent)intent.getParcelableExtra(PARAM_INTENT); |
| finalizeRequest(originalIntent); |
| mIntentPool.put(originalIntent); |
| |
| final Exception exception = |
| (Exception)intent.getSerializableExtra(PARAM_EXCEPTION); |
| final VideoEditorProject videoProject = getProject(projectPath); |
| final boolean cancelled = intent.getBooleanExtra(PARAM_CANCELLED, false); |
| if (!cancelled && videoProject != null && exception == null) { |
| final Uri uri = (Uri)intent.getParcelableExtra(PARAM_MOVIE_URI); |
| videoProject.addExportedMovieUri(uri); |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorExportComplete( |
| projectPath, filename, exception, cancelled); |
| } |
| } else { // Progress |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorExportProgress(projectPath, filename, |
| intent.getIntExtra(PARAM_PROGRESS_VALUE, -1)); |
| } |
| |
| // The original request is still pending |
| } |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_SAVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorSaved(projectPath, ex); |
| } |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_RELEASE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.release(); |
| if (mVideoProject == videoProject) { |
| mVideoProject = null; |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorReleased(projectPath, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_VIDEO_EDITOR_DELETE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.release(); |
| if (mVideoProject == videoProject) { |
| mVideoProject = null; |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onVideoEditorDeleted(projectPath, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_ADD_VIDEO_URI: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| |
| final MovieMediaItem movieMediaItem = (MovieMediaItem)result; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null && extraResult != null) { |
| // The aspect ratio has changed |
| videoProject.setAspectRatio((Integer)extraResult); |
| } |
| |
| if (ex == null) { |
| videoProject.insertMediaItem(movieMediaItem, afterMediaItemId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemAdded(projectPath, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), movieMediaItem, |
| afterMediaItemId, MediaVideoItem.class, (Integer)extraResult, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_ADD_IMAGE_URI: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String afterMediaItemId = |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| |
| final MovieMediaItem movieMediaItem = (MovieMediaItem)result; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null && extraResult != null) { |
| // The aspect ratio has changed |
| videoProject.setAspectRatio((Integer)extraResult); |
| } |
| |
| if (ex == null) { |
| videoProject.insertMediaItem(movieMediaItem, afterMediaItemId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemAdded(projectPath, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), movieMediaItem, |
| afterMediaItemId, MediaImageItem.class, (Integer)extraResult, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_LOAD: { |
| // Note that this message is handled only if the download |
| // cannot start. |
| final Uri data = (Uri)intent.getParcelableExtra(PARAM_FILENAME); |
| final String mimeType = intent.getStringExtra(PARAM_ATTRIBUTES); |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaLoaded(projectPath, data, mimeType, null, ex); |
| } |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_LOAD_STATUS: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final Intent originalIntent = (Intent)intent.getParcelableExtra(PARAM_INTENT); |
| final Uri data = (Uri)originalIntent.getParcelableExtra(PARAM_FILENAME); |
| final String mimeType = originalIntent.getStringExtra(PARAM_ATTRIBUTES); |
| |
| finalizeRequest(originalIntent); |
| mIntentPool.put(originalIntent); |
| |
| final String filename = intent.getStringExtra(PARAM_FILENAME); |
| |
| if (ex == null && filename != null) { |
| final VideoEditorProject videoProject = getProject(projectPath); |
| videoProject.addDownload(data.toString(), mimeType, filename); |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaLoaded(projectPath, data, mimeType, filename, ex); |
| } |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_MOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| if (ex == null) { |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.setMediaItems((List<MovieMediaItem>)result); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemMoved(projectPath, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| intent.getStringExtra(PARAM_RELATIVE_STORYBOARD_ITEM_ID), ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_REMOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieTransition transition = (MovieTransition)result; |
| if (ex == null) { |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| videoProject.removeMediaItem(mediaItemId, transition); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemRemoved(projectPath, mediaItemId, transition, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_RENDERING_MODE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final int renderingMode = intent.getIntExtra(PARAM_MEDIA_ITEM_RENDERING_MODE, |
| MediaItem.RENDERING_MODE_BLACK_BORDER); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final MovieMediaItem mediaItem = videoProject.getMediaItem(mediaItemId); |
| if (mediaItem != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| mediaItem.setRenderingMode(renderingMode); |
| } else { |
| mediaItem.setAppRenderingMode(mediaItem.getRenderingMode()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemRenderingModeSet(projectPath, mediaItemId, renderingMode, |
| ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_DURATION: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.updateMediaItem((MovieMediaItem)result); |
| } else { |
| final MovieMediaItem oldMediaItem = videoProject.getMediaItem(mediaItemId); |
| if (oldMediaItem != null) { |
| videoProject.setClean(false); |
| oldMediaItem.setAppExtractBoundaries(0, oldMediaItem.getDuration()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemDurationSet(projectPath, mediaItemId, |
| intent.getLongExtra(PARAM_DURATION, 0), ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_BOUNDARIES: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| final MovieMediaItem mediaItem = (MovieMediaItem)result; |
| videoProject.updateMediaItem(mediaItem); |
| } else { |
| final MovieMediaItem oldMediaItem = videoProject.getMediaItem(mediaItemId); |
| if (oldMediaItem != null) { |
| videoProject.setClean(false); |
| oldMediaItem.setAppExtractBoundaries( |
| oldMediaItem.getBoundaryBeginTime(), |
| oldMediaItem.getBoundaryEndTime()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemBoundariesSet(projectPath, mediaItemId, |
| intent.getLongExtra(PARAM_BEGIN_BOUNDARY, 0), |
| intent.getLongExtra(PARAM_END_BOUNDARY, 0), ex); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_GET_THUMBNAILS: { |
| if (finalize) { |
| finalizeRequest(intent); |
| break; |
| } |
| |
| final Bitmap bitmap = (Bitmap)result; |
| final int index = (Integer)extraResult; |
| boolean used = false; |
| for (ApiServiceListener listener : mListeners) { |
| used |= listener.onMediaItemThumbnail(projectPath, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| bitmap, index, intent.getIntExtra(PARAM_TOKEN, 0), |
| ex); |
| } |
| |
| if (used == false) { |
| if (bitmap != null) { |
| bitmap.recycle(); |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_VOLUME: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieMediaItem mediaItem = videoProject.getMediaItem(mediaItemId); |
| if (mediaItem != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| mediaItem.setVolume(intent.getIntExtra(PARAM_VOLUME, 0)); |
| } else { |
| mediaItem.setAppVolume(mediaItem.getVolume()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_SET_MUTE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieMediaItem mediaItem = videoProject.getMediaItem(mediaItemId); |
| if (mediaItem != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| mediaItem.setMute(intent.getBooleanExtra(PARAM_MUTE, false)); |
| } else { |
| mediaItem.setAppMute(mediaItem.isMuted()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM_STATUS: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final int progress = intent.getIntExtra(PARAM_PROGRESS_VALUE, 0); |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemExtractAudioWaveformProgress(projectPath, mediaItemId, |
| progress); |
| } |
| |
| break; |
| } |
| |
| case OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (ex == null && videoProject != null) { |
| if (result != null) { |
| final MovieMediaItem mediaItem = videoProject.getMediaItem(mediaItemId); |
| if (mediaItem != null) { |
| videoProject.setClean(false); |
| mediaItem.setWaveformData((WaveformData)result); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onMediaItemExtractAudioWaveformComplete(projectPath, mediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_TRANSITION_INSERT_ALPHA: |
| case OP_TRANSITION_INSERT_CROSSFADE: |
| case OP_TRANSITION_INSERT_FADE_BLACK: |
| case OP_TRANSITION_INSERT_SLIDING: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String afterMediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| |
| final MovieTransition movieTransition; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final Transition transition = (Transition)result; |
| // This is null for start transitions |
| if (ex == null) { |
| movieTransition = new MovieTransition(transition); |
| videoProject.addTransition(movieTransition, afterMediaItemId); |
| } else { |
| movieTransition = null; |
| } |
| } else { |
| movieTransition = null; |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onTransitionInserted(projectPath, movieTransition, |
| afterMediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_TRANSITION_REMOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String transitionId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.removeTransition(transitionId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onTransitionRemoved(projectPath, transitionId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_TRANSITION_SET_DURATION: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String transitionId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final long durationMs = intent.getLongExtra(PARAM_DURATION, 0); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final MovieTransition transition = videoProject.getTransition(transitionId); |
| if (transition != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| transition.setDuration(durationMs); |
| } else { |
| transition.setAppDuration(transition.getDuration()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onTransitionDurationSet(projectPath, transitionId, durationMs, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_TRANSITION_GET_THUMBNAIL: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final Bitmap[] bitmaps = (Bitmap[])result; |
| boolean used = false; |
| for (ApiServiceListener listener : mListeners) { |
| used |= listener.onTransitionThumbnails(projectPath, |
| intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID), |
| bitmaps, ex); |
| } |
| |
| if (used == false) { |
| if (bitmaps != null) { |
| for (int i = 0; i < bitmaps.length; i++) { |
| if (bitmaps[i] != null) { |
| bitmaps[i].recycle(); |
| } |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_OVERLAY_ADD: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| |
| final MovieOverlay movieOverlay = (MovieOverlay)result; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.addOverlay(mediaItemId, movieOverlay); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onOverlayAdded(projectPath, movieOverlay, mediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_OVERLAY_REMOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final String overlayId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.removeOverlay(mediaItemId, overlayId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onOverlayRemoved(projectPath, overlayId, mediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_OVERLAY_SET_START_TIME: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final String overlayId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final long startTimeMs = intent.getLongExtra(PARAM_START_TIME, 0); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final MovieOverlay overlay = videoProject.getOverlay(mediaItemId, overlayId); |
| if (overlay != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| overlay.setStartTime(startTimeMs); |
| } else { |
| overlay.setAppStartTime(overlay.getStartTime()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onOverlayStartTimeSet(projectPath, overlayId, mediaItemId, |
| startTimeMs, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_OVERLAY_SET_DURATION: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final String overlayId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final long durationMs = intent.getLongExtra(PARAM_DURATION, 0); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final MovieOverlay overlay = videoProject.getOverlay(mediaItemId, overlayId); |
| if (overlay != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| overlay.setDuration(durationMs); |
| } else { |
| overlay.setAppDuration(overlay.getDuration()); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onOverlayDurationSet(projectPath, overlayId, mediaItemId, |
| durationMs, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_OVERLAY_SET_ATTRIBUTES: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final String overlayId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final Bundle userAttributes = intent.getBundleExtra(PARAM_ATTRIBUTES); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| final MovieOverlay overlay = videoProject.getOverlay(mediaItemId, |
| overlayId); |
| if (overlay != null) { |
| videoProject.setClean(false); |
| overlay.updateUserAttributes(userAttributes); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onOverlayUserAttributesSet(projectPath, overlayId, mediaItemId, |
| userAttributes, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_EFFECT_ADD_COLOR: |
| case OP_EFFECT_ADD_IMAGE_KEN_BURNS:{ |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| |
| final MovieEffect movieEffect = (MovieEffect)result; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.addEffect(mediaItemId, movieEffect); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onEffectAdded(projectPath, movieEffect, mediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_EFFECT_REMOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String mediaItemId = intent.getStringExtra( |
| PARAM_RELATIVE_STORYBOARD_ITEM_ID); |
| final String effectId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.removeEffect(mediaItemId, effectId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onEffectRemoved(projectPath, effectId, mediaItemId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_ADD: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final MovieAudioTrack movieAudioTrack = (MovieAudioTrack)result; |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.addAudioTrack(movieAudioTrack); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onAudioTrackAdded(projectPath, movieAudioTrack, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_REMOVE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| videoProject.removeAudioTrack(audioTrackId); |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onAudioTrackRemoved(projectPath, audioTrackId, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_BOUNDARIES: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final long beginBoundary = intent.getLongExtra(PARAM_BEGIN_BOUNDARY, 0); |
| final long endBoundary = intent.getLongExtra(PARAM_END_BOUNDARY, 0); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| if (ex == null) { |
| final MovieAudioTrack audioTrack = |
| videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| audioTrack.setExtractBoundaries(beginBoundary, endBoundary); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onAudioTrackBoundariesSet(projectPath, audioTrackId, |
| beginBoundary, endBoundary, ex); |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_LOOP: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieAudioTrack audioTrack = videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| audioTrack.enableLoop(intent.getBooleanExtra(PARAM_LOOP, false)); |
| } else { |
| audioTrack.enableAppLoop(audioTrack.isLooping()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_DUCK: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieAudioTrack audioTrack = videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| audioTrack.enableDucking(intent.getBooleanExtra(PARAM_DUCK, false)); |
| } else { |
| audioTrack.enableAppDucking(audioTrack.isDuckingEnabled()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_VOLUME: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieAudioTrack audioTrack = videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| audioTrack.setVolume(intent.getIntExtra(PARAM_VOLUME, 0)); |
| } else { |
| audioTrack.setAppVolume(audioTrack.getVolume()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_SET_MUTE: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (videoProject != null) { |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final MovieAudioTrack audioTrack = videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| if (ex == null) { |
| audioTrack.setMute(intent.getBooleanExtra(PARAM_MUTE, false)); |
| } else { |
| audioTrack.setAppMute(audioTrack.isMuted()); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM_STATUS: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| final int progress = intent.getIntExtra(PARAM_PROGRESS_VALUE, 0); |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onAudioTrackExtractAudioWaveformProgress(projectPath, audioTrackId, |
| progress); |
| } |
| |
| break; |
| } |
| |
| case OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| |
| final String audioTrackId = intent.getStringExtra(PARAM_STORYBOARD_ITEM_ID); |
| |
| final VideoEditorProject videoProject = getProject(projectPath); |
| if (ex == null && videoProject != null) { |
| if (result != null) { |
| final MovieAudioTrack audioTrack = |
| videoProject.getAudioTrack(audioTrackId); |
| if (audioTrack != null) { |
| videoProject.setClean(false); |
| audioTrack.setWaveformData((WaveformData)result); |
| } |
| } |
| } |
| |
| for (ApiServiceListener listener : mListeners) { |
| listener.onAudioTrackExtractAudioWaveformComplete(projectPath, |
| audioTrackId, ex); |
| } |
| |
| break; |
| } |
| |
| default: { |
| if (finalize) { |
| finalizeRequest(intent); |
| } |
| break; |
| } |
| } |
| |
| if (finalize) { |
| mIntentPool.put(intent); |
| } |
| } |
| |
| /** |
| * Finalizes a request. Calls the listeners that are interested in project status |
| * change and stops this service if there are no more pending intents. |
| * |
| * @param intent The intent that just completed |
| */ |
| private void finalizeRequest(Intent intent) { |
| mPendingIntents.remove(intent.getStringExtra(PARAM_REQUEST_ID)); |
| |
| final String projectPath = intent.getStringExtra(PARAM_PROJECT_PATH); |
| if (projectPath != null) { |
| final boolean projectEdited = isProjectBeingEdited(projectPath); |
| if (projectEdited == false) { |
| for (ApiServiceListener listener : mListeners) { |
| listener.onProjectEditState(projectPath, projectEdited); |
| } |
| } |
| } |
| |
| if (mPendingIntents.size() == 0) { |
| // Cancel the current timer if any. Extend the timeout by 5000 ms. |
| mHandler.removeCallbacks(mStopRunnable); |
| |
| // Start a timer which will stop the service if the queue of |
| // pending intent will be empty at that time. |
| // This prevents the service from starting & stopping too often. |
| mHandler.postDelayed(mStopRunnable, 5000); |
| logd("completeRequest: Stopping service in 5000 ms"); |
| } |
| } |
| |
| /** |
| * Checks if the current project is the project specified by the specified path. |
| * |
| * @param projectPath The project path |
| * |
| * @return The video editor project |
| */ |
| private VideoEditorProject getProject(String projectPath) { |
| if (mVideoProject != null) { |
| if (mVideoProject.getPath().equals(projectPath)) { |
| return mVideoProject; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Check if the current editor is the project specified by the specified path |
| * |
| * @param projectPath The project path |
| * |
| * @return The video editor |
| */ |
| private synchronized VideoEditor getVideoEditor(String projectPath) { |
| if (mVideoEditor != null) { |
| if (mVideoEditor.getPath().equals(projectPath)) { |
| return mVideoEditor; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Release the editor |
| */ |
| private synchronized void releaseEditor() { |
| if (mVideoEditor != null) { |
| logd("releaseEditor (current): " + mVideoEditor.getPath()); |
| mVideoEditor.release(); |
| mVideoEditor = null; |
| mGeneratePreviewListener = null; |
| |
| System.gc(); |
| } |
| } |
| |
| /** |
| * Release the specified editor |
| * |
| * @param projectPath The project path |
| */ |
| private synchronized void releaseEditor(String projectPath) { |
| if (mVideoEditor != null) { |
| if (mVideoEditor.getPath().equals(projectPath)) { |
| logd("releaseEditor: " + projectPath); |
| mVideoEditor.release(); |
| mVideoEditor = null; |
| mGeneratePreviewListener = null; |
| |
| System.gc(); |
| } |
| } |
| } |
| |
| /** |
| * Release the current editor if it is *not* the current editor |
| * |
| * @param projectPath The project path |
| * |
| * @return The current video editor |
| */ |
| private synchronized VideoEditor releaseEditorNot(String projectPath) { |
| if (mVideoEditor != null) { |
| if (!mVideoEditor.getPath().equals(projectPath)) { |
| logd("releaseEditorNot: " + mVideoEditor.getPath()); |
| mVideoEditor.release(); |
| mVideoEditor = null; |
| mGeneratePreviewListener = null; |
| |
| System.gc(); |
| } |
| } |
| |
| return mVideoEditor; |
| } |
| |
| /** |
| * Generate the preview |
| * |
| * @param videoEditor The video editor |
| * @param updatePreviewFrame true to show preview frame when done |
| */ |
| private void generatePreview(VideoEditor videoEditor, boolean updatePreviewFrame) { |
| try { |
| videoEditor.generatePreview(mGeneratePreviewListener); |
| if (mGeneratePreviewListener != null) { |
| // This is the last callback which is always fired last to |
| // let the UI know that generate preview completed |
| mGeneratePreviewListener.onProgress(null, |
| updatePreviewFrame ? ACTION_UPDATE_FRAME : ACTION_NO_FRAME_UPDATE, 100); |
| } |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| /** |
| * Exports a movie in a distinct worker thread. |
| * |
| * @param videoEditor The video editor |
| * @param intent The intent |
| */ |
| private void exportMovie(final VideoEditor videoEditor, final Intent intent) { |
| mExportCancelled = false; |
| new Thread() { |
| @Override |
| public void run() { |
| final String filename = intent.getStringExtra(PARAM_FILENAME); |
| final int height = intent.getIntExtra(PARAM_HEIGHT, -1); |
| final int bitrate = intent.getIntExtra(PARAM_BITRATE, -1); |
| |
| // Create the export status Intent |
| final Intent statusIntent = mIntentPool.get(); |
| statusIntent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_EXPORT_STATUS); |
| statusIntent.putExtra(PARAM_PROJECT_PATH, intent.getStringExtra( |
| PARAM_PROJECT_PATH)); |
| statusIntent.putExtra(PARAM_FILENAME, filename); |
| statusIntent.putExtra(PARAM_INTENT, intent); |
| Exception resultException = null; |
| |
| try { |
| videoEditor.export(filename, height, bitrate, new ExportProgressListener() { |
| @Override |
| public void onProgress(VideoEditor videoEditor, String filename, |
| int progress) { |
| final Intent progressIntent = mIntentPool.get(); |
| progressIntent.putExtra(PARAM_OP, OP_VIDEO_EDITOR_EXPORT_STATUS); |
| progressIntent.putExtra(PARAM_PROJECT_PATH, intent.getStringExtra( |
| PARAM_PROJECT_PATH)); |
| progressIntent.putExtra(PARAM_FILENAME, filename); |
| progressIntent.putExtra(PARAM_INTENT, intent); |
| progressIntent.putExtra(PARAM_PROGRESS_VALUE, progress); |
| mVideoThread.submit(progressIntent); |
| logv("Export progress: " + progress + " for: " + filename); |
| } |
| }); |
| |
| statusIntent.putExtra(PARAM_CANCELLED, mExportCancelled); |
| if (!mExportCancelled) { |
| if (new File(filename).exists()) { |
| statusIntent.putExtra(PARAM_MOVIE_URI, exportToGallery(filename)); |
| } else { |
| resultException = new IllegalStateException("Export file does not exist: " + filename); |
| } |
| logv("Export complete for: " + filename); |
| } else { |
| logv("Export cancelled by user, file name: " + filename); |
| } |
| } catch (Exception ex) { |
| logv("Export error for: " + filename); |
| ex.printStackTrace(); |
| resultException = ex; |
| } |
| |
| // Complete the request |
| statusIntent.putExtra(PARAM_EXCEPTION, resultException); |
| mVideoThread.submit(statusIntent); |
| } |
| }.start(); |
| } |
| |
| /** |
| * Extract the audio waveform of a media item |
| * |
| * @param intent The original Intent |
| * @param videoEditor The video editor |
| * @param mediaItem The media item |
| */ |
| private void extractMediaItemAudioWaveform(final Intent intent, final VideoEditor videoEditor, |
| final MediaVideoItem mediaItem) throws IOException { |
| mediaItem.extractAudioWaveform( |
| new ExtractAudioWaveformProgressListener() { |
| @Override |
| public void onProgress(int progress) { |
| final Intent progressIntent = mIntentPool.get(); |
| progressIntent.putExtra(PARAM_OP, OP_MEDIA_ITEM_EXTRACT_AUDIO_WAVEFORM_STATUS); |
| progressIntent.putExtra(PARAM_PROJECT_PATH, |
| intent.getStringExtra(PARAM_PROJECT_PATH)); |
| progressIntent.putExtra(PARAM_INTENT, intent); |
| progressIntent.putExtra(PARAM_STORYBOARD_ITEM_ID, mediaItem.getId()); |
| progressIntent.putExtra(PARAM_PROGRESS_VALUE, progress); |
| |
| completeRequest(progressIntent, videoEditor, null, null, null, true); |
| } |
| }); |
| } |
| |
| /** |
| * Extract the audio waveform of an AudioTrack |
| * |
| * @param intent The original Intent |
| * @param videoEditor The video editor |
| * @param audioTrack The audio track |
| */ |
| private void extractAudioTrackAudioWaveform(final Intent intent, final VideoEditor videoEditor, |
| final AudioTrack audioTrack) throws IOException { |
| audioTrack.extractAudioWaveform( |
| new ExtractAudioWaveformProgressListener() { |
| @Override |
| public void onProgress(int progress) { |
| final Intent progressIntent = mIntentPool.get(); |
| progressIntent.putExtra(PARAM_OP, |
| OP_AUDIO_TRACK_EXTRACT_AUDIO_WAVEFORM_STATUS); |
| progressIntent.putExtra(PARAM_PROJECT_PATH, |
| intent.getStringExtra(PARAM_PROJECT_PATH)); |
| progressIntent.putExtra(PARAM_INTENT, intent); |
| progressIntent.putExtra(PARAM_STORYBOARD_ITEM_ID, audioTrack.getId()); |
| progressIntent.putExtra(PARAM_PROGRESS_VALUE, progress); |
| |
| completeRequest(progressIntent, videoEditor, null, null, null, true); |
| } |
| }); |
| } |
| |
| /** |
| * Get the media item following the specified media item |
| * |
| * @param videoEditor The video editor |
| * @param mediaItemId The media item id |
| * @return The next media item |
| */ |
| private static MediaItem nextMediaItem(VideoEditor videoEditor, String mediaItemId) { |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| if (mediaItemId == null) { |
| if (mediaItems.size() > 0) { |
| return mediaItems.get(0); |
| } |
| } else { |
| final int mediaItemCount = mediaItems.size(); |
| for (int i = 0; i < mediaItemCount; i++) { |
| MediaItem mi = mediaItems.get(i); |
| if (mi.getId().equals(mediaItemId)) { |
| if (i < mediaItemCount - 1) { |
| return mediaItems.get(i + 1); |
| } else { |
| break; |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Export the movie to the Gallery |
| * |
| * @param filename The filename |
| * @return The video MediaStore URI |
| */ |
| private Uri exportToGallery(String filename) { |
| // Save the name and description of a video in a ContentValues map. |
| final ContentValues values = new ContentValues(2); |
| values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); |
| values.put(MediaStore.Video.Media.DATA, filename); |
| // Add a new record (identified by uri) |
| final Uri uri = getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, |
| values); |
| sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, |
| Uri.parse("file://"+ filename))); |
| return uri; |
| } |
| |
| /** |
| * Apply a theme to the entire movie. This method shall be used when the |
| * theme is changing. |
| * |
| * @param videoEditor The video editor |
| * @param themeId The theme id |
| */ |
| private void applyThemeToMovie(VideoEditor videoEditor, String themeId) throws IOException { |
| final Context context = getApplicationContext(); |
| final MovieTheme theme = MovieTheme.getTheme(context, themeId); |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| |
| // Add the transitions |
| final int mediaItemsCount = mediaItems.size(); |
| if (mediaItemsCount > 0) { |
| // Remove all the transitions |
| for (int i = 0; i < mediaItemsCount; i++) { |
| final MediaItem mi = mediaItems.get(i); |
| if (i == 0) { |
| final Transition beginTransition = mi.getBeginTransition(); |
| if (beginTransition != null) { |
| videoEditor.removeTransition(beginTransition.getId()); |
| } |
| } |
| |
| final Transition endTransition = mi.getEndTransition(); |
| if (endTransition != null) { |
| videoEditor.removeTransition(endTransition.getId()); |
| } |
| } |
| |
| // Add the begin transition to the first media item |
| final MovieTransition beginMovieTransition = theme.getBeginTransition(); |
| if (beginMovieTransition != null) { |
| final MediaItem firstMediaItem = mediaItems.get(0); |
| videoEditor.addTransition( |
| beginMovieTransition.buildTransition(context, null, firstMediaItem)); |
| } |
| |
| // Add the mid transitions |
| final MovieTransition midMovieTransition = theme.getMidTransition(); |
| if (midMovieTransition != null) { |
| for (int i = 0; i < mediaItemsCount - 1; i++) { |
| videoEditor.addTransition( |
| midMovieTransition.buildTransition(context, |
| mediaItems.get(i), mediaItems.get(i + 1))); |
| } |
| } |
| |
| // Add the end transition to the last media item |
| final MovieTransition endMovieTransition = theme.getEndTransition(); |
| if (endMovieTransition != null) { |
| final MediaItem lastMediaItem = mediaItems.get(mediaItemsCount - 1); |
| videoEditor.addTransition( |
| endMovieTransition.buildTransition(context, lastMediaItem, null)); |
| } |
| } |
| |
| // Add the overlay |
| final MovieOverlay movieOverlay = theme.getOverlay(); |
| if (movieOverlay != null && mediaItemsCount > 0) { |
| // Remove all the overlay for the first media item |
| final MediaItem mediaItem = mediaItems.get(0); |
| final List<Overlay> overlays = mediaItem.getAllOverlays(); |
| if (overlays.size() > 0) { |
| mediaItem.removeOverlay(overlays.get(0).getId()); |
| } |
| |
| // Add the new overlay |
| final int scaledWidth, scaledHeight; |
| if (mediaItem instanceof MediaVideoItem) { |
| scaledWidth = ((MediaVideoItem)mediaItem).getWidth(); |
| scaledHeight = ((MediaVideoItem)mediaItem).getHeight(); |
| } else { |
| scaledWidth = ((MediaImageItem)mediaItem).getScaledWidth(); |
| scaledHeight = ((MediaImageItem)mediaItem).getScaledHeight(); |
| } |
| |
| final Overlay overlay = new OverlayFrame(mediaItem, generateId(), |
| ImageUtils.buildOverlayBitmap(getApplicationContext(), null, |
| movieOverlay.getType(), movieOverlay.getTitle(), |
| movieOverlay.getSubtitle(), scaledWidth, scaledHeight), |
| movieOverlay.getStartTime(), movieOverlay.getDuration()); |
| |
| // Set the user attributes |
| final Bundle userAttributes = movieOverlay.buildUserAttributes(); |
| for (String name : userAttributes.keySet()) { |
| if (MovieOverlay.getAttributeType(name).equals(Integer.class)) { |
| overlay.setUserAttribute(name, |
| Integer.toString(userAttributes.getInt(name))); |
| } else { // Strings |
| overlay.setUserAttribute(name, userAttributes.getString(name)); |
| } |
| } |
| mediaItem.addOverlay(overlay); |
| } |
| |
| final MovieAudioTrack at = theme.getAudioTrack(); |
| if (at != null) { |
| // Remove all audio tracks |
| final List<AudioTrack> audioTracks = videoEditor.getAllAudioTracks(); |
| while (audioTracks.size() > 0) { |
| videoEditor.removeAudioTrack(audioTracks.get(0).getId()); |
| } |
| |
| // Add the new audio track |
| final AudioTrack audioTrack = new AudioTrack(videoEditor, generateId(), |
| FileUtils.getAudioTrackFilename(context, at.getRawResourceId())); |
| |
| // Enable looping if necessary |
| if (at.isLooping()) { |
| audioTrack.enableLoop(); |
| } |
| |
| // Enable ducking |
| audioTrack.enableDucking(DUCK_THRESHOLD, DUCK_TRACK_VOLUME); |
| audioTrack.setVolume(DEFAULT_AUDIO_TRACK_VOLUME); |
| videoEditor.addAudioTrack(audioTrack); |
| } |
| } |
| |
| /** |
| * Apply a theme |
| * |
| * @param videoEditor The video editor |
| * @param themeId The theme id |
| * @param mediaItem The mediaItem |
| */ |
| private void applyThemeToMediaItem(VideoEditor videoEditor, String themeId, |
| MediaItem mediaItem) throws IOException { |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| final int mediaItemsCount = mediaItems.size(); |
| if (mediaItemsCount == 0) { |
| return; |
| } |
| |
| // We would only add transitions if the transitions don't exist |
| final Transition beginTransition = mediaItem.getBeginTransition(); |
| final Transition endTransition = mediaItem.getEndTransition(); |
| |
| final Context context = getApplicationContext(); |
| final MovieTheme theme = MovieTheme.getTheme(context, themeId); |
| |
| final MediaItem firstMediaItem = mediaItems.get(0); |
| if (beginTransition == null) { |
| // Add the begin transition |
| final MovieTransition beginMovieTransition = theme.getBeginTransition(); |
| if (beginMovieTransition != null) { |
| if (firstMediaItem == mediaItem) { |
| videoEditor.addTransition( |
| beginMovieTransition.buildTransition(context, null, mediaItem)); |
| } |
| } |
| } |
| |
| // Add the mid transitions |
| final MovieTransition midMovieTransition = theme.getMidTransition(); |
| if (midMovieTransition != null) { |
| for (int i = 0; i < mediaItemsCount; i++) { |
| final MediaItem mi = mediaItems.get(i); |
| if (mi == mediaItem) { |
| if (i > 0) { // Not the first one |
| if (beginTransition == null) { |
| // Add transition before this media item |
| videoEditor.addTransition(midMovieTransition.buildTransition(context, |
| mediaItems.get(i - 1), mi)); |
| } |
| } |
| |
| if (i < mediaItemsCount - 1) { // Not the last one |
| if (endTransition == null) { |
| // Add the transition after this media item |
| videoEditor.addTransition(midMovieTransition.buildTransition(context, |
| mi, mediaItems.get(i + 1))); |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| if (endTransition == null) { |
| // Add the end transition to the last media item |
| final MovieTransition endMovieTransition = theme.getEndTransition(); |
| final MediaItem lastMediaItem = mediaItems.get(mediaItemsCount - 1); |
| if (endMovieTransition != null && lastMediaItem == mediaItem) { |
| videoEditor.addTransition( |
| endMovieTransition.buildTransition(context, lastMediaItem, null)); |
| } |
| } |
| |
| // Add the overlay |
| final MovieOverlay movieOverlay = theme.getOverlay(); |
| if (movieOverlay != null) { |
| if (firstMediaItem == mediaItem) { |
| // Remove the overlay |
| final List<Overlay> overlays = mediaItem.getAllOverlays(); |
| if (overlays.size() > 0) { |
| mediaItem.removeOverlay(overlays.get(0).getId()); |
| } |
| |
| // Add the new overlay |
| final int scaledWidth, scaledHeight; |
| if (mediaItem instanceof MediaVideoItem) { |
| scaledWidth = ((MediaVideoItem)mediaItem).getWidth(); |
| scaledHeight = ((MediaVideoItem)mediaItem).getHeight(); |
| } else { |
| scaledWidth = ((MediaImageItem)mediaItem).getScaledWidth(); |
| scaledHeight = ((MediaImageItem)mediaItem).getScaledHeight(); |
| } |
| |
| final Overlay overlay = new OverlayFrame(mediaItem, generateId(), |
| ImageUtils.buildOverlayBitmap(getApplicationContext(), null, |
| movieOverlay.getType(), movieOverlay.getTitle(), |
| movieOverlay.getSubtitle(), scaledWidth, scaledHeight), |
| movieOverlay.getStartTime(), |
| Math.min(movieOverlay.getDuration(), |
| mediaItem.getDuration() - movieOverlay.getStartTime())); |
| |
| // Set the user attributes |
| final Bundle userAttributes = movieOverlay.buildUserAttributes(); |
| for (String name : userAttributes.keySet()) { |
| if (MovieOverlay.getAttributeType(name).equals(Integer.class)) { |
| overlay.setUserAttribute(name, |
| Integer.toString(userAttributes.getInt(name))); |
| } else { // Strings |
| overlay.setUserAttribute(name, userAttributes.getString(name)); |
| } |
| } |
| mediaItem.addOverlay(overlay); |
| } |
| } |
| } |
| |
| /** |
| * Apply theme transitions after an item was removed |
| * |
| * @param videoEditor The video editor |
| * @param themeId The theme id |
| * @param removedItemPosition The position of the removed item |
| * @param beginTransition The removed item begin transition |
| * @param endTransition The removed item end transition |
| * |
| * @return The transition that was added |
| */ |
| private Transition applyThemeAfterRemove(VideoEditor videoEditor, String themeId, |
| int removedItemPosition, Transition beginTransition, |
| Transition endTransition) throws IOException { |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| final int mediaItemCount = mediaItems.size(); |
| if (mediaItemCount == 0) { |
| return null; |
| } |
| |
| final Context context = getApplicationContext(); |
| final MovieTheme theme = MovieTheme.getTheme(context, themeId); |
| |
| Transition transition = null; |
| if (removedItemPosition == 0) { // First item removed |
| if (theme.getBeginTransition() != null && beginTransition != null) { |
| transition = |
| theme.getBeginTransition().buildTransition(context, null, mediaItems.get(0)); |
| videoEditor.addTransition(transition); |
| } |
| } else if (removedItemPosition == mediaItemCount) { // Last item removed |
| if (theme.getEndTransition() != null && endTransition != null) { |
| transition = theme.getEndTransition().buildTransition(context, |
| mediaItems.get(mediaItemCount - 1), null); |
| videoEditor.addTransition(transition); |
| } |
| } else { // Mid item removed |
| if (theme.getMidTransition() != null && beginTransition != null) { |
| transition = |
| theme.getMidTransition().buildTransition(context, |
| mediaItems.get(removedItemPosition - 1), |
| mediaItems.get(removedItemPosition)); |
| videoEditor.addTransition(transition); |
| } |
| } |
| |
| return transition; |
| } |
| |
| /** |
| * Apply theme transitions after an item was moved |
| * |
| * @param videoEditor The video editor |
| * @param themeId The theme id |
| * @param movedMediaItem The moved media item |
| * @param originalItemPosition The original media item position |
| * @param beginTransition The moved item begin transition |
| * @param endTransition The moved item end transition |
| */ |
| private void applyThemeAfterMove(VideoEditor videoEditor, String themeId, |
| MediaItem movedMediaItem, int originalItemPosition, Transition beginTransition, |
| Transition endTransition) throws IOException { |
| final List<MediaItem> mediaItems = videoEditor.getAllMediaItems(); |
| final int mediaItemCount = mediaItems.size(); |
| if (mediaItemCount == 0) { |
| return; |
| } |
| |
| final Context context = getApplicationContext(); |
| final MovieTheme theme = MovieTheme.getTheme(context, themeId); |
| |
| if (originalItemPosition == 0) { // First item moved |
| if (theme.getBeginTransition() != null && beginTransition != null) { |
| final Transition transition = |
| theme.getBeginTransition().buildTransition(context, null, mediaItems.get(0)); |
| videoEditor.addTransition(transition); |
| } |
| } else if (originalItemPosition == mediaItemCount - 1) { // Last item moved |
| if (theme.getEndTransition() != null && endTransition != null) { |
| final Transition transition = theme.getEndTransition().buildTransition(context, |
| mediaItems.get(mediaItemCount - 1), null); |
| videoEditor.addTransition(transition); |
| } |
| } else { // Mid item moved |
| final int newPosition = mediaItems.indexOf(movedMediaItem); |
| if (newPosition > originalItemPosition) { // Moved forward |
| if (endTransition != null && theme.getMidTransition() != null) { |
| final Transition transition = theme.getMidTransition().buildTransition( |
| context, mediaItems.get(originalItemPosition - 1), |
| mediaItems.get(originalItemPosition)); |
| videoEditor.addTransition(transition); |
| } |
| } else { // Moved backward |
| if (beginTransition != null && theme.getMidTransition() != null) { |
| final Transition transition = theme.getMidTransition().buildTransition( |
| context, mediaItems.get(originalItemPosition), |
| mediaItems.get(originalItemPosition + 1)); |
| videoEditor.addTransition(transition); |
| } |
| } |
| } |
| |
| // Apply the theme at the new position |
| applyThemeToMediaItem(videoEditor, themeId, movedMediaItem); |
| } |
| |
| /** |
| * Copy the media items |
| * |
| * @param mediaItems The media items |
| * |
| * @return The list of media items |
| */ |
| private List<MovieMediaItem> copyMediaItems(List<MediaItem> mediaItems) { |
| final List<MovieMediaItem> movieMediaItems |
| = new ArrayList<MovieMediaItem>(mediaItems.size()); |
| MovieMediaItem prevMediaItem = null; |
| for (MediaItem mediaItem : mediaItems) { |
| final MovieTransition prevTransition; |
| if (prevMediaItem != null) { |
| prevTransition = prevMediaItem.getEndTransition(); |
| } else if (mediaItem.getBeginTransition() != null) { |
| prevTransition = new MovieTransition(mediaItem.getBeginTransition()); |
| } else { |
| prevTransition = null; |
| } |
| |
| final MovieMediaItem movieMediaItem = new MovieMediaItem(mediaItem, prevTransition); |
| movieMediaItems.add(movieMediaItem); |
| prevMediaItem = movieMediaItem; |
| } |
| |
| return movieMediaItems; |
| } |
| |
| /** |
| * Copy the audio tracks |
| * |
| * @param audioTracks The audio tracks |
| * |
| * @return The list of audio tracks |
| */ |
| private List<MovieAudioTrack> copyAudioTracks(List<AudioTrack> audioTracks) { |
| final List<MovieAudioTrack> movieAudioTracks |
| = new ArrayList<MovieAudioTrack>(audioTracks.size()); |
| for (AudioTrack audioTrack : audioTracks) { |
| movieAudioTracks.add(new MovieAudioTrack(audioTrack)); |
| } |
| return movieAudioTracks; |
| } |
| |
| private static void logd(String message) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, message); |
| } |
| } |
| |
| private static void logv(String message) { |
| if (Log.isLoggable(TAG, Log.VERBOSE)) { |
| Log.v(TAG, message); |
| } |
| } |
| |
| /** |
| * Worker thread that processes intents and maintains its own intent queue. |
| */ |
| private class IntentProcessor extends Thread { |
| private final BlockingQueue<Intent> mIntentQueue; |
| |
| public IntentProcessor(String threadName) { |
| super("IntentProcessor-" + threadName); |
| mIntentQueue = new LinkedBlockingQueue<Intent>(); |
| } |
| |
| @Override |
| public void run() { |
| try { |
| while(true) { |
| processIntent(mIntentQueue.take()); |
| } |
| } catch (InterruptedException e) { |
| Log.e(TAG, "Terminating " + getName()); |
| } |
| } |
| |
| /** |
| * Submits a new intent for processing. |
| * |
| * @param intent The intent to be processed |
| */ |
| public void submit(Intent intent) { |
| if (isAlive()) { |
| mIntentQueue.add(intent); |
| } else { |
| Log.e(TAG, getName() + " should be started before submitting tasks."); |
| } |
| } |
| |
| /** |
| * Removes an intent from the queue. |
| * |
| * @param intent The intent to be removed |
| * |
| * @return true if the intent is removed |
| */ |
| public boolean cancel(Intent intent) { |
| return mIntentQueue.remove(intent); |
| } |
| |
| public Iterator<Intent> getIntentQueueIterator() { |
| return mIntentQueue.iterator(); |
| } |
| |
| public void quit() { |
| // Display an error if the queue is not empty and clear it. |
| final int queueSize = mIntentQueue.size(); |
| if (queueSize > 0) { |
| Log.e(TAG, "Thread queue is not empty. Size: " + queueSize); |
| mIntentQueue.clear(); |
| } |
| interrupt(); |
| } |
| } |
| } |