FFmpeg手撕视频(Android端)

14,070 阅读14分钟

前言

FFmpeg是非常强大的音视频处理工具,我们可以使用它来处理视频合成、剪辑、加特效等等操作。

官方文档至上 FFmpeg的官方文档

FFmpeg的官方文档命令真的是太多太多,而且都是英文,感觉精通完这些命令,都够学一门新语言了!

SO 为了让一些小伙伴们快速的用上FFmpeg,我整理了一些FFmpeg的常用的知识和大部分日常用到的命令供大家查阅!

还不收藏吗?废话不多说上干货!

Android端使用FFmpeg

FFmpeg的环境集成挺麻烦的推荐大家使用这个库,一键集成FFmpeg! Skr~

RxFFmpeg

关于音视频需要了解的东西

音/视频流

在音视频领域,我们把一路音/视频称为一路流。如我们小时候经常使用VCD看港片,在里边可以选择粤语或国语声音,其实就是CD视频文件中存放了两路音频流,用户可以选择其中一路进行播放。

容器

我们熟悉的mp4,rmvb,mkv,avi是多媒体容器文件格式(或称多媒体封装格式),所谓容器是指将不同的数据流(视频流,音频流,字幕流等)封装在一个文件(载体)中。 播放时各种流分别进行解码等处理后,然后输出到显示器和音响等设备进行播放。多媒体容器格式不同于编码格式,一个容器中可以封装多种编码格式的媒体流。 流封装了实际的媒体数据,如视频流,音频流和字幕流等。一般情况下,流中的数据只能使用一种编码格式。

channel

是音频中的概念,称之为声道。在一路音频流中,可以有单声道,双声道或立体声。

帧率

帧率(frames per second, fps)是每秒画面刷新的次数,帧率越高视频越流畅。一般来说30fps就是可以接受的,60fps则可以明显提升交互感和逼真感,但是一般超过75fps一般就不容易察觉到有明显的流畅度提升了。

分辨率

分辨率表示画面的精细程度,通常用像素密度来表示,常用的单位为ppi(像素每英寸)。通常像素密度越高画面越精细,模糊程度越低。对于视频文件而言,像素密度是无法控制的(由播放器和显示设备决定)。我们通常用视频的像素数来表示它的分辨率如1080x640, 640x320等。

比特率

比特率(bit rate)又称码率,表示多媒体流每秒输出的字节数,单位为KB/s,Kbps等。同样的压缩算法下,比特率越高音视频的质量越好。

可变码率(Variable Bitrate, VBR)

指的是编码器的输出码率可以根据输入源信号的复杂度进行自适应调整,以在输出质量保持不变的条件下尽可能减少数据量。VBR适用于存储,不太适用流式传输。

固定码率(Constant Bitrate, CBR)

指的是编码器输出码率固定,CBR不适合存储,对于复杂内容可能没有足够码率进行编码,从而导致质量下降,同时会在简单内容部分浪费一些码率。

采样率

每秒钟对音频信号的采样次数,采样频率越高声音还原度越高,声音更加自然,单位是赫兹 Hz。音频文件一般使用的采样率是 44.1kHz,也就是一秒钟采样44100次,实验发现低于这个值就会有较明显的损失,而高于这个值人的耳朵已经很难分辨,而且增大了数字音频所占用的空间。

视频编码

视频流可以看做图片的序列,我们把这个序列中的一张图片称为一帧。若存储视频中所有帧则会数据量过大,不便于存储和传输。所幸统计表明大多数视频相邻帧之间的区别并不大,所以对于一段变化不大的视频,我们可以先完整编码帧A,其后的B帧只需要编码与A帧不同的部分,B帧后的C帧则只编码与B帧的差异。如此递推,将一段视频编码为一个序列。当某个图像与之前的图像变化很大无法参考前面的帧来生成,我们就结束上一个序列将该帧完整编码开始一个新的序列。

H264是目前流行的一种视频编码算法,它定义了三种帧:完整编码的I帧,参考I帧生成只包含差异的P帧,以及以及参考前后帧编码的B帧。

H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。通常,我们也把完整编码的I帧称为关键帧。因为解码非关键帧需要解码其参考的帧,因此在截图等不需要全部解码的操作中,经常截取关键帧以提升性能。

FFmpeg的基本字符命令

-i 输入文件的地址

-y 表示直接覆盖已经存在的输出文件

-n 表示若某个输出文件已经存在则退出(若没有设置-y或-n选项,且某个输出文件已经存在ffmpeg会询问是否要覆盖输出文件)

-codec(-c) 指定输入输出的解码编码器 copy 则将输入流直接复制到输出流不进行编解码操作

-c:v 或-vcodec 可以为所有视频流指定编码器,-c:v:1为第2个视频流指定编解码器。xvid(使用XVID编码压缩视频,不能改的)

-c:a 或-acodec 可以为所有音频流指定编码器,-c:a:12为第13个视频流指定编解码器。

-pixel_format/-pix_fmt 指定转换格式 例:yuv420p

-vol 音量

-ss 选项用于设置流的开始时间,可以设置输入输出或者滤镜。在开始时间之前的帧将被跳过不被处理(输入不被解码,输出不被编码,滤镜不被处理)。

例:ffmpeg -ss 2 -t 10 -i test.mp4 test.mov

时长的格式:如-t 10, -t 23.167 时分秒: 如-t 10:23, -t 21:31:00.233

-t 选项用于用于设置输入输出,-t在-i前可以限制输入时长,-t在输出文件前可以限制输出时长

-to 选项类似于-t选项,不同的是-to指定结束时刻,-t指定持续时间

-f 强制设置输入输出的文件格式,默认情况下ffmpeg会根据文件后缀名判断文件格式

-filter / -filter_complex 使用过滤器对流进行处理 复杂过滤器

-vf 代替-filter:v处理视频流

-af 代替-filter:a处理音频流

-vframes 设置要输出的视频帧的数量: 例:ffmpeg -i test.mp4 -vframes 10 test.mov

-aframes 设置要输出的音频帧的数量。这是-frames:a的一个过时的别名。

-vn 不将视频流写到输出文件中无视频的意思 例:ffmpeg -i test.mp4 -vn -a:c copy out.mp3

-an 不将音频流写到输出文件中无音频的意思 例:ffmpeg -i test.mp4 -v:c copy -an out.mp4

-r 设置某个流的帧率按ffmpeg官方文档说-r与-framerate作用相同,但实际测试时发现不同。-framerate 用于限制输入,而-r用于限制输出 例:ffmpeg -i test.mp4 -r:v 30 test.mov

-s 设置帧的大小 例:ffmpeg -i test.mp4 -s 1080x680 out.mp4

-aspect [:stream_specifier] 宽高比(输出,每个流) 设置方面指定的视频显示宽高比。aspect可以是浮点数字符串,也可以是num:den形式的字符串,其中num和den是宽高比的分子和分母。例如“4:3”,“16:9”,“1.3333”和“1.7777”是有效的参数值。如果与-vcodec副本一起使用,则会影响存储在容器级别的宽高比,但不会影响存储在编码帧中的宽高比(如果存在)。

-threads 设置处理线程数 例:ffmpeg -threads 8 -i test.mp4 out.mp4

-shortest 当最短的输入流结束后即停止编码和输出 例:ffmpeg -i bgm.mp3 -i test.mp4 -shortest output.mp4

-sample_fmts 设置音频采样格式。使用-sample_fmts获取支持的样本格式列表。

-b:a 指定音频码率。 b 是 bitrate的缩写, a是 audio的缩写

-b:v 指定视频码率。 b 是 bitrate的缩写, v是 video的缩写

-loop 循环输入流。只工作于图像流

concat 拼接视频 例:ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -c copy output.mp4

Filter(滤镜)

复杂过滤器(filter_complex)

过滤器会对已解码的帧进行处理,处理后的帧会被重新编码输出

scale

指定操作源的大小,iw指定按整型取视频的宽度,ih指定按整型取视频的高度。-1为按原图比例变化 例:iw/2:-1视频缩小一倍

overlay

指定操作源摆放的位置 overlay=30:10 main_w和main_h为底层视频的宽和高,overlay_w和overlay_h为叠加视频的宽和高

delogo

删除水印 例:delogo=x=800:y=20:w=70:h=80

crop

裁剪 格式:crop=out_w:out_h: x :y out_w: 输出的宽度。可以使用 in_w 表式输入视频的宽度。 out_h: 输出的高度。可以使用 in_h 表式输入视频的高度。 x : X坐标 y : Y坐标 x和y 设置为 0,说明从左上角开始裁剪。如果不写是从中心点裁剪

setpts

setpts=0.5*PTS表示每帧视频的pts时间戳都乘0.5,也就是视频加快一倍

-map

可用于处理复杂输出,如可以将指定的多路流输出到一个输出文件,也可以指定输出到多个文件。"[v]" 复杂滤镜输出的别名作为输出文件的一路流。上面 map的用法是将复杂滤镜输出的视频和音频输出到指定文件中。

水平翻转hflip

镜像

crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right]

各种特效curves

curves='vintage'(复古) 'strong_contrast'(强对比度)'lighter'(变亮) 'negate'(底片) 'none' 'color_negative'(彩色底片);

边缘检测edgedetect

光晕vignette=PI/4

变暗

colorlevels=rimin=0.058:gimin=0.058:bimin=0.058

增加对比度

fftfilt=dc_Y=0:weight_Y='exp(-4 * ((Y+X)/(W+H)))

降噪

hqdn3d=luma_spatial=15.0

锐化

fftfilt=dc_Y=0:weight_Y='1+squish(1-(Y+X)/100)'

低通滤波

fftfilt=dc_Y=0:weight_Y='squish((Y+X)/100-1)'

高通滤波

fftfilt=dc_Y=128:weight_Y='squish(1-(Y+X)/100)'

fade 视频淡入淡出效果

例:fade=in:0:25, fade=out:975:25 从0桢开始淡入25帧,从975开始淡出25帧 fade=in:5:20:color=yellow 开始淡入前为黄色 fade=in:0:25:alpha=1 淡入完成后过去15帧的透明度 fade=t=in:st=5.5:d=0.5 5.5秒开始,淡入0.5秒 d为时长

fps滤镜通过删除帧或者复制帧的方法强制设置帧率 例:ffmpeg -y -i test.mp4 -vf "fps=60" out.mp4

常用命令

视频格式转换

ffmpeg -i out.mp4 -y out.avi

视频转Gif

视频转Gif

ffmpeg -i out.mp4 -y out.gif

从0开始截10s转Gif

ffmpeg -i out.mp4 -ss 00:00:00 -t 10 -y out.gif

视频转多张图片

每秒生成一个张图片

ffmpeg -i input_test.mp4 -vf fps=1 out%03d.png

每分钟生成一张图片

ffmpeg -i input_test.mp4 -vf fps=1/60 out%03d.png

截取视频中的某段视频

ffmpeg -ss 2 -i test.mp4 -t 10 -y -f out_test.mp4

截取从2s开始10秒的视频

视频分片(把视频切为一段一段)

ffmpeg -i test.mp4 -c copy -map 0 -f segment -segment_time 10 video/part-%d.mp4

截取视频中某个时间段的单张图片或多张图片

ffmpeg -ss 2 -i test.mp4 -r 1 -t 2 -y -f image2 image-%3.jpeg

单张图片或多张图片转视频

ffmpeg -loop 1 -i img%3d.png -t 10-y output.mp4

Gif转视频

ffmpeg -i input.gif -y output.mp4

多个视频合并

ffmpeg -i "concat:input1.mp4|input2.mp4|input3.mp4" -c copy output.mp4

修改视频分辨率

ffmpeg -i input_test.mp4 -s 320*240 out_test.mp4

给视频添加音频

ffmpeg -i input.mp4 -i input1.mp3 -y output.mp4

提取视频中的音频

ffmpeg -i test.mp4 -vn -a:c copy out.mp3

视频静音

ffmpeg -i input.mp4 -an -vcodec copy output.mp4

音频合并

ffmpeg -i "concat:test1.mp3|test2.mp3" -acodec copy output.mp3

高级命令

音频混音

ffmpeg -i input_01.wav -i input_02.wav -filter_complex amix=inputs=2:duration=shortest:dropout_transition=3 output.wav

以两个音频文件时长较短的音频文件时长作为最终输出的时长

  • inputs: The number of inputs. If unspecified, it defaults to 2.//输入的数量,如果没有指明,默认为2.
  • duration: 决定了流的结束
  • longest: 最长输入的持续时间
  • shortest:最短输入的持续时间
  • first: 第一个输入的持续时间
  • dropout_transition: 输入流结束时(音频)容量重整化的转换时间(以秒为单位)。 默认值为2秒。

添加文字水印

ffmpeg -y -i test.mp4 -vf "drawtext=fontfile=CourierNew.ttf:text='hello world':x=100:y=50:fontsize=24" out.mp4

Android端添加文字水印

由于Android端字体库的原因,我可以通过把文字转成图片,再加到视频上的方式来添加,以下是文字转图片的源码:

    /**
     * 文本转成Bitmap
     * @param text 文本内容
     * @param context 上下文
     * @return 图片的bitmap
     */
    private static Bitmap textToBitmap(String text , Context context) {
        float scale = context.getResources().getDisplayMetrics().scaledDensity;
        TextView tv = new TextView(context);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        tv.setLayoutParams(layoutParams);
        tv.setText(text);
        tv.setTextSize(scale * TEXT_SIZE);
        tv.setGravity(Gravity.CENTER_HORIZONTAL);
        tv.setDrawingCacheEnabled(true);
        tv.setTextColor(TEXT_COLOR);
        tv.setBackgroundColor(Color.WHITE);
        tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
        tv.buildDrawingCache();
        Bitmap bitmap = tv.getDrawingCache();
        int rate = bitmap.getHeight() / 20;
        return Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/rate, 20, false);
    }
 
    /**
     * 文字生成图片
     * @param filePath filePath
     * @param text text
     * @param context context
     * @return 生成图片是否成功
     */
    public static boolean textToPicture(String filePath, String text , Context context){
        Bitmap bitmap = textToBitmap(text , context);
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(filePath);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }finally {
            try {
                if(outputStream != null){
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    } 
    

添加图片水印

添加单个图片水印

ffmpeg -i input.mp4 -i water.png -filter_complex "[0:v][1:v]overlay=main_w-overlay_w-10:10" -y output.mp4

添加多个图片水印

ffmpeg -i input.mp4 -i photo1.png -i photo2.png -filter_complex "[0:v]fade=in:st=0:d=2,curves=vintage[img];[1:v]scale=300:-1[img1];[img][img1]overlay=10:10[out1];[out1][2:v]overlay=main_w:main_h" -y output.mp4

添加了两张图片图片,第一张设置为宽度300,高度-1为根据原图片比例缩放,在视频左上角,第二张图片在视频右下角,fade是淡入淡出效果器,视频效果为从0s开始淡入,淡入2s,视频为复古效果curves=vintage

跑马灯效果

ffmpeg -i video2.mp4 -i logo.png -filter_complex "[1:v]scale=50*50[logo];[0:v]scale=200*200[bg];[bg][logo]overlay=x='if(gte(t,0),-overlay_w+(mod(n,main_w+overlay_w))+5,NAN)':y=0" -y output.mp4

含义: 时间t大于0,那么就开始从子内容的宽度的x-overlay_w位置开始,然后每一帧n计数,帧数n除以(背景main_w+子内容背景overlay_w)求除数+1设置为x坐标,即可循环 overlay=30:10 main_w和main_h为底层视频的宽和高,overlay_w和overlay_h为叠加视频的宽和高

视频去水印

ffmpeg -i test.flv -vf delogo=x=20:y=20:w=70:h=80 output.flv

x,y :指定水印的位置,即图片左上角的坐标 w,h:给出水印的宽高

添加动画

ffmpeg -y -i test.mp4 -t 10 -loop 1 -framerate 6 -i test_%3d.png -filter_comple 'overlay=10:main_h-overlay_h-10' out.mp4

将多张图片(test_001.png, ani002.png...)组成动画, 然后将这个动画叠加在视频的左下角。-t 10 -loop 1会循环播放动画,持续10s

淡入淡出效果器

ffmepg -i input.wav -filter_complex afade=t=in:ss=0:d-4 output.wav

(淡入)把 input 文件的前5s 做一个淡入效果,输出到 output.wav 中

ffmpeg -i input.wav -filter_complex afade=t=out:st=200:d=5 output.wav

(淡出)将input.wav文件从200s开始,做5s的淡出效果,并放到output.wav文件中

视频提亮效果器

ffmpeg -i input.flv -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=0.25 -f mp4 output.mp4

提亮参数是brightness,取值范围是从-1.0到1.0,默认值是0

为视频增加对比度效果

ffmpeg -i input.flv -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=1.5 -f mp4 output.mp4

对比度参数是contrast,取值范围是从-2.0到2.0,默认值是1.0

截取视频区域

ffmpeg -i input.mp4 -an -vf "crop=480:480:120:0" -vcodec libx264 -b:v 800k output.mp4

旋转视频

ffmpeg -i input.mp4 -vf "transpose=1" -b:v 600k output.mp4

改变视频音量

ffmpeg -i input.mp4 -af 'volume=0.5' output.mp4

压缩视频

-preset superfast 可以加快合成视频时间

视频压缩主要是改变视频的码率、比特率和分辨率去压缩,但是需要控制好缩小的码率和分辨率,以保证视频的质量符合你的需求 ffmpeg -i input.mp4 -b:v 600k -y output.mp4

推荐比特率

阿里云给的建议

END

FFmpeg十分强大,本文的知识只是FFmpeg的一小部分,对于初识FFmpeg的同学用于常见的视频操作还是完全OK的,精通音视频还是有很长的路要走,更多的FFmpeg知道请移步FFmpeg的官方文档

欢迎靓仔靓女们收藏一波,谢谢~