我正在尝试使用我的应用程序拍摄视频(进入手机的相机,然后返回视频),以便在从相机返回时在我的VideoView
中正确显示。但是,如果我拍摄风景视频,它会自动旋转为“面朝上”,如下图所示。但是,这在VideoView
中播放得非常糟糕,因为它显示了额外的空间,如果视频作为风景视频返回,我无法更改(不知道如何)小部件的方向。因此,我所采取的路线是尝试旋转视频本身,当它回到VideoView
时。
我正在尝试这个建议here,但有两个问题。首先,在我的视频尝试保存之前,我无法运行建议的方法,因为这是我唯一一次获得视频的正确路径(或者是否有其他方式来获取其路径?)。其次,如果我在保存时尝试实现代码(而不是在视频返回VideoView
时),由于IllegalArgumentException,我的文件将无法保存。基本上它不喜欢这一行:mediaMetadataRetriever.setDataSource(this, Uri.parse(videoPath));
,或者更具体地说它不接受videoPath
作为参数。我尝试记录videoPath
,然后找到了我的视频保存位置的正确路径:/storage/emulated/0/Movies/Cute Videos/Video94.mp4
。所以,如果这是我正确的道路,那为什么它会给我一个例外?我不知道该怎么做。保存时我不应该尝试使用此示例代码吗?
如果您能解释错误,我可能会错误地实现示例代码。
以下是我正在尝试整合的代码:
getVideoAspectRatio();
if (isVideoLandscaped()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
而且:
videoView.setMediaController(new MediaController(this));
和2种方法:
private void getVideoAspectRatio() {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(this, Uri.parse(videoUrl));
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
videoWidth = Integer.parseInt(width);
videoHeight = Integer.parseInt(height);
}
private boolean isVideoLandscaped() {
if (videoWidth > videoHeight) {
return true;
} else return false;
}
MakePhotoVideo.java
package org.azurespot.makecute;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
import org.azurespot.R;
import org.azurespot.cutecollection.CuteCollection;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
public class MakePhotoVideo extends ActionBarActivity {
private static final int ACTION_TAKE_PHOTO = 1;
private static final int ACTION_TAKE_VIDEO = 2;
private static final String BITMAP_STORAGE_KEY = "viewbitmap";
private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY = "imageviewvisibility";
private ImageView mImageView;
private Bitmap mImageBitmap;
private static final String VIDEO_STORAGE_KEY = "viewvideo";
private static final String VIDEOVIEW_VISIBILITY_STORAGE_KEY = "videoviewvisibility";
private VideoView mVideoView;
private Uri mVideoUri;
File fileVideo;
private String mCurrentPhotoPath;
String videoPath;
int videoWidth;
int videoHeight;
private static final String JPEG_FILE_PREFIX = "IMG_";
private static final String JPEG_FILE_SUFFIX = ".jpg";
private PhotoStorageDirFactory mPhotoStorageDirFactory = null;
/* Photo album for this application */
private String getAlbumName() {
return getString(R.string.album_name);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_make_photo_video);
mImageView = (ImageView) findViewById(R.id.taken_photo);
mVideoView = (VideoView) findViewById(R.id.video_view);
mVideoView.setVisibility(View.INVISIBLE);
mImageBitmap = null;
mVideoUri = null;
Button photoBtn = (Button) findViewById(R.id.click);
setBtnListenerOrDisable(
photoBtn,
mTakePicOnClickListener,
MediaStore.ACTION_IMAGE_CAPTURE
);
Button videoBtn = (Button) findViewById(R.id.record_video);
setBtnListenerOrDisable(
videoBtn,
mTakeVidOnClickListener,
MediaStore.ACTION_VIDEO_CAPTURE
);
mPhotoStorageDirFactory = new BasePhotoDirFactory();
// Shows the up carat near app icon in ActionBar
getSupportActionBar().setDisplayUseLogoEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
public void viewCollection(View v){
// finishes/restarts the activity so the unsaved video does not corrupt
Intent intent = getIntent();
finish();
startActivity(intent);
// goes to Cute Collection activity
Intent i = new Intent(this, CuteCollection.class);
startActivity(i);
}
private File getAlbumDir() {
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = mPhotoStorageDirFactory.getAlbumStorageDir(getAlbumName());
if (storageDir != null) {
if (! storageDir.mkdirs()) {
if (! storageDir.exists()){
Log.d("Camera", "failed to create directory");
return null;
}
}
}
} else {
Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
}
return storageDir;
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File albumF = getAlbumDir();
File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
return imageF;
}
private File setUpPhotoFile() throws IOException {
File f = createImageFile();
mCurrentPhotoPath = f.getAbsolutePath();
return f;
}
private void setPic() {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
bitmap = rotateBitmap(bitmap, 90);
savePhoto(bitmap);
/* Associate the Bitmap to the ImageView, make sure the VideoView
* is cleared to replace with ImageView */
mImageView.setImageBitmap(bitmap);
mVideoUri = null;
mImageView.setVisibility(View.VISIBLE);
mVideoView.setVisibility(View.INVISIBLE);
}
// save your photo to SD card
private void savePhoto(final Bitmap bitmapPhoto){
// set OnClickListener to save the photo
mImageView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
boolean success = false;
File photoDir = new File(Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES) + "/Cute Photos");
photoDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String photoName = "Photo"+ n +".jpg";
File filePhoto = new File (photoDir, photoName);
// if (filePhoto.exists ()) filePhoto.delete ();
try {
FileOutputStream out = new FileOutputStream(filePhoto);
bitmapPhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
success = true;
} catch (Exception e) {
e.printStackTrace();
}
if (success) {
Toast toast = Toast.makeText(getApplicationContext(), "Cute photo saved!",
Toast.LENGTH_LONG);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
} else {
Toast.makeText(getApplicationContext(),
"Error during image saving", Toast.LENGTH_SHORT).show();
}
}
});
}
// save your video to SD card
protected void saveVideo(final Uri uriVideo){
// click the video to save it
mVideoView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
boolean success = false;
if(event.getAction() == MotionEvent.ACTION_UP) {
try {
// make the directory
File vidDir = new File(android.os.Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_MOVIES) + File.separator + "Cute Videos");
vidDir.mkdirs();
// create unique identifier
Random generator = new Random();
int n = 100;
n = generator.nextInt(n);
// create file name
String videoName = "Video" + n + ".mp4";
fileVideo = new File(vidDir.getAbsolutePath(), videoName);
videoPath = fileVideo.getAbsolutePath();
Log.d("TAG", "Value of videoPath:" + videoPath);
fileVideo.setWritable(true, false);
getVideoAspectRatio();
if (isVideoLandscaped()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
OutputStream out = new FileOutputStream(fileVideo);
InputStream in = getContentResolver().openInputStream(uriVideo);
byte buffer[] = new byte[1024];
int length = 0;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
out.close();
in.close();
success = true;
} catch (Exception e) {
e.printStackTrace();
}
if (success) {
Toast toast = Toast.makeText(getApplicationContext(), "Cute video saved!",
Toast.LENGTH_SHORT);
LinearLayout toastLayout = (LinearLayout) toast.getView();
toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(30);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
toast.show();
} else {
Toast.makeText(getApplicationContext(),
"Error during video saving", Toast.LENGTH_SHORT).show();
}
}
return true;
}
});
}
public Bitmap rotateBitmap(Bitmap source, int angle)
{
Matrix matrix = new Matrix();
matrix.set(matrix);
matrix.setRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
source.getHeight(), matrix, false);
}
private void galleryAddPic() {
Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
private void dispatchTakePictureIntent(int actionCode) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
switch(actionCode) {
case ACTION_TAKE_PHOTO:
File f;
try {
f = setUpPhotoFile();
mCurrentPhotoPath = f.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
} catch (IOException e) {
e.printStackTrace();
f = null;
mCurrentPhotoPath = null;
}
break;
default:
break;
} // switch
startActivityForResult(takePictureIntent, actionCode);
}
// Captures video from Android camera component
protected void dispatchTakeVideoIntent() {
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
// set the video image quality to high
takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
}
}
private void handleCameraPhoto() {
if (mCurrentPhotoPath != null) {
setPic();
galleryAddPic();
mCurrentPhotoPath = null;
}
}
// Post recorded video into VideoView
private void handleCameraVideo(Intent intent) {
mVideoUri = intent.getData();
mVideoView.setMediaController(new MediaController(this));
mVideoView.setVideoURI(mVideoUri);
mImageBitmap = null;
mVideoView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.INVISIBLE);
mVideoView.start();
// saves video to file
saveVideo(mVideoUri);
}
Button.OnClickListener mTakePicOnClickListener =
new Button.OnClickListener() {
@Override
public void onClick(View v) {
dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
}
};
Button.OnClickListener mTakeVidOnClickListener =
new Button.OnClickListener() {
@Override
public void onClick(View v) {
dispatchTakeVideoIntent();
}
};
private void getVideoAspectRatio() {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(this, Uri.parse(videoPath));
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
videoWidth = Integer.parseInt(width);
videoHeight = Integer.parseInt(height);
}
private boolean isVideoLandscaped() {
if (videoWidth > videoHeight) {
return true;
} else return false;
}
// Intent data is how the photo and video transfer into their views
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ACTION_TAKE_PHOTO: {
if (resultCode == RESULT_OK) {
handleCameraPhoto();
}
break;
} // ACTION_TAKE_PHOTO
case ACTION_TAKE_VIDEO: {
if (resultCode == RESULT_OK) {
handleCameraVideo(data);
}
break;
} // ACTION_TAKE_VIDEO
} // switch
}
// Some lifecycle callbacks so that the image can survive orientation change
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
outState.putParcelable(VIDEO_STORAGE_KEY, mVideoUri);
outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
outState.putBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY, (mVideoUri != null) );
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mImageBitmap = savedInstanceState.getParcelable(BITMAP_STORAGE_KEY);
mVideoUri = savedInstanceState.getParcelable(VIDEO_STORAGE_KEY);
mImageView.setImageBitmap(mImageBitmap);
mImageView.setVisibility(
savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
mVideoView.setVideoURI(mVideoUri);
mVideoView.setVisibility(
savedInstanceState.getBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY) ?
ImageView.VISIBLE : ImageView.INVISIBLE
);
}
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
* http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
private void setBtnListenerOrDisable(
Button btn,
Button.OnClickListener onClickListener,
String intentName
) {
if (isIntentAvailable(this, intentName)) {
btn.setOnClickListener(onClickListener);
} else {
btn.setText(
getText(R.string.cannot).toString() + " " + btn.getText());
btn.setClickable(false);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Makes the UP caret go back to the previous fragment MakeCuteFragment
switch (item.getItemId()) {
case android.R.id.home:
android.app.FragmentManager fm= getFragmentManager();
fm.popBackStack();
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
logcat的
03-30 23:09:36.580 17940-17940/org.azurespot W/System.err﹕ java.lang.IllegalArgumentException
03-30 23:09:36.580 17940-17940/org.azurespot W/System.err﹕ at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:72)
03-30 23:09:36.580 17940-17940/org.azurespot W/System.err﹕ at android.media.MediaMetadataRetriever.setDataSource(MediaMetadataRetriever.java:159)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at org.azurespot.makecute.MakePhotoVideo.getVideoAspectRatio(MakePhotoVideo.java:417)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at org.azurespot.makecute.MakePhotoVideo.access$000(MakePhotoVideo.java:43)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at org.azurespot.makecute.MakePhotoVideo$2.onTouch(MakePhotoVideo.java:280)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.View.dispatchTouchEvent(View.java:8276)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2477)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2209)
03-30 23:09:36.590 17940-17940/org.azurespot W/System.err﹕ at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2332)
答案 0 :(得分:0)
这个Uri类不支持硬编码路径。当然没有很好的记录。例如,对于使用路径,需要“file://”作为前缀,URI约定。我尝试编码但不能直接使用Uri。但是我觉得有一种方法可以让你不会得到例外。片段,使用您的样本:
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource("/storage/emulated/0/Movies/Cute Videos/Video94.mp4");
注意:
如果这对您有用,那么您可以通过mediaMetadataRetriever检索/检查路径,我预测。
在我看来,你需要用URI标准建立你的文件,似乎很痛苦。这是一个示例@ Create a new document,并搜索“创建新文档”。在下面看看“创建新文档后,您可以获取其URI ...”。就像我说的,这似乎太多了,但你想使用mediaMetadataRetriever。
让我们发布...