1. 程式人生 > >ffmpeg超詳細綜合教程(三)——安卓手機攝像頭編碼

ffmpeg超詳細綜合教程(三)——安卓手機攝像頭編碼

本文的示例將實現:讀取安卓手機攝像頭資料並使用H.264編碼格式實時編碼儲存為flv檔案。示例包含了

1、編譯適用於安卓平臺的ffmpeg庫

2、在java中通過JNI使用ffmpeg

3、讀取安卓攝像頭資料並在後臺執行緒中使用ffmpeg進行編碼的基本流程

具有較強的綜合性。

編譯適用於安卓平臺的ffmpeg庫

平時我們編譯ffmpeg類庫都是在x86平臺下,而安卓手機屬於arm平臺,所以要先通過交叉編譯的方法在x86平臺下編譯出可以在arm平臺下使用的ffmpeg類庫。Google就為我們提供了一套可以再Linux下完成該任務的交叉編譯工具鏈——NDK,下載時可以看到google同時提供了ndk的windows版和linux版,前者可以在windows下通過cygwin(可以理解為一個在windows下模擬linux的工具)使用。我這裡使用的是Android-ndk-r10e,在cygwin下使用。ndk的下載安裝基本是傻瓜式的,但要注意linux系統對x86和x64是分的很清楚的,不像windows那麼隨性,此外還需要提醒的是,在下載安裝cygwin時,只需要選擇binutils , gcc , gcc-mingw , gdb , make這幾個元件。安裝完成後,在cygwin安裝目錄下點選cygwin.bat開啟命令視窗,輸入make -version驗證是否安裝成功。

做好以上的準備工作之後就可以正式開始ffmpeg原始碼的編譯了。 首先需要修改configure檔案,確保類庫版本號不會出現在.so字尾名的後面,否則安卓平臺無法識別這樣的類庫 找到下面幾句
  1. SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'    
  2. LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'    
  3. SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'    
  4. SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'    
修改為
  1. SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'    
  2. LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'    
  3. SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'    
  4. SLIB_INSTALL_LINKS='$(SLIBNAME)'   
之後進行常規的configure配置,make, make install步驟即可,下面給出一個常規的指令碼示例
  1. make clean    
  2. export NDK=xxxx/android-ndk-r10e  
  3. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt    
  4. export PLATFORM=$NDK/platforms/android-8/arch-arm    
  5. export PREFIX=../android_ffmpeglib  
  6.   ./configure --target-os=linux --prefix=$PREFIX \    
  7. --enable-cross-compile \    
  8. --enable-runtime-cpudetect \    
  9. --disable-asm \    
  10. --arch=arm \    
  11. --cc=$PREBUILT/windows/bin/arm-linux-androideabi-gcc \    
  12. --cross-prefix=$PREBUILT/windows/bin/arm-linux-androideabi- \    
  13. --disable-stripping \    
  14. --nm=$PREBUILT/windows/bin/arm-linux-androideabi-nm \    
  15. --sysroot=$PLATFORM \    
  16. --enable-gpl --enable-shared --disable-static --enable-small \    
  17. --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \    
  18. --extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"     
  19. make    
  20. make install   
成功編譯後,可以看到如下類庫

在java中通過JNI使用ffmpeg

JNI即java本地介面,java native interface,它是一個協議, 該協議用來溝通Java程式碼和外部的本地C/C++程式碼, 通過該協議 Java程式碼可以呼叫外部的原生代碼, 外部的C/C++ 程式碼可以呼叫Java程式碼。簡單來說,就是將一個C語言的方法對映到Java的某個方法上; 
JNI中的一些概念 : 
-- native : Java語言中修飾本地方法的修飾符, 被該修飾符修飾的方法沒有方法體;
-- Native方法 : 在Java語言中被native關鍵字修飾的方法是Native方法;
-- JNI層 : Java宣告Native方法的部分;
-- JNI函式 : JNIEnv提供的函式, 這些函式在jni.h中進行定義;
-- JNI方法 : Native方法對應的JNI層實現的 C/C++方法, 即在jni目錄中實現的那些C語言程式碼;
具體流程可以參照ndk的sample目錄下的hellojni專案,總結起來有如下幾個步驟 1、建立常規Android專案 2、宣告Native方法,形如public native String stringFromJNI(); 3、建立c檔案 在工程根目錄下建立 jni 目錄, 然後建立一個c語言原始檔, 在檔案中引入 include <jni.h>。c語言方法宣告形如
  1. jstring  
  2. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  
  3.                                                   jobject thiz )  
jstring 是 Java語言中的String型別, 方法名格式為 : Java_完整包名類名_方法名();
-- JNIEnv引數 : 代表的是Java環境, 通過這個環境可以呼叫Java裡面的方法;
-- jobject引數 : 呼叫C語言方法的物件, thiz物件表示當前的物件, 即呼叫JNI方法所在的類;
4、編寫Android.mk檔案 就好比常用的makefile,形如
  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. LOCAL_MODULE    := hello-jni  
  4. LOCAL_SRC_FILES := hello-jni.c  
  5. include $(BUILD_SHARED_LIBRARY)  
-- LOCAL_PATH : 代表mk檔案所在的目錄;
-- include $(CLEAR_VARS) : 編譯工具函式, 通過該函式可以進行一些初始化操作;
-- LOCAL_MODULE : 編譯後的 .so 字尾檔案叫什麼名字;
-- LOCAL_SRC_FILES: 指定編譯的原始檔名稱;
-- include $(BUILD_SHARED_LIBRARY) : 告訴編譯器需要生成動態庫;
5、NDK編譯生成動態庫 利用上面寫好的.mk檔案,進入 cygdrive 找到windows目錄下對應的檔案, 編譯完成之後, 會自動生成so檔案並放在libs目錄下, 之後就可以在Java中呼叫C語言方法了;
6、java中載入動態庫 在Java類中的靜態程式碼塊中使用System.LoadLibrary()方法載入編譯好的 .so 動態庫,形如
  1. static {  
  2.         System.loadLibrary("hello-jni");  
  3.     }  
需要說明的是,除錯JNI程式非常麻煩,無法進入c檔案中進行單步除錯,只能通過輸出一些資訊然後利用logcat進行除錯,所以也可以先在vs裡面把c檔案的內容跑一遍簡單驗證一下。 參照上面的基本流程,具體到利用ffmpeg實現安卓攝像頭資料的編碼 第一步:我們宣告如下四個Native方法
  1. //JNI
  2. //初始化,讀取待編碼資料的寬和高
  3.     publicnativeint initial(int width,int height);  
  4. //讀取yuv資料進行編碼
  5.     publicnativeint encode(byte[] yuvimage);  
  6. //清空快取的幀
  7.     publicnativeint flush();  
  8. //清理
  9.     publicnativeint close();  

第二步:對應的c檔案內容如下,基本就是一個將yuv資料編碼為H.264的flv檔案的流程,唯一需要注意的就是安卓攝像頭拍攝資料的畫素格式為NV21,需要轉換為YUV420P才能進行編碼
  1. /** 
  2.  * 基於FFmpeg安卓攝像頭編碼 
  3.  * FFmpeg Android Camera Encoder 
  4.  * 
  5.  * 張暉 Hui Zhang 
  6.  * [email protected] 
  7.  * 中國傳媒大學/數字電視技術 
  8.  * Communication University of China / Digital TV Technology 
  9.  * 
  10.  * 
  11.  */
  12. #include <stdio.h>
  13. #include <time.h> 
  14. #include "libavcodec/avcodec.h"
  15. #include "libavformat/avformat.h"
  16. #include "libswscale/swscale.h"
  17. #include "libavutil/log.h"
  18. #ifdef ANDROID
  19. #include <jni.h>
  20. #include <android/log.h>
  21. #define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
  22. #define LOGI(format, ...)  __android_log_print(ANDROID_LOG_INFO,  "(=_=)", format, ##__VA_ARGS__)
  23. #else
  24. #define LOGE(format, ...)  printf("(>_<) " format "\n", ##__VA_ARGS__)
  25. #define LOGI(format, ...)  printf("(^_^) " format "\n", ##__VA_ARGS__)
  26. #endif
  27. AVFormatContext *ofmt_ctx;  
  28. AVStream* video_st;  
  29. AVCodecContext* pCodecCtx;  
  30. AVCodec* pCodec;  
  31. AVPacket enc_pkt;  
  32. AVFrame *pFrameYUV;  
  33. int framecnt = 0;  
  34. int yuv_width;  
  35. int yuv_height;  
  36. int y_length;  
  37. int uv_length;  
  38. int64_t start_time;  
  39. //Output FFmpeg's av_log()
  40. void custom_log(void *ptr, int level, constchar* fmt, va_list vl){  
  41.     FILE *fp=fopen("/storage/emulated/0/av_log.txt","a+");  
  42.     if(fp){  
  43.         vfprintf(fp,fmt,vl);  
  44.         fflush(fp);  
  45.         fclose(fp);  
  46.     }  
  47. }  
  48. JNIEXPORT jint JNICALL Java_com_zhanghui_test_MainActivity_initial  
  49.   (JNIEnv *env, jobject obj,jint width,jint height)  
  50. {  
  51.     constchar* out_path = "/sdcard/zhanghui/testffmpeg.flv";  
  52.     yuv_width=width;  
  53.     yuv_height=height;  
  54.     y_length=width*height;  
  55.     uv_length=width*height/4;  
  56.     //FFmpeg av_log() callback
  57.     av_log_set_callback(custom_log);  
  58.     av_register_all();  
  59.     //output initialize
  60. 相關推薦

    ffmpeg詳細綜合教程——手機攝像頭編碼

    本文的示例將實現:讀取安卓手機攝像頭資料並使用H.264編碼格式實時編碼儲存為flv檔案。示例包含了 1、編譯適用於安卓平臺的ffmpeg庫 2、在java中通過JNI使用ffmpeg 3、讀取安卓攝像頭資料並在後臺執行緒中使用ffmpeg進行編碼的

    Android 開發:常用控制元件以及仿《微門戶》登入介面實現

    一、常用控制元件: 1、文字類控制元件 TextView 負責展示文字,非編輯 EditText 可編輯文字控制元件 2、按鈕類控制元件 Button 按鈕 ImageButton 圖片按鈕 RadioButton與RadioGroup 單

    SVNTortoiseSVN詳細教程--SVN更新及如何解決衝突檔案

    一. SVN更新(SVN Update)及如何解決衝突檔案: 1. SVN update: 更新原生代碼與SVN伺服器上最新的版本一致,只要在需要更新的資料夾上點選右鍵或者在檔案下空白處點選右鍵,選擇”SVN Update” (獲取指定版本中

    Spring入門詳細教程

    前言 本篇緊接著spring入門詳細教程(二),建議閱讀本篇前,先閱讀第一篇和第二篇。連結如下: Spring入門詳細教程(一) https://www.cnblogs.com/jichi/p/10165538.html Spring入門詳細教程(二) https://www.cnblogs.

    基於FFmpeg的視訊播放器開發系列教程

       本篇開始講解音訊解碼播放,該專案用Qt的音訊類QAudioFormat, QAudioOutput等進行解碼,先講解一些關於音訊的知識。 1.取樣頻率   指每秒鐘取得聲音樣本的次數。取樣的過程就是抽取某點的頻率值,很顯然,在一秒中內抽取的點越多,獲取得頻率資

    搭建 rabc 後臺系列教程—— rbac 詳細操作 [ 2.0 版本 ]

    1、下載 left.php 覆蓋 backend/views/layouts/left.php 進入 127.0.0.1/advanced/backend/web/ 預覽效果 2、點選“路由”,將下圖中的幾個路由移動到右邊 檢視 auth_item 資料表,發現新增了幾行我們剛剛新增的記錄 3、點選

    FFmpeg教程視訊解碼器

    視訊解碼知識 純淨的視訊解碼流程       壓縮編碼資料->畫素資料。       例如解碼H.264,就是“H.264碼流->YUV”。 一般的視訊解碼流程        視訊碼流一

    Spring 新手教程 註入和自己主動裝配

    文件 auto 進行 上下文 xxx -s 重要 mls 上下 Spring註入是指在啟動Spring容器載入bean配置的時候。對類變量的賦值。 兩種經常使用註入方式:設值註入和構造註入 以下就這部分知識看代碼以及代碼中的註解: 1、首先看S

    學習 MeteoInfo二次開發教程

    開發教程 cnblogs false raw class .dll dll inf legend 1.breakList的問題 ((PolygonBreak) aLS.breakList[0]).DrawFill=false; 新的類庫將LegendScheme的brea

    CodeArt入門教程

    pan center account 根據 領域對象 保存 顏色 單一職責原則 用例 5.領域模型設計   下面我們創建賬戶子系統(AccountSubsystem),賬戶子系統雖然被門戶服務使用,但是子系統本身是獨立於任何服務存在的。所以我們為賬戶子系統創建獨立的項目解決

    Dapper入門教程——Dapper Query查詢

    int32 method ppi ransac try spa can () raw 介紹 查詢方法(Query)是IDbConnection的擴展方法,它可以用來執行查詢(select)並映射結果到C#實體(Model、Entity)類 查詢結果可以映射成如下類型: A

    微信公眾平臺開發教程 基礎框架搭建

    開發 images wxs user 設計實現 bytes trre 來源 app 上一章,我們已經初步講解了微信公眾賬號開發的基本原理,今天我們來探索設計實現。 首先我們設計了模塊層次圖,當然圖中只是給出一種實現方式,不局限於此。具體見下圖。 主要功能介紹如下: 1)請求

    Git 教程:倉庫與分支

    ide 不但 clas version span 右上角 director discard pre 遠程倉庫 到目前為止,我們已經掌握了如何在Git倉庫裏對一個文件進行時光穿梭,你再也不用擔心文件備份或者丟失的問題了。 可是有用過集中式版本控制系統SVN的童鞋會站出來說,這

    python基礎教程

    new gre multi mar tro ext blog 信息 無法 序列概覽   Python 包含6 種內建的序列,這裏重點討論最常用的兩種類型:列表和元組。   列表與元組的主要區別在於,列表可以修改,元組則不能。也就是說如果要根據要求來添加元素,那麽列表可以會更

    Spring Boot參考教程內部應用監控Actuator

    使用方式 實現類 pat igp pid localhost aid 倉庫 默認 3. 內部應用監控(Actuator) 如上2.4中所述,傳統spring工程中工程的初始化過程,bean的生命周期,應用的內部健康情況均無法監控,為了解決這個問題,spring boot提供

    Python入門教程

    循環 三個參數 normal 編程 list bili lan 函數返回 容易   按理說,這篇是收尾了。可能有一點術語,但大都是顧名思義的。重要概念【類】在第二個標題下說明。函數式編程(縮寫:FP)  如果對此有興趣,可以看scheme視頻教程(SICP公開課)(sche

    企業級 SpringCloud 教程 服務消費者Feign

    pom https www. ram cat -h 客戶端 tin script 上一篇文章,講述了如何通過RestTemplate+Ribbon去消費服務,這篇文章主要講述如何通過Feign去消費服務。一、Feign簡介 Feign是一個聲明式的偽Http客戶端,它使得寫

    Spring Cloud Commons教程忽略網絡接口

    ech 什麽 targe 地址 分享圖片 ans str log list 有時,忽略某些命名網絡接口是有用的,因此可以將其從服務發現註冊中排除(例如,在Docker容器中運行)。可以設置正則表達式的列表,這將導致所需的網絡接口被忽略。以下配置將忽略“docker0”接口

    Spring Cloud Stream教程持續發布 - 訂閱支持

    點對點 cst 服務 均值 而不是 兩個 搭建 另一個 進行 應用之間的通信遵循發布訂閱模式,其中通過共享主題廣播數據。這可以在下圖中看到,它顯示了一組交互式的Spring Cloud Stream應用程序的典型部署。 SCSt傳感器圖6. Spring Cloud Str

    Golang入門教程beego 快速開發 HTTP 框架

    應用 inf ado .com home clas lan mime iyu   beego 是一個快速開發 Go 應用的 HTTP 框架,他可以用來快速開發 API、Web 及後端服務等各種應用,是一個 RESTful 的框架,主要設計靈感來源於 tornado、sina