You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
13 KiB
334 lines
13 KiB
package com.frank.ffmpeg.activity
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.os.Environment
|
|
import android.os.Handler
|
|
import android.os.Message
|
|
import android.os.Bundle
|
|
import android.util.Log
|
|
import android.view.View
|
|
import android.widget.LinearLayout
|
|
import android.widget.TextView
|
|
import android.widget.Toast
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
|
|
|
import java.io.File
|
|
import java.util.ArrayList
|
|
import java.util.Locale
|
|
|
|
import com.frank.ffmpeg.R
|
|
import com.frank.ffmpeg.adapter.WaterfallAdapter
|
|
import com.frank.ffmpeg.handler.FFmpegHandler
|
|
import com.frank.ffmpeg.util.FFmpegUtil
|
|
import com.frank.ffmpeg.util.FileUtil
|
|
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_INFO
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_BEGIN
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_FINISH
|
|
import com.frank.ffmpeg.handler.FFmpegHandler.MSG_PROGRESS
|
|
import com.frank.ffmpeg.listener.OnItemClickListener
|
|
import java.lang.StringBuilder
|
|
|
|
/**
|
|
* Using ffmpeg command to handle audio
|
|
* Created by frank on 2018/1/23.
|
|
*/
|
|
|
|
class AudioHandleActivity : BaseActivity() {
|
|
private val appendFile = PATH + File.separator + "heart.m4a"
|
|
|
|
private var layoutAudioHandle: RecyclerView? = null
|
|
private var layoutProgress: LinearLayout? = null
|
|
private var txtProgress: TextView? = null
|
|
private var currentPosition: Int = 0
|
|
private var ffmpegHandler: FFmpegHandler? = null
|
|
|
|
private val outputPath1 = PATH + File.separator + "output1.mp3"
|
|
private val outputPath2 = PATH + File.separator + "output2.mp3"
|
|
private var isJointing = false
|
|
private var infoBuilder: StringBuilder? = null
|
|
|
|
@SuppressLint("HandlerLeak")
|
|
private val mHandler = object : Handler() {
|
|
override fun handleMessage(msg: Message) {
|
|
super.handleMessage(msg)
|
|
when (msg.what) {
|
|
MSG_BEGIN -> {
|
|
layoutProgress!!.visibility = View.VISIBLE
|
|
layoutAudioHandle!!.visibility = View.GONE
|
|
}
|
|
MSG_FINISH -> {
|
|
layoutProgress!!.visibility = View.GONE
|
|
layoutAudioHandle!!.visibility = View.VISIBLE
|
|
if (isJointing) {
|
|
isJointing = false
|
|
FileUtil.deleteFile(outputPath1)
|
|
FileUtil.deleteFile(outputPath2)
|
|
}
|
|
if (infoBuilder != null) {
|
|
Toast.makeText(this@AudioHandleActivity,
|
|
infoBuilder.toString(), Toast.LENGTH_LONG).show()
|
|
infoBuilder = null
|
|
}
|
|
}
|
|
MSG_PROGRESS -> {
|
|
val progress = msg.arg1
|
|
val duration = msg.arg2
|
|
if (progress > 0) {
|
|
txtProgress!!.visibility = View.VISIBLE
|
|
txtProgress!!.text = String.format(Locale.getDefault(), "%d%%", progress)
|
|
} else {
|
|
txtProgress!!.visibility = View.GONE
|
|
}
|
|
}
|
|
MSG_INFO -> {
|
|
if (infoBuilder == null) infoBuilder = StringBuilder()
|
|
infoBuilder?.append(msg.obj)
|
|
}
|
|
else -> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override val layoutId: Int
|
|
get() = R.layout.activity_audio_handle
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
initView()
|
|
ffmpegHandler = FFmpegHandler(mHandler)
|
|
}
|
|
|
|
private fun initView() {
|
|
layoutProgress = getView(R.id.layout_progress)
|
|
txtProgress = getView(R.id.txt_progress)
|
|
val list = listOf(
|
|
getString(R.string.audio_transform),
|
|
getString(R.string.audio_cut),
|
|
getString(R.string.audio_concat),
|
|
getString(R.string.audio_mix),
|
|
getString(R.string.audio_play),
|
|
getString(R.string.audio_speed),
|
|
getString(R.string.audio_echo),
|
|
getString(R.string.audio_tremolo),
|
|
getString(R.string.audio_denoise),
|
|
getString(R.string.audio_add_equalizer),
|
|
getString(R.string.audio_silence),
|
|
getString(R.string.audio_volume),
|
|
getString(R.string.audio_encode),
|
|
getString(R.string.pcm_concat))
|
|
|
|
layoutAudioHandle = findViewById(R.id.list_audio_item)
|
|
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
|
|
layoutAudioHandle?.layoutManager = layoutManager
|
|
|
|
val adapter = WaterfallAdapter(list)
|
|
adapter.setOnItemClickListener(object : OnItemClickListener {
|
|
override fun onItemClick(position: Int) {
|
|
currentPosition = position
|
|
selectFile()
|
|
}
|
|
})
|
|
layoutAudioHandle?.adapter = adapter
|
|
}
|
|
|
|
override fun onViewClick(view: View) {
|
|
|
|
}
|
|
|
|
override fun onSelectedFile(filePath: String) {
|
|
doHandleAudio(filePath)
|
|
}
|
|
|
|
/**
|
|
* Using ffmpeg cmd to handle audio
|
|
*
|
|
* @param srcFile srcFile
|
|
*/
|
|
private fun doHandleAudio(srcFile: String) {
|
|
var commandLine: Array<String>? = null
|
|
if (!FileUtil.checkFileExist(srcFile)) {
|
|
return
|
|
}
|
|
if (!FileUtil.isAudio(srcFile)) {
|
|
showToast(getString(R.string.wrong_audio_format))
|
|
return
|
|
}
|
|
when (currentPosition) {
|
|
0 -> if (useFFmpeg) { //use FFmpeg to transform
|
|
val transformFile = PATH + File.separator + "transformAudio.mp3"
|
|
commandLine = FFmpegUtil.transformAudio(srcFile, transformFile)
|
|
} else { //use MediaCodec and libmp3lame to transform
|
|
Thread {
|
|
val transformPath = PATH + File.separator + "transformAudio.mp3"
|
|
try {
|
|
mHandler.obtainMessage(MSG_BEGIN).sendToTarget()
|
|
val clazz = Class.forName("com.frank.mp3.Mp3Converter")
|
|
val instance = clazz.newInstance()
|
|
val method = clazz.getDeclaredMethod("convertToMp3", String::class.java, String::class.java)
|
|
method.invoke(instance, srcFile, transformPath)
|
|
mHandler.obtainMessage(MSG_FINISH).sendToTarget()
|
|
} catch (e: Exception) {
|
|
Log.e("AudioHandleActivity", "convert mp3 error=" + e.message)
|
|
}
|
|
}.start()
|
|
}
|
|
1 //cut audio, it's best not include special characters
|
|
-> {
|
|
val suffix = FileUtil.getFileSuffix(srcFile)
|
|
if (suffix == null || suffix.isEmpty()) {
|
|
return
|
|
}
|
|
val cutFile = PATH + File.separator + "cutAudio" + suffix
|
|
commandLine = FFmpegUtil.cutAudio(srcFile, 10.5f, 15.0f, cutFile)
|
|
}
|
|
2 //concat audio
|
|
-> {
|
|
if (!FileUtil.checkFileExist(appendFile)) {
|
|
return
|
|
}
|
|
concatAudio(srcFile)
|
|
return
|
|
}
|
|
3 //mix audio
|
|
-> {
|
|
if (!FileUtil.checkFileExist(appendFile)) {
|
|
return
|
|
}
|
|
val mixSuffix = FileUtil.getFileSuffix(srcFile)
|
|
if (mixSuffix == null || mixSuffix.isEmpty()) {
|
|
return
|
|
}
|
|
commandLine = if (mixAudio) {
|
|
val mixAudio = PATH + File.separator + "mix" + mixSuffix
|
|
FFmpegUtil.mixAudio(srcFile, appendFile, mixAudio)
|
|
} else {
|
|
val mergeAudio = PATH + File.separator + "merge" + mixSuffix
|
|
FFmpegUtil.mergeAudio(srcFile, appendFile, mergeAudio)
|
|
}
|
|
}
|
|
4 //use AudioTrack to play audio
|
|
-> {
|
|
val audioIntent = Intent(this@AudioHandleActivity, AudioPlayActivity::class.java)
|
|
audioIntent.data = Uri.parse(srcFile)
|
|
startActivity(audioIntent)
|
|
return
|
|
}
|
|
5 //change audio speed
|
|
-> {
|
|
val speed = 2.0f // funny effect, range from 0.5 to 100.0
|
|
val speedPath = PATH + File.separator + "speed.mp3"
|
|
commandLine = FFmpegUtil.changeAudioSpeed(srcFile, speedPath, speed)
|
|
}
|
|
6 // setting echo effect
|
|
-> {
|
|
val echo = 1000 // echo effect, range from 0 to 90000
|
|
val echoPath = PATH + File.separator + "echo.mp3"
|
|
commandLine = FFmpegUtil.audioEcho(srcFile, echo, echoPath)
|
|
}
|
|
7 //tremolo effect
|
|
-> {
|
|
val frequency = 5 // range from 0.1 to 20000.0
|
|
val depth = 0.9f // range from 0 to 1
|
|
val tremoloPath = PATH + File.separator + "tremolo.mp3"
|
|
commandLine = FFmpegUtil.audioTremolo(srcFile, frequency, depth, tremoloPath)
|
|
}
|
|
8 //audio denoise
|
|
-> {
|
|
val noisePath = PATH + File.separator + "denoise.mp3"
|
|
commandLine = FFmpegUtil.audioDenoise(srcFile, noisePath)
|
|
}
|
|
9 // equalizer plus
|
|
-> {
|
|
// key:band value:gain=[0-20]
|
|
val bandList = arrayListOf<String>()
|
|
bandList.add("6b=5")
|
|
bandList.add("8b=4")
|
|
bandList.add("10b=3")
|
|
bandList.add("12b=2")
|
|
bandList.add("14b=1")
|
|
bandList.add("16b=0")
|
|
val equalizePath = PATH + File.separator + "equalize.mp3"
|
|
commandLine = FFmpegUtil.audioEqualizer(srcFile, bandList, equalizePath)
|
|
}
|
|
10 //silence detect
|
|
-> {
|
|
commandLine = FFmpegUtil.audioSilenceDetect(srcFile)
|
|
}
|
|
11 // modify volume
|
|
-> {
|
|
val volume = 0.5f // 0.0-1.0
|
|
val volumePath = PATH + File.separator + "volume.mp3"
|
|
commandLine = FFmpegUtil.audioVolume(srcFile, volume, volumePath)
|
|
}
|
|
12 //audio encode
|
|
-> {
|
|
val pcmFile = PATH + File.separator + "raw.pcm"
|
|
val wavFile = PATH + File.separator + "convert.mp3"
|
|
//sample rate, normal is 8000/16000/44100
|
|
val sampleRate = 44100
|
|
//channel num of pcm
|
|
val channel = 2
|
|
commandLine = FFmpegUtil.encodeAudio(pcmFile, wavFile, sampleRate, channel)
|
|
}
|
|
13 //concat PCM streams
|
|
-> {
|
|
val srcPCM = PATH + File.separator + "audio.pcm"
|
|
val appendPCM = PATH + File.separator + "audio.pcm"
|
|
val concatPCM = PATH + File.separator + "concat.pcm"
|
|
if (!FileUtil.checkFileExist(srcPCM) || !FileUtil.checkFileExist(appendPCM)) {
|
|
return
|
|
}
|
|
|
|
mHandler.obtainMessage(MSG_BEGIN).sendToTarget()
|
|
FileUtil.concatFile(srcPCM, appendPCM, concatPCM)
|
|
mHandler.obtainMessage(MSG_FINISH).sendToTarget()
|
|
return
|
|
}
|
|
else -> {
|
|
}
|
|
}
|
|
if (ffmpegHandler != null && commandLine != null) {
|
|
ffmpegHandler!!.executeFFmpegCmd(commandLine)
|
|
}
|
|
}
|
|
|
|
private fun concatAudio(selectedPath: String) {
|
|
if (ffmpegHandler == null || selectedPath.isEmpty() || appendFile.isEmpty()) {
|
|
return
|
|
}
|
|
isJointing = true
|
|
val targetPath = PATH + File.separator + "concatAudio.mp3"
|
|
val transformCmd1 = FFmpegUtil.transformAudio(selectedPath, "libmp3lame", outputPath1)
|
|
val transformCmd2 = FFmpegUtil.transformAudio(appendFile, "libmp3lame", outputPath2)
|
|
val fileList = ArrayList<String>()
|
|
fileList.add(outputPath1)
|
|
fileList.add(outputPath2)
|
|
val jointVideoCmd = FFmpegUtil.concatAudio(fileList, targetPath)
|
|
val commandList = ArrayList<Array<String>>()
|
|
commandList.add(transformCmd1)
|
|
commandList.add(transformCmd2)
|
|
commandList.add(jointVideoCmd)
|
|
ffmpegHandler!!.executeFFmpegCmds(commandList)
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
super.onDestroy()
|
|
mHandler.removeCallbacksAndMessages(null)
|
|
}
|
|
|
|
companion object {
|
|
|
|
private val PATH = Environment.getExternalStorageDirectory().path
|
|
|
|
private const val useFFmpeg = true
|
|
|
|
private const val mixAudio = true
|
|
}
|
|
|
|
}
|
|
|