本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

PhotoMovie <https://github.com/yellowcath/PhotoMovie>
(https://github.com/yellowcath/PhotoMovie)
<https://github.com/yellowcath/PhotoMovie>可轻松实现类似抖音、微视、美拍的照片电影功能。效果如下

滤镜效果



转场效果

 

基本用法

可参照DemoPresenter
<https://github.com/yellowcath/PhotoMovie/blob/master/app/src/main/java/com/hw/photomovie/sample/DemoPresenter.java>
//添加图片 List<PhotoData> photoDataList = new LinkedList<PhotoData>();
photoDataList.add(new
SimplePhotoData(context,photoPath1,PhotoData.STATE_LOCAL)); ...
photoDataList.add(new
SimplePhotoData(context,photoPathN,PhotoData.STATE_LOCAL)); //生成图片源 PhotoSource
photoSource = new PhotoSource(photoDataList); //生成照片电影(使用预定义的水平转场动画) PhotoMovie
photoMovie = PhotoMovieFactory.generatePhotoMovie(photoSource,
PhotoMovieFactory.PhotoMovieType.HORIZONTAL_TRANS); //生成负责绘制电影内容的MovieRenderer
MovieRenderer movieRenderer = new GLTextureMovieRender(glTextureView); /** * OR
MovieRenderer movieRenderer = new GLSurfaceMovieRenderer(glSurfaceView); */
//照片电影播放器 PhotoMoviePlayer photoMoviePlayer = new PhotoMoviePlayer(context);
photoMoviePlayer.setMovieRenderer(mMovieRenderer);
photoMoviePlayer.setMovieListener(...); photoMoviePlayer.setLoop(true);
photoMoviePlayer.setOnPreparedListener(new
PhotoMoviePlayer.OnPreparedListener() { @Override public void
onPreparing(PhotoMoviePlayer moviePlayer, float progress) { } @Override public
void onPrepared(PhotoMoviePlayer moviePlayer, int prepared, int total) {
mPhotoMoviePlayer.start(); } @Override public void onError(PhotoMoviePlayer
moviePlayer) { } }); photoMoviePlayer.prepare();
轻松扩展

PhotoMovie使用模块化的设计,每个部分都可以自定义然后替换,主要类图如下

 

*
MovieSegment
:电影片段,每个电影片段都有特定的时长,在这段时间之内以特定的方式播放图片,例如ScaleSegment会对图片做缩放动画、EndGaussianBlurSegment会对图片做从清晰到模糊的高斯模糊动画

*
PhotoMovie
:核心类,代表照片电影本身,由图片源(PhotoSource)和若干电影片段(MovieSegment)组成一个完整的照片电影,图片通过PhotoAllocator分配给MovieSegment

*
MovieLayer:为MovieSegment扩展绘制多层特效的功能,例如SubtitleLayer提供字幕展示

*
IMovieFilter:为整个照片电影提供滤镜

*
MovieRenderer
:负责把照片电影渲染到指定的输出界面,例如TextureView(GLTextureMovieRender)、GLSurfaceView(GLSurfaceMovieRenderer)

*
PhotoMoviePlayer:提供类似MediaPlayer的接口,负责播放照片电影,播放进度由IMovieTimer控制

扩展电影类型

目前内置了6种类型,后两种即是抖音的左右切换和上下切换,Thaw和WINDOW仿自美拍
public enum PhotoMovieType { THAW, //融雪 SCALE, //缩放 SCALE_TRANS, //缩放 & 平移
WINDOW, //窗扉 HORIZONTAL_TRANS,//横向平移 VERTICAL_TRANS//纵向平移 }
这里以微视的渐变特效为例展示如何扩展
分析得出,渐变特效首先图片居中放置,然后全程做一个微弱的放大动画,后半部分同时透明度变化消失 ,更直观的流程如下图

 

可见需要两个不同的片段类型 首先创建FitCenterScaleSegment,继承FitCenterSegment,实现单张图片的放大动画
public class FitCenterScaleSegment extends FitCenterSegment { /** * 缩放动画范围 */
private float mScaleFrom; private float mScaleTo; private float mProgress; /**
* @param duration 片段时长 * @param scaleFrom 缩放范围 * @param scaleTo 缩放范围 */ public
FitCenterScaleSegment(int duration, float scaleFrom, float scaleTo) {
super(duration); mScaleFrom = scaleFrom; mScaleTo = scaleTo; } @Override
protected void onDataPrepared() { super.onDataPrepared(); } @Override public
void drawFrame(GLESCanvas canvas, float segmentProgress) { mProgress =
segmentProgress; if (!mDataPrepared) { return; } drawBackground(canvas); float
scale = mScaleFrom + (mScaleTo - mScaleFrom) * mProgress;
//FitCenterSegment已经具有缩放能力,这里传缩放值即可 drawContent(canvas, scale); }
//提升这两个函数的访问权限,供转场时使用 @Override public void drawContent(GLESCanvas canvas,
float scale) { super.drawContent(canvas, scale); } @Override public void
drawBackground(GLESCanvas canvas) { super.drawBackground(canvas); } }
然后创建转场片段GradientTransferSegment,其父类TransitionSegment同时持有上一个与下一个片段,
可以在其基础上实现任意转场功能
public class GradientTransferSegment extends
TransitionSegment<FitCenterScaleSegment, FitCenterScaleSegment> { /** * 缩放动画范围
*/ private float mPreScaleFrom; private float mPreScaleTo; private float
mNextScaleFrom; private float mNextScaleTo; public GradientTransferSegment(int
duration, float preScaleFrom, float preScaleTo, float nextScaleFrom, float
nextScaleTo) { mPreScaleFrom = preScaleFrom; mPreScaleTo = preScaleTo;
mNextScaleFrom = nextScaleFrom; mNextScaleTo = nextScaleTo;
setDuration(duration); } @Override protected void onDataPrepared() { }
@Override public void drawFrame(GLESCanvas canvas, float segmentProgress) {
//下一个片段开始放大 float nextScale = mNextScaleFrom + (mNextScaleTo - mNextScaleFrom)
* segmentProgress; mNextSegment.drawContent(canvas, nextScale);
//上一个片段继续放大同时变透明 float preScale = mPreScaleFrom + (mPreScaleTo - mPreScaleFrom)
* segmentProgress; float alpha = 1 - segmentProgress;
mPreSegment.drawBackground(canvas); canvas.save(); canvas.setAlpha(alpha);
mPreSegment.drawContent(canvas, preScale); canvas.restore(); }
创建照片电影
private static PhotoMovie initGradientPhotoMovie(PhotoSource photoSource) {
List<MovieSegment> segmentList = new ArrayList<>(photoSource.size()); for (int
i = 0; i < photoSource.size(); i++) { if (i == 0) { segmentList.add(new
FitCenterScaleSegment(1600, 1f, 1.1f)); } else { segmentList.add(new
FitCenterScaleSegment(1600, 1.05f, 1.1f)); } if (i < photoSource.size() - 1) {
segmentList.add(new GradientTransferSegment(800, 1.1f, 1.15f, 1.0f, 1.05f)); }
} return new PhotoMovie(photoSource, segmentList); }
然后将这个PhotoMovie正常播放即可,效果如下



扩展滤镜

目前内置了9个滤镜
public enum FilterType { NONE, CAMEO,//浮雕 GRAY,//黑白 KUWAHARA,//水彩
SNOW,//飘雪(动态) LUT1, LUT2, LUT3, LUT4, LUT5, }
先看IMovieFilter
public interface IMovieFilter { void doFilter(PhotoMovie photoMovie,int
elapsedTime, FboTexture inputTexture, FboTexture outputTexture); void
release(); }
外部会提供一个输入纹理,然后由IMovieFilter处理之后绘制到输出纹理上,即实现了滤镜效果

BaseMovieFilter已经实现了基本的输入输出流程,例如要做最基本的黑白滤镜,只需更换FRAGMENT_SHADER即可
public class GrayMovieFilter extends BaseMovieFilter { protected static final
String FRAGMENT_SHADER = "" + "varying highp vec2 textureCoordinate;\n" + " \n"
+ "uniform sampler2D inputImageTexture;\n" + " \n" + "void main()\n" + "{\n" +
" mediump vec4 color = texture2D(inputImageTexture, textureCoordinate);\n" + "
mediump float gray = color.r*0.3+color.g*0.59+color.b*0.11;\n"+ " gl_FragColor
= vec4(gray,gray,gray,1.0);\n"+ "}"; public GrayMovieFilter(){
super(VERTEX_SHADER,FRAGMENT_SHADER); } }
同时PhotoMovie提供了对Lut滤镜的支持

Lut其实就是Lookup Table(颜色查找表),根据原图的RGB值去相应的lut图里面查找对应转换后的RGB值,从而实现各种滤镜效果

下面表格第一行为原图

lut 效果

public class LutMovieFilter extends TwoTextureMovieFilter { public
LutMovieFilter(Bitmap lutBitmap){
super(loadShaderFromAssets("shader/two_vertex.glsl"),loadShaderFromAssets("shader/lut.glsl"));
setBitmap(lutBitmap); } }
在LutMovieFilter的构造函数传入上面表格里的lut图,即可实现相应的滤镜效果,前面提到的黑白滤镜也可用这个方式实现

录制功能

GLMovieRecorder
<https://github.com/yellowcath/PhotoMovie/blob/master/library/src/main/java/record/GLMovieRecorder.java>
提供了将照片电影录制为mp4的功能

可参照DemoPresenter
<https://github.com/yellowcath/PhotoMovie/blob/master/app/src/main/java/com/hw/photomovie/sample/DemoPresenter.java>
的saveVideo()函数
GLMovieRecorder recorder = new GLMovieRecorder();
recorder.configOutput(width, height(), bitrate,frameRate,iFrameInterval,
outputPath); recorder.setDataSource(movieRenderer); recorder.startRecord(new
GLMovieRecorder.OnRecordListener() { @Override public void
onRecordFinish(boolean success) { ...... } @Override public void
onRecordProgress(int recordedDuration, int totalDuration) { ...... } });
背景音乐
mPhotoMoviePlayer.setMusic(context, mMusicUri);
PhotoMovie只提供了播放背景音乐的功能,录制完成之后需自行合成,Demo里使用了 VideoProcessor
<https://github.com/yellowcath/VideoProcessor>进行合成
VideoProcessor.mixAudioTrack(context, videPath, audioPath,outputPath, null,
null, 0,100, 1f, 1f);

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信