文章目录
前言
在用户上传视频和目标人物图片后,前端此时陷入等待,不断请求,直到后端调用算法完毕,将所有包含目标人物的主要 GIF 片段返回。在返回后,前端需要下载这些动图片段,并将其保存在文件夹下,而后对这些片段进行拼接,得到初步剪辑的视频,进入剪辑页面,再由用户根据需要进行细剪。
在本周,我主要实现了读取特定文件夹下的 gif 片段,并将其拼接,合成视频的功能。
初次尝试
因为在签名的视频文件传输功能上,分片视频的合并就是通过分片文件流拼接实现的。所以这里初步的想法是将视频片段看做普通文件,将文件流简单合并在一起,再保存为相应的格式
- 首先遍历文件夹,获取所有gif片段的路径
- 而后根据路径读取文件,获取文件流,将其拼接
public void getUrlList(String strPath) {
File dir = new File(strPath);//文件夹dir
File[] files = dir.listFiles();//文件夹下的所有文件或文件夹
if (files != null){
for (int i = 0; i < files.length; i++) {
PageLog.dTag(TAG,files[i].getAbsolutePath());
UrlList.add(files[i].getAbsolutePath());//对于文件才把它的路径加到filelist中
}
}
}
public void uniteFiles() {
try {
VideoUrl = base_path + "/Video/" + fileName + ".gif";
File unitedFile = new File(VideoUrl);
unitedFile.createNewFile();
PageLog.dTag(TAG,VideoUrl);
FileOutputStream fos = new FileOutputStream(unitedFile);
RandomAccessFile ra = null;
for (int i = 0; i < UrlList.size(); i++) {
ra = new RandomAccessFile(UrlList.get(i), "r");
if (i != 0) {
ra.seek(6+7+7); //gif的文件头
}
byte[] buffer = new byte[1024 * 8];
int len = 0;
while ((len = ra.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
ra.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
但是这样直接拼接,虽然最终保存的动图可以播放,但是其只能播放第一个动图的内容。因此不顾 gif 文件的特殊格式,直接以文件形式合并并不可行
实现GIF片段的合并
第二个想法是,首先合并gif片段,再进行格式转换,转换为视频的mp4等格式
这部分网上资源较少,我的思路是首先获取到每个小 gif 的每一帧,将其保存在数组中,最后根据所有帧,合并为一个大的 gif
获取所有帧
public void getBitmapArrayByGif(String strPath) {
// 所有 gif 片段的路径
ArrayList<String> urlList = new ArrayList<>();
File dir = new File(strPath);//文件夹dir
File[] files = dir.listFiles();//文件夹下的所有文件或文件夹
if (files != null){
for (int i = 0; i < files.length; i++) {
PageLog.dTag(TAG,files[i].getAbsolutePath());
urlList.add(files[i].getAbsolutePath());//对于文件才把它的路径加到filelist中
}
}
try {
// 所有 gif 中的每一帧
ArrayList<Bitmap> allFrames = new ArrayList<>();
for(String path:urlList){
GifDrawable gifFromAssets = new GifDrawable(path);
int totalCount = gifFromAssets.getNumberOfFrames();
for (int i=0; i<totalCount; i++)
{
allFrames.add(gifFromAssets.seekToFrameAndGet(i));
}
}
PageLog.dTag(TAG, "all frames add finish");
createGif(allFrames);
} catch (Exception e) {
PageLog.dTag(TAG, e.toString());
}
}
将所有帧合并为 GIF
private void createGif(ArrayList<Bitmap> allFrames) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.start(baos);
for(int i = 0; i<allFrames.size(); i++){
encoder.addFrame(allFrames.get(i));
}
encoder.finish();
try {
VideoUrl = base_path + "/Video/" + fileName + ".gif";
File unitedFile = new File(VideoUrl);
unitedFile.createNewFile();
PageLog.dTag(TAG,VideoUrl);
FileOutputStream fos = new FileOutputStream(unitedFile);
baos.writeTo(fos);
baos.flush();
fos.flush();
baos.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
这样顺利的合成了可以正常播放的总GIF片段,但是其耗时较长。在一开始,我将这一方法放到了主线程,结果报了 “ANR” 的错误。而后将其作为一个异步任务 AsyncTask,在后台执行,前端显示进度条。