效果图如下:
获取本地视频
关键代码:
/**
* 重要说明:
* <p>
* 疑问一:
* 该方法,查到的 视频路径 和 视频的缩略图 是一样的,并且格式都是 .mp4 。视频的缩略图应该是 .jpg或.png的后缀啊,
* 为什么会这样呢?抱歉,本人查了不少资料,还不知道具体原因。如果您知道原因,欢迎留言,评论。
* <p>
* <p>
* 疑问二:
* 除此之外,还有一个问题就是:明明手机的本地是有很多视频,但是有的机型就是查不到。
* <p>
* 亲测:小米2s,本地是很多视频的,但是获取不到,要么只查到几个,
* 而 小米8,魅族note8,就可以查到本地所有的视频。
* <p>
* 我又下载了 抖音和快手 app,他们也是存在同样的情况:
* 小米2s中,该方法从本地查到了2个视频,他们也是只查到了2个视频,其实本地是不止2个视频的。
* <p>
* 至于为什么有的机型,查不到本地所有的视频呢? 抱歉,同样也是不清楚具体原因。如果您知道原因,欢迎留言,评论。
*/
private List<LocalVideoBean> getLocalAllVideo() {
List<LocalVideoBean> sysVideoList = new ArrayList<>();
// MediaStore.Video.Thumbnails.DATA:视频缩略图的文件路径
/* String[] thumbColumns = {MediaStore.Video.Thumbnails.DATA,
MediaStore.Video.Thumbnails.VIDEO_ID};*/
// 视频其他信息的查询条件
String[] mediaColumns = {MediaStore.Video.Media._ID,
MediaStore.Video.Media.DATA, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.SIZE, MediaStore.Video.Thumbnails.DATA};
Cursor cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
mediaColumns, null, null, MediaStore.Video.VideoColumns.DATE_ADDED + " DESC");
if (cursor == null) {
return sysVideoList;
}
if (cursor.moveToFirst()) {
do {
LocalVideoBean info = new LocalVideoBean();
info.setPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)));
info.setDuration(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)));
info.setSize(cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)));
info.setThumbPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Thumbnails.DATA)));
sysVideoList.add(info);
} while (cursor.moveToNext());
}
cursor.close();
return sysVideoList;
}
LocalVideoAdapter.java
public class LocalVideoAdapter extends BaseQuickAdapter<LocalVideoBean, BaseViewHolder> {
public LocalVideoAdapter(@Nullable List<LocalVideoBean> data) {
super(R.layout.item_local_video, data);
}
@Override
protected void convert(BaseViewHolder helper, LocalVideoBean item) {
TextView tvTip = helper.getView(R.id.video_tip);
ImageView imageView = helper.getView(R.id.image_view);
RequestOptions options = new RequestOptions();
options.error(R.drawable.default_image)
.placeholder(R.drawable.default_image)
.override(130, 130);
//说明:虽然获取的视频缩略图的路径,但是真实返回的还是一个视频的路径
//因此,这里的file其实是一个视频文件
File file = new File(item.getThumbPath());
//glid虽然加载的为视频file,但是只会把视频的第一帧的图片加载显示出来
//如果这里不想用glide,去显示图片,也可以通过media.getFrameAtTime()方法,获取视频的第一帧,
// 参考文章地址:https://blog.csdn.net/bzlj2912009596/article/details/80446256
Glide.with(mContext)
.load(Uri.fromFile(file))//这个方法在华为手机上,没问题。
//经过测试发现,在华为手机上,这个方法的加载,直接把手机卡死了,小米8未出现同样问题,
//初步判断是太耗内存,上面的方法就没问题。
//.load(new File(item.getThumbPath()))
.apply(options)
.into(imageView);
//bitmap 太大,加载卡顿,不理想
//Bitmap videoThumbBit = VideoThumbUtils.getVideoThumb(item.getThumbPath());
//Bitmap videoThumbBit = VideoThumbUtils.getVideoThumbnail(item.getThumbPath(), 100, 100);
/* Glide.with(mContext)
.load(videoThumbBit)
.apply(options)
.into(imageView);*/
helper.setText(R.id.video_duration, TimeUtils.convertSecondsToTime(item.getDuration() / 1000));
if (item.getDuration() / 1000 > 15) {
tvTip.setVisibility(View.VISIBLE);
} else {
tvTip.setVisibility(View.GONE);
}
}
}
LocalVideoBean.java
public class LocalVideoBean {
private String path;
private long duration;
private long size;
private String thumbPath;
private Bitmap bitmap;
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public String getThumbPath() {
return thumbPath;
}
public void setThumbPath(String thumbPath) {
this.thumbPath = thumbPath;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
视频剪裁
关键代码:
//得到视频路径,new 出视频file,我们就可以对视频进行剪裁处理了(文字,滤镜...)
//这里推荐一个开源框架,Android-Video-Trimmer: https://github.com/iknow4/Android-Video-Trimmer 还不错。
// 缺点是:使用ffmpeg进行视频裁剪。会让你的app增大许多,20-30M
private void videoCrop(String videoPath) {
if (!TextUtils.isEmpty(videoPath)) {
Bundle bundle = new Bundle();
bundle.putString(VIDEO_PATH_KEY, videoPath);
Intent intent = new Intent(MainActivity.this, VideoTrimmerActivity.class);
intent.putExtras(bundle);
startActivityForResult(intent, VIDEO_TRIM_REQUEST_CODE);
}
}
结果处理关键代码:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i(TAG, "onActivityResult: requestCode=" + requestCode + " resultCode=" + resultCode);
if (resultCode == RESULT_OK && requestCode == 66) {
if (data != null) {
LocalVideoBean localVideoBean = getVideoPath(data);
tvPath.setText("视频路径:\n" + localVideoBean.getPath() + "\n\n" + " 视频缩略图路径:\n" + localVideoBean.getThumbPath());
videoCrop(localVideoBean.getPath());
} else {
Toast.makeText(this, "data is null", Toast.LENGTH_SHORT).show();
}
} else if (requestCode == VIDEO_REQUESTCODE && resultCode == 11) {
String video_path = data.getStringExtra("video_path");
String poster_path = data.getStringExtra("poster_path");
tvPath.setText("视频路径:\n" + video_path + "\n\n" + " 视频缩略图路径:\n" + poster_path);
videoCrop(video_path);
} else if (requestCode == VIDEO_REQUESTCODE && resultCode == 22) {//拍摄的视频路径,可以返回到这里
} else if (requestCode == VideoTrimmerActivity.VIDEO_TRIM_REQUEST_CODE && resultCode == RESULT_OK) {
if (data != null) {
String clippingPath = data.getStringExtra("clippingPath");
tv_clipping_path.setText("剪裁结果路径:\n" + clippingPath);
}
}
}
视频拍摄
VideoRecorder
WeiXinRecordedDemo
CameraView :https://github.com/CJT2325/CameraView
说明:截止2019-02-25 start 1.7k 最新更新于2年前
CameraView :https://github.com/natario1/CameraView
说明:截止2019-02-25 start 1.7k 最新更新于几天前
cameraview: https://github.com/google/cameraview
说明:截止2019-02-25 start 4.2k 最新更新于2年前
项目源码
ps:项目源码中,都已经做了很详细的说明。