1、創建文件夾
/**
* 判斷目錄是否存在,不存在則判斷是否創建成功
*
* @param file 文件
* @return {@code true}: 存在或創建成功<br>{@code false}: 不存在或創建失敗
*/
public static boolean createOrExistsDir(File file) {
// 如果存在,是目錄則返回true,是文件則返回false,不存在則返回是否創建成功
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
2、複製視頻
/**
* 複製單個文件
*
* @param oldPath$Name String 原文件路徑+文件名 如:data/user/0/com.test/files/abc.txt
* @param newPath$Name String 複製後路徑+文件名 如:data/user/0/com.test/cache/abc.txt
* @return <code>true</code> if and only if the file was copied;
* <code>false</code> otherwise
*/
public static boolean copyFile(String oldPath$Name, String newPath$Name) {
try {
File oldFile = new File(oldPath$Name);
if (!oldFile.exists()) {
Log.e("--Method--", "copyFile: oldFile not exist.");
return false;
} else if (!oldFile.isFile()) {
Log.e("--Method--", "copyFile: oldFile not file.");
return false;
} else if (!oldFile.canRead()) {
Log.e("--Method--", "copyFile: oldFile cannot read.");
return false;
}
/* 如果不需要打log,可以使用下面的語句
if (!oldFile.exists() || !oldFile.isFile() || !oldFile.canRead()) {
return false;
}
*/
FileInputStream fileInputStream = new FileInputStream(oldPath$Name); //讀入原文件
FileOutputStream fileOutputStream = new FileOutputStream(newPath$Name);
byte[] buffer = new byte[1024];
int byteRead;
while ((byteRead = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, byteRead);
}
fileInputStream.close();
fileOutputStream.flush();
fileOutputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
3、創建文件夾,複製視頻,通知系統圖庫更新
private void saveVideoToSDCard(File videoFile, String name) {
String gpname = "123";
if (TextUtils.isEmpty(gpname)){
gpname="公棚";
}
String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
LogUtils.i("創建目錄=" + dir);
File dirFile = new File(dir);
if (FileUtils.createOrExistsDir(dirFile)) {
String target = dir + File.separator + "" + name+ ".mp4";
LogUtils.i("移動文件=" + target);
if (FileUtils.copyFile(videoFile.getAbsolutePath(), target)) {
// showShortToast("視頻已保存:" + target);
LogUtils.i("視頻已保存:" + target);
// zipVideo(target);
//通知系統圖片更新
int duration;
try {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(target);
duration = Integer.parseInt(mmr.extractMetadata
(MediaMetadataRetriever.METADATA_KEY_DURATION));
AlbumNotifyHelper.insertVideoToMediaStore(this, target, 0, duration);
} catch (Exception e) {
e.printStackTrace();
AlbumNotifyHelper.insertVideoToMediaStore(this, target, 0, 0);
}
LogUtils.i("移動視頻成功");
} else {
showShortToast("移動視頻失敗");
}
} else {
showShortToast("創建文件夾失敗");
}
refreshFiles();
}
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
/**
* CreateAt : 2017/5/24
* Describe : 相冊更新通知幫助類
* 創建時間單位ms
* 視頻時長單位ms
*
*/
public class AlbumNotifyHelper {
public static final String TAG = AlbumNotifyHelper.class.getSimpleName();
///////////////////////////////////////////////////////////////////////////
// 下面是對外公開的重載的方法
///////////////////////////////////////////////////////////////////////////
public static void notifyScanDcim(Context context, String filePath) {
scanFile(context, filePath);
}
public static void insertVideoToMediaStore(Context context, String filePath, long dateTaken, long duration) {
insertVideoToMediaStore(context, filePath, dateTaken, 0, 0, duration);
}
/* public static void insertVideoToMediaStore(Context context, VideoUtil.VideoInfo videoInfo) {
insertVideoToMediaStore(context, videoInfo.originalVideoFilePath, videoInfo.dateTaken, videoInfo.width, videoInfo.height, videoInfo.duringTime);
}*/
public static void insertImageToMediaStore(Context context, String filePath, long createTime) {
insertImageToMediaStore(context, filePath, createTime, 0, 0);
}
///////////////////////////////////////////////////////////////////////////
// 掃描系統相冊核心方法
///////////////////////////////////////////////////////////////////////////
/**
* 針對系統文夾只需要掃描,不用插入內容提供者,不然會重複
*
* @param context 上下文
* @param filePath 文件路徑
*/
public static void scanFile(Context context, String filePath) {
if (!checkFile(filePath))
return;
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(new File(filePath)));
context.sendBroadcast(intent);
}
///////////////////////////////////////////////////////////////////////////
// 非系統相冊像MediaContent中插入數據,核心方法
///////////////////////////////////////////////////////////////////////////
/**
* 針對非系統文件夾下的文件,使用該方法
* 插入時初始化公共字段
*
* @param filePath 文件
* @param time ms
* @return ContentValues
*/
private static ContentValues initCommonContentValues(String filePath, long time) {
ContentValues values = new ContentValues();
File saveFile = new File(filePath);
long timeMillis = getTimeWrap(time);
values.put(MediaStore.MediaColumns.TITLE, saveFile.getName());
values.put(MediaStore.MediaColumns.DISPLAY_NAME, saveFile.getName());
values.put(MediaStore.MediaColumns.DATE_MODIFIED, timeMillis);
values.put(MediaStore.MediaColumns.DATE_ADDED, timeMillis);
values.put(MediaStore.MediaColumns.DATA, saveFile.getAbsolutePath());
values.put(MediaStore.MediaColumns.SIZE, saveFile.length());
return values;
}
/**
* 保存到照片到本地,並插入MediaStore以保證相冊可以查看到,這是更優化的方法,防止讀取的照片獲取不到寬高
*
* @param context 上下文
* @param filePath 文件路徑
* @param createTime 創建時間 <=0時爲當前時間 ms
* @param width 寬度
* @param height 高度
*/
public static void insertImageToMediaStore(Context context, String filePath, long createTime, int width, int height) {
if (!checkFile(filePath))
return;
createTime = getTimeWrap(createTime);
ContentValues values = initCommonContentValues(filePath, createTime);
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, createTime);
values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
if (width > 0) values.put(MediaStore.Images.ImageColumns.WIDTH, 0);
if (height > 0) values.put(MediaStore.Images.ImageColumns.HEIGHT, 0);
}
values.put(MediaStore.MediaColumns.MIME_TYPE, getPhotoMimeType(filePath));
context.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
/**
* 保存到視頻到本地,並插入MediaStore以保證相冊可以查看到,這是更優化的方法,防止讀取的視頻獲取不到寬高
*
* @param context 上下文
* @param filePath 文件路徑
* @param createTime 創建時間 <=0時爲當前時間 ms
* @param duration 視頻長度 ms
* @param width 寬度
* @param height 高度
*/
public static void insertVideoToMediaStore(Context context, String filePath, long createTime, int width, int height, long duration) {
if (!checkFile(filePath))
return;
createTime = getTimeWrap(createTime);
ContentValues values = initCommonContentValues(filePath, createTime);
values.put(MediaStore.Video.VideoColumns.DATE_TAKEN, createTime);
if (duration > 0)
values.put(MediaStore.Video.VideoColumns.DURATION, duration);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
if (width > 0) values.put(MediaStore.Video.VideoColumns.WIDTH, width);
if (height > 0) values.put(MediaStore.Video.VideoColumns.HEIGHT, height);
}
values.put(MediaStore.MediaColumns.MIME_TYPE, getVideoMimeType(filePath));
context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
}
// 是不是系統相冊
private static boolean isSystemDcim(String path) {
return path.toLowerCase().contains("dcim") || path.toLowerCase().contains("camera");
}
// 獲取照片的mine_type
private static String getPhotoMimeType(String path) {
String lowerPath = path.toLowerCase();
if (lowerPath.endsWith("jpg") || lowerPath.endsWith("jpeg")) {
return "image/jpeg";
} else if (lowerPath.endsWith("png")) {
return "image/png";
} else if (lowerPath.endsWith("gif")) {
return "image/gif";
}
return "image/jpeg";
}
// 獲取video的mine_type,暫時只支持mp4,3gp
private static String getVideoMimeType(String path) {
String lowerPath = path.toLowerCase();
if (lowerPath.endsWith("mp4") || lowerPath.endsWith("mpeg4")) {
return "video/mp4";
} else if (lowerPath.endsWith("3gp")) {
return "video/3gp";
}
return "video/mp4";
}
// 獲得轉化後的時間
private static long getTimeWrap(long time) {
if (time <= 0) {
return System.currentTimeMillis();
}
return time;
}
// 檢測文件存在
private static boolean checkFile(String filePath) {
//boolean result = FileUtil.fileIsExist(filePath);
boolean result = false;
File mFile = new File(filePath);
if (mFile.exists()) {
result = true;
}
Log.e(TAG, "文件不存在 path = " + filePath);
return result;
}
}
4、瀏覽文件夾
private void refreshFiles() {
String gpname = MasterApplication.getInstance().getCurrentUser().shopname;
String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
LogUtils.i("創建目錄=" + dir);
File dirFile = new File(dir);
if (FileUtils.createOrExistsDir(dirFile)) {
List<String> files = FileUtils.getFilesAllName(dir);
if (ListUtils.isEmpty(files)) {
return;
}
for (int i = 0; i < files.size(); i++) {
String fileName = files.get(i);
String suffix = fileName.substring(0, fileName.lastIndexOf("."));
LogUtils.i("文件名=" + fileName);
LogUtils.i("suffix=" + suffix);
if (!TextUtils.isEmpty(suffix)) {
String[] arrs = suffix.split("/");
if (arrs != null && arrs.length > 1) {
suffix = arrs[arrs.length - 1];
LogUtils.i("文件名=" + suffix);
for (int j = 0; j < lists.size(); j++) {
if (suffix.equals(lists.get(j).getZh())) {
lists.get(j).setLocalVideo(fileName);
}
}
}
}
}
mAdapter.setDataAndRefreshUI(lists);
} else {
LogUtils.e("創建文件夾失敗");
}
}
public static List<String> getFilesAllName(String path) {
File file=new File(path);
File[] files=file.listFiles();
if (files == null){Log.e("error","空目錄");return null;}
List<String> s = new ArrayList<>();
for(int i =0;i<files.length;i++){
s.add(files[i].getAbsolutePath());
}
return s;
}
5、必要時還可以對視頻進行壓縮,使用的是VideoCompress:https://github.com/wuxiaoqiang625/VideoCompress
private void zipVideo(String inputDir) {
String gpname = MasterApplication.getInstance().getCurrentUser().shopname;
String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "我的APP" + File.separator + "視頻集錦" + File.separator + "" + gpname;
LogUtils.i("創建目錄=" + dir);
File dirFile = new File(dir);
if (FileUtils.createOrExistsDir(dirFile)) {
final String destPath = dir + File.separator + "" + zuhuanhao + ".mp4";
// final String destPath = outputDir + File.separator + "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss", getLocale()).format(new Date()) + ".mp4";
VideoCompress.compressVideoMedium(inputDir, destPath, new VideoCompress.CompressListener() {
@Override
public void onStart() {
showProgressDialog("正在壓縮視頻...");
// startTime = System.currentTimeMillis();
// setTime(startTime, "開始時間");
LogUtils.i("壓縮前大小 = " + getFileSize(inputDir));
}
@Override
public void onSuccess() {
dismissProgressDialog();
showShortToast("視頻已保存:" + destPath);
//通知系統更新
int duration;
try {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(destPath);
duration = Integer.parseInt(mmr.extractMetadata
(MediaMetadataRetriever.METADATA_KEY_DURATION));
AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destPath, 0, duration);
} catch (Exception e) {
e.printStackTrace();
AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destPath, 0, 0);
}
refreshFiles();
// endTime = System.currentTimeMillis();
// setTime(endTime, "結束時間");
LogUtils.i("壓縮後大小 = " + getFileSize(destPath));
// openFile(new File(destPath));
}
@Override
public void onFail() {
dismissProgressDialog();
showShortToast("視頻壓縮失敗");
// endTime = System.currentTimeMillis();
// setTime(endTime, "失敗時間");
}
@Override
public void onProgress(float percent) {
// LogUtils.i("壓縮進度+" + String.valueOf(percent) + "%");
}
});
}
}
6、壓縮視頻效果並不理想,很模糊,可以使用RxFFmpeg進行格式轉換,mp4轉flv,24mb的視頻可以變成10mb,並且清晰度還不錯。缺點是包內存急速增加。
文章
FFmpeg手撕視頻(Android端)
一句代碼:
ffmpeg -i /storage/emulated/0/xxx.mp4 -b:v 4000k -y /storage/emulated/0/xxx.flv
4000k是碼率,根據自己需要調整,值越大越清晰