第6讲-FFmepg-移动端平台-视频编码+音频编码

第一点:分析视频编码原理?->流程?

流程整理


视频编码.png
第一步:注册组件->编码器、解码器等等…

第二步:初始化封装格式上下文

第三步:打开输入文件

第四步:创建输出码流->视频流->今后设置->设置为视频流

第五步:查找视频编码器

第六步:打开视频编码器

第七步:写入文件头信息(有些文件头信息)->一般情况下都会有

第八步:循环编码视频像素数据->视频压缩数据

第九步:将编码后的视频压缩数据写入文件中

第十步:输入像素数据读取完毕后回调函数
作用:输出编码器中剩余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);
    
}
GOP原理-MPEG格式.png

第二点:视频编码->实现功能->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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容