第6講-FFmepg-移動端平臺-視頻編碼+音頻編碼

第一點:分析視頻編碼原理?->流程?

流程整理


視頻編碼.png
第一步:注冊組件->編碼器浆兰、解碼器等等…

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

第三步:打開輸入文件

第四步:創(chuàng)建輸出碼流->視頻流->今后設(shè)置->設(shè)置為視頻流

第五步:查找視頻編碼器

第六步:打開視頻編碼器

第七步:寫入文件頭信息(有些文件頭信息)->一般情況下都會有

第八步:循環(huán)編碼視頻像素數(shù)據(jù)->視頻壓縮數(shù)據(jù)

第九步:將編碼后的視頻壓縮數(shù)據(jù)寫入文件中

第十步:輸入像素數(shù)據(jù)讀取完畢后回調(diào)函數(shù)
作用:輸出編碼器中剩余AVPacket

第十一步:寫入文件尾部信息

第十二步:釋放內(nèi)存,關(guān)閉編碼器等等…

代碼部分

#include <jni.h>
#include <string>
#include "android/log.h"

extern "C"{
//導入音視頻頭文件庫
//核心庫
#include "libavcodec/avcodec.h"
//封裝格式處理庫
#include "libavformat/avformat.h"
//工具庫
#include "libavutil/imgutils.h"
//視頻像素數(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();
    //注意事項: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è)置->不可少
    //目標:設(shè)置為是一個視頻編碼器上下文->指定的是視頻編碼器
    //上下文種類:視頻解碼器悠反、視頻編碼器残黑、音頻解碼器、音頻編碼器
    //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è)置讀取像素數(shù)據(jù)格式->編碼的是像素數(shù)據(jù)格式->視頻像素數(shù)據(jù)格式->YUV420P(YUV422P斋否、YUV444P等等...)
    //注意:這個類型是根據(jù)你解碼的時候指定的解碼的視頻像素數(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表示:時間(單位:每秒)
    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ù)據(jù)位數(shù)奇徒,一般我們用的單位是kbps即千位每秒
    //視頻碼率計算如下?
    //基本的算法是:【碼率】(kbps)=【視頻大小 - 音頻大小】(bit位) /【時間】(秒)
    //例如:Test.mov時間 = 24缨硝,文件大小(視頻+音頻) = 1.73MB
    //視頻大小 = 1.34MB(文件占比:77%) = 1.34MB * 1024 * 1024 * 8 = 字節(jié)大小 = 468365字節(jié) = 468Kbps
    //音頻大小 = 376KB(文件占比:21%)
    //計算出來值->碼率 : 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宜肉,那么你無法進行編碼匀钧,解碼)
    //    視頻第1幀->視頻序列中的第一個幀始終都是I幀,因為它是關(guān)鍵幀
    //P幀->向前預測幀->預測前面的一幀類型谬返,處理數(shù)據(jù)(前面->I幀之斯、B幀)
    //    P幀數(shù)據(jù)->根據(jù)前面的一幀數(shù)據(jù)->進行處理->得到了P幀
    //B幀->前后預測幀(雙向預測幀)->前面一幀和后面一幀
    //    B幀壓縮率高,但是對解碼性能要求較高遣铝。
    //總結(jié):I只需要考慮自己 = 1幀佑刷,P幀考慮自己+前面一幀 = 2幀,B幀考慮自己+前后幀 = 3幀
    //    說白了->P幀和B幀是對I幀壓縮
    //每250幀酿炸,插入1個I幀瘫絮,I幀越少,視頻越小->默認值->視頻不一樣
    avcodec_context->gop_size = 250;

    //2.8 設(shè)置量化參數(shù)->數(shù)學算法(高級算法)->不講解了
    //總結(jié):量化系數(shù)越小填硕,視頻越是清晰
    //一般情況下都是默認值麦萤,最小量化系數(shù)默認值是10鹿鳖,最大量化系數(shù)默認值是51
    avcodec_context->qmin = 10;
    avcodec_context->qmax = 51;

    //2.9 設(shè)置b幀最大值->設(shè)置不需要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

第二點:視頻編碼->實現(xiàn)功能->yuv編碼為h264

流程整理

yuv:視頻像素數(shù)據(jù)格式
h264:視頻壓縮數(shù)據(jù)格式
1、查找編碼器壮莹?
獲取編碼器名稱
找不到編碼器->h264
重要原因是因為:編譯庫沒有依賴x264庫(默認情況下)
第一步:下載x264庫
通過git下載:git clone git://git.videolan.org/x264.git

第二步:解壓這個庫

第三步:編寫腳本->編譯x264的.a靜態(tài)庫
指定編譯平臺類型:iOS平臺翅帜、安卓平臺、Mac平臺命满、Windows平臺等等…
編寫Android平臺.a靜態(tài)庫->課前準備好了->研究一下

第四步:編譯Android動態(tài)庫->編譯FFmpeg>修改腳本文件涝滴?
加入x264庫,將其編譯進去

代碼整理

build_x264_android.sh

#!/bin/bash

#進入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

#安裝編譯動態(tài)庫
sudo make install

echo "Android h264 builds finished"

build-ffmpeg-armeabi-x264.sh

#!/bin/bash

#第一步:進入到指定目錄
cd ffmpeg-3.4

#第二步:指定NDK路徑(編譯什么樣的平臺->采用什么樣的平臺編譯器)
#Android平臺NDK技術(shù)->做C/C++開發(fā)->編譯Andrroid平臺下.so動態(tài)庫
#注意:放在英文目錄(中文目錄報錯)
#修改一:修改為你自己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手機通用的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動態(tài)庫存放位置
#修改二:這個目錄你需要修改為你自己目錄
PREFIX=/Users/yangshaohong/Desktop/ffmpeg-android/android-build/$ARCH

#第七步:編寫執(zhí)行編譯腳本->調(diào)用FFmpeg進行配置
#定義了Shell腳本函數(shù)(方法)
#編譯一部分(編譯:編解碼庫->核心庫歼疮、工具庫、視頻像素數(shù)據(jù)處理庫概作、音頻采樣數(shù)據(jù)處理庫等等...)
#你是如何知道這些庫需要編譯腋妙,那個庫不需要編譯?
#教你方法?
#有兩種方式你可以查看
#方式一:命令行查看
#方式二:通過打開文件查看
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動態(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"

實例工程

腳本部分 https://pan.baidu.com/s/1hsb8dSS
工程部分 https://pan.baidu.com/s/1pLNwYc7

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市愚屁,隨后出現(xiàn)的幾起案子济竹,更是在濱河造成了極大的恐慌,老刑警劉巖霎槐,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件送浊,死亡現(xiàn)場離奇詭異,居然都是意外死亡丘跌,警方通過查閱死者的電腦和手機袭景,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闭树,“玉大人耸棒,你說我怎么就攤上這事”ㄈ瑁” “怎么了与殃?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碍现。 經(jīng)常有香客問我幅疼,道長,這世上最難降的妖魔是什么昼接? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任爽篷,我火速辦了婚禮,結(jié)果婚禮上慢睡,老公的妹妹穿的比我還像新娘狼忱。我一直安慰自己膨疏,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布钻弄。 她就那樣靜靜地躺著,像睡著了一般者吁。 火紅的嫁衣襯著肌膚如雪窘俺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天复凳,我揣著相機與錄音瘤泪,去河邊找鬼。 笑死育八,一個胖子當著我的面吹牛对途,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播髓棋,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼实檀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了按声?” 一聲冷哼從身側(cè)響起膳犹,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎签则,沒想到半個月后须床,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡渐裂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年豺旬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柒凉。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡族阅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扛拨,到底是詐尸還是另有隱情耘分,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布绑警,位于F島的核電站求泰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏计盒。R本人自食惡果不足惜渴频,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望北启。 院中可真熱鬧卜朗,春花似錦拔第、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逛万,卻和暖如春泳猬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宇植。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工得封, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人指郁。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓忙上,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闲坎。 傳聞我的和親對象是個殘疾皇子疫粥,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容