Merge "Adding simple crop ui. Refactoring." into gb-ub-photos-bryce
diff --git a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
index b0b1fe0..864e130 100644
--- a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
+++ b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
@@ -37,6 +37,7 @@
public static final int ICE_CREAM_SANDWICH_MR1 = 15;
public static final int JELLY_BEAN = 16;
public static final int JELLY_BEAN_MR1 = 17;
+ public static final int JELLY_BEAN_MR2 = 18;
}
public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16;
@@ -188,6 +189,9 @@
public static final boolean HAS_CANCELLATION_SIGNAL =
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
+ public static final boolean HAS_MEDIA_MUXER =
+ Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2;
+
public static int getIntFieldIfExists(Class<?> klass, String fieldName,
Class<?> obj, int defaultVal) {
try {
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
index 7a75a6d..a55cc9d 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -23,6 +23,7 @@
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
@@ -48,37 +49,9 @@
private static final String TAG = "CamreaFilmStripDataProvider";
private static final int DEFAULT_DECODE_SIZE = 3000;
- private static final String ORDER_CLAUSE = ImageColumns.DATE_TAKEN + " DESC, "
- + ImageColumns._ID + " DESC";
private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" };
- private static final int COL_ID = 0;
- private static final int COL_TITLE = 1;
- private static final int COL_MIME_TYPE = 2;
- private static final int COL_DATE_TAKEN = 3;
- private static final int COL_DATE_MODIFIED = 4;
- private static final int COL_DATA = 5;
- private static final int COL_ORIENTATION = 6;
- private static final int COL_WIDTH = 7;
- private static final int COL_HEIGHT = 8;
- private static final int COL_SIZE = 9;
- private static final String[] PROJECTION = {
- ImageColumns._ID, // 0, int
- ImageColumns.TITLE, // 1, string
- ImageColumns.MIME_TYPE, // 2, tring
- ImageColumns.DATE_TAKEN, // 3, int
- ImageColumns.DATE_MODIFIED, // 4, int
- ImageColumns.DATA, // 5, string
- ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270
- ImageColumns.WIDTH, // 7, int
- ImageColumns.HEIGHT, // 8, int
- ImageColumns.SIZE // 9, int
- };
-
- // 32K buffer.
- private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024];
-
- private List<LocalImageData> mImages;
+ private List<LocalData> mImages;
private Listener mListener;
private View mCameraPreviewView;
@@ -92,8 +65,8 @@
mPlaceHolder = new ColorDrawable(placeHolderColor);
}
- public void setCameraPreviewInfo(int width, int height, int orientation) {
- addOrReplaceCameraData(buildCameraImageData(width, height, orientation));
+ public void setCameraPreviewInfo(int width, int height) {
+ addOrReplaceCameraData(buildCameraImageData(width, height));
}
@Override
@@ -117,7 +90,6 @@
}
}
- @Override
public void requestLoad(ContentResolver resolver) {
QueryTask qtask = new QueryTask();
qtask.execute(resolver);
@@ -129,17 +101,8 @@
return null;
}
- LocalImageData data = mImages.get(dataID);
-
- if (data.isCameraData) return mCameraPreviewView;
-
- ImageView v = new ImageView(c);
- v.setImageDrawable(mPlaceHolder);
-
- v.setScaleType(ImageView.ScaleType.FIT_XY);
- LoadBitmapTask task = new LoadBitmapTask(data, v);
- task.execute();
- return v;
+ return mImages.get(dataID).getView(
+ c, mSuggestedWidth, mSuggestedHeight, mPlaceHolder);
}
@Override
@@ -147,88 +110,74 @@
mListener = listener;
}
- private LocalImageData buildCameraImageData(int width, int height, int orientation) {
- LocalImageData d = new LocalImageData();
- d.width = width;
- d.height = height;
- d.orientation = orientation;
- d.isCameraData = true;
- d.supportedAction = ImageData.ACTION_NONE;
+ private LocalData buildCameraImageData(int width, int height) {
+ LocalData d = new CameraPreviewData(width, height);
return d;
}
- private void addOrReplaceCameraData(LocalImageData data) {
- if (mImages == null) mImages = new ArrayList<LocalImageData>();
+ private void addOrReplaceCameraData(LocalData data) {
+ if (mImages == null) mImages = new ArrayList<LocalData>();
if (mImages.size() == 0) {
mImages.add(0, data);
return;
}
- LocalImageData first = mImages.get(0);
- if (first.isCameraData) {
+ LocalData first = mImages.get(0);
+ if (first.getType() == ImageData.TYPE_CAMERA_PREVIEW) {
mImages.set(0, data);
} else {
mImages.add(0, data);
}
}
- private LocalImageData buildCursorImageData(Cursor c) {
- LocalImageData d = new LocalImageData();
- d.id = c.getInt(COL_ID);
- d.title = c.getString(COL_TITLE);
- d.mimeType = c.getString(COL_MIME_TYPE);
- d.path = c.getString(COL_DATA);
- d.orientation = c.getInt(COL_ORIENTATION);
- d.width = c.getInt(COL_WIDTH);
- d.height = c.getInt(COL_HEIGHT);
- d.supportedAction = ImageData.ACTION_PROMOTE | ImageData.ACTION_DEMOTE;
- if (d.width <= 0 || d.height <= 0) {
- Log.v(TAG, "warning! zero dimension for "
- + d.path + ":" + d.width + "x" + d.height);
- Dimension dim = decodeDimension(d.path);
- if (dim != null) {
- d.width = dim.width;
- d.height = dim.height;
- } else {
- Log.v(TAG, "warning! dimension decode failed for " + d.path);
- Bitmap b = BitmapFactory.decodeFile(d.path);
- if (b == null) return null;
- d.width = b.getWidth();
- d.height = b.getHeight();
+ private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
+ @Override
+ protected List<LocalData> doInBackground(ContentResolver... resolver) {
+ List<LocalData> l = null;
+ Cursor c = resolver[0].query(
+ Images.Media.EXTERNAL_CONTENT_URI,
+ LocalPhotoData.QUERY_PROJECTION,
+ MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
+ LocalPhotoData.QUERY_ORDER);
+ if (c == null) return null;
+
+ // build up the list.
+ l = new ArrayList<LocalData>();
+ c.moveToFirst();
+ while (!c.isLast()) {
+ LocalData data = LocalPhotoData.buildFromCursor(c);
+ if (data != null) {
+ l.add(data);
+ } else {
+ Log.e(TAG, "Error decoding file:"
+ + c.getString(LocalPhotoData.COL_DATA));
+ }
+ c.moveToNext();
}
+ c.close();
+ return l;
}
- if (d.orientation == 90 || d.orientation == 270) {
- int b = d.width;
- d.width = d.height;
- d.height = b;
+
+ @Override
+ protected void onPostExecute(List<LocalData> l) {
+ boolean changed = (l != mImages);
+ LocalData first = null;
+ if (mImages != null && mImages.size() > 0) {
+ first = mImages.get(0);
+ if (first.getType() != ImageData.TYPE_CAMERA_PREVIEW) first = null;
+ }
+ mImages = l;
+ if (first != null) addOrReplaceCameraData(first);
+ // both might be null.
+ if (changed) mListener.onDataLoaded();
}
- return d;
}
- private Dimension decodeDimension(String path) {
- BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inJustDecodeBounds = true;
- Bitmap b = BitmapFactory.decodeFile(path, opts);
- if (b == null) return null;
- Dimension d = new Dimension();
- d.width = opts.outWidth;
- d.height = opts.outHeight;
- return d;
- }
-
- private class Dimension {
- public int width;
- public int height;
- }
-
- private class LocalImageData implements FilmStripView.ImageData {
- public boolean isCameraData;
+ private abstract static class LocalData implements FilmStripView.ImageData {
public int id;
public String title;
public String mimeType;
public String path;
- // from MediaStore, can only be 0, 90, 180, 270;
- public int orientation;
// width and height should be adjusted according to orientation.
public int width;
public int height;
@@ -245,101 +194,194 @@
}
@Override
- public int getType() {
- if (isCameraData) return ImageData.TYPE_CAMERA_PREVIEW;
- return ImageData.TYPE_PHOTO;
+ public boolean isActionSupported(int action) {
+ return false;
}
@Override
- public boolean isActionSupported(int action) {
- return ((action & supportedAction) != 0);
+ abstract public int getType();
+
+ abstract View getView(Context c, int width, int height, Drawable placeHolder);
+ }
+
+ private class CameraPreviewData extends LocalData {
+ private int mWidth;
+ private int mHeight;
+
+ CameraPreviewData(int w, int h) {
+ mWidth = w;
+ mHeight = h;
}
@Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getType() {
+ return ImageData.TYPE_CAMERA_PREVIEW;
+ }
+
+ @Override
+ View getView(Context c, int width, int height, Drawable placeHolder) {
+ return mCameraPreviewView;
+ }
+ }
+
+ private static class LocalPhotoData extends LocalData {
+ static final String QUERY_ORDER = ImageColumns.DATE_TAKEN + " DESC, "
+ + ImageColumns._ID + " DESC";
+ static final String[] QUERY_PROJECTION = {
+ ImageColumns._ID, // 0, int
+ ImageColumns.TITLE, // 1, string
+ ImageColumns.MIME_TYPE, // 2, tring
+ ImageColumns.DATE_TAKEN, // 3, int
+ ImageColumns.DATE_MODIFIED, // 4, int
+ ImageColumns.DATA, // 5, string
+ ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270
+ ImageColumns.WIDTH, // 7, int
+ ImageColumns.HEIGHT, // 8, int
+ ImageColumns.SIZE // 9, int
+ };
+
+ private static final int COL_ID = 0;
+ private static final int COL_TITLE = 1;
+ private static final int COL_MIME_TYPE = 2;
+ private static final int COL_DATE_TAKEN = 3;
+ private static final int COL_DATE_MODIFIED = 4;
+ private static final int COL_DATA = 5;
+ private static final int COL_ORIENTATION = 6;
+ private static final int COL_WIDTH = 7;
+ private static final int COL_HEIGHT = 8;
+ private static final int COL_SIZE = 9;
+
+
+ // 32K buffer.
+ private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024];
+
+ // from MediaStore, can only be 0, 90, 180, 270;
+ public int orientation;
+
+ static LocalPhotoData buildFromCursor(Cursor c) {
+ LocalPhotoData d = new LocalPhotoData();
+ d.id = c.getInt(COL_ID);
+ d.title = c.getString(COL_TITLE);
+ d.mimeType = c.getString(COL_MIME_TYPE);
+ d.path = c.getString(COL_DATA);
+ d.orientation = c.getInt(COL_ORIENTATION);
+ d.width = c.getInt(COL_WIDTH);
+ d.height = c.getInt(COL_HEIGHT);
+ if (d.width <= 0 || d.height <= 0) {
+ Log.v(TAG, "warning! zero dimension for "
+ + d.path + ":" + d.width + "x" + d.height);
+ Dimension dim = decodeDimension(d.path);
+ if (dim != null) {
+ d.width = dim.width;
+ d.height = dim.height;
+ } else {
+ Log.v(TAG, "warning! dimension decode failed for " + d.path);
+ Bitmap b = BitmapFactory.decodeFile(d.path);
+ if (b == null) return null;
+ d.width = b.getWidth();
+ d.height = b.getHeight();
+ }
+ }
+ if (d.orientation == 90 || d.orientation == 270) {
+ int b = d.width;
+ d.width = d.height;
+ d.height = b;
+ }
+ return d;
+ }
+
+ @Override
+ View getView(Context c,
+ int decodeWidth, int decodeHeight, Drawable placeHolder) {
+ ImageView v = new ImageView(c);
+ v.setImageDrawable(placeHolder);
+
+ v.setScaleType(ImageView.ScaleType.FIT_XY);
+ LoadBitmapTask task = new LoadBitmapTask(v, decodeWidth, decodeHeight);
+ task.execute();
+ return v;
+ }
+
+
+ @Override
public String toString() {
- return "LocalImageData:" + ",data=" + path + ",mimeType=" + mimeType
+ return "LocalPhotoData:" + ",data=" + path + ",mimeType=" + mimeType
+ "," + width + "x" + height + ",orientation=" + orientation;
}
- }
-
- private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalImageData>> {
- private ContentResolver mResolver;
- private LocalImageData mCameraImageData;
@Override
- protected List<LocalImageData> doInBackground(ContentResolver... resolver) {
- List<LocalImageData> l = null;
- Cursor c = resolver[0].query(Images.Media.EXTERNAL_CONTENT_URI, PROJECTION,
- MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
- ORDER_CLAUSE);
- if (c == null) return null;
- l = new ArrayList<LocalImageData>();
- c.moveToFirst();
- while (!c.isLast()) {
- LocalImageData data = buildCursorImageData(c);
- if (data != null) l.add(data);
- else Log.e(TAG, "Error decoding file:" + c.getString(COL_DATA));
- c.moveToNext();
- }
- c.close();
- return l;
+ public int getType() {
+ return TYPE_PHOTO;
}
- @Override
- protected void onPostExecute(List<LocalImageData> l) {
- boolean changed = (l != mImages);
- LocalImageData first = null;
- if (mImages != null && mImages.size() > 0) {
- first = mImages.get(0);
- if (!first.isCameraData) first = null;
- }
- mImages = l;
- if (first != null) addOrReplaceCameraData(first);
- // both might be null.
- if (changed && mListener != null) mListener.onDataLoaded();
- }
- }
-
- private class LoadBitmapTask extends AsyncTask<Void, Void, Bitmap> {
- private LocalImageData mData;
- private ImageView mView;
-
- public LoadBitmapTask(
- LocalImageData d, ImageView v) {
- mData = d;
- mView = v;
+ private static Dimension decodeDimension(String path) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inJustDecodeBounds = true;
+ Bitmap b = BitmapFactory.decodeFile(path, opts);
+ if (b == null) return null;
+ Dimension d = new Dimension();
+ d.width = opts.outWidth;
+ d.height = opts.outHeight;
+ return d;
}
- @Override
- protected Bitmap doInBackground(Void... v) {
- BitmapFactory.Options opts = null;
- Bitmap b;
- int sample = 1;
- while (mSuggestedWidth * sample < mData.width
- || mSuggestedHeight * sample < mData.height) {
- sample *= 2;
+ private static class Dimension {
+ public int width;
+ public int height;
+ }
+
+ private class LoadBitmapTask extends AsyncTask<Void, Void, Bitmap> {
+ private ImageView mView;
+ private int mDecodeWidth;
+ private int mDecodeHeight;
+
+ public LoadBitmapTask(ImageView v, int decodeWidth, int decodeHeight) {
+ mView = v;
+ mDecodeWidth = decodeWidth;
+ mDecodeHeight = decodeHeight;
}
- opts = new BitmapFactory.Options();
- opts.inSampleSize = sample;
- opts.inTempStorage = DECODE_TEMP_STORAGE;
- if (isCancelled()) return null;
- b = BitmapFactory.decodeFile(mData.path, opts);
- if (mData.orientation != 0) {
+
+ @Override
+ protected Bitmap doInBackground(Void... v) {
+ BitmapFactory.Options opts = null;
+ Bitmap b;
+ int sample = 1;
+ while (mDecodeWidth * sample < width
+ || mDecodeHeight * sample < height) {
+ sample *= 2;
+ }
+ opts = new BitmapFactory.Options();
+ opts.inSampleSize = sample;
+ opts.inTempStorage = DECODE_TEMP_STORAGE;
if (isCancelled()) return null;
- Matrix m = new Matrix();
- m.setRotate((float) mData.orientation);
- b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
+ b = BitmapFactory.decodeFile(path, opts);
+ if (orientation != 0) {
+ if (isCancelled()) return null;
+ Matrix m = new Matrix();
+ m.setRotate((float) orientation);
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
+ }
+ return b;
}
- return b;
- }
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap == null) {
- Log.e(TAG, "Cannot decode bitmap file:" + mData.path);
- return;
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap == null) {
+ Log.e(TAG, "Cannot decode bitmap file:" + path);
+ return;
+ }
+ mView.setImageBitmap(bitmap);
}
- mView.setImageBitmap(bitmap);
}
}
}
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java
index f2ffa87..eea9f01 100644
--- a/src/com/android/camera/ui/FilmStripView.java
+++ b/src/com/android/camera/ui/FilmStripView.java
@@ -88,7 +88,6 @@
public ImageData getImageData(int id);
public void suggestSize(int w, int h);
- public void requestLoad(ContentResolver r);
public void setListener(Listener listener);
}
diff --git a/src/com/android/gallery3d/app/TrimVideo.java b/src/com/android/gallery3d/app/TrimVideo.java
index baab889..1e77281 100644
--- a/src/com/android/gallery3d/app/TrimVideo.java
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -237,7 +237,7 @@
public void run() {
try {
VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile,
- mTrimStartTime, mTrimEndTime, mVideoView.getDuration());
+ mTrimStartTime, mTrimEndTime);
// Update the database for adding a new video file.
SaveVideoFileUtils.insertContent(mDstFileInfo,
getContentResolver(), mUri);
diff --git a/src/com/android/gallery3d/app/VideoUtils.java b/src/com/android/gallery3d/app/VideoUtils.java
index 8ffc3d5..a3c3ef2 100644
--- a/src/com/android/gallery3d/app/VideoUtils.java
+++ b/src/com/android/gallery3d/app/VideoUtils.java
@@ -19,6 +19,14 @@
package com.android.gallery3d.app;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaMuxer;
+import android.util.Log;
+
+import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.util.SaveVideoFileInfo;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.TimeToSampleBox;
@@ -29,17 +37,49 @@
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class VideoUtils {
+ private static final String LOGTAG = "VideoUtils";
+ private static final int DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024;
- public static void startMute(String filePath, SaveVideoFileInfo dstFileInfo) throws IOException {
+ /**
+ * Remove the sound track.
+ */
+ public static void startMute(String filePath, SaveVideoFileInfo dstFileInfo)
+ throws IOException {
+ if (ApiHelper.HAS_MEDIA_MUXER) {
+ genVideoUsingMuxer(filePath, dstFileInfo.mFile.getPath(), -1, -1,
+ false, true);
+ } else {
+ startMuteUsingMp4Parser(filePath, dstFileInfo);
+ }
+ }
+
+ /**
+ * Shortens/Crops tracks
+ */
+ public static void startTrim(File src, File dst, int startMs, int endMs)
+ throws IOException {
+ if (ApiHelper.HAS_MEDIA_MUXER) {
+ genVideoUsingMuxer(src.getPath(), dst.getPath(), startMs, endMs,
+ true, true);
+ } else {
+ trimUsingMp4Parser(src, dst, startMs, endMs);
+ }
+ }
+
+ private static void startMuteUsingMp4Parser(String filePath,
+ SaveVideoFileInfo dstFileInfo) throws FileNotFoundException, IOException {
File dst = dstFileInfo.mFile;
File src = new File(filePath);
RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
@@ -67,16 +107,123 @@
IsoFile out = new DefaultMp4Builder().build(movie);
FileOutputStream fos = new FileOutputStream(dst);
FileChannel fc = fos.getChannel();
- out.getBox(fc); // This one build up the memory.
+ out.getBox(fc); // This one build up the memory.
fc.close();
fos.close();
}
/**
- * Shortens/Crops a track
+ * @param srcPath the path of source video file.
+ * @param dstPath the path of destination video file.
+ * @param startMs starting time in milliseconds for trimming. Set to
+ * negative if starting from beginning.
+ * @param endMs end time for trimming in milliseconds. Set to negative if
+ * no trimming at the end.
+ * @param useAudio true if keep the audio track from the source.
+ * @param useVideo true if keep the video track from the source.
+ * @throws IOException
*/
- public static void startTrim(File src, File dst, int startMs, int endMs, int totalMs) throws IOException {
+ private static void genVideoUsingMuxer(String srcPath, String dstPath,
+ int startMs, int endMs, boolean useAudio, boolean useVideo)
+ throws IOException {
+ // Set up MediaExtractor to read from the source.
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(srcPath);
+
+ int trackCount = extractor.getTrackCount();
+
+ // Set up MediaMuxer for the destination.
+ MediaMuxer muxer;
+ muxer = new MediaMuxer(dstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+
+ // Set up the tracks and retrieve the max buffer size for selected
+ // tracks.
+ HashMap<Integer, Integer> indexMap = new HashMap<Integer,
+ Integer>(trackCount);
+ int bufferSize = -1;
+ for (int i = 0; i < trackCount; i++) {
+ MediaFormat format = extractor.getTrackFormat(i);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+
+ boolean selectCurrentTrack = false;
+
+ if (mime.startsWith("audio/") && useAudio) {
+ selectCurrentTrack = true;
+ } else if (mime.startsWith("video/") && useVideo) {
+ selectCurrentTrack = true;
+ }
+
+ if (selectCurrentTrack) {
+ extractor.selectTrack(i);
+ int dstIndex = muxer.addTrack(format);
+ indexMap.put(i, dstIndex);
+ if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
+ int newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
+ bufferSize = newSize > bufferSize ? newSize : bufferSize;
+ }
+ }
+ }
+
+ if (bufferSize < 0) {
+ bufferSize = DEFAULT_BUFFER_SIZE;
+ }
+
+ // Set up the orientation and starting time for extractor.
+ MediaMetadataRetriever retrieverSrc = new MediaMetadataRetriever();
+ retrieverSrc.setDataSource(srcPath);
+ String degreesString = retrieverSrc.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ if (degreesString != null) {
+ int degrees = Integer.parseInt(degreesString);
+ if (degrees >= 0) {
+ muxer.setOrientationHint(degrees);
+ }
+ }
+
+ if (startMs > 0) {
+ extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ }
+
+ // Copy the samples from MediaExtractor to MediaMuxer. We will loop
+ // for copying each sample and stop when we get to the end of the source
+ // file or exceed the end time of the trimming.
+ int offset = 0;
+ int trackIndex = -1;
+ ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
+ BufferInfo bufferInfo = new BufferInfo();
+
+ muxer.start();
+ while (true) {
+ bufferInfo.offset = offset;
+ bufferInfo.size = extractor.readSampleData(dstBuf, offset);
+ if (bufferInfo.size < 0) {
+ Log.d(LOGTAG, "Saw input EOS.");
+ bufferInfo.size = 0;
+ break;
+ } else {
+ bufferInfo.presentationTimeUs = extractor.getSampleTime();
+ if (endMs > 0 && bufferInfo.presentationTimeUs > (endMs * 1000)) {
+ Log.d(LOGTAG, "The current sample is over the trim end time.");
+ break;
+ } else {
+ bufferInfo.flags = extractor.getSampleFlags();
+ trackIndex = extractor.getSampleTrackIndex();
+
+ muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
+ bufferInfo);
+ extractor.advance();
+ }
+ }
+ }
+
+ muxer.stop();
+ muxer.release();
+ return;
+ }
+
+ private static void trimUsingMp4Parser(File src, File dst, int startMs, int endMs)
+ throws FileNotFoundException, IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
Movie movie = MovieCreator.build(randomAccessFile.getChannel());
@@ -84,22 +231,25 @@
List<Track> tracks = movie.getTracks();
movie.setTracks(new LinkedList<Track>());
- double startTime = startMs/1000;
- double endTime = endMs/1000;
+ double startTime = startMs / 1000;
+ double endTime = endMs / 1000;
boolean timeCorrected = false;
- // Here we try to find a track that has sync samples. Since we can only start decoding
- // at such a sample we SHOULD make sure that the start of the new fragment is exactly
- // such a frame
+ // Here we try to find a track that has sync samples. Since we can only
+ // start decoding at such a sample we SHOULD make sure that the start of
+ // the new fragment is exactly such a frame.
for (Track track : tracks) {
if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
if (timeCorrected) {
- // This exception here could be a false positive in case we have multiple tracks
- // with sync samples at exactly the same positions. E.g. a single movie containing
- // multiple qualities of the same video (Microsoft Smooth Streaming file)
-
- throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
+ // This exception here could be a false positive in case we
+ // have multiple tracks with sync samples at exactly the
+ // same positions. E.g. a single movie containing multiple
+ // qualities of the same video (Microsoft Smooth Streaming
+ // file)
+ throw new RuntimeException(
+ "The startTime has already been corrected by" +
+ " another track with SyncSample. Not Supported.");
}
startTime = correctTimeToSyncSample(track, startTime, false);
endTime = correctTimeToSyncSample(track, endTime, true);
@@ -116,20 +266,23 @@
for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
for (int j = 0; j < entry.getCount(); j++) {
- // entry.getDelta() is the amount of time the current sample covers.
+ // entry.getDelta() is the amount of time the current sample
+ // covers.
if (currentTime <= startTime) {
// current sample is still before the new starttime
startSample = currentSample;
}
if (currentTime <= endTime) {
- // current sample is after the new start time and still before the new endtime
+ // current sample is after the new start time and still
+ // before the new endtime
endSample = currentSample;
} else {
// current sample is after the end of the cropped video
break;
}
- currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
+ currentTime += (double) entry.getDelta()
+ / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
}
@@ -139,15 +292,8 @@
randomAccessFile.close();
}
- protected static long getDuration(Track track) {
- long duration = 0;
- for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
- duration += entry.getCount() * entry.getDelta();
- }
- return duration;
- }
-
- private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
+ private static double correctTimeToSyncSample(Track track, double cutHere,
+ boolean next) {
double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
long currentSample = 0;
double currentTime = 0;
@@ -155,10 +301,13 @@
TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
for (int j = 0; j < entry.getCount(); j++) {
if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
- // samples always start with 1 but we start with zero therefore +1
- timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
+ // samples always start with 1 but we start with zero
+ // therefore +1
+ timeOfSyncSamples[Arrays.binarySearch(
+ track.getSyncSamples(), currentSample + 1)] = currentTime;
}
- currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
+ currentTime += (double) entry.getDelta()
+ / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
}
@@ -176,5 +325,4 @@
return timeOfSyncSamples[timeOfSyncSamples.length - 1];
}
-
}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index 874a7c9..1a7d200 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -360,15 +360,7 @@
ImageFilter.setActivityForMemoryToasts(this);
Resources res = getResources();
- // TODO: add a mechanism to set the resources in FiltersManagmer
- ImageFilterBorder filterBorder = (ImageFilterBorder) FiltersManager.getManager().getFilter(ImageFilterBorder.class);
- filterBorder.setResources(res);
- filterBorder = (ImageFilterBorder) FiltersManager.getPreviewManager().getFilter(ImageFilterBorder.class);
- filterBorder.setResources(res);
- ImageFilterFx filterFx = (ImageFilterFx) FiltersManager.getManager().getFilter(ImageFilterFx.class);
- filterFx.setResources(res);
- filterFx = (ImageFilterFx) FiltersManager.getPreviewManager().getFilter(ImageFilterFx.class);
- filterFx.setResources(res);
+ FiltersManager.setResources(res);
ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen));
// TODO: get those values from XML.
@@ -554,6 +546,7 @@
ImageFilter.resetStatics();
FiltersManager.getPreviewManager().freeRSFilterScripts();
FiltersManager.getManager().freeRSFilterScripts();
+ FiltersManager.getHighresManager().freeRSFilterScripts();
FiltersManager.reset();
CachingPipeline.destroyRenderScriptContext();
super.onDestroy();
diff --git a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
index a8c8d7b..e63323b 100644
--- a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
+++ b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
@@ -34,12 +34,13 @@
private static final String LOGTAG = "FilteringPipeline";
private boolean DEBUG = false;
- private static long HIRES_DELAY = 100; // in ms
+ private static long HIRES_DELAY = 300; // in ms
private volatile boolean mPipelineIsOn = false;
private CachingPipeline mAccessoryPipeline = null;
private CachingPipeline mPreviewPipeline = null;
+ private CachingPipeline mHighresPreviewPipeline = null;
private HandlerThread mHandlerThread = null;
private final static int NEW_PRESET = 0;
@@ -111,7 +112,11 @@
}
RenderingRequest request = (RenderingRequest) msg.obj;
- mAccessoryPipeline.render(request);
+ if (msg.what == COMPUTE_HIGHRES_RENDERING_REQUEST) {
+ mHighresPreviewPipeline.render(request);
+ } else {
+ mAccessoryPipeline.render(request);
+ }
Message uimsg = mUIHandler.obtainMessage(NEW_RENDERING_REQUEST);
uimsg.obj = request;
mUIHandler.sendMessage(uimsg);
@@ -126,8 +131,12 @@
Process.THREAD_PRIORITY_FOREGROUND);
mHandlerThread.start();
mProcessingHandler = new Handler(mHandlerThread.getLooper(), this);
- mAccessoryPipeline = new CachingPipeline(FiltersManager.getManager(), "Accessory");
- mPreviewPipeline = new CachingPipeline(FiltersManager.getPreviewManager(), "Preview");
+ mAccessoryPipeline = new CachingPipeline(
+ FiltersManager.getManager(), "Accessory");
+ mPreviewPipeline = new CachingPipeline(
+ FiltersManager.getPreviewManager(), "Preview");
+ mHighresPreviewPipeline = new CachingPipeline(
+ FiltersManager.getHighresManager(), "Highres");
}
public synchronized static FilteringPipeline getPipeline() {
@@ -145,6 +154,7 @@
Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
mAccessoryPipeline.setOriginal(bitmap);
mPreviewPipeline.setOriginal(bitmap);
+ mHighresPreviewPipeline.setOriginal(bitmap);
}
public void postRenderingRequest(RenderingRequest request) {
@@ -194,16 +204,19 @@
public void setPreviewScaleFactor(float previewScaleFactor) {
mAccessoryPipeline.setPreviewScaleFactor(previewScaleFactor);
mPreviewPipeline.setPreviewScaleFactor(previewScaleFactor);
+ mHighresPreviewPipeline.setPreviewScaleFactor(previewScaleFactor);
}
public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
mAccessoryPipeline.setHighResPreviewScaleFactor(highResPreviewScaleFactor);
mPreviewPipeline.setHighResPreviewScaleFactor(highResPreviewScaleFactor);
+ mHighresPreviewPipeline.setHighResPreviewScaleFactor(highResPreviewScaleFactor);
}
public static synchronized void reset() {
sPipeline.mAccessoryPipeline.reset();
sPipeline.mPreviewPipeline.reset();
+ sPipeline.mHighresPreviewPipeline.reset();
sPipeline.mHandlerThread.quit();
sPipeline = null;
}
@@ -213,6 +226,7 @@
if (mPipelineIsOn) {
assert(mPreviewPipeline.isInitialized());
assert(mAccessoryPipeline.isInitialized());
+ assert(mHighresPreviewPipeline.isInitialized());
updatePreviewBuffer();
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index 232de64..66ad106 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -15,6 +15,7 @@
*/
package com.android.gallery3d.filtershow.filters;
+import android.content.res.Resources;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import java.util.HashMap;
@@ -140,4 +141,10 @@
representations.add(getRepresentation(ImageFilterDraw.class));
}
+ public void setFilterResources(Resources resources) {
+ ImageFilterBorder filterBorder = (ImageFilterBorder) getFilter(ImageFilterBorder.class);
+ filterBorder.setResources(resources);
+ ImageFilterFx filterFx = (ImageFilterFx) getFilter(ImageFilterFx.class);
+ filterFx.setResources(resources);
+ }
}
diff --git a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
index 4b6b1d3..9de9b7b 100644
--- a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
+++ b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
@@ -16,12 +16,15 @@
package com.android.gallery3d.filtershow.filters;
+import android.content.res.Resources;
+
import java.util.HashMap;
import java.util.Vector;
public class FiltersManager extends BaseFiltersManager {
private static FiltersManager sInstance = null;
private static FiltersManager sPreviewInstance = null;
+ private static FiltersManager sHighresInstance = null;
public FiltersManager() {
init();
@@ -41,9 +44,22 @@
return sInstance;
}
+ public static FiltersManager getHighresManager() {
+ if (sHighresInstance == null) {
+ sHighresInstance = new FiltersManager();
+ }
+ return sHighresInstance;
+ }
+
public static void reset() {
sInstance = null;
sPreviewInstance = null;
+ sHighresInstance = null;
}
+ public static void setResources(Resources resources) {
+ FiltersManager.getManager().setFilterResources(resources);
+ FiltersManager.getPreviewManager().setFilterResources(resources);
+ FiltersManager.getHighresManager().setFilterResources(resources);
+ }
}