Android高仿抖音照片電影功能的實現(xiàn)代碼
PhotoMovie (https://github.com/yellowcath/PhotoMovie) 可輕松實現(xiàn)類似抖音、微視、美拍的照片電影功能。
效果如下
濾鏡效果

轉(zhuǎn)場效果

使用
首先在項目根目錄的build.gradle文件里添加maven庫
allprojects {
repositories {
...
maven { url 'https://www.jitpack.io' }
}
}
再在需要用的Module的build.gradle里添加依賴
dependencies {
compile 'com.github.yellowcath:PhotoMovie:1.5.0'
}
基本用法
可參照 DemoPresenter
//添加圖片
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);
//生成照片電影(使用預定義的水平轉(zhuǎn)場動畫)
PhotoMovie photoMovie = PhotoMovieFactory.generatePhotoMovie(photoSource, PhotoMovieFactory.PhotoMovieType.HORIZONTAL_TRANS);
//生成負責繪制電影內(nèi)容的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:電影片段,每個電影片段都有特定的時長,在這段時間之內(nèi)以特定的方式播放圖片,例如ScaleSegment會對圖片做縮放動畫、EndGaussianBlurSegment會對圖片做從清晰到模糊的高斯模糊動畫
- PhotoMovie:核心類,代表照片電影本身,由圖片源(PhotoSource)和若干電影片段(MovieSegment)組成一個完整的照片電影,圖片通過PhotoAllocator分配給MovieSegment
- MovieLayer:為MovieSegment擴展繪制多層特效的功能,例如SubtitleLayer提供字幕展示
- IMovieFilter:為整個照片電影提供濾鏡
- MovieRenderer:負責把照片電影渲染到指定的輸出界面,例如TextureView(GLTextureMovieRender)、GLSurfaceView(GLSurfaceMovieRenderer)
- PhotoMoviePlayer:提供類似MediaPlayer的接口,負責播放照片電影,播放進度由IMovieTimer控制
擴展電影類型
目前內(nèi)置了6種類型,后兩種即是抖音的左右切換和上下切換,Thaw和WINDOW仿自美拍
public enum PhotoMovieType {
THAW, //融雪
SCALE, //縮放
SCALE_TRANS, //縮放 & 平移
WINDOW, //窗扉
HORIZONTAL_TRANS,//橫向平移
VERTICAL_TRANS//縱向平移
}
這里以微視的漸變特效為例展示如何擴展
分析得出,漸變特效首先圖片居中放置,然后全程做一個微弱的放大動畫,后半部分同時透明度變化消失
可見需要兩個不同的片段類型
首先創(chuàng)建FitCenterScaleSegment,繼承FitCenterSegment,實現(xiàn)單張圖片的放大動畫
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已經(jīng)具有縮放能力,這里傳縮放值即可
drawContent(canvas, scale);
}
//提升這兩個函數(shù)的訪問權限,供轉(zhuǎn)場時使用
@Override
public void drawContent(GLESCanvas canvas, float scale) {
super.drawContent(canvas, scale);
}
@Override
public void drawBackground(GLESCanvas canvas) {
super.drawBackground(canvas);
}
}
然后創(chuàng)建轉(zhuǎn)場片段GradientTransferSegment,其父類TransitionSegment同時持有上一個與下一個片段,可以在其基礎上實現(xiàn)任意轉(zhuǎn)場功能
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);
//上一個片段繼續(xù)放大同時變透明
float preScale = mPreScaleFrom + (mPreScaleTo - mPreScaleFrom) * segmentProgress;
float alpha = 1 - segmentProgress;
mPreSegment.drawBackground(canvas);
canvas.save();
canvas.setAlpha(alpha);
mPreSegment.drawContent(canvas, preScale);
canvas.restore();
}
創(chuàng)建照片電影
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正常播放即可,效果如下

擴展濾鏡
目前內(nèi)置了9個濾鏡
public enum FilterType {
NONE,
CAMEO,//浮雕
GRAY,//黑白
KUWAHARA,//水彩
SNOW,//飄雪(動態(tài))
LUT1,
LUT2,
LUT3,
LUT4,
LUT5,
}
先看IMovieFilter
public interface IMovieFilter {
void doFilter(PhotoMovie photoMovie,int elapsedTime, FboTexture inputTexture, FboTexture outputTexture);
void release();
}
外部會提供一個輸入紋理,然后由IMovieFilter處理之后繪制到輸出紋理上,即實現(xiàn)了濾鏡效果
BaseMovieFilter已經(jīng)實現(xiàn)了基本的輸入輸出流程,例如要做最基本的黑白濾鏡,只需更換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(顏色查找表),根據(jù)原圖的RGB值去相應的lut圖里面查找對應轉(zhuǎn)換后的RGB值,從而實現(xiàn)各種濾鏡效果

lut(原圖)

lut_2.jpg

原圖

濾鏡效果圖
public class LutMovieFilter extends TwoTextureMovieFilter {
public LutMovieFilter(Bitmap lutBitmap){
super(loadShaderFromAssets("shader/two_vertex.glsl"),loadShaderFromAssets("shader/lut.glsl"));
setBitmap(lutBitmap);
}
}
在LutMovieFilter的構(gòu)造函數(shù)傳入上面表格里的lut圖,即可實現(xiàn)相應的濾鏡效果,前面提到的黑白濾鏡也可用這個方式實現(xiàn)
錄制功能
GLMovieRecorder 提供了將照片電影錄制為mp4的功能
可參照 DemoPresenter 的saveVideo()函數(shù)
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 進行合成
VideoProcessor.mixAudioTrack(context, videPath, audioPath,outputPath, null, null, 0,100, 1f, 1f);
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android ConnectivityManager網(wǎng)絡管理詳解
在android平臺中ConnectivityManager主要負責查詢網(wǎng)絡連接狀態(tài)以及在連接狀態(tài)有變化的時候發(fā)出通知,這篇文章主要介紹了Android ConnectivityManager網(wǎng)絡管理2022-11-11
Android開發(fā)之Notification手機狀態(tài)欄通知用法實例分析
這篇文章主要介紹了Android開發(fā)之Notification手機狀態(tài)欄通知用法,結(jié)合實例形式分析了Android Notification手機狀態(tài)欄通知的常見函數(shù)、功能及使用技巧,需要的朋友可以參考下2019-03-03
Android RecyclerView實現(xiàn)吸頂動態(tài)效果流程分析
RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實現(xiàn)數(shù)據(jù)縱向滾動,也可以實現(xiàn)橫向滾動(ListView做不到橫向滾動)。接下來講解RecyclerView的用法2022-12-12

