第一点:分析视频编码原理?->流程?
流程整理
第一步:注册组件->编码器、解码器等等…
第二步:初始化封装格式上下文
第三步:打开输入文件
第四步:创建输出码流->视频流->今后设置->设置为视频流
第五步:查找视频编码器
第六步:打开视频编码器
第七步:写入文件头信息(有些文件头信息)->一般情况下都会有
第八步:循环编码视频像素数据->视频压缩数据
第九步:将编码后的视频压缩数据写入文件中
第十步:输入像素数据读取完毕后回调函数
作用:输出编码器中剩余AVPacket
第十一步:写入文件尾部信息
第十二步:释放内存,关闭编码器等等…
代码部分
#include <jni.h>
#include <string>
#include "android/log.h"
extern "C"{
//导入音视频头文件库
//核心库
#include "libavcodec/avcodec.h"
//封装格式处理库
#include "libavformat/avformat.h"
//工具库
#include "libavutil/imgutils.h"
//视频像素数据格式库
#include "libswscale/swscale.h"
JNIEXPORT void JNICALL Java_com_tz_dream_ffmpeg_android_video_encode_MainActivity_ffmpegVideoEncode(
JNIEnv *env, jobject jobj, jstring jinFilePath, jstring joutFilePath);
}
//视频编码
JNIEXPORT void JNICALL Java_com_tz_dream_ffmpeg_android_video_encode_MainActivity_ffmpegVideoEncode(
JNIEnv *env, jobject jobj, jstring jinFilePath, jstring joutFilePath) {
//第一步:注册组件->编码器、解码器等等…
av_register_all();
//第二步:初始化封装格式上下文->视频编码->处理为视频压缩数据格式
AVFormatContext* avformat_context = avformat_alloc_context();
//注意事项:FFmepg程序推测输出文件类型->视频压缩数据格式类型
const char* coutFilePath = env->GetStringUTFChars(joutFilePath, NULL);
//得到视频压缩数据格式类型(h264、h265、mpeg2等等...)
AVOutputFormat *avoutput_format = av_guess_format(NULL, coutFilePath, NULL);
//指定类型
avformat_context->oformat = avoutput_format;
//第三步:打开输出文件
//参数一:输出流
//参数二:输出文件
//参数三:权限->输出到文件中
if (avio_open(&avformat_context->pb, coutFilePath, AVIO_FLAG_WRITE) < 0){
__android_log_print(ANDROID_LOG_INFO, "main", "打开输出文件失败");
return;
}
//第四步:创建输出码流->创建了一块内存空间->并不知道他是什么类型流->希望他是视频流
AVStream* av_video_stream = avformat_new_stream(avformat_context, NULL);
//第五步:查找视频编码器
//1、获取编码器上下文
AVCodecContext *avcodec_context = av_video_stream->codec;
//2、设置编解码器上下文参数->必需设置->不可少
//目标:设置为是一个视频编码器上下文->指定的是视频编码器
//上下文种类:视频解码器、视频编码器、音频解码器、音频编码器
//2.1 设置视频编码器ID
avcodec_context->codec_id = avoutput_format->video_codec;
//2.2 设置编码器类型->视频编码器
//视频编码器->AVMEDIA_TYPE_VIDEO
//音频编码器->AVMEDIA_TYPE_AUDIO
avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;
//2.3 设置读取像素数据格式->编码的是像素数据格式->视频像素数据格式->YUV420P(YUV422P、YUV444P等等...)
//注意:这个类型是根据你解码的时候指定的解码的视频像素数据格式类型
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;
//2.4 设置视频宽高->视频尺寸
avcodec_context->width = 640;
avcodec_context->height = 352;
//2.5 设置帧率->表示每秒25帧
//视频信息->帧率 : 25.000 fps
//f表示:帧数
//ps表示:时间(单位:每秒)
avcodec_context->time_base.num = 1;
avcodec_context->time_base.den = 25;
//2.6 设置码率
//2.6.1 什么是码率?
//含义:每秒传送的比特(bit)数单位为 bps(Bit Per Second),比特率越高,传送数据速度越快。
//单位:bps,"b"表示数据量,"ps"表示每秒
//目的:视频处理->视频码率
//2.6.2 什么是视频码率?
//含义:视频码率就是数据传输时单位时间传送的数据位数,一般我们用的单位是kbps即千位每秒
//视频码率计算如下?
//基本的算法是:【码率】(kbps)=【视频大小 - 音频大小】(bit位) /【时间】(秒)
//例如:Test.mov时间 = 24,文件大小(视频+音频) = 1.73MB
//视频大小 = 1.34MB(文件占比:77%) = 1.34MB * 1024 * 1024 * 8 = 字节大小 = 468365字节 = 468Kbps
//音频大小 = 376KB(文件占比:21%)
//计算出来值->码率 : 468Kbps->表示1000,b表示位(bit->位)
//总结:码率越大,视频越大
avcodec_context->bit_rate = 468000;
//2.7 设置GOP->影响到视频质量问题->画面组->一组连续画面
//MPEG格式画面类型:3种类型->分为->I帧、P帧、B帧
//I帧->内部编码帧->原始帧(原始视频数据)
// 完整画面->关键帧(必需的有,如果没有I,那么你无法进行编码,解码)
// 视频第1帧->视频序列中的第一个帧始终都是I帧,因为它是关键帧
//P帧->向前预测帧->预测前面的一帧类型,处理数据(前面->I帧、B帧)
// P帧数据->根据前面的一帧数据->进行处理->得到了P帧
//B帧->前后预测帧(双向预测帧)->前面一帧和后面一帧
// B帧压缩率高,但是对解码性能要求较高。
//总结:I只需要考虑自己 = 1帧,P帧考虑自己+前面一帧 = 2帧,B帧考虑自己+前后帧 = 3帧
// 说白了->P帧和B帧是对I帧压缩
//每250帧,插入1个I帧,I帧越少,视频越小->默认值->视频不一样
avcodec_context->gop_size = 250;
//2.8 设置量化参数->数学算法(高级算法)->不讲解了
//总结:量化系数越小,视频越是清晰
//一般情况下都是默认值,最小量化系数默认值是10,最大量化系数默认值是51
avcodec_context->qmin = 10;
avcodec_context->qmax = 51;
//2.9 设置b帧最大值->设置不需要B帧
avcodec_context->max_b_frames = 0;
//第二点:查找编码器->h264
//找不到编码器->h264
//重要原因是因为:编译库没有依赖x264库(默认情况下FFmpeg没有编译进行h264库)
//第一步:编译h264库
AVCodec *avcodec = avcodec_find_encoder(avcodec_context->codec_id);
if (avcodec == NULL){
__android_log_print(ANDROID_LOG_INFO, "main", "找不到解码器");
return;
}
__android_log_print(ANDROID_LOG_INFO, "main", "解码器名称为:%s", avcodec->name);
}
第二点:视频编码->实现功能->yuv编码为h264
流程整理
yuv:视频像素数据格式
h264:视频压缩数据格式
1、查找编码器?
获取编码器名称
找不到编码器->h264
重要原因是因为:编译库没有依赖x264库(默认情况下)
第一步:下载x264库
通过git下载:git clone git://git.videolan.org/x264.git
第二步:解压这个库
第三步:编写脚本->编译x264的.a静态库
指定编译平台类型:iOS平台、安卓平台、Mac平台、Windows平台等等…
编写Android平台.a静态库->课前准备好了->研究一下
第四步:编译Android动态库->编译FFmpeg>修改脚本文件?
加入x264库,将其编译进去
代码整理
build_x264_android.sh
#!/bin/bash
#进入x264库
cd x264
#指定编译平台->Android平台.a静态库
#指定NDK目录
NDK_DIR=/Users/yangshaohong/Desktop/tools/eclipse/android-ndk/android-ndk-r10e
#指定编译的x264平台架构类型->arm架构->系统版本
SYSROOT=$NDK_DIR/platforms/android-18/arch-arm
#指定链接工具->Android平台下arm连接器
TOOLCHAIN=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#指定输出编译好的.a静态库存放路径
PREFIX=/Users/yangshaohong/Desktop/ffmpeg-android/android-build-x264
ADDI_CFLAGS="-marm"
#设置编译参数
function build_h264
{
./configure \
--prefix=$PREFIX \
--host=arm-linux \
--enable-static \
--disable-asm \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
}
#执行脚本
build_h264
#安装编译动态库
sudo make install
echo "Android h264 builds finished"
build-ffmpeg-armeabi-x264.sh
#!/bin/bash
#第一步:进入到指定目录
cd ffmpeg-3.4
#第二步:指定NDK路径(编译什么样的平台->采用什么样的平台编译器)
#Android平台NDK技术->做C/C++开发->编译Andrroid平台下.so动态库
#注意:放在英文目录(中文目录报错)
#修改一:修改为你自己NDK存放目录
NDK_DIR=/Users/yangshaohong/Desktop/tools/eclipse/android-ndk/android-ndk-r10e
#第三步:配置Android系统版本(支持最小的版本)
#指定使用NDK Platform版本(对应系统版本)
SYSROOT=$NDK_DIR/platforms/android-18/arch-arm
#第四步:指定编译工具链->(通俗:指定编译器)->CPU架构(Android手机通用的CPU架构类型):armeabi
TOOLCHAIN=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#第五步:指定CPU平台架构类型
#指定编译后的安装目录
ARCH=arm
ADDI_CFLAGS="-marm"
#第六步:指定编译成功之后,.so动态库存放位置
#修改二:这个目录你需要修改为你自己目录
PREFIX=/Users/yangshaohong/Desktop/ffmpeg-android/android-build/$ARCH
#第七步:编写执行编译脚本->调用FFmpeg进行配置
#定义了Shell脚本函数(方法)
#编译一部分(编译:编解码库->核心库、工具库、视频像素数据处理库、音频采样数据处理库等等...)
#你是如何知道这些库需要编译,那个库不需要编译?
#教你方法?
#有两种方式你可以查看
#方式一:命令行查看
#方式二:通过打开文件查看
function build_armeabi
{
./configure \
--prefix=$PREFIX \
--target-os=android \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-symver \
--enable-small \
#需要
--enable-gpl \
#禁用所有编码器
--disable-encoders \
#通过libx264库启用H.264编码
--enable-libx264 \
#启用编码器名称
--enable-encoder=libx264 \
# 启用几个图片编码,由于生成视频预览
--enable-encoder=mjpeg \
--enable-encoder=png \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=$ARCH \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
#和FFmpeg动态库一起编译,指定你之前编译好的x264静态库和头文件
--extra-cflags="-I/Users/yangshaohong/Desktop/ffmpeg-android/android-build-x264/include" \
--extra-ldflags="-L/Users/yangshaohong/Desktop/ffmpeg-android/android-build-x264/lib" \
--enable-pic \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install
}
#第八步:执行函数->开始编译
build_armeabi
echo "Android armeabi builds finished"
实例工程
脚本部分 https://pan.baidu.com/s/1hsb8dSS
工程部分 https://pan.baidu.com/s/1pLNwYc7