|
|
@ -11,9 +11,12 @@ import android.util.Log |
|
|
|
import android.view.View |
|
|
|
import android.view.View |
|
|
|
import android.widget.LinearLayout |
|
|
|
import android.widget.LinearLayout |
|
|
|
import android.widget.TextView |
|
|
|
import android.widget.TextView |
|
|
|
|
|
|
|
import androidx.recyclerview.widget.RecyclerView |
|
|
|
|
|
|
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager |
|
|
|
|
|
|
|
|
|
|
|
import com.frank.ffmpeg.FFmpegCmd |
|
|
|
import com.frank.ffmpeg.FFmpegCmd |
|
|
|
import com.frank.ffmpeg.R |
|
|
|
import com.frank.ffmpeg.R |
|
|
|
|
|
|
|
import com.frank.ffmpeg.adapter.WaterfallAdapter |
|
|
|
import com.frank.ffmpeg.format.VideoLayout |
|
|
|
import com.frank.ffmpeg.format.VideoLayout |
|
|
|
import com.frank.ffmpeg.gif.HighQualityGif |
|
|
|
import com.frank.ffmpeg.gif.HighQualityGif |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler |
|
|
@ -28,6 +31,7 @@ import java.util.ArrayList |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_BEGIN |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_BEGIN |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_FINISH |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_FINISH |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_PROGRESS |
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_PROGRESS |
|
|
|
|
|
|
|
import com.frank.ffmpeg.listener.OnItemClickListener |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* video process by FFmpeg command |
|
|
|
* video process by FFmpeg command |
|
|
@ -35,10 +39,10 @@ import com.frank.ffmpeg.handler.FFmpegHandler.MSG_PROGRESS |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class VideoHandleActivity : BaseActivity() { |
|
|
|
class VideoHandleActivity : BaseActivity() { |
|
|
|
|
|
|
|
|
|
|
|
private var layoutVideoHandle: LinearLayout? = null |
|
|
|
private var layoutVideoHandle: RecyclerView? = null |
|
|
|
private var layoutProgress: LinearLayout? = null |
|
|
|
private var layoutProgress: LinearLayout? = null |
|
|
|
private var txtProgress: TextView? = null |
|
|
|
private var txtProgress: TextView? = null |
|
|
|
private var viewId: Int = 0 |
|
|
|
private var currentPosition: Int = 0 |
|
|
|
private var ffmpegHandler: FFmpegHandler? = null |
|
|
|
private var ffmpegHandler: FFmpegHandler? = null |
|
|
|
|
|
|
|
|
|
|
|
private val appendPath = PATH + File.separator + "snow.mp4" |
|
|
|
private val appendPath = PATH + File.separator + "snow.mp4" |
|
|
@ -91,7 +95,7 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
override fun onCreate(savedInstanceState: Bundle?) { |
|
|
|
override fun onCreate(savedInstanceState: Bundle?) { |
|
|
|
super.onCreate(savedInstanceState) |
|
|
|
super.onCreate(savedInstanceState) |
|
|
|
|
|
|
|
|
|
|
|
hideActionBar() |
|
|
|
// hideActionBar() |
|
|
|
intView() |
|
|
|
intView() |
|
|
|
ffmpegHandler = FFmpegHandler(mHandler) |
|
|
|
ffmpegHandler = FFmpegHandler(mHandler) |
|
|
|
} |
|
|
|
} |
|
|
@ -99,40 +103,49 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
private fun intView() { |
|
|
|
private fun intView() { |
|
|
|
layoutProgress = getView(R.id.layout_progress) |
|
|
|
layoutProgress = getView(R.id.layout_progress) |
|
|
|
txtProgress = getView(R.id.txt_progress) |
|
|
|
txtProgress = getView(R.id.txt_progress) |
|
|
|
layoutVideoHandle = getView(R.id.layout_video_handle) |
|
|
|
val list = listOf( |
|
|
|
initViewsWithClick( |
|
|
|
getString(R.string.video_transform), |
|
|
|
R.id.btn_video_transform, |
|
|
|
getString(R.string.video_cut), |
|
|
|
R.id.btn_video_cut, |
|
|
|
getString(R.string.video_concat), |
|
|
|
R.id.btn_video_concat, |
|
|
|
getString(R.string.video_screen_shot), |
|
|
|
R.id.btn_screen_shot, |
|
|
|
getString(R.string.video_water_mark), |
|
|
|
R.id.btn_water_mark, |
|
|
|
getString(R.string.video_remove_logo), |
|
|
|
R.id.btn_generate_gif, |
|
|
|
getString(R.string.video_from_photo), |
|
|
|
R.id.btn_screen_record, |
|
|
|
getString(R.string.video_to_gif), |
|
|
|
R.id.btn_combine_video, |
|
|
|
getString(R.string.video_multi), |
|
|
|
R.id.btn_multi_video, |
|
|
|
getString(R.string.video_reverse), |
|
|
|
R.id.btn_reverse_video, |
|
|
|
getString(R.string.video_denoise), |
|
|
|
R.id.btn_denoise_video, |
|
|
|
getString(R.string.video_image), |
|
|
|
R.id.btn_to_image, |
|
|
|
getString(R.string.video_pip), |
|
|
|
R.id.btn_pip, |
|
|
|
getString(R.string.video_moov), |
|
|
|
R.id.btn_moov, |
|
|
|
getString(R.string.video_speed), |
|
|
|
R.id.btn_speed, |
|
|
|
getString(R.string.video_flv), |
|
|
|
R.id.btn_flv, |
|
|
|
getString(R.string.video_thumbnail), |
|
|
|
R.id.btn_thumbnail, |
|
|
|
getString(R.string.video_subtitle), |
|
|
|
R.id.btn_add_subtitle, |
|
|
|
getString(R.string.video_rotate), |
|
|
|
R.id.btn_rotate, |
|
|
|
getString(R.string.video_gop)) |
|
|
|
R.id.btn_gop, |
|
|
|
|
|
|
|
R.id.btn_remove_logo |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onViewClick(view: View) { |
|
|
|
layoutVideoHandle = findViewById(R.id.list_video_item) |
|
|
|
viewId = view.id |
|
|
|
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) |
|
|
|
if (viewId == R.id.btn_combine_video) { |
|
|
|
layoutVideoHandle?.layoutManager = layoutManager |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val adapter = WaterfallAdapter(list) |
|
|
|
|
|
|
|
adapter.setOnItemClickListener(object : OnItemClickListener { |
|
|
|
|
|
|
|
override fun onItemClick(position: Int) { |
|
|
|
|
|
|
|
currentPosition = position |
|
|
|
|
|
|
|
if (getString(R.string.video_from_photo) == list[position]) { |
|
|
|
handlePhoto() |
|
|
|
handlePhoto() |
|
|
|
return |
|
|
|
} else { |
|
|
|
} |
|
|
|
|
|
|
|
selectFile() |
|
|
|
selectFile() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
layoutVideoHandle?.adapter = adapter |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onViewClick(view: View) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
override fun onSelectedFile(filePath: String) { |
|
|
|
override fun onSelectedFile(filePath: String) { |
|
|
|
doHandleVideo(filePath) |
|
|
|
doHandleVideo(filePath) |
|
|
@ -156,28 +169,28 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
if (suffix == null || suffix.isEmpty()) { |
|
|
|
if (suffix == null || suffix.isEmpty()) { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
when (viewId) { |
|
|
|
when (currentPosition) { |
|
|
|
R.id.btn_video_transform//transform format |
|
|
|
0 //transform format |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val transformVideo = PATH + File.separator + "transformVideo.mp4" |
|
|
|
val transformVideo = PATH + File.separator + "transformVideo.mp4" |
|
|
|
commandLine = FFmpegUtil.transformVideo(srcFile, transformVideo) |
|
|
|
commandLine = FFmpegUtil.transformVideo(srcFile, transformVideo) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_video_cut//cut video |
|
|
|
1 //cut video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val cutVideo = PATH + File.separator + "cutVideo" + suffix |
|
|
|
val cutVideo = PATH + File.separator + "cutVideo" + suffix |
|
|
|
val startTime = 5.5f |
|
|
|
val startTime = 5.5f |
|
|
|
val duration = 20.0f |
|
|
|
val duration = 20.0f |
|
|
|
commandLine = FFmpegUtil.cutVideo(srcFile, startTime, duration, cutVideo) |
|
|
|
commandLine = FFmpegUtil.cutVideo(srcFile, startTime, duration, cutVideo) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_video_concat//concat video together |
|
|
|
2 //concat video together |
|
|
|
-> concatVideo(srcFile) |
|
|
|
-> concatVideo(srcFile) |
|
|
|
R.id.btn_screen_shot//video snapshot |
|
|
|
3 //video snapshot |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val screenShot = PATH + File.separator + "screenShot.jpg" |
|
|
|
val screenShot = PATH + File.separator + "screenShot.jpg" |
|
|
|
val time = 10.5f |
|
|
|
val time = 10.5f |
|
|
|
commandLine = FFmpegUtil.screenShot(srcFile, time, screenShot) |
|
|
|
commandLine = FFmpegUtil.screenShot(srcFile, time, screenShot) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_water_mark//add watermark to video |
|
|
|
4 //add watermark to video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
// the unit of bitRate is kb |
|
|
|
// the unit of bitRate is kb |
|
|
|
var bitRate = 500 |
|
|
|
var bitRate = 500 |
|
|
@ -217,14 +230,14 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_remove_logo//Remove logo from video: suppress logo by a simple interpolation |
|
|
|
5 //Remove logo from video: suppress logo by a simple interpolation |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val removeLogoPath = PATH + File.separator + "removeLogo" + suffix |
|
|
|
val removeLogoPath = PATH + File.separator + "removeLogo" + suffix |
|
|
|
val widthL = 64 |
|
|
|
val widthL = 64 |
|
|
|
val heightL = 40 |
|
|
|
val heightL = 40 |
|
|
|
commandLine = FFmpegUtil.removeLogo(srcFile, 10, 10, widthL, heightL, removeLogoPath) |
|
|
|
commandLine = FFmpegUtil.removeLogo(srcFile, 10, 10, widthL, heightL, removeLogoPath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_generate_gif//convert video into gif |
|
|
|
7 //convert video into gif |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val video2Gif = PATH + File.separator + "video2Gif.gif" |
|
|
|
val video2Gif = PATH + File.separator + "video2Gif.gif" |
|
|
|
val gifStart = 10 |
|
|
|
val gifStart = 10 |
|
|
@ -247,7 +260,7 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
convertGifInHighQuality(video2Gif, srcFile, gifStart, gifDuration, frameRate) |
|
|
|
convertGifInHighQuality(video2Gif, srcFile, gifStart, gifDuration, frameRate) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_multi_video//combine video which layout could be horizontal of vertical |
|
|
|
8 //combine video which layout could be horizontal of vertical |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val input1 = PATH + File.separator + "input1.mp4" |
|
|
|
val input1 = PATH + File.separator + "input1.mp4" |
|
|
|
val input2 = PATH + File.separator + "input2.mp4" |
|
|
|
val input2 = PATH + File.separator + "input2.mp4" |
|
|
@ -257,17 +270,17 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
} |
|
|
|
} |
|
|
|
commandLine = FFmpegUtil.multiVideo(input1, input2, outputFile, VideoLayout.LAYOUT_HORIZONTAL) |
|
|
|
commandLine = FFmpegUtil.multiVideo(input1, input2, outputFile, VideoLayout.LAYOUT_HORIZONTAL) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_reverse_video//video reverse |
|
|
|
9 //video reverse |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val output = PATH + File.separator + "reverse.mp4" |
|
|
|
val output = PATH + File.separator + "reverse.mp4" |
|
|
|
commandLine = FFmpegUtil.reverseVideo(srcFile, output) |
|
|
|
commandLine = FFmpegUtil.reverseVideo(srcFile, output) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_denoise_video//noise reduction of video |
|
|
|
10 //noise reduction of video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val denoise = PATH + File.separator + "denoise.mp4" |
|
|
|
val denoise = PATH + File.separator + "denoise.mp4" |
|
|
|
commandLine = FFmpegUtil.denoiseVideo(srcFile, denoise) |
|
|
|
commandLine = FFmpegUtil.denoiseVideo(srcFile, denoise) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_to_image//convert video to picture |
|
|
|
11 //convert video to picture |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val imagePath = PATH + File.separator + "Video2Image/" |
|
|
|
val imagePath = PATH + File.separator + "Video2Image/" |
|
|
|
val imageFile = File(imagePath) |
|
|
|
val imageFile = File(imagePath) |
|
|
@ -281,7 +294,7 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
val mFrameRate = 10//frameRate |
|
|
|
val mFrameRate = 10//frameRate |
|
|
|
commandLine = FFmpegUtil.videoToImage(srcFile, mStartTime, mDuration, mFrameRate, imagePath) |
|
|
|
commandLine = FFmpegUtil.videoToImage(srcFile, mStartTime, mDuration, mFrameRate, imagePath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_pip//combine into picture-in-picture video |
|
|
|
12 //combine into picture-in-picture video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val inputFile1 = PATH + File.separator + "beyond.mp4" |
|
|
|
val inputFile1 = PATH + File.separator + "beyond.mp4" |
|
|
|
val inputFile2 = PATH + File.separator + "small_girl.mp4" |
|
|
|
val inputFile2 = PATH + File.separator + "small_girl.mp4" |
|
|
@ -295,7 +308,7 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
val picInPic = PATH + File.separator + "PicInPic.mp4" |
|
|
|
val picInPic = PATH + File.separator + "PicInPic.mp4" |
|
|
|
commandLine = FFmpegUtil.picInPicVideo(inputFile1, inputFile2, x, y, picInPic) |
|
|
|
commandLine = FFmpegUtil.picInPicVideo(inputFile1, inputFile2, x, y, picInPic) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_moov//moov box moves ahead, which is behind mdat box of mp4 video |
|
|
|
13 //moov box moves ahead, which is behind mdat box of mp4 video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
if (!srcFile.endsWith(FileUtil.TYPE_MP4)) { |
|
|
|
if (!srcFile.endsWith(FileUtil.TYPE_MP4)) { |
|
|
|
showToast(getString(R.string.tip_not_mp4_video)) |
|
|
|
showToast(getString(R.string.tip_not_mp4_video)) |
|
|
@ -316,12 +329,12 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
Log.e(TAG, "move moov use time=" + (System.currentTimeMillis() - start)) |
|
|
|
Log.e(TAG, "move moov use time=" + (System.currentTimeMillis() - start)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_speed//playing speed of video |
|
|
|
14 //playing speed of video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val speed = PATH + File.separator + "speed.mp4" |
|
|
|
val speed = PATH + File.separator + "speed.mp4" |
|
|
|
commandLine = FFmpegUtil.changeSpeed(srcFile, speed, 2f, false) |
|
|
|
commandLine = FFmpegUtil.changeSpeed(srcFile, speed, 2f, false) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_flv//rebuild the keyframe index of flv |
|
|
|
15 //rebuild the keyframe index of flv |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
if (!".flv".equals(FileUtil.getFileSuffix(srcFile)!!, ignoreCase = true)) { |
|
|
|
if (!".flv".equals(FileUtil.getFileSuffix(srcFile)!!, ignoreCase = true)) { |
|
|
|
Log.e(TAG, "It's not flv file, suffix=" + FileUtil.getFileSuffix(srcFile)!!) |
|
|
|
Log.e(TAG, "It's not flv file, suffix=" + FileUtil.getFileSuffix(srcFile)!!) |
|
|
@ -330,25 +343,25 @@ class VideoHandleActivity : BaseActivity() { |
|
|
|
val outputPath = PATH + File.separator + "frame_index.flv" |
|
|
|
val outputPath = PATH + File.separator + "frame_index.flv" |
|
|
|
commandLine = FFmpegUtil.buildFlvIndex(srcFile, outputPath) |
|
|
|
commandLine = FFmpegUtil.buildFlvIndex(srcFile, outputPath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_thumbnail// insert thumbnail into video |
|
|
|
16 // insert thumbnail into video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val thumbnailPath = PATH + File.separator + "thumb.jpg" |
|
|
|
val thumbnailPath = PATH + File.separator + "thumb.jpg" |
|
|
|
val thumbVideoPath = PATH + File.separator + "thumbnailVideo" + suffix |
|
|
|
val thumbVideoPath = PATH + File.separator + "thumbnailVideo" + suffix |
|
|
|
commandLine = FFmpegUtil.insertPicIntoVideo(srcFile, thumbnailPath, thumbVideoPath) |
|
|
|
commandLine = FFmpegUtil.insertPicIntoVideo(srcFile, thumbnailPath, thumbVideoPath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_add_subtitle//add subtitle into video |
|
|
|
17 //add subtitle into video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val subtitlePath = PATH + File.separator + "test.ass" |
|
|
|
val subtitlePath = PATH + File.separator + "test.ass" |
|
|
|
val addSubtitlePath = PATH + File.separator + "subtitle.mkv" |
|
|
|
val addSubtitlePath = PATH + File.separator + "subtitle.mkv" |
|
|
|
commandLine = FFmpegUtil.addSubtitleIntoVideo(srcFile, subtitlePath, addSubtitlePath) |
|
|
|
commandLine = FFmpegUtil.addSubtitleIntoVideo(srcFile, subtitlePath, addSubtitlePath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_rotate// set the rotate degree of video |
|
|
|
18 // set the rotate degree of video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val rotateDegree = 90 |
|
|
|
val rotateDegree = 90 |
|
|
|
val addSubtitlePath = PATH + File.separator + "rotate" + rotateDegree + suffix |
|
|
|
val addSubtitlePath = PATH + File.separator + "rotate" + rotateDegree + suffix |
|
|
|
commandLine = FFmpegUtil.rotateVideo(srcFile, rotateDegree, addSubtitlePath) |
|
|
|
commandLine = FFmpegUtil.rotateVideo(srcFile, rotateDegree, addSubtitlePath) |
|
|
|
} |
|
|
|
} |
|
|
|
R.id.btn_gop// change the gop(key frame interval) of video |
|
|
|
19 // change the gop(key frame interval) of video |
|
|
|
-> { |
|
|
|
-> { |
|
|
|
val gop = 30 |
|
|
|
val gop = 30 |
|
|
|
val gopPath = PATH + File.separator + "gop" + gop + suffix |
|
|
|
val gopPath = PATH + File.separator + "gop" + gop + suffix |
|
|
|