内容
- 了解
- 编译
- 使用
FFmpeg
- 简介
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序,它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的,而且还支持众多第三方库扩展。
- 安装
brew install ffmpeg --with-ffplay
- 使用三件套
ffmpeg -i 1.mp4 //查看视频Meta信息
ffplay -ar 44100 -channels 2 -f s16le -i audiokits_decode.pcm //播放pcm
ffprobe -show_packets -of xml >packet_out.xml test.mp4 //查看帧信息(编码数据)
ffprobe -show_frames -of xml >packet_out.xml test.mp4 //查看帧信息(解码数据)
编译
- 交叉编译
是指在一个平台上(如PC)生成另外一个平台(Android/IOS等)的可执行程序。 - 交叉编译工具链
包含在NDK中,通过它PC便可以编译出能在Android上运行的程序。
工具链通常包含以下部分:
CC: 编译器,对C、C++源文件进行编译,生成汇编文件(指令助记符,汇编函数)
AS: 翻译汇编文件成机器码,生成目标文件
AR: 打包器,用于库操作
LD:链接器,将多个目标文件链接成一个库或者可执行文件
GDB: 调试工具 - 编译三部曲
很多知名开源库的编译步骤都是一样的,configure(配置) - make(编译) -make install(安装)。
以 FDK-AAC 为例- 先下载并解压源码
- 本机编译
jackzhoudeMacBook-Pro:fdk-aac-0.1.5 jack.zhou$ pwd /Users/jack.zhou/Desktop/run_ffmpeg_bin/fdk-aac-0.1.5 jackzhoudeMacBook-Pro:fdk-aac-0.1.5 jack.zhou$ ./configure --prefix=/Users/jack.zhou/Desktop/run_ffmpeg_bin/fdk-aac-0.1.5/out jackzhoudeMacBook-Pro:fdk-aac-0.1.5 jack.zhou$ make -j9 jackzhoudeMacBook-Pro:fdk-aac-0.1.5 jack.zhou$ make install
- 产物
-
交叉编译
交叉编译需要指定使用交叉编译工具链进行编译,才能编译出匹配目标平台的产物。交叉编译配置很多参数,通常使用shell脚本进行编译。例如交叉编译fdk-aac脚本如下:#!/bin/bash PROJECT_PATH=`pwd` NDK=/Users/jack.zhou/soft/android/ndk/android-ndk-r15c PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64 PLATFORM=$NDK/platforms/android-9/arch-arm export PATH=$PATH:$PREBUILT/bin:$PLATFORM/usr/include: export LDFLAGS="-L$PLATFORM/usr/lib -L$PREBUILT/arm-linux-androideabi/lib" export CFLAGS="-I$PLATFORM/usr/include -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv7-a -mtune=xscale -msoft-float -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wa,--noexecstack -MMD -MP" export CPPFLAGS="$CFLAGS" export CFLAGS="$CFLAGS" export CXXFLAGS="$CFLAGS" export LDFLAGS="$LDFLAGS" export AS=$PREBUILT/bin/arm-linux-androideabi-as export LD=$PREBUILT/bin/arm-linux-androideabi-ld export CXX="$PREBUILT/bin/arm-linux-androideabi-g++ --sysroot=${PLATFORM}" export CC="$PREBUILT/bin/arm-linux-androideabi-gcc --sysroot=${PLATFORM}" export NM=$PREBUILT/bin/arm-linux-androideabi-nm export STRIP=$PREBUILT/bin/arm-linux-androideabi-strip export RANLIB=$PREBUILT/bin/arm-linux-androideabi-ranlib export AR=$PREBUILT/bin/arm-linux-androideabi-ar ./configure --host=armv7a \ --enable-static \ --disable-shared \ --prefix=$PROJECT_PATH/output/armv7a/ make clean make -j8 make install
-
编译FFmpeg
#!/bin/bash PROJECT_PATH=`pwd` CPU=arm cd ffmpeg-3.3.1 export NDK=/Users/jack.zhou/soft/android/ndk/android-ndk-r15c export PLATFORM=$NDK/platforms/android-16/arch-arm/ export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64 export PREFIX=$PROJECT_PATH/output/android/$CPU # export OUT_STATIC_LIB_PATH=$PREFIX/lib ./configure \ --prefix=$PREFIX \ --target-os=linux \ --arch=arm \ --enable-gpl \ --disable-shared \ --disable-stripping \ --enable-ffmpeg \ --disable-ffplay \ --disable-ffserver \ --enable-ffprobe \ --disable-avdevice \ --disable-indevs \ --disable-devices \ --disable-debug \ --disable-asm \ --disable-yasm \ --disable-doc \ --disable-bsfs \ --disable-indevs \ --disable-outdevs \ --enable-cross-compile \ --cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \ --enable-filter=aresample \ --enable-bsf=aac_adtstoasc \ --enable-bsf=h264_mp4toannexb \ --enable-small \ --enable-dct \ --enable-dwt \ --enable-lsp \ --enable-mdct \ --enable-rdft \ --enable-fft \ --enable-static \ --enable-version3 \ --enable-nonfree \ --disable-encoders \ --enable-encoder=pcm_s16le \ --enable-encoder=aac \ --enable-encoder=libmp3lame \ --enable-encoder=libfdk_aac \ --enable-encoder=libx264 \ --enable-encoder=mp2 \ --disable-decoders \ --enable-decoder=aac \ --enable-decoder=mjpeg \ --enable-decoder=png \ --enable-decoder=gif \ --enable-decoder=mp3 \ --enable-decoder=h264 \ --enable-decoder=pcm_s16le \ --disable-parsers \ --enable-parser=mjpeg \ --enable-parser=png \ --enable-parser=aac \ --enable-parser=h264 \ --enable-parser=mpeg4video \ --enable-parser=mpegvideo \ --enable-parser=mpegaudio \ --disable-muxers \ --enable-muxer=avi \ --enable-muxer=flv \ --enable-muxer=mp4 \ --enable-muxer=m4v \ --enable-muxer=mp3 \ --enable-muxer=mov \ --enable-muxer=h264 \ --enable-muxer=wav \ --enable-muxer=adts \ --disable-demuxers \ --enable-demuxer=mjpeg \ --enable-demuxer=m4v \ --enable-demuxer=gif \ --enable-demuxer=mov \ --enable-demuxer=avi \ --enable-demuxer=flv \ --enable-demuxer=h264 \ --enable-demuxer=aac \ --enable-demuxer=mp3 \ --enable-demuxer=wav \ --disable-protocols \ --enable-protocol=rtmp \ --enable-protocol=file \ --enable-protocol=http \ --disable-filters \ --enable-filter=transpose \ --enable-filter=crop \ --enable-filter=vflip \ --enable-filter=hflip \ --enable-libx264 \ --enable-libfdk_aac \ --enable-libmp3lame \ --sysroot=$PLATFORM \ --extra-cflags="-marm -march=armv7-a -I$PLATFORM/usr/include -Iexternal_libs/fdk-aac/output/armv7a/include -Iexternal_libs/libx264/output/armv7a/include -Iexternal_libs/lame/output/armv7a/include" \ --extra-ldflags="-marm -march=armv7-a -Lexternal_libs/fdk-aac/output/armv7a/lib -Lexternal_libs/libx264/output/armv7a/lib -Lexternal_libs/lame/output/armv7a/lib" make -j6 make install
脚本已上传 compile-ffmpeg
使用
1. 命令行
- 通过可执行文件 FFmpegBox
修改编译脚本,编译出ffmpeg/ffprobe
放在assets目录,运行命令前拷贝到手机目录
runtime执行ffmpeg命令,调用对应的可执行文件 - 通过命令行入口函数
ffmpeg是由ffmpeg.c编译而来,修改ffmpeg.c提供命令入口
Android 集成 FFmpeg (二) 以命令方式调用 FFmpeg
Android 集成 FFmpeg (三) 获取 FFmpeg 执行进度
2. api
- Examples
- 结构体
FFMPEG有几个最重要的结构体,包含了解协议,解封装,解码操作- AVFormatContext
贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况):struct AVInputFormat *iformat:输入数据的封装格式 AVIOContext *pb:输入数据的缓存 unsigned int nb_streams:视音频流的个数 AVStream **streams:视音频流 char filename[1024]:文件名 int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000) int bit_rate:比特率(单位bps,转换为kbps需要除以1000) AVDictionary *metadata:元数据
- AVStream
存储每一个视频/音频流信息的结构体. AVStream重要的变量如下所示:int index:标识该视频/音频流 AVCodecContext *codec:指向该视频/音频流的AVCodecContext(它们是一一对应的关系) AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。FFMPEG其他结构体中也有这个字段,但是根据我的经验,只有AVStream中的time_base是可用的。PTS*time_base=真正的时间 int64_t duration:该视频/音频流长度 AVDictionary *metadata:元数据信息 AVRational avg_frame_rate:帧率(注:对视频来说,这个挺重要的)
- AVCodecContext
编解码器上下文,列举部分重要变量:enum AVMediaType codec_type:编解码器的类型(视频,音频...) struct AVCodec *codec:采用的解码器AVCodec(H.264,MPEG2...) int bit_rate:平均比特率 uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等) AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s) int width, height:如果是视频的话,代表宽和高 int refs:运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了) int sample_rate:采样率(音频) int channels:声道数(音频) enum AVSampleFormat sample_fmt:采样格式 int profile:型(H.264里面就有,其他编码标准应该也有) int level:级(和profile差不太多)
- AVCodec
存储编解码器信息,列举部分变量:const char *name:编解码器的名字,比较短 const char *long_name:编解码器的名字,全称,比较长 enum AVMediaType type:指明了类型,是视频,音频,还是字幕 enum AVCodecID id:ID,不重复 const AVRational *supported_framerates:支持的帧率(仅视频) const enum AVPixelFormat *pix_fmts:支持的像素格式(仅视频) const int *supported_samplerates:支持的采样率(仅音频) const enum AVSampleFormat *sample_fmts:支持的采样格式(仅音频) const uint64_t *channel_layouts:支持的声道数(仅音频) int priv_data_size:私有数据的大小
- AVPacket
存储压缩编码数据相关信息的结构体。
uint8_t *data:压缩编码的数据。 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。 注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流 因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。 int size:data的大小 int64_t pts:显示时间戳 int64_t dts:解码时间戳 int stream_index:标识该AVPacket所属的视频/音频流。
- AVFrame
存储解码之后的原始数据,即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM
uint8_t *data[AV_NUM_DATA_POINTERS]:解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM) int linesize[AV_NUM_DATA_POINTERS]:data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。 int width, height:视频帧宽和高(1920x1080,1280x720...) int nb_samples:音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个 int format:解码后原始数据类型(YUV420,YUV422,RGB24...) int key_frame:是否是关键帧 enum AVPictureType pict_type:帧类型(I,B,P...) AVRational sample_aspect_ratio:宽高比(16:9,4:3...) int64_t pts:显示时间戳 int coded_picture_number:编码帧序号
- AVPacket
- AVFormatContext
- 函数
av_register_all():注册所有组件
avformat_open_input():打开输入视频文件
avformat_find_stream_info():获取视频文件信息
avcodec_find_decoder():查找解码器
avcodec_open1():打开解码器
av_read_frame():从输入文件读取一帧压缩数据
avcodec_decode_video2():解码一桢压缩数据
avcodec_close():关闭解码器
avformat_close_input():关闭输入视频文件 - JNI
- Demo