blob: 0d75a7aebdd32233d896333f8f49ebee3d833940 [file] [log] [blame]
/*
* Copyright (c) 2016,2017 The Linux Foundation. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.android.gallery3d.video;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;
import android.webkit.URLUtil;
import com.android.gallery3d.R;
import com.android.gallery3d.app.GalleryActivity;
import com.android.gallery3d.app.MovieControllerOverlay;
import com.android.gallery3d.app.MovieControllerOverlayNew;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class VideoSnapshotExt implements IVideoSnapshotListener {
private static String TAG = "VideoSnapshotExt";
private static final boolean DEBUG = false;
private static final String FOLDER = Environment.getExternalStorageDirectory() +
File.separator + "Pictures" + File.separator + "VideoSnapshots";
private static final String FILE_PREFIX = "VideoSnapshot_";
private static final String FILE_EXT = ".jpg";
private static final String TIME_STAMP_NAME = "yyyyMMdd_HHmmss";
private static final int CONTENT_VALUES_SIZE = 9;
private CodeauroraVideoView mVideoView;
private Context mContext;
private ContentResolver mContentResolver;
@Override
public void onVideoSnapshot() {
if (canVideoSnap()) {
int currentPosition = mVideoView.getMediaPlayer().getCurrentPosition();
doVideoSnap(mVideoView.getUri(), currentPosition);
}
}
@Override
public boolean canVideoSnapshot() {
return canVideoSnap();
}
public void init(MovieControllerOverlay controller, CodeauroraVideoView videoView,
boolean isLocalFile) {
mContext = videoView.getContext();
mContentResolver = mContext.getContentResolver();
mVideoView = videoView;
if (controller instanceof MovieControllerOverlayNew) {
((MovieControllerOverlayNew) controller).showVideoSnapshotButton(isLocalFile);
((MovieControllerOverlayNew) controller).setVideoSnapshotListener(this);
}
}
private boolean canVideoSnap() {
if (mVideoView.getMediaPlayer() == null) {
Log.w(TAG, "can't videoSnapshot because mMediaPlayer is null !");
return false;
}
return true;
}
private void doVideoSnap(Uri uri, int snapPosition) {
VideoSnapParameters parameters = new VideoSnapParameters(uri, snapPosition);
new VideoSnapTask().execute(parameters);
}
private class VideoSnapParameters {
private int mSnapPosition;
private Uri mUri;
/**
* @param snapPosition milliseconds
*/
public VideoSnapParameters(Uri uri, int snapPosition) {
mUri = uri;
mSnapPosition = snapPosition;
}
public int getSnapPosition() {
return mSnapPosition;
}
public Uri getUri() {
return mUri;
}
}
private class VideoSnapTask extends AsyncTask<VideoSnapParameters, Void, Bitmap> {
SimpleDateFormat mDateFormat = new SimpleDateFormat(TIME_STAMP_NAME, Locale.getDefault());
@Override
protected Bitmap doInBackground(VideoSnapParameters... params) {
VideoSnapParameters parameters = params[0];
return doVideoSnap(parameters.getUri(), parameters.getSnapPosition());
}
@Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
saveVideoSnap(mContentResolver, result);
}
}
private boolean isPositionValid(int currentPosition, long duration) {
return (currentPosition <= duration) && (currentPosition >= 0);
}
private Bitmap doVideoSnap(Uri videoUri, int snapPosition) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
if (videoUri == null || !URLUtil.isValidUrl(videoUri.toString())) {
return null;
}
retriever.setDataSource(mContext, videoUri);
String durationString =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long duration =
(durationString == null) ? 0 : Long.parseLong(durationString); // milliseconds
if (DEBUG) {
Log.d(TAG, "doVideoSnap at " + snapPosition + "/" + duration);
}
if (isPositionValid(snapPosition, duration)) {
// convert ms to us.
Bitmap bitmap = retriever.getFrameAtTime(snapPosition * 1000,
MediaMetadataRetriever.OPTION_CLOSEST);
if (bitmap == null) {
Log.w(TAG, "frame cannot be retrieved. ");
} else {
try {
retriever.release();
} catch (IOException e) {
// Ignore errors occurred while releasing the retriever.
}
if (DEBUG) {
Log.d(TAG, "retriever get frame resolution : " +
bitmap.getHeight() + "x" + bitmap.getWidth());
}
Log.d(GalleryActivity.QSST, "video snapshot done");
return bitmap;
}
}
try {
retriever.release();
} catch (IOException e) {
// Ignore errors occurred while releasing the retriever.
}
return null;
}
private void saveVideoSnap(ContentResolver resolver, Bitmap videoSnap) {
File folder = new File(FOLDER);
if (!folder.exists()) {
folder.mkdirs();
}
long dateTaken = System.currentTimeMillis();
String fileName = mDateFormat.format(new Date(dateTaken));
String fileTitle = FILE_PREFIX + fileName;
File file = new File(folder, fileTitle + FILE_EXT);
try {
FileOutputStream fos = new FileOutputStream(file);
videoSnap.compress(Bitmap.CompressFormat.JPEG, 85, fos);
fos.close();
addImage(resolver, file.getPath(), fileTitle, dateTaken, file.length(), 0,
videoSnap.getWidth(), videoSnap.getHeight());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void addImage(ContentResolver resolver, String path, String title, long date,
long size, int orientation, int width, int height) {
// Insert into MediaStore.
ContentValues values = new ContentValues(CONTENT_VALUES_SIZE);
values.put(ImageColumns.TITLE, title);
values.put(ImageColumns.DISPLAY_NAME, title + ".jpg");
values.put(ImageColumns.DATE_TAKEN, date);
values.put(ImageColumns.MIME_TYPE, "image/jpeg");
values.put(ImageColumns.ORIENTATION, orientation);
values.put(ImageColumns.DATA, path);
values.put(ImageColumns.SIZE, size);
values.put(ImageColumns.WIDTH, width);
values.put(ImageColumns.HEIGHT, height);
try {
resolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (Throwable th) {
Log.w(TAG, "Failed to write MediaStore" + th);
}
}
}