Merge "Bug fix: 3421011 ANR during delete video clip" into honeycomb

This commit is contained in:
Gil Dobjanschi
2011-02-03 17:43:09 -08:00
committed by Android (Google) Code Review
2 changed files with 124 additions and 70 deletions

View File

@@ -23,7 +23,6 @@ import java.nio.IntBuffer;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@@ -58,6 +57,10 @@ class MediaArtistNativeHelper {
private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG); private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG);
private final VideoEditor mVideoEditor; private final VideoEditor mVideoEditor;
/*
* Semaphore to control preview calls
*/
private final Semaphore mLock;
private EditSettings mStoryBoardSettings; private EditSettings mStoryBoardSettings;
@@ -79,11 +82,6 @@ class MediaArtistNativeHelper {
private int mProgressToApp; private int mProgressToApp;
/*
* Semaphore to control preview calls
*/
private final Semaphore mLock = new Semaphore(1, true);
private String mRenderPreviewOverlayFile; private String mRenderPreviewOverlayFile;
private int mRenderPreviewRenderingMode; private int mRenderPreviewRenderingMode;
@@ -1775,9 +1773,10 @@ class MediaArtistNativeHelper {
* *
* @param projectPath The path where the VideoEditor stores all files * @param projectPath The path where the VideoEditor stores all files
* related to the project * related to the project
* @param lock The semaphore
* @param veObj The video editor reference * @param veObj The video editor reference
*/ */
public MediaArtistNativeHelper(String projectPath, VideoEditor veObj) { public MediaArtistNativeHelper(String projectPath, Semaphore lock, VideoEditor veObj) {
mProjectPath = projectPath; mProjectPath = projectPath;
if (veObj != null) { if (veObj != null) {
mVideoEditor = veObj; mVideoEditor = veObj;
@@ -1785,8 +1784,11 @@ class MediaArtistNativeHelper {
mVideoEditor = null; mVideoEditor = null;
throw new IllegalArgumentException("video editor object is null"); throw new IllegalArgumentException("video editor object is null");
} }
if (mStoryBoardSettings == null) if (mStoryBoardSettings == null) {
mStoryBoardSettings = new EditSettings(); mStoryBoardSettings = new EditSettings();
}
mLock = lock;
_init(mProjectPath, "null"); _init(mProjectPath, "null");
mAudioTrackPCMFilePath = null; mAudioTrackPCMFilePath = null;
@@ -1932,16 +1934,8 @@ class MediaArtistNativeHelper {
/** /**
* Release the native helper object * Release the native helper object
*/ */
void releaseNativeHelper() { void releaseNativeHelper() throws InterruptedException {
try { release();
release();
} catch (IllegalStateException ex) {
Log.e(TAG, "Illegal State exeption caught in releaseNativeHelper");
throw ex;
} catch (RuntimeException ex) {
Log.e(TAG, "Runtime exeption caught in releaseNativeHelper");
throw ex;
}
} }
/** /**
@@ -3735,18 +3729,15 @@ class MediaArtistNativeHelper {
*/ */
Bitmap getPixels(String inputFile, int width, int height, long timeMS) { Bitmap getPixels(String inputFile, int width, int height, long timeMS) {
if (inputFile == null) { if (inputFile == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException("Invalid input file");
} }
int newWidth = 0;
int newHeight = 0;
Bitmap tempBitmap = null;
/* Make width and height as even */ /* Make width and height as even */
newWidth = (width + 1) & 0xFFFFFFFE; final int newWidth = (width + 1) & 0xFFFFFFFE;
newHeight = (height + 1) & 0xFFFFFFFE; final int newHeight = (height + 1) & 0xFFFFFFFE;
/* Create a temp bitmap for resized thumbnails */ /* Create a temp bitmap for resized thumbnails */
Bitmap tempBitmap = null;
if ((newWidth != width) || (newHeight != height)) { if ((newWidth != width) || (newHeight != height)) {
tempBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); tempBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
} }
@@ -3770,6 +3761,7 @@ class MediaArtistNativeHelper {
if (tempBitmap != null) { if (tempBitmap != null) {
tempBitmap.recycle(); tempBitmap.recycle();
} }
return bitmap; return bitmap;
} }
@@ -3787,17 +3779,15 @@ class MediaArtistNativeHelper {
* *
* @return The frames as bitmaps in bitmap array * @return The frames as bitmaps in bitmap array
**/ **/
public Bitmap[] getPixelsList(String filename, int width, int height, long startMs, long endMs, Bitmap[] getPixelsList(String filename, int width, int height, long startMs, long endMs,
int thumbnailCount) { int thumbnailCount) {
int[] rgb888 = null; int[] rgb888 = null;
int thumbnailSize = 0; int thumbnailSize = 0;
int newWidth = 0;
int newHeight = 0;
Bitmap tempBitmap = null; Bitmap tempBitmap = null;
/* Make width and height as even */ /* Make width and height as even */
newWidth = (width + 1) & 0xFFFFFFFE; final int newWidth = (width + 1) & 0xFFFFFFFE;
newHeight = (height + 1) & 0xFFFFFFFE; final int newHeight = (height + 1) & 0xFFFFFFFE;
thumbnailSize = newWidth * newHeight * 4; thumbnailSize = newWidth * newHeight * 4;
/* Create a temp bitmap for resized thumbnails */ /* Create a temp bitmap for resized thumbnails */
@@ -3820,7 +3810,8 @@ class MediaArtistNativeHelper {
bitmaps = new Bitmap[MAX_THUMBNAIL_PERMITTED]; bitmaps = new Bitmap[MAX_THUMBNAIL_PERMITTED];
thumbnailCount = MAX_THUMBNAIL_PERMITTED; thumbnailCount = MAX_THUMBNAIL_PERMITTED;
} catch (Throwable ex) { } catch (Throwable ex) {
throw new RuntimeException("Memory allocation fails, thumbnail count too large: "+thumbnailCount); throw new RuntimeException("Memory allocation fails, thumbnail count too large: "
+ thumbnailCount);
} }
} }
IntBuffer tmpBuffer = IntBuffer.allocate(thumbnailSize); IntBuffer tmpBuffer = IntBuffer.allocate(thumbnailSize);
@@ -3848,6 +3839,7 @@ class MediaArtistNativeHelper {
if (tempBitmap != null) { if (tempBitmap != null) {
tempBitmap.recycle(); tempBitmap.recycle();
} }
return bitmaps; return bitmaps;
} }
@@ -3908,7 +3900,7 @@ class MediaArtistNativeHelper {
* *
* @throws InterruptedException * @throws InterruptedException
*/ */
void lock() throws InterruptedException { private void lock() throws InterruptedException {
if (Log.isLoggable(TAG, Log.DEBUG)) { if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbing semaphore", new Throwable()); Log.d(TAG, "lock: grabbing semaphore", new Throwable());
} }
@@ -3918,31 +3910,10 @@ class MediaArtistNativeHelper {
} }
} }
/**
* Tries to grab the semaphore with a specified time out which arbitrates access to the editor
*
* @param timeoutMs time out in ms.
*
* @return true if the semaphore is acquired, false otherwise
* @throws InterruptedException
*/
boolean lock(long timeoutMs) throws InterruptedException {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbing semaphore with timeout " + timeoutMs, new Throwable());
}
boolean acquireSem = mLock.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbed semaphore status " + acquireSem);
}
return acquireSem;
}
/** /**
* Release the semaphore which arbitrates access to the editor * Release the semaphore which arbitrates access to the editor
*/ */
void unlock() { private void unlock() {
if (Log.isLoggable(TAG, Log.DEBUG)) { if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "unlock: releasing semaphore"); Log.d(TAG, "unlock: releasing semaphore");
} }

View File

@@ -27,6 +27,9 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
@@ -118,11 +121,12 @@ public class VideoEditorImpl implements VideoEditor {
/* /*
* Instance variables * Instance variables
*/ */
private long mDurationMs; private final Semaphore mLock;
private final String mProjectPath; private final String mProjectPath;
private final List<MediaItem> mMediaItems = new ArrayList<MediaItem>(); private final List<MediaItem> mMediaItems = new ArrayList<MediaItem>();
private final List<AudioTrack> mAudioTracks = new ArrayList<AudioTrack>(); private final List<AudioTrack> mAudioTracks = new ArrayList<AudioTrack>();
private final List<Transition> mTransitions = new ArrayList<Transition>(); private final List<Transition> mTransitions = new ArrayList<Transition>();
private long mDurationMs;
private int mAspectRatio; private int mAspectRatio;
/* /*
@@ -138,7 +142,8 @@ public class VideoEditorImpl implements VideoEditor {
* related to the project * related to the project
*/ */
public VideoEditorImpl(String projectPath) throws IOException { public VideoEditorImpl(String projectPath) throws IOException {
mMANativeHelper = new MediaArtistNativeHelper(projectPath, this); mLock = new Semaphore(1, true);
mMANativeHelper = new MediaArtistNativeHelper(projectPath, mLock, this);
mProjectPath = projectPath; mProjectPath = projectPath;
final File projectXml = new File(projectPath, PROJECT_FILENAME); final File projectXml = new File(projectPath, PROJECT_FILENAME);
if (projectXml.exists()) { if (projectXml.exists()) {
@@ -417,15 +422,20 @@ public class VideoEditorImpl implements VideoEditor {
boolean semAcquireDone = false; boolean semAcquireDone = false;
try { try {
mMANativeHelper.lock(); lock();
semAcquireDone = true; semAcquireDone = true;
if (mMANativeHelper == null) {
throw new IllegalStateException("The video editor is not initialized");
}
mMANativeHelper.export(filename, mProjectPath, height,bitrate, mMANativeHelper.export(filename, mProjectPath, height,bitrate,
mMediaItems, mTransitions, mAudioTracks, listener); mMediaItems, mTransitions, mAudioTracks, listener);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.e(TAG, "Sem acquire NOT successful in export"); Log.e(TAG, "Sem acquire NOT successful in export");
} finally { } finally {
if (semAcquireDone) { if (semAcquireDone) {
mMANativeHelper.unlock(); unlock();
} }
} }
} }
@@ -436,9 +446,13 @@ public class VideoEditorImpl implements VideoEditor {
public void generatePreview(MediaProcessingProgressListener listener) { public void generatePreview(MediaProcessingProgressListener listener) {
boolean semAcquireDone = false; boolean semAcquireDone = false;
try { try {
mMANativeHelper.lock(); lock();
semAcquireDone = true; semAcquireDone = true;
if (mMANativeHelper == null) {
throw new IllegalStateException("The video editor is not initialized");
}
if ((mMediaItems.size() > 0) || (mAudioTracks.size() > 0)) { if ((mMediaItems.size() > 0) || (mAudioTracks.size() > 0)) {
mMANativeHelper.previewStoryBoard(mMediaItems, mTransitions, mAudioTracks, mMANativeHelper.previewStoryBoard(mMediaItems, mTransitions, mAudioTracks,
listener); listener);
@@ -447,7 +461,7 @@ public class VideoEditorImpl implements VideoEditor {
Log.e(TAG, "Sem acquire NOT successful in previewStoryBoard"); Log.e(TAG, "Sem acquire NOT successful in previewStoryBoard");
} finally { } finally {
if (semAcquireDone) { if (semAcquireDone) {
mMANativeHelper.unlock(); unlock();
} }
} }
} }
@@ -675,11 +689,26 @@ public class VideoEditorImpl implements VideoEditor {
*/ */
public void release() { public void release() {
stopPreview(); stopPreview();
mMediaItems.clear();
mAudioTracks.clear(); boolean semAcquireDone = false;
mTransitions.clear(); try {
mMANativeHelper.releaseNativeHelper(); lock();
mMANativeHelper = null; semAcquireDone = true;
if (mMANativeHelper != null) {
mMediaItems.clear();
mAudioTracks.clear();
mTransitions.clear();
mMANativeHelper.releaseNativeHelper();
mMANativeHelper = null;
}
} catch (Exception ex) {
Log.e(TAG, "Sem acquire NOT successful in export", ex);
} finally {
if (semAcquireDone) {
unlock();
}
}
} }
/* /*
@@ -854,11 +883,15 @@ public class VideoEditorImpl implements VideoEditor {
boolean semAcquireDone = false; boolean semAcquireDone = false;
try { try {
semAcquireDone = mMANativeHelper.lock(ENGINE_ACCESS_MAX_TIMEOUT_MS); semAcquireDone = lock(ENGINE_ACCESS_MAX_TIMEOUT_MS);
if (semAcquireDone == false) { if (semAcquireDone == false) {
throw new IllegalStateException("Timeout waiting for semaphore"); throw new IllegalStateException("Timeout waiting for semaphore");
} }
if (mMANativeHelper == null) {
throw new IllegalStateException("The video editor is not initialized");
}
if (mMediaItems.size() > 0) { if (mMediaItems.size() > 0) {
final Rect frame = surfaceHolder.getSurfaceFrame(); final Rect frame = surfaceHolder.getSurfaceFrame();
result = mMANativeHelper.renderPreviewFrame(surface, result = mMANativeHelper.renderPreviewFrame(surface,
@@ -871,7 +904,7 @@ public class VideoEditorImpl implements VideoEditor {
throw new IllegalStateException("The thread was interrupted"); throw new IllegalStateException("The thread was interrupted");
} finally { } finally {
if (semAcquireDone) { if (semAcquireDone) {
mMANativeHelper.unlock(); unlock();
} }
} }
return result; return result;
@@ -1568,11 +1601,15 @@ public class VideoEditorImpl implements VideoEditor {
boolean semAcquireDone = false; boolean semAcquireDone = false;
if (!mPreviewInProgress) { if (!mPreviewInProgress) {
try{ try{
semAcquireDone = mMANativeHelper.lock(ENGINE_ACCESS_MAX_TIMEOUT_MS); semAcquireDone = lock(ENGINE_ACCESS_MAX_TIMEOUT_MS);
if (semAcquireDone == false) { if (semAcquireDone == false) {
throw new IllegalStateException("Timeout waiting for semaphore"); throw new IllegalStateException("Timeout waiting for semaphore");
} }
if (mMANativeHelper == null) {
throw new IllegalStateException("The video editor is not initialized");
}
if (mMediaItems.size() > 0) { if (mMediaItems.size() > 0) {
mPreviewInProgress = true; mPreviewInProgress = true;
mMANativeHelper.previewStoryBoard(mMediaItems, mTransitions, mMANativeHelper.previewStoryBoard(mMediaItems, mTransitions,
@@ -1581,7 +1618,7 @@ public class VideoEditorImpl implements VideoEditor {
callbackAfterFrameCount, listener); callbackAfterFrameCount, listener);
} }
/** /**
* release on complete by calling stopPreview * Release The lock on complete by calling stopPreview
*/ */
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(TAG, "The thread was interrupted", new Throwable()); Log.w(TAG, "The thread was interrupted", new Throwable());
@@ -1605,7 +1642,7 @@ public class VideoEditorImpl implements VideoEditor {
*/ */
} finally { } finally {
mPreviewInProgress = false; mPreviewInProgress = false;
mMANativeHelper.unlock(); unlock();
} }
return result; return result;
} }
@@ -1791,4 +1828,50 @@ public class VideoEditorImpl implements VideoEditor {
Log.w(TAG, "Native helper was not ready!"); Log.w(TAG, "Native helper was not ready!");
} }
} }
/**
* Grab the semaphore which arbitrates access to the editor
*
* @throws InterruptedException
*/
private void lock() throws InterruptedException {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbing semaphore", new Throwable());
}
mLock.acquire();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbed semaphore");
}
}
/**
* Tries to grab the semaphore with a specified time out which arbitrates access to the editor
*
* @param timeoutMs time out in ms.
*
* @return true if the semaphore is acquired, false otherwise
* @throws InterruptedException
*/
private boolean lock(long timeoutMs) throws InterruptedException {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbing semaphore with timeout " + timeoutMs, new Throwable());
}
boolean acquireSem = mLock.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "lock: grabbed semaphore status " + acquireSem);
}
return acquireSem;
}
/**
* Release the semaphore which arbitrates access to the editor
*/
private void unlock() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "unlock: releasing semaphore");
}
mLock.release();
}
} }