一、綜述
之前做了一個 Android 應用,有一個功能是:用戶錄製視頻並保存到本地,然後可以查看在當前 APP 中錄製的視頻並進行一些“上傳”或“刪除”操作。
這裏主要有兩個功能,首先要錄製視頻然後保存到一個特定的文件夾,這個文件夾就用來存放本應用中錄製的視頻。然後可以將這個文件夾中的所有文件以列表的形式展示給用戶看,並附帶縮略圖。
剛開始的時候我想自己寫一個錄製的功能,無奈本領不濟,左改右改都不好用,要麼是視頻被拉伸,要麼是錄製時不清晰,所以最後還是決定使用手機自帶的錄像功能,現在把該模塊的 Demo 拿上來與大家交流一下。
Demo
二、代碼解析
這裏的代碼解析只貼了一些重要代碼,想要完整代碼可以下載上面的 Demo 運行。
1. 錄製及保存
從代碼中我們看出,我們點擊“錄製”按鈕,會首先生成一個文件的 uri,這個 uri 就代表將要錄製的視頻文件,uri 裏有這個文件保存的文件夾以及這個文件的名字。
之後我們通過隱式 Intent 啓動系統的錄像功能,並將文件的 uri 傳入,文件就能根據設置的 uri 保存到指定文件夾。
/**
* 點擊錄製視頻,調用系統播放器開始錄像
*/
main_button_record.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
fileUri = Uri.fromFile(createMediaFile());
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.setAction("android.media.action.VIDEO_CAPTURE");
intent.addCategory("android.intent.category.DEFAULT");
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // 設置視頻文件的名字
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // 設置視頻質量爲高
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
}
});
/**
* 創建一個文件的 uri,供視頻錄製完保存
*/
private File createMediaFile() throws IOException {
// 如果 SD 卡存在,則在外部存儲建立一個文件夾用於存放視頻
if ((Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))) {
// 選擇自己的文件夾
String path = Environment.getExternalStorageDirectory().getPath();
// Constants.video_url 是一個常量,代表存放視頻的文件夾
File mediaStorageDir = new File(path + Constants.video_url);
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.e("TAG", "文件夾創建失敗");
return null;
}
}
// 文件根據當前的毫秒數給自己命名
String timeStamp = String.valueOf(System.currentTimeMillis());
timeStamp = timeStamp.substring(7);
String imageFileName = "V" + timeStamp;
String suffix = ".mp4";
File mediaFile = new File(mediaStorageDir + File.separator + imageFileName + suffix);
return mediaFile;
}
return null;
}
2. 查看視頻文件列表
這裏有兩個重要方法。
一個是獲取文件的列表,在這個方法中,我們先判斷外部 SD 卡是否可用,如果可用,就獲取之前保存文件的目錄下的所有文件。文件的屬性中有一個最後修改時間,我們就拿來用作文件的發佈時間。
第二個方法是用於獲取視頻的縮略圖的,這個方法需要文件的路徑並通過路徑獲取縮略圖。路徑 filePath 使用 files.getPath() 或 files.getAbsolutePath() 都可以。
/**
* 獲取文件列表
*/
public void GetVideoFiles(){
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// 獲取該文件夾下的所有文件
String path = Environment.getExternalStorageDirectory().getPath();
File mediaStorageDir = new File(path + Constants.video_url);
File[] files = mediaStorageDir.listFiles();
if (files != null && files.length > 0) {
for(int i = 0; i < files.length; i++){
VideoItem videoItem = new VideoItem();
videoItem.setVideoName(files[i].getName());
String publish_time = new SimpleDateFormat("yyyy年MM月dd日").format(files[i].lastModified());
videoItem.setTime(publish_time);
videoItem.setBitmap(getVideoThumbnail(files[i].getAbsolutePath()));
mVideoItems.add(videoItem);
}
} else {
Toast.makeText(this, "沒有視頻文件!", Toast.LENGTH_SHORT).show();
}
}
}
/**
* 根據視頻文件的路徑獲取視頻的縮略圖
* filePath 使用 files.getPath() 或 files.getAbsolutePath() 都可以
*/
public Bitmap getVideoThumbnail(String filePath) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime();
} catch(IllegalArgumentException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
return bitmap;
}