主要功能:
1、相册选择图片、视频,支持多选
2、自定义拍照、拍摄10s短视频
3、图片压缩、短视频裁剪、压缩,采用鲁班压缩算法
4、图片、视频预览功能,已在库中处理动态权限问题
请看效果:
hi.gif Screenshot_20180302-142722.jpg
Screenshot_20180302-142819.jpg Screenshot_20180302-142911.jpg Screenshot_20180302-142849.jpg
Screenshot_20180302-142929.jpg Screenshot_20180302-142954.jpg Screenshot_20180302-143144.jpg
在第一张gif中没有展示拍照和拍视频,因为用的模拟器背景一坨黑,整个功能模块已经封装到library库中,只需要简单的调用便可以跑起来的啦,项目结构如下:
q.png
我们的主要功能逻辑、界面等均在picture_library中,后面smallVideLib2是一个拍摄小视频专用库,在picture_library中进行调用,视频处理逻辑是比较复杂的:
视频 :
拍摄视频
用smallVideLib2库进行拍摄,采用ffmpeg+x264方案,软编码,拍摄的样式大小可控,输出的就是我们想要的
选择视频
可能大部分为手机自带相机拍摄,基于android sdk MedieaCodec采用硬编码,一般都很大,而且不转码的情况下很多网络播放器无法播放,考虑到实际项目中上传等待时间以及流量的问题,必须进行裁剪二次压缩编码
视频处理流程
1、显示视频列表,大于5分钟的不显示
2、小于10秒可以直接进行压缩上传;大于10秒小于5分钟的视频必须要进行裁剪
3、裁剪过程实际上是对视频进行了 解码解压------拆分------合并------编码压缩 的过程,所以对这个过程通常会是一个耗时操作,微信相册也是如此
4、保存视频,回传视频保存地址
接下来看下功能比较多的picture_library库的结构:
isoviewer-1.0-RC-27.jar 是一个视频编辑处理开源库,项目承担了视频裁剪和压缩的工作,jniLibs下面是一堆ffmpeg编译好的.so文件,在项目中我们两个地方需要用到它,一个是拍摄软编码、另一个则是裁剪时的二次编码,这里的工程还是比较大的,其中主要几个我说一哈:
CameraActivity.java:
拍照和拍摄视频界面,公用一个界面,这里点击是拍照,长按是拍视频
PictureEditAudioActivity.java:
视频编辑界面,也就是裁剪压缩界面
CameraActivity.java:
拍照和拍摄视频界面,公用一个界面,这里点击是拍照,长按是拍视频
PicturePreviewActivity.java:
图片viewpager预览界面,和视频界面公用一个界面
PictureVideoPlayActivity.java:
视频播放界面
PictureSelectorActivity.java:
图片选择库主界面,也是最重要的一个界面
还有少数页面当前项目没有使用这里就不解释了,而其他的类或者包里面的类都是为这些界面服务的,整个库数据都是通过一层层回调出去的,请在研究源码时注意注意!!!
至此,整体就比较清晰了,关于实现细节,由于内容比较多完全可以分为多篇文章来讲解,不是这一篇文章就能说得清楚得,就留给读者在下载来慢慢研究吧,最后给大家展示下外部使用代码吧:
/**
* crate by yy on 2018-3-1
* describle:图片、视频选择库,功能如下:
* 1、相册选择图片、视频,支持多选
* 2、自定义拍照、拍摄10s短视频
* 3、图片压缩、短视频裁剪、压缩,采用鲁班压缩算法
* 4、预览功能,已在库中处理动态权限问题
*/
public class MainActivity extends AppCompatActivity {
Button picture, video;
LinearLayout result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
picture = findViewById(R.id.picture);
video = findViewById(R.id.video);
result = findViewById(R.id.result);
picture.setOnClickListener(view -> initPictureSelector(PictureMimeType.ofImage()));
video.setOnClickListener(view -> initPictureSelector(PictureMimeType.ofVideo()));
}
/**
* 打开相册初始化,回传数据在onActivityResult方法中
*
* @param chooseMode 打开的类型
*/
public void initPictureSelector(int chooseMode) {
PictureSelector.create(this)
.openGallery(chooseMode)// 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()
.theme(R.style.picture_default_style)// 主题样式设置 具体参考 libray中values/styles
.maxSelectNum(9)// 最大图片选择数量
.minSelectNum(1)// 最小选择数量
.imageSpanCount(4)// 每行显示个数
.selectionMode(PictureConfig.MULTIPLE)// 多选 or 单选 PictureConfig.MULTIPLE : PictureConfig.SINGLE
.previewImage(true)// 是否可预览图片
.previewVideo(true)// 是否可预览视频
.enablePreviewAudio(false)// 是否预览音频
// .compressGrade(Luban.THIRD_GEAR)// luban压缩档次,默认3档 Luban.FIRST_GEAR、Luban.CUSTOM_GEAR
.isCamera(true)// 是否显示拍照按钮
.isZoomAnim(true)// 图片列表点击 缩放效果 默认true
.setOutputCameraPath(Constant.IMAGE_CACHE)// 自定义拍照保存路径
.compress(true)// 是否压缩
.compressMode(PictureConfig.LUBAN_COMPRESS_MODE)//系统自带 or 鲁班压缩 PictureConfig.SYSTEM_COMPRESS_MODE or LUBAN_COMPRESS_MODE
// //.sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效
.glideOverride(160, 160)// glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度
.isGif(false)// 是否显示gif图片
.openClickSound(false)// 是否开启点击声音
// .selectionMedia(selectList)// 是否传入已选图片
// //.previewEggs(false)// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中)
// .compressGrade(Luban.CUSTOM_GEAR)
.compressGrade(Luban.CUSTOM_GEAR)
.compressMaxKB(1024)//压缩最大值kb compressGrade()为Luban.CUSTOM_GEAR有效
.minimumCompressSize(500) //add by tanhaiqin, 图片大小 <= 500KB(数字可变) 不需要压缩
// //.compressWH() // 压缩宽高比 compressGrade()为Luban.CUSTOM_GEAR有效
// //.videoQuality()// 视频录制质量 0 or 1
.videoSecond(5 * 60)//显示多少秒以内的视频
// //.recordVideoSecond()//录制视频秒数 默认60秒
.forResult(PictureConfig.CHOOSE_REQUEST);
}
/**
* 处理 PictureSelectorActivity.java 返回的数据
* 注意 图片压缩 已经是在picture lib中处理, 界面仅仅是展示获取的LocalMedia数据,不做再次压缩!
*
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PictureConfig.CHOOSE_REQUEST:
// 图片选择,共用一个数据通道:返回时图片,可能为列表,视频只能有一个
List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data);
LogUtils.i("TEST===> selectList.size = " + selectList.size());
for (int i = 0; i < selectList.size(); i++) {
handleLocalMedia(selectList.get(i));
}
break;
}
}
}
private void handleLocalMedia(LocalMedia media) {
int pictureType = PictureMimeType.isPictureType(media.getPictureType());
switch (pictureType) {
case PictureConfig.TYPE_IMAGE:
LogUtils.e("TEST===> media path = " + media.getPath()
+ ", compressPath = " + media.getCompressPath()
+ ", height = " + media.getHeight()
+ ", width = " + media.getWidth());
TextView textView = new TextView(this);
textView.setText(" media path =" + media.getPath());
result.addView(textView);
break;
case PictureConfig.TYPE_VIDEO:
if (TextUtils.isEmpty(media.getPath())) return;
if (!StringUtils.fileIsExists(media.getPath())) {
LogUtils.e("文件可能不存在了~");
return;
}
LogUtils.e("TEST===> video path = " + media.getPath()
+ ", compressPath = " + media.getCompressPath()
+ ", height = " + media.getHeight()
+ ", width = " + media.getWidth());
TextView textView1 = new TextView(this);
textView1.setText(" video path =" + media.getPath());
result.addView(textView1);
break;
}
}
}
使用上其实是炒鸡简单得。哈哈,需要自定义得话就得去改库了哈,特此说明此项目参考了许多其他项目,我把参考列在文章结尾。写本文章得目的也是为了把在上家公司做得比较难得部分独立备份出来,对自己,对他人都是一个帮助,因为当时写得时候网上去东拼西凑得,而且别人得demo还有不少得bug,说多了泪。。。
整个库以及测试代码我已上传github:
一个功能完善的相册库,类似微信朋友圈相册
希望大家多多支持,多多star,谢谢!!
参考
1、Picture Selector Library for Android or 多图片选择器
2、A Java API to read, write and create MP4 files
3、利用FFmpeg视频录制微信小视频与其压缩处理
网友评论