第一點(diǎn):分析視頻編碼原理?->流程?
流程整理
視頻編碼.png
第一步:注冊組件->編碼器、解碼器等等…
第二步:初始化封裝格式上下文
第三步:打開輸入文件
第四步:創(chuàng)建輸出碼流->視頻流->今后設(shè)置->設(shè)置為視頻流
第五步:查找視頻編碼器
第六步:打開視頻編碼器
第七步:寫入文件頭信息(有些文件頭信息)->一般情況下都會有
第八步:循環(huán)編碼視頻像素?cái)?shù)據(jù)->視頻壓縮數(shù)據(jù)
第九步:將編碼后的視頻壓縮數(shù)據(jù)寫入文件中
第十步:輸入像素?cái)?shù)據(jù)讀取完畢后回調(diào)函數(shù)
作用:輸出編碼器中剩余AVPacket
第十一步:寫入文件尾部信息
第十二步:釋放內(nèi)存,關(guān)閉編碼器等等…
代碼部分
#include <jni.h>
#include <string>
#include "android/log.h"
extern "C"{
//導(dǎo)入音視頻頭文件庫
//核心庫
#include "libavcodec/avcodec.h"
//封裝格式處理庫
#include "libavformat/avformat.h"
//工具庫
#include "libavutil/imgutils.h"
//視頻像素?cái)?shù)據(jù)格式庫
#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();
//第二步:初始化封裝格式上下文->視頻編碼->處理為視頻壓縮數(shù)據(jù)格式
AVFormatContext* avformat_context = avformat_alloc_context();
//注意事項(xiàng):FFmepg程序推測輸出文件類型->視頻壓縮數(shù)據(jù)格式類型
const char* coutFilePath = env->GetStringUTFChars(joutFilePath, NULL);
//得到視頻壓縮數(shù)據(jù)格式類型(h264、h265、mpeg2等等...)
AVOutputFormat *avoutput_format = av_guess_format(NULL, coutFilePath, NULL);
//指定類型
avformat_context->oformat = avoutput_format;
//第三步:打開輸出文件
//參數(shù)一:輸出流
//參數(shù)二:輸出文件
//參數(shù)三:權(quán)限->輸出到文件中
if (avio_open(&avformat_context->pb, coutFilePath, AVIO_FLAG_WRITE) < 0){
__android_log_print(ANDROID_LOG_INFO, "main", "打開輸出文件失敗");
return;
}
//第四步:創(chuàng)建輸出碼流->創(chuàng)建了一塊內(nèi)存空間->并不知道他是什么類型流->希望他是視頻流
AVStream* av_video_stream = avformat_new_stream(avformat_context, NULL);
//第五步:查找視頻編碼器
//1、獲取編碼器上下文
AVCodecContext *avcodec_context = av_video_stream->codec;
//2、設(shè)置編解碼器上下文參數(shù)->必需設(shè)置->不可少
//目標(biāo):設(shè)置為是一個(gè)視頻編碼器上下文->指定的是視頻編碼器
//上下文種類:視頻解碼器、視頻編碼器、音頻解碼器、音頻編碼器
//2.1 設(shè)置視頻編碼器ID
avcodec_context->codec_id = avoutput_format->video_codec;
//2.2 設(shè)置編碼器類型->視頻編碼器
//視頻編碼器->AVMEDIA_TYPE_VIDEO
//音頻編碼器->AVMEDIA_TYPE_AUDIO
avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;
//2.3 設(shè)置讀取像素?cái)?shù)據(jù)格式->編碼的是像素?cái)?shù)據(jù)格式->視頻像素?cái)?shù)據(jù)格式->YUV420P(YUV422P、YUV444P等等...)
//注意:這個(gè)類型是根據(jù)你解碼的時(shí)候指定的解碼的視頻像素?cái)?shù)據(jù)格式類型
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;
//2.4 設(shè)置視頻寬高->視頻尺寸
avcodec_context->width = 640;
avcodec_context->height = 352;
//2.5 設(shè)置幀率->表示每秒25幀
//視頻信息->幀率 : 25.000 fps
//f表示:幀數(shù)
//ps表示:時(shí)間(單位:每秒)
avcodec_context->time_base.num = 1;
avcodec_context->time_base.den = 25;
//2.6 設(shè)置碼率
//2.6.1 什么是碼率?
//含義:每秒傳送的比特(bit)數(shù)單位為 bps(Bit Per Second),比特率越高,傳送數(shù)據(jù)速度越快。
//單位:bps,"b"表示數(shù)據(jù)量,"ps"表示每秒
//目的:視頻處理->視頻碼率
//2.6.2 什么是視頻碼率?
//含義:視頻碼率就是數(shù)據(jù)傳輸時(shí)單位時(shí)間傳送的數(shù)據(jù)位數(shù),一般我們用的單位是kbps即千位每秒
//視頻碼率計(jì)算如下?
//基本的算法是:【碼率】(kbps)=【視頻大小 - 音頻大小】(bit位) /【時(shí)間】(秒)
//例如:Test.mov時(shí)間 = 24,文件大小(視頻+音頻) = 1.73MB
//視頻大小 = 1.34MB(文件占比:77%) = 1.34MB * 1024 * 1024 * 8 = 字節(jié)大小 = 468365字節(jié) = 468Kbps
//音頻大小 = 376KB(文件占比:21%)
//計(jì)算出來值->碼率 : 468Kbps->表示1000,b表示位(bit->位)
//總結(jié):碼率越大,視頻越大
avcodec_context->bit_rate = 468000;
//2.7 設(shè)置GOP->影響到視頻質(zhì)量問題->畫面組->一組連續(xù)畫面
//MPEG格式畫面類型:3種類型->分為->I幀、P幀、B幀
//I幀->內(nèi)部編碼幀->原始幀(原始視頻數(shù)據(jù))
// 完整畫面->關(guān)鍵幀(必需的有,如果沒有I,那么你無法進(jìn)行編碼,解碼)
// 視頻第1幀->視頻序列中的第一個(gè)幀始終都是I幀,因?yàn)樗顷P(guān)鍵幀
//P幀->向前預(yù)測幀->預(yù)測前面的一幀類型,處理數(shù)據(jù)(前面->I幀、B幀)
// P幀數(shù)據(jù)->根據(jù)前面的一幀數(shù)據(jù)->進(jìn)行處理->得到了P幀
//B幀->前后預(yù)測幀(雙向預(yù)測幀)->前面一幀和后面一幀
// B幀壓縮率高,但是對解碼性能要求較高。
//總結(jié):I只需要考慮自己 = 1幀,P幀考慮自己+前面一幀 = 2幀,B幀考慮自己+前后幀 = 3幀
// 說白了->P幀和B幀是對I幀壓縮
//每250幀,插入1個(gè)I幀,I幀越少,視頻越小->默認(rèn)值->視頻不一樣
avcodec_context->gop_size = 250;
//2.8 設(shè)置量化參數(shù)->數(shù)學(xué)算法(高級算法)->不講解了
//總結(jié):量化系數(shù)越小,視頻越是清晰
//一般情況下都是默認(rèn)值,最小量化系數(shù)默認(rèn)值是10,最大量化系數(shù)默認(rèn)值是51
avcodec_context->qmin = 10;
avcodec_context->qmax = 51;
//2.9 設(shè)置b幀最大值->設(shè)置不需要B幀
avcodec_context->max_b_frames = 0;
//第二點(diǎn):查找編碼器->h264
//找不到編碼器->h264
//重要原因是因?yàn)椋壕幾g庫沒有依賴x264庫(默認(rèn)情況下FFmpeg沒有編譯進(jìn)行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
第二點(diǎn):視頻編碼->實(shí)現(xiàn)功能->yuv編碼為h264
流程整理
yuv:視頻像素?cái)?shù)據(jù)格式
h264:視頻壓縮數(shù)據(jù)格式
1、查找編碼器?
獲取編碼器名稱
找不到編碼器->h264
重要原因是因?yàn)椋壕幾g庫沒有依賴x264庫(默認(rèn)情況下)
第一步:下載x264庫
通過git下載:git clone git://git.videolan.org/x264.git
第二步:解壓這個(gè)庫
第三步:編寫腳本->編譯x264的.a靜態(tài)庫
指定編譯平臺類型:iOS平臺、安卓平臺、Mac平臺、Windows平臺等等…
編寫Android平臺.a靜態(tài)庫->課前準(zhǔn)備好了->研究一下
第四步:編譯Android動(dòng)態(tài)庫->編譯FFmpeg>修改腳本文件?
加入x264庫,將其編譯進(jìn)去
代碼整理
build_x264_android.sh
#!/bin/bash
#進(jìn)入x264庫
cd x264
#指定編譯平臺->Android平臺.a靜態(tài)庫
#指定NDK目錄
NDK_DIR=/Users/yangshaohong/Desktop/tools/eclipse/android-ndk/android-ndk-r10e
#指定編譯的x264平臺架構(gòu)類型->arm架構(gòu)->系統(tǒng)版本
SYSROOT=$NDK_DIR/platforms/android-18/arch-arm
#指定鏈接工具->Android平臺下arm連接器
TOOLCHAIN=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#指定輸出編譯好的.a靜態(tài)庫存放路徑
PREFIX=/Users/yangshaohong/Desktop/ffmpeg-android/android-build-x264
ADDI_CFLAGS="-marm"
#設(shè)置編譯參數(shù)
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
}
#執(zhí)行腳本
build_h264
#安裝編譯動(dòng)態(tài)庫
sudo make install
echo "Android h264 builds finished"
build-ffmpeg-armeabi-x264.sh
#!/bin/bash
#第一步:進(jìn)入到指定目錄
cd ffmpeg-3.4
#第二步:指定NDK路徑(編譯什么樣的平臺->采用什么樣的平臺編譯器)
#Android平臺NDK技術(shù)->做C/C++開發(fā)->編譯Andrroid平臺下.so動(dòng)態(tài)庫
#注意:放在英文目錄(中文目錄報(bào)錯(cuò))
#修改一:修改為你自己NDK存放目錄
NDK_DIR=/Users/yangshaohong/Desktop/tools/eclipse/android-ndk/android-ndk-r10e
#第三步:配置Android系統(tǒng)版本(支持最小的版本)
#指定使用NDK Platform版本(對應(yīng)系統(tǒng)版本)
SYSROOT=$NDK_DIR/platforms/android-18/arch-arm
#第四步:指定編譯工具鏈->(通俗:指定編譯器)->CPU架構(gòu)(Android手機(jī)通用的CPU架構(gòu)類型):armeabi
TOOLCHAIN=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#第五步:指定CPU平臺架構(gòu)類型
#指定編譯后的安裝目錄
ARCH=arm
ADDI_CFLAGS="-marm"
#第六步:指定編譯成功之后,.so動(dòng)態(tài)庫存放位置
#修改二:這個(gè)目錄你需要修改為你自己目錄
PREFIX=/Users/yangshaohong/Desktop/ffmpeg-android/android-build/$ARCH
#第七步:編寫執(zhí)行編譯腳本->調(diào)用FFmpeg進(jìn)行配置
#定義了Shell腳本函數(shù)(方法)
#編譯一部分(編譯:編解碼庫->核心庫、工具庫、視頻像素?cái)?shù)據(jù)處理庫、音頻采樣數(shù)據(jù)處理庫等等...)
#你是如何知道這些庫需要編譯,那個(gè)庫不需要編譯?
#教你方法?
#有兩種方式你可以查看
#方式一:命令行查看
#方式二:通過打開文件查看
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 \
# 啟用幾個(gè)圖片編碼,由于生成視頻預(yù)覽
--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動(dòng)態(tài)庫一起編譯,指定你之前編譯好的x264靜態(tài)庫和頭文件
--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
}
#第八步:執(zhí)行函數(shù)->開始編譯
build_armeabi
echo "Android armeabi builds finished"
實(shí)例工程
腳本部分 https://pan.baidu.com/s/1hsb8dSS
工程部分 https://pan.baidu.com/s/1pLNwYc7

