美文网首页iOS高级开发FFmpeg
第6讲-FFmepg-移动端平台-视频编码+音频编码

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

作者: 泰克2008 | 来源:发表于2017-11-28 11:07 被阅读7次

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

    流程整理


    视频编码.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

    相关文章

      网友评论

        本文标题:第6讲-FFmepg-移动端平台-视频编码+音频编码

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