美文网首页Android音视频系列
android ffmpeg mp4转gif模糊问题

android ffmpeg mp4转gif模糊问题

作者: 坑逼的严 | 来源:发表于2021-08-20 16:05 被阅读0次

    最近老是要生成一些gif图片,图片转gif或者是视频转gif,网上的工具软件又要钱,于是自己写了一个,记录一下代码。
    https://github.com/microshow/RxFFmpeg
    这是一个优秀的开源ffmpeg库,可自己输入命令执行,还有进度返回,以及停止命令操作。平台架构全,支持 armeabi-v7a, arm64-v8a, x86, x86_64。一般我们用到armeabi-v7a, arm64-v8a就行了,要不是小米应用市场年底明确要求要64位架构,也懒得引用这么多。

    视频转gif

    1629446394831.gif

    一般我们的命令就是直接将mp4转gif,如下:

    val text =
         "ffmpeg -y -ss ${mMinPercentage} -t ${mMaxPercentage-mMinPercentage} -i ${resultPhotos!!.get(0).path} -r 15 -preset superfast /storage/emulated/0/1.gif"
    val commands = text.split(" ").toTypedArray()
    开始执行FFmpeg命令
    RxFFmpegInvoke.getInstance()
          .runCommandRxJava(commands)
          .subscribe(MyRxFFmpegSubscriber(this@VideoToGifActivity))
    
    
    ${mMinPercentage} -t ${mMaxPercentage-mMinPercentage}
    

    是时间设置比如:3 -t 4 就是从第三秒开始,往后延续4秒也就是3秒到7秒之间。这个要理解

    -i ${resultPhotos!!.get(0).path}
    

    指的是视频地址,这里要视频全路径,比如:/storage/emulated/0/1.mp4

    -r 15
    

    是指15帧

    -preset superfast
    

    是设置ffmpeg执行速度还能设置 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo,速度越慢,越高质量。
    但是这样生成出来的gif是很模糊的。

    加入调色板
    public void gotoPalettegen(){
      val text =
                "ffmpeg -ss ${mMinPercentage} -t ${mMaxPercentage - mMinPercentage} -i ${resultPhotos!!.get(0).path} -r ${mFrame} -vf fps=${mFrame},scale=${mCurrentWidth}:${mCurrentHeight}:flags=lanczos,palettegen -y /storage/emulated/0/JJGif/pt.png"
            Log.d("yanjin","text= ${text}")
            val commands = text.split(" ").toTypedArray()
            //开始执行FFmpeg命令
            RxFFmpegInvoke.getInstance()
                .runCommandRxJava(commands)
                .subscribe(object : RxFFmpegSubscriber() {
                    override fun onError(message: String?) {
                        if (showLoading != null) {
                            showLoading!!.dismissAllowingStateLoss()
                        }
                    }
    
                    override fun onFinish() {
                          goToGif()
                    }
    
                    override fun onProgress(p: Int, progressTime: Long) {}
                    override fun onCancel() {
                        if (showLoading != null) {
                            showLoading!!.dismissAllowingStateLoss()
                        }
                    }
                })
    }
    
    public void gotoGif(){
      val text =
                "ffmpeg -ss ${mMinPercentage} -t ${mMaxPercentage - mMinPercentage} -i ${resultPhotos!!.get(0).path} -r ${mFrame} -vf fps=${mFrame},scale=${mCurrentWidth}:${mCurrentHeight}:flags=lanczos,palettegen -y /storage/emulated/0/JJGif/pt.png"
            Log.d("yanjin","text= ${text}")
            val commands = text.split(" ").toTypedArray()
            //开始执行FFmpeg命令
            RxFFmpegInvoke.getInstance()
                .runCommandRxJava(commands)
                .subscribe(object : RxFFmpegSubscriber() {
                    override fun onError(message: String?) {
                        if (showLoading != null) {
                            showLoading!!.dismissAllowingStateLoss()
                        }
                    }
    
                    override fun onFinish() {
                        val text =
                            "ffmpeg -v warning -ss ${mMinPercentage} -t ${mMaxPercentage - mMinPercentage} -i ${resultPhotos!!.get(0).path} " +
                                    "-i /storage/emulated/0/JJGif/pt.png -r ${mFrame} " +
                                    "-lavfi fps=${mFrame},scale=${mCurrentWidth}:${mCurrentHeight}:flags=lanczos[x];[x][1:v]paletteuse -y /storage/emulated/0/JJGif/${resultPhotos!!.get(0).name}.gif"
                        Log.d("yanjin", "text= ${text}")
                        val commands = text.split(" ").toTypedArray()
                        //开始执行FFmpeg命令
                        RxFFmpegInvoke.getInstance()
                            .runCommandRxJava(commands)
                            .subscribe(MyRxFFmpegSubscriber(this@VideoToGifActivity))
                    }
    
                    override fun onProgress(p: Int, progressTime: Long) {}
                    override fun onCancel() {
                        if (showLoading != null) {
                            showLoading!!.dismissAllowingStateLoss()
                        }
                    }
                })
    }
    
    class MyRxFFmpegSubscriber(activity: VideoToGifActivity):RxFFmpegSubscriber() {
            private var weakReference:WeakReference<VideoToGifActivity>? = null
            init {
                weakReference = WeakReference(activity)
            }
            override fun onError(message: String?) {
                if(weakReference == null || weakReference!!.get() == null){
                    return
                }
                if (weakReference!!.get()!!.showLoading != null) {
                    weakReference!!.get()!!.showLoading!!.dismissAllowingStateLoss()
                }
            }
    
            override fun onFinish() {
                if(weakReference == null || weakReference!!.get() == null){
                    return
                }
                weakReference!!.get()!!.mTvNext?.postDelayed(object : Runnable {
                    override fun run() {
                        Toast.makeText(App.getInstance(), "已保存gif到${weakReference!!.get()!!.path}", Toast.LENGTH_LONG)
                        if (weakReference!!.get()!!.showLoading != null) {
                            weakReference!!.get()!!.showLoading!!.dismissAllowingStateLoss()
                        }
                        weakReference!!.get()!!.gotoFinishActivity()
                    }
    
                }, 1000)
            }
    
            override fun onProgress(progress: Int, progressTime: Long) {
                if(weakReference == null || weakReference!!.get() == null){
                    return
                }
                var totalProgressTime = (weakReference!!.get()!!.mMaxPercentage - weakReference!!.get()!!.mMinPercentage) * 1000000
                if (weakReference!!.get()!!.showLoading != null) {
                    var progress: Float =
                        (progressTime.toFloat() / totalProgressTime.toFloat()).toFloat()
                    if (progress > 0.95f) {
                        weakReference!!.get()!!.showLoading!!.onProgressChanged(1.0f)
                    } else {
                        weakReference!!.get()!!.showLoading!!.onProgressChanged(progress)
                    }
                }
            }
    
            override fun onCancel() {
                if(weakReference == null || weakReference!!.get() == null){
                    return
                }
                if (weakReference!!.get()!!.showLoading != null) {
                    weakReference!!.get()!!.showLoading!!.dismissAllowingStateLoss()
                }
            }
    
        }
    

    我们在执行每一个视频转gif的时候,都要先生成这个视频的调色图片也就是gotoPalettegen方法。
    我们分析下他的命令

    ${mMinPercentage} -t ${mMaxPercentage - mMinPercentage}
    

    老样子,还是时间设置

    -i ${resultPhotos!!.get(0).path}
    

    还是视频地址

    -r ${mFrame}
    

    还是帧率

    -vf fps=${mFrame},scale=${mCurrentWidth}:${mCurrentHeight}:flags=lanczos,palettegen
    

    这个就是生成调色板的图。值得一提的是设置宽高,就是如下这段代码:

    scale=${mCurrentWidth}:${mCurrentHeight}:
    

    如果我们写生固定数字,比如:scale=500:500: 那么他就生成是500*500的图片,如果视屏比例不是1:1,那么就会产生压缩,如果不想被压缩,那么ffmpeg还支持宽度固定,高度自适应比如:scale=360:-1: 把高度设置为-1时,就能设置这个功能了。

    -y /storage/emulated/0/JJGif/pt.png
    

    就是调色板存放路径了,作为一个程序员,尤其是android程序员,权限问题一定不能忘记。
    然后在完成后再执行真正的生成gif代码也就是gotoGif方法
    老样子,我们看命令
    老代码就不说了,说说不同点

    -i /storage/emulated/0/JJGif/pt.png 
    

    这就是刚刚生成的调色板图片,我们引入他
    然后设置全局应用

    -lavfi fps=${mFrame},scale=${mCurrentWidth}:${mCurrentHeight}:flags=lanczos[x];[x][1:v]paletteuse
    

    当然还有局部应用,大家可以去网上查查。

    -y /storage/emulated/0/JJGif/${resultPhotos!!.get(0).name}.gif
    

    这个就是生成gif了。
    代码麻烦了一点,多了层嵌套。

    图片转gif

    我们可以用ffmpeg,命令简单,但是我这里再介绍一个好用的开源库Gifflen
    https://github.com/lchad/Gifflen-Android

    var list: ArrayList<File> = ArrayList()
            var mGiffle = Gifflen.Builder()
                .color(0)
                .delay(mCurrentSpeed) //每相邻两帧之间播放的时间间隔.
                .quality(mCurrentQuality)
                .width(mCurrentWidth) //生成Gif文件的宽度(像素).
                .height(mCurrentHeight) //生成Gif文件的高度(像素).
                .bgColor(mCurrentColor)
                .listener(object : Gifflen.OnEncodeFinishListener {
                    override fun onEncodeFinish(path: String?) {
                        Toast.makeText(App.getInstance(), "已保存gif到$path", Toast.LENGTH_LONG)
                            .show()
                        if(showLoading != null){
                            showLoading!!.dismissAllowingStateLoss()
                        }
                        dismissAllowingStateLoss()
                        gotoFinishActivity(path)
                    }
    
                    override fun onEncodeProgress(progress: Float) {
                        if(showLoading != null){
                            showLoading!!.onProgressChanged(progress)
                        }
                    }
    
                })
                .build()
            resultPhotos?.forEach {
                list.add(File(it.path))
            }
            //线程池异步调用
            ExecutorsTools.getInstance().addTask(RunnableImp(mGiffle,list))
    

    相关文章

      网友评论

        本文标题:android ffmpeg mp4转gif模糊问题

        本文链接:https://www.haomeiwen.com/subject/ybetiltx.html