Android 利用V4L2 預覽MJPEG格式 USB camera

介紹

上一篇文章Android 利用V4L2 調(diào)用camera介紹了使用V4L2 接口預覽camera的基本方法。目前接觸過的usb camera支持的圖像格式基本上只包括3種:

  • YUV
  • MJPEG
  • H264
    其中YUV是原始數(shù)據(jù),MJPEG和H264都是壓縮編碼的數(shù)據(jù)衰猛。所以對于MJPEG和H264需要先解碼為原始圖像數(shù)據(jù),才能給到android顯示霸琴。這篇文章主要介紹對MJPEG數(shù)據(jù)的處理

準備

先看一下此次修改的效果圖


GIF 2022-1-18 15-35-09.gif
  • UI
    讓用戶自己選擇對應的格式和分辨率
  • 解碼
    一般可采用的有opencv,ffmpeg,libyuv严卖。此次采用libyuv烟具,libyuv是一個谷歌的開源項目梢什,跨平臺,處理速度很快朝聋。針對此次MJPEG處理是比較合適的嗡午。libyuv解碼MJPEG需要用到libjpeg

camera使用的基本流程在文章Android 利用V4L2 調(diào)用camera已經(jīng)介紹過,

正常的流程主要是以下幾步

  1. SurfaceView創(chuàng)建
  2. SurfaceView創(chuàng)建成功回調(diào)
  3. 打開camera
  4. camera打開成功回調(diào)
  5. 獲取camera參數(shù)
  6. 彈框用戶選擇對應分辨率
  7. 設置對應pixformat和分辨率
  8. 開始預覽
  9. 獲取到MJPEG數(shù)據(jù)后冀痕,利用libyuv解碼
  10. Android nativieWindow顯示

此次大體流程沒有變化荔睹,著重介紹修改的地方

讓用戶選擇預覽圖像格式和分辨率

在camera打開成功的回調(diào)種,即 CameraStateCallback 的回調(diào)onOpened 獲取usb camera支持的參數(shù)金度,camera參數(shù)格式可以參見文章Android 利用V4L2 調(diào)用camera中的獲取camera的參數(shù)

 class CameraStateCallback implements IStateCallback {

        @Override
        public void onOpened() {
            Log.d(TAG, "onOpened");

            parameters = adCamera.getCameraParameters();

            pixformats = new String[parameters.size()];
            resolutions = new String[parameters.size()][];

            String sPixFormat;
            for (int i = 0; i < parameters.size(); i++) {
                Log.e(TAG, "format:" + parameters.get(i).pixFormat);
                switch (parameters.get(i).pixFormat) {
                    case YUYV:
                        sPixFormat = "YUYV";
                        break;
                    case MJPEG:
                        sPixFormat = "MJPEG";
                        break;
                    case H264:
                        sPixFormat = "H264";
                        break;
                    default:
                        sPixFormat = "UNKNOW";
                        break;
                }

                pixformats[i] = sPixFormat;

                int resolutionSize = parameters.get(i).frames.size();

                resolutions[i] = new String[resolutionSize];

                for (int j = 0; j < resolutionSize; j++) {
                    String resolution = parameters.get(i).frames.get(j).width + "*" + parameters.get(i).frames.get(j).height;
                    resolutions[i][j] = resolution;
                }
            }

            showDialog();
        }

將獲取到的參數(shù)解析到以下兩個數(shù)組中

    String[] pixformats;
    String[][] resolutions;

dialog采用加載ExpandableListView应媚,這里就不詳細介紹。
點擊確認后猜极,設置預覽參數(shù)并開始預覽

ret = adCamera.setPreviewParameter(previewWidth, previewHeight, parameters.get(pixClick).pixFormat);

surfaceView.setAspectRatio(previewWidth, previewHeight);
...
adCamera.setSurface(surfaceHolder);
...
cameraDataCallback = new CameraDataCallback();
adCamera.startPreview(cameraDataCallback);

libjpeg庫的編譯移植與使用

這里使用AS編譯libJPEG-turbo源碼

  • 新建Android工程libjpeg中姜,并將libjpeg-turbo源碼全部拷貝到src/main/cpp目錄下


    libjpeg.png
  • 修改Android工程的build.gradle,配置libjpeg-turbo的CmakeLists.txt

defaultConfig {
        applicationId "com.test.libjpeg"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        externalNativeBuild {
            cmake {
                cppFlags ""
                // 配置編譯的平臺版本
                abiFilters "armeabi-v7a", "arm64-v8a"
            }
        }

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    externalNativeBuild {
        cmake {
            path "src\\main\\cpp\\CMakeLists.txt"http://路徑改為cpp文件夾下CMakeList的路徑
        }

    }
  • 編譯工程跟伏,生成對應的so和.h 文件
    編譯過程很順利丢胚,沒碰到什么問題。倒是從網(wǎng)上下載下來的總是有編譯錯誤受扳,所以這里就不提供工程了携龟。

將libjpeg 生成的so和h文件添加到AnV4L2Camera工程中

  • 在v4l2camera模塊cpp文件下新建libjpeg文件夾,將libjpeg幾個相關頭文件拷貝到該目錄下


    includelibjpeg.png

其中 jconfig.h 在工程libjpeg以下目錄

./app/.cxx/cmake/debug/arm64-v8a/jconfig.h
./app/.cxx/cmake/debug/armeabi-v7a/jconfig.h

其他頭文件都在在工程libjpeg以下目錄

./app/src/main/cpp/
  • 在 v4l2camera模塊新建文件夾src\main\jniLibs勘高,將libjpeg.so拷貝到該文件夾下


    libjpegso.png
  • 修改v4l2camera模塊 CMakeLists.txt

 # 指定libjpeg頭文件的路徑
include_directories(include libjpeg)

# 指定libjpeg動態(tài)庫路徑
set(jpeglibs "${CMAKE_SOURCE_DIR}/../jniLibs")

# 導入第三方庫:libjpeg.so
add_library(libjpeg SHARED IMPORTED)
set_target_properties(libjpeg PROPERTIES
        IMPORTED_LOCATION "${jpeglibs}/${ANDROID_ABI}/libjpeg.so")

target_link_libraries( # Specifies the target library.
        v4l-android
        android
        libjpeg

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

libyuv移植

這里采用直接將libyuv源碼導入到AnV4L2Camera工程中

  • 下載libyuv https://chromium.googlesource.com/libyuv/libyuv
  • 將 libyuv 源碼 include 目錄下的 libyuv 目錄下的頭文件和 libyuv.h 一起拷貝到 v4l2camera模塊下的 src\main\cpp\include目錄
  • 將 libyuv 源碼 source 目錄下的全部文件拷貝到 v4l2camera模塊新建文件夾src\main\cpp\libyuv
libyuv.png
  • 修改v4l2camera模塊 CMakeLists.txt
# 導入libyuv頭文件路徑
include_directories(include libjpeg)

# 打開宏HAVE_JPEG峡蟋,libyuv才會去編譯和使用libjpeg
add_definitions(-DHAVE_JPEG)

# 導入libyuv 源文件路徑
file(GLOB src_files *.cpp  libyuv/source/*.cc)

libyuv解碼MJPEG

        int src_width = 0;
        int src_height = 0;

        libyuv::MJPGSize(raw, rSize, &src_width, &src_height);

        //經(jīng)圖片保存坟桅,16進制查看保存的改格式為   64 82 35 ff    --   B G R A
        //stride 跨距, 它描述一行像素中, 該顏色分量所占的 byte 數(shù)目
        libyuv::MJPGToARGB(raw, rSize, preview, width * 4, src_width,
                                 src_height, width, height);

#ifdef SAVE_JPEG
        if (!WRITE_FILE) {
            const char *path = "/sdcard/argb.bmp"; // 路徑
            bmp_write(preview, width, height, path);
            WRITE_FILE = true;
        }
#endif

        //WINDOW_FORMAT_RGBA_8888  排列順序為 R G B A
        unsigned char temp;
        for (int i = 0; i < width * height * 4; i = i + 4) {
            temp = preview[i+2];
            preview[i+2] = preview[i];
            preview[i] = temp;
        }

raw是通過v4l2獲取到的mjpeg格式數(shù)據(jù),主要通過libyuv::MJPGToARGB將數(shù)據(jù)轉(zhuǎn)換成rgba數(shù)據(jù)蕊蝗。 通過將轉(zhuǎn)換后的數(shù)據(jù)保存成bmp仅乓,用hex格式打開發(fā)現(xiàn),數(shù)據(jù)保存的格式為BGRA蓬戚,這個可能windows上或bmp格式的數(shù)據(jù)就是用這種方式保存的夸楣,屬于little endian。 android上WINDOW_FORMAT_RGBA_8888 排列順序為 RGBA子漩,所以還需要做下轉(zhuǎn)換豫喧,顏色才能正常。

相關代碼已經(jīng)更新到demo https://github.com/yizhongliu/AnV4L2Camera

參考
https://blog.csdn.net/AndrExpert/article/details/100123845
https://blog.csdn.net/tyyj90/article/details/120294921

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幢泼,一起剝皮案震驚了整個濱河市紧显,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旭绒,老刑警劉巖鸟妙,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挥吵,居然都是意外死亡重父,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門忽匈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來房午,“玉大人,你說我怎么就攤上這事丹允」幔” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵雕蔽,是天一觀的道長折柠。 經(jīng)常有香客問我,道長批狐,這世上最難降的妖魔是什么扇售? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮嚣艇,結(jié)果婚禮上承冰,老公的妹妹穿的比我還像新娘。我一直安慰自己食零,他們只是感情好困乒,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贰谣,像睡著了一般娜搂。 火紅的嫁衣襯著肌膚如雪迁霎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天百宇,我揣著相機與錄音欧引,去河邊找鬼。 笑死恳谎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的憋肖。 我是一名探鬼主播因痛,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岸更!你這毒婦竟也來了鸵膏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤怎炊,失蹤者是張志新(化名)和其女友劉穎谭企,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體评肆,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡债查,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓜挽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盹廷。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖久橙,靈堂內(nèi)的尸體忽然破棺而出俄占,到底是詐尸還是另有隱情,我是刑警寧澤淆衷,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布缸榄,位于F島的核電站,受9級特大地震影響祝拯,放射性物質(zhì)發(fā)生泄漏甚带。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一鹿驼、第九天 我趴在偏房一處隱蔽的房頂上張望欲低。 院中可真熱鬧,春花似錦畜晰、人聲如沸砾莱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腊瑟。三九已至聚假,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闰非,已是汗流浹背膘格。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留财松,地道東北人瘪贱。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像辆毡,于是被迫代替她去往敵國和親菜秦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

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