直接上代码,至于布局文件就不上传了,都是很简单的控件:
MainActivity.java
package com.example.videorecorddemo;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import com.example.videorecorddemo.StorageUtils.StorageInfo;
@SuppressLint("SdCardPath")
public class MainActivity extends Activity {
public static String localPath = "/mnt/sdcard/video.mp4";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_play_video).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), VideoPlayActivity.class));
}
});
findViewById(R.id.btn_record_video).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), VideoRecordActivity.class));
}
});
List<StorageInfo> infos = StorageUtils.getStorageList();
if(infos != null && infos.size() > 0) {
localPath = infos.get(0).path + "/video.mp4";
Log.d("DEBUG", localPath);
}
}
}
VideoRecordActivity.java
package com.example.videorecorddemo;
import java.io.File;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnErrorListener;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class VideoRecordActivity extends Activity {
private Button btn_VideoStart, btn_VideoStop;
private SurfaceView sv_view;
private boolean isRecording;
private MediaRecorder mediaRecorder;
List<Size> sizes;
private Camera mCamera = null;
private SurfaceHolder surfaceHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_record);
btn_VideoStart = (Button) findViewById(R.id.btn_VideoStart);
btn_VideoStop = (Button) findViewById(R.id.btn_VideoStop);
sv_view = (SurfaceView) findViewById(R.id.sv_view);
btn_VideoStart.setOnClickListener(click);
btn_VideoStop.setOnClickListener(click);
try {
surfaceHolder = sv_view.getHolder();
mCamera = Camera.open();
sizes = mCamera.getParameters().getSupportedPictureSizes();
for (Size s : sizes) {
Log.d("DEBUG", "w: " + s.width + "; h: " + s.height);
}
mCamera.setDisplayOrientation(90);
final int screenWidth = getResources().getDisplayMetrics().widthPixels;
final int screenHeight = getResources().getDisplayMetrics().heightPixels;
Log.d("DEBUG", "Screen width: " + screenWidth + "; height: " + screenHeight);
// 声明Surface不维护自己的缓冲区,针对Android3.0以下设备支持
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("DEBUG", "SurfaceHolder width: " + width + "; height: " + height);
if(surfaceHolder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
e.printStackTrace();
}
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Size bestSize = getBestVideoSize();
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
Log.d("DEBUG", "width: " + profile.videoFrameWidth + "; height: " + profile.videoFrameHeight);
final int svWidth = screenWidth;
int svHeight = 0;
if(bestSize != null) {
svHeight = screenWidth * bestSize.height / bestSize.width;
} else {
svHeight = screenWidth * 3 / 4;
}
RelativeLayout.LayoutParams llp = (RelativeLayout.LayoutParams) sv_view.getLayoutParams();
llp.width = svWidth;
llp.height = svHeight;
sv_view.setLayoutParams(llp);
} catch (Exception e) {
e.printStackTrace();
}
}
private View.OnClickListener click = new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_VideoStart:
start();
btn_VideoStart.postDelayed(new Runnable() {
@Override
public void run() {
// stop();
Toast.makeText(getApplicationContext(), "6s", Toast.LENGTH_LONG).show();
}
}, 6*1000);
break;
case R.id.btn_VideoStop:
stop();
break;
default:
break;
}
}
};
@SuppressLint("NewApi")
protected void initMediaRecorder() {
try {
mCamera.stopPreview();
File file = new File(MainActivity.localPath);
Log.d("DEBUG", "File path: " + file.getAbsolutePath());
if (file.exists()) {
// 如果文件存在,删除它,演示代码保证设备上只有一个录音文件
file.delete();
}
mediaRecorder = new MediaRecorder();
mediaRecorder.reset();
mCamera.unlock();
mediaRecorder.setCamera(mCamera);
// 设置音频录入源
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置视频图像的录入源
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// 设置录入媒体的输出格式
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
Size size = getBestVideoSize();
if(size != null) {
mediaRecorder.setVideoSize(size.width, size.height);
}
// 设置音频的编码格式
Log.d("DEBUG", "SDK_INT: " + android.os.Build.VERSION.SDK_INT);
if(android.os.Build.VERSION.SDK_INT >= 16) { //虽然此接口要求系统版本至少是10,但是在某些恶心机器却崩溃了,故把系统版本提升到16
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//设置声道数
mediaRecorder.setAudioChannels(2);
} else {
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
}
// 设置视频的编码格式
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
// 设置录制视频文件的输出路径
mediaRecorder.setOutputFile(file.getAbsolutePath());
// 设置捕获视频图像的预览界面
mediaRecorder.setPreviewDisplay(sv_view.getHolder().getSurface());
if(android.os.Build.VERSION.SDK_INT >= 9) {
mediaRecorder.setOrientationHint(90);
}
mediaRecorder.setOnErrorListener(new OnErrorListener() {
@Override
public void onError(MediaRecorder mr, int what, int extra) {
// 发生错误,停止录制
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
mediaRecorder = null;
isRecording=false;
Toast.makeText(VideoRecordActivity.this, "录制出错", Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressLint("NewApi")
protected void start() {
try {
initMediaRecorder();
// 准备、开始
mediaRecorder.prepare();
mediaRecorder.start();
isRecording = true;
Toast.makeText(VideoRecordActivity.this, "开始录像", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
protected void stop() {
if (isRecording) {
// 如果正在录制,停止并释放资源
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
mediaRecorder = null;
isRecording=false;
Toast.makeText(VideoRecordActivity.this, "停止录像,并保存文件", Toast.LENGTH_SHORT).show();
}
}
private final int TARGET_WIDTH = 320;
private final int TARGET_HEIGHT = 240;
private final int TARGET_PIXEL = TARGET_WIDTH * TARGET_HEIGHT;
private Size getBestVideoSize() {
Size size = null;
if(sizes != null && sizes.size() > 0) {
for(int i=0; i<sizes.size(); i++) {
Size s = sizes.get(i);
if(s.width * s.height <= TARGET_PIXEL) { //当前分辨率比目标分辨率小
if(size == null) {
size = s;
} else if(size.width * size.height < s.width * s.height) {
size = s;
}
Log.d("DEBUG", "getVideoSize w: " + s.width + "; h: " + s.height);
}
}
}
return size;
}
@Override
protected void onDestroy() {
if (isRecording) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
if(mCamera != null) {
mCamera.release();
}
super.onDestroy();
}
}
VideoPlayActivity.java
package com.example.videorecorddemo;
import android.app.Activity;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.MediaController;
import android.widget.RelativeLayout;
import android.widget.VideoView;
public class VideoPlayActivity extends Activity {
VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_play);
initView();
initData();
initListener();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
protected void initView() {
videoView = (VideoView) findViewById(R.id.video_view);
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
Log.d("DEBUG", "width: " + profile.videoFrameWidth + "; height: " + profile.videoFrameHeight);
final int DP300 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
final int svWidth = DP300; //profile.videoFrameWidth;
final int svHeight = DP300 * 3 / 4; //profile.videoFrameHeight;
Log.d("DEBUG", "svWidth: " + svWidth + "; svHeight: " + svHeight);
RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) videoView.getLayoutParams();
rlp.width = svWidth;
rlp.height = svHeight;
// videoView.setLayoutParams(rlp);
}
protected void initData() {
initVideo();
}
private void initVideo() {
/* 设置路径 */
videoView.setVideoPath(MainActivity.localPath);
/* 设置模式-播放进度条 */
videoView.setMediaController(new MediaController(VideoPlayActivity.this));
videoView.requestFocus();
videoView.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
}
});
}
protected void initListener() {
findViewById(R.id.btn_play).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
videoView.start();
}
});
findViewById(R.id.btn_pause).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
videoView.pause();
}
});
}
}
StorageUtils.java
/*
* Copyright 2013 BenC Zhang.
*
* Licensed under the Apache License and DW&&BenC zhang License, Version x.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.duowan.com
* 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.example.videorecorddemo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.StringTokenizer;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.util.Log;
public class StorageUtils {
private static final String TAG = "StorageUtils";
/**
* SDCard File List
*/
private static File[] mStorageList;
/**
* List Of The Potential Volume Daemons
*/
public static class StorageInfo {
public final String path;
public final boolean internal;
public final boolean readonly;
public final int display_number;
StorageInfo(String path, boolean internal, boolean readonly,
int display_number) {
this.path = path;
this.internal = internal;
this.readonly = readonly;
this.display_number = display_number;
}
public String getDisplayName() {
StringBuilder res = new StringBuilder();
if (internal) {
res.append("Internal SD card");
} else if (display_number > 1) {
res.append("SD card " + display_number);
} else {
res.append("SD card");
}
if (readonly) {
res.append(" (Read only)");
}
return res.toString();
}
}
public static List<StorageInfo> getStorageList() {
List<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_internal = !Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state
.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
BufferedReader buf_reader = null;
try {
HashSet<String> paths = new HashSet<String>();
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
int cur_display_number = 1;
Log.d(TAG, "/proc/mounts");
while ((line = buf_reader.readLine()) != null) {
Log.d(TAG, line);
if (line.contains("vfat") || line.contains("/mnt")) {
StringTokenizer tokens = new StringTokenizer(line, " ");
String unused = tokens.nextToken(); // device
String mount_point = tokens.nextToken(); // mount point
if (paths.contains(mount_point)) {
continue;
}
unused = tokens.nextToken(); // file system
List<String> flags = Arrays.asList(tokens.nextToken()
.split(",")); // flags
boolean readonly = flags.contains("ro");
if (mount_point.equals(def_path)) {
paths.add(def_path);
list.add(0, new StorageInfo(def_path,
def_path_internal, readonly, -1));
} else if (line.contains("/dev/block/vold")) {
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, false,
readonly, cur_display_number++));
}
}
}
}
if (!paths.contains(def_path) && def_path_available) {
list.add(0, new StorageInfo(def_path, def_path_internal,
def_path_readonly, -1));
}
for (StorageInfo storageInfo : list) {
System.err.println("storageInfo.getDisplayName():"
+ storageInfo.getDisplayName());
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {
}
}
}
return list;
}
// -----------------------------------------------------------------
public static String getDefaultExternalStorageRootDirectory() {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)
&& Environment.getExternalStorageDirectory().exists()) {
return Environment.getExternalStorageDirectory().toString();
} else {
return "data/data/";
}
}
private static final File[] mVolumeDaemonList = new File[] {
new File(Environment.getRootDirectory(), "etc/vold.fstab"),
new File(Environment.getRootDirectory(), "etc/vold.conf") };
/**
* Returns an array of files containing the paths to all of the external
* storage directories (Emulated/Removable). As a fall back, it reads in the
* volume daemon file and parses the contents.
* <p/>
* <b>Note:</b> This method takes advantage of a hidden method inside
* {@link StorageManager} which was not introduced until API 14
* (ICE_CREAM_SANDWICH/4.0.1).
* <p/>
*
* @param context
* {@link Context} used to get StorageManager
*/
public static final File[] getExternalStorageList(Context context) {
mStorageList = null;
try {
if (mStorageList == null) {
try {
mStorageList = (Build.VERSION.SDK_INT >= 14) ?
getExternalStorageList((StorageManager) context.getSystemService(Context.STORAGE_SERVICE))
: getVolumeDaemonExternalStorageList();
} catch (Exception e) {
}
}
return mStorageList;
} catch (Exception e) {
e.printStackTrace();
}
return mStorageList;
}
private static void getExternalStorageWithEnv() {
System.getenv("SECONDARY_STORAGE");
System.getenv("EXTERNAL_STORAGE");
}
/**
* Checks to see if more than one external storage directory exists.
*
* @param context
* {@link Context} used to get StorageManager
* @return
*/
public static final boolean isExtraExternalStorageDirectoryExist(
Context context) {
getExternalStorageList(context);
return mStorageList != null ? mStorageList.length >= 2 : false;
}
/**
* Returns an array of files containing the paths to all of the external
* storage directories (Emulated/Removable) provided by the volume daemon
* config file.
*
* @return
*/
public static final File[] getVolumeDaemonExternalStorageList() {
for (File daemon : mVolumeDaemonList) {
try {
if (daemon.exists() && daemon.canRead()) {
final String[] stringArray = readFileIntoStringArray(daemon);
final List<File> fileList = new ArrayList<File>();
for (String str : stringArray) {
final File file = new File(
str.split(" ")[2].split(":")[0]);
if (!doesFileExistInList(fileList, file)) {
fileList.add(file);
}
}
return (!fileList.isEmpty() ? fileList
.toArray(new File[fileList.size()]) : null);
}
} catch (Exception e) {
}
}
return null;
}
private static final File[] getExternalStorageList(
StorageManager storageManager) throws Exception {
final Method method = storageManager.getClass().getMethod(
"getVolumePaths");
final String[] strList = (String[]) method.invoke(storageManager);
final List<File> fileList = new ArrayList<File>();
for (String path : strList) {
final File file = new File(path);
if (!doesFileExistInList(fileList, file)) {
fileList.add(file);
}
}
return (!fileList.isEmpty() ? fileList
.toArray(new File[fileList.size()]) : null);
}
private static final boolean doesFileExistInList(List<File> fileList,
File newFile) throws Exception {
try {
if (newFile == null || fileList == null) {
// File Is Null Or List Is Null
return true;
}
if (!newFile.exists()) {
// The File Doesn't Exist
return true;
}
if (!newFile.isDirectory()) {
// File Is Not A Directory
return true;
}
if (!newFile.canRead()) {
// Can't Read The File
return true;
}
if (Build.VERSION.SDK_INT >= 10) {
if (newFile.getTotalSpace() <= 0) {
// File Has No Space
// Filters Usbdisk Out
return true;
}
}
if (newFile.getName().equalsIgnoreCase("tmp")) {
// This Folder Showed Up On My Droid X, Filter It Out.
return true;
}
if (fileList.contains(newFile)) {
// File Is In The List
return true;
}
// Make Sure The File Isn't In The List As A Link Of Some Sort
// More Of An In Depth Look
if (Build.VERSION.SDK_INT >= 10) {
for (File file : fileList) {
if (file.getFreeSpace() == newFile.getFreeSpace()
&& file.getUsableSpace() == newFile.getUsableSpace()) {
// Same Free/Usable Space
// Must Be Same Files
return true;
}
}
}
// File Passed All Of My Tests
return false;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@SuppressWarnings("resource")
private static final String[] readFileIntoStringArray(File file) {
try {
final Scanner scanner = new Scanner(file);
final List<String> stringList = new ArrayList<String>();
while (scanner.hasNext()) {
final String line = scanner.nextLine();
if (line != null) {
if (line.length() > 0) {
if (!line.startsWith("#")) {
stringList.add(line);
}
}
}
}
return !stringList.isEmpty() ? stringList
.toArray(new String[stringList.size()]) : null;
} catch (Exception e) {
return null;
}
}
// ---------------------------------------------------------
public static class ExternalStorage {
public static final String SD_CARD = "sdCard";
public static final String EXTERNAL_SD_CARD = "externalSdCard";
/**
* @return True if the external storage is available. False otherwise.
*/
public static boolean isAvailable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
public static String getSdCardPath() {
return Environment.getExternalStorageDirectory().getPath() + "/";
}
/**
* @return True if the external storage is writable. False otherwise.
*/
public static boolean isWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/**
* @return A map of all storage locations available
*/
@SuppressWarnings("resource")
public static Map<String, File> getAllStorageLocations() {
Map<String, File> map = new HashMap<String, File>(10);
List<String> mMounts = new ArrayList<String>(10);
List<String> mVold = new ArrayList<String>(10);
mMounts.add("/mnt/sdcard");
mVold.add("/mnt/sdcard");
try {
File mountFile = new File("/proc/mounts");
if (mountFile.exists()) {
Scanner scanner = new Scanner(mountFile);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("/dev/block/vold/")) {
String[] lineElements = line.split(" ");
String element = lineElements[1];
// don't add the default mount path
// it's already in the list.
if (!element.equals("/mnt/sdcard"))
mMounts.add(element);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
File voldFile = new File("/system/etc/vold.fstab");
if (voldFile.exists()) {
Scanner scanner = new Scanner(voldFile);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("dev_mount")) {
String[] lineElements = line.split(" ");
String element = lineElements[2];
if (element.contains(":"))
element = element.substring(0,
element.indexOf(":"));
if (!element.equals("/mnt/sdcard"))
mVold.add(element);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < mMounts.size(); i++) {
String mount = mMounts.get(i);
if (!mVold.contains(mount))
mMounts.remove(i--);
}
mVold.clear();
List<String> mountHash = new ArrayList<String>(10);
for (String mount : mMounts) {
File root = new File(mount);
if (root.exists() && root.isDirectory() && root.canWrite()) {
File[] list = root.listFiles();
String hash = "[";
if (list != null) {
for (File f : list) {
hash += f.getName().hashCode() + ":" + f.length()
+ ", ";
}
}
hash += "]";
if (!mountHash.contains(hash)) {
String key = SD_CARD + "_" + map.size();
if (map.size() == 0) {
key = SD_CARD;
} else if (map.size() == 1) {
key = EXTERNAL_SD_CARD;
}
mountHash.add(hash);
map.put(key, root);
}
}
}
mMounts.clear();
if (map.isEmpty()) {
map.put(SD_CARD, Environment.getExternalStorageDirectory());
}
return map;
}
}
}