1、创建文件夹
/**
* 判断目录是否存在,不存在则判断是否创建成功
*
* @param file 文件
* @return {@code true}: 存在或创建成功
{@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 true
if and only if the file was copied;
* false
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 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 getFilesAllName(String path) {
File file=new File(path);
File[] files=file.listFiles();
if (files == null){Log.e("error","空目录");return null;}
List s = new ArrayList<>();
for(int i =0;i
5、必要时还可以对视频进行压缩,使用的是VideoCompress:https://github.com/Tourenathan-G5organisation/SiliCompressor
class VideoCompressAsyncTask extends AsyncTask {
Context mContext;
public VideoCompressAsyncTask(Context context) {
mContext = context;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgressDialog("正在处理视频...");
}
@Override
protected String doInBackground(String... paths) {
LogUtils.e("paths[0]=" + paths[0]);
LogUtils.e("paths[1]=" + paths[1]);
String filePath = null;
try {
int outWidth = 0;
int outHeight = 0;
// if (mOriginalWidth > mOriginalHeight) {
// //横屏
// outWidth = 720;
// outHeight = 480;
// } else {
// //竖屏
// outWidth = 480;
// outHeight = 720;
// }
//默认竖屏
// outWidth = 480;
// outHeight = 720;
// filePath = SiliCompressor.with(KSListActivity.this)
// .compressVideo(paths[0], paths[1], outWidth, outHeight, 900000);
// 720p
outWidth = 720;
outHeight = 1280;
filePath = SiliCompressor.with(KSListActivity.this)
.compressVideo(paths[0], paths[1], outWidth, outHeight, 1500000);
// SiliCompressor.with(mContext).compressVideo(
// paths[0], paths[1]);
// //This bellow is just a temporary solution to test that method call works
// boolean b = Boolean.parseBoolean(paths[0]);
// if (b) {
// filePath = SiliCompressor.with(mContext).compressVideo(paths[1], paths[2]);
// } else {
// Uri videoContentUri = Uri.parse(paths[1]);
// // Example using the bitrate and video size parameters
// /*filePath = SiliCompressor.with(mContext).compressVideo(
// videoContentUri,
// paths[2],
// 1280,
// 720,
// 1500000);*/
// filePath = SiliCompressor.with(mContext).compressVideo(
// videoContentUri,
// paths[2]);
// }
} catch (Exception e) {
showShortToast("视频处理失败");
dismissProgressDialog();
e.printStackTrace();
}
return filePath;
}
@Override
protected void onPostExecute(String compressedFilePath) {
super.onPostExecute(compressedFilePath);
LogUtils.e("compressVideo onSuccess=========" + compressedFilePath);
String gpname = MasterApplication.getInstance().getCurrentUser().shopname;
String dir = Environment.getExternalStorageDirectory().toString() + File.separator + "xxx" + File.separator + "视频" + File.separator + "" + gpname;
final String destPath = dir + File.separator + "" + zuhuanhao + ".mp4";
File destFile = FileUtils.renameFile(compressedFilePath, destPath);
dismissProgressDialog();
showMessage("视频已保存:" + destFile.getAbsolutePath());
//复制文件
//通知系统更新
int duration;
try {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(destFile.getAbsolutePath());
duration = Integer.parseInt(mmr.extractMetadata
(MediaMetadataRetriever.METADATA_KEY_DURATION));
AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destFile.getAbsolutePath(), 0, duration);
} catch (Exception e) {
e.printStackTrace();
AlbumNotifyHelper.insertVideoToMediaStore(KSListActivity.this, destFile.getAbsolutePath(), 0, 0);
}
Log.i("Silicompressor", "Path: " + compressedFilePath);
}
}
文件改名:
/**
* oldPath 和 newPath必须是新旧文件的绝对路径
*/
public static File renameFile(String oldPath, String newPath) {
if (TextUtils.isEmpty(oldPath)) {
return null;
}
if (TextUtils.isEmpty(newPath)) {
return null;
}
File oldFile = new File(oldPath);
File newFile = new File(newPath);
boolean b = oldFile.renameTo(newFile);
File file2 = new File(newPath);
return file2;
}
使用方法:
new VideoCompressAsyncTask(this).execute(inputDir, dir);
6、压缩视频效果并不理想,很模糊,可以使用RxFFmpeg进行格式转换,mp4转flv,24mb的视频可以变成10mb,并且清晰度还不错。缺点是包内存急速增加。
文章
FFmpeg手撕视频(Android端)
使用此项目很快捷使用
RxFFmpeg 是基于 ( FFmpeg 4.0 + X264 + mp3lame + fdk-aac ) 编译的适用于 Android 平台的音视频编辑、视频剪辑的快速处理框架,包含以下功能(视频拼接,转码,压缩,裁剪,片头片尾,分离音视频,变速,添加静态贴纸和gif动态贴纸,添加字幕,添加滤镜,添加背景音乐,加速减速视频,倒放音视频,音频裁剪,变声,混音,图片合成视频,视频解码图片,抖音首页,视频播放器等主流特色功能
一句代码:
ffmpeg -i /storage/emulated/0/xxx.mp4 -b:v 4000k -y /storage/emulated/0/xxx.flv
4000k是码率,根据自己需要调整,值越大越清晰