android Camera(二): NV21高效處理與libyuv使用

繼上篇Camera數(shù)據(jù)采集適配時會涉及到的YUV數(shù)據(jù)的剪切與方向感應(yīng)時的旋轉(zhuǎn)處理(對YUV數(shù)據(jù)不懂的可以網(wǎng)上搜索相關(guān)的文章),后面還有MediaCodec硬編碼時,不同的手機(jī)可能還需要將NV21數(shù)據(jù)轉(zhuǎn)換成NV12,I420或者YV12,這里就講述NV21與yuv中另外三種NV12,I420,YV12的相互轉(zhuǎn)換,剪切,旋轉(zhuǎn)操作

android Camera(一):采集與預(yù)覽尺寸適配
1:對這幾種格式的轉(zhuǎn)換,網(wǎng)上博客的寫法也是很多, 單單的格式轉(zhuǎn)換,或者單單旋轉(zhuǎn)角度鏡像旋轉(zhuǎn),或者單單剪切都還算比較簡單的,但是如果要將YUV數(shù)據(jù)即要轉(zhuǎn)換同時又要旋轉(zhuǎn)角度鏡像旋轉(zhuǎn),又要裁剪的話就是非常復(fù)雜的算法了

\color{red}{如果分三部操作,先剪切,再旋轉(zhuǎn),再轉(zhuǎn)換會三次循環(huán)遍歷YUV數(shù)據(jù)非常慢 (可以說看到這種分多部操作的就一定行不通)}
測試一個1280*720大小的YUV數(shù)據(jù)在旋轉(zhuǎn)操作時可能就要20ms的時間(OPPO A57測試,高性能手機(jī)會非常快),如果分三部操作時就大概需要60ms的時間了, 對于一個最普通的按25幀率的采集來算,平均一秒 1000 / 25 = 40平均每40毫秒就采集一幀, 光轉(zhuǎn)換旋轉(zhuǎn)操作就超過了幀的時間,更何況MediaCodec編碼也是要耗時的.
\color{red}{因此對于這種情況就需要高效率的算法,盡量在一次遍歷YUV數(shù)據(jù)時就完成剪切,旋轉(zhuǎn),并轉(zhuǎn)換同時進(jìn)行的操作}

2:libyuv的使用

其實(shí)對于這種高效率的算法, Google已經(jīng)開源了一個叫做libyuv的庫(C++)專門用于YUV數(shù)據(jù)的處理,里邊提供了很多YUV數(shù)據(jù)類型之間的轉(zhuǎn)換,旋轉(zhuǎn),鏡像,剪切算法.
這里也有個疑問,libyuv是個高效率的算法鹉勒,為何里邊找不到nv12或者nv21的旋轉(zhuǎn)與鏡像操作方法(有懂的請不吝賜教),只能根據(jù)libyuv中的NV12與I420之間的旋轉(zhuǎn)源碼算法另外寫了一個nv12的旋轉(zhuǎn)鏡像操作代碼, 提供nv12或者nv21所有相關(guān)方法(nv12與nv21算法大致都相似,只是UV與VU的區(qū)別)

源碼入口,nv21與nv12,yuv12,i420之間的轉(zhuǎn)換,裁剪,旋轉(zhuǎn),鏡像并同時進(jìn)行的操作https://github.com/youxiaochen/CameraMedia

直接上代碼

RotateNV_UV.h,提供NV21(NV12)的UV旋轉(zhuǎn)鏡像操作方法, 所有YUV類型中Y數(shù)據(jù)的操作算法都是一樣的

/**
 * Created by you on 2018-03-06. NV21,NV12之間的UV旋轉(zhuǎn)操作
 * NV21與NV12區(qū)別只是UV與VU交差的區(qū)別,因此以下方法NV21,NV12可通用
 * 所有_X的交叉即可用作NV12與NV21之間的UV轉(zhuǎn)換
 */

#ifndef CAMERAMEDIACODEC_ROTATENV_UV_H
#define CAMERAMEDIACODEC_ROTATENV_UV_H

#include <libyuv.h>

/**
 * 適合NV21或者NV12的UV數(shù)據(jù)的操作
 */
void TransposeWxH_C2(const uint16_t* src,
                     int src_stride,
                     uint16_t* dst,
                     int dst_stride,
                     int width,
                     int height);

/**
 * 適合NV21或者NV12的UV數(shù)據(jù)的操作,并轉(zhuǎn)換UV順序
 */
void TransposeWxH_C2_X(const uint16_t* src,
                     int src_stride,
                     uint16_t* dst,
                     int dst_stride,
                     int width,
                     int height);

/**
 * 鏡面翻轉(zhuǎn)uint8_t類型的數(shù)據(jù),并交叉變換一個步長的順序,適合UV與VU之間的鏡面轉(zhuǎn)換, 與源碼稍作修改
 */
void MirrorRow_C_X(const uint8_t* src,
                   uint8_t* dst,
                   int width);

/**
 * 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)90
 * @param width UV數(shù)據(jù)的寬
 * @param height UV數(shù)據(jù)的高
 */
void RotateNV_UV90(const uint16_t* src,
                int src_stride,
                uint16_t* dst,
                int dst_stride,
                int width,
                int height);

/**
 * 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)270
 * @param width UV數(shù)據(jù)的寬
 * @param height UV數(shù)據(jù)的高
 */
void RotateNV_UV270(const uint16_t* src,
                int src_stride,
                uint16_t* dst,
                int dst_stride,
                int width,
                int height);

/**
 * 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)180
 */
void RotateNV_UV180(const uint8_t* src,
                    int src_stride,
                    uint8_t* dst,
                    int dst_stride,
                    int width,
                    int height);

/**
 * 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)90,并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
 * @param width UV數(shù)據(jù)的寬
 * @param height UV數(shù)據(jù)的高
 */
void RotateNV_UV90_X(const uint16_t* src,
                   int src_stride,
                   uint16_t* dst,
                   int dst_stride,
                   int width,
                   int height);

/**
 * 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)270, 并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
 * @param width UV數(shù)據(jù)的寬
 * @param height UV數(shù)據(jù)的高
 */
void RotateNV_UV270_X(const uint16_t* src,
                    int src_stride,
                    uint16_t* dst,
                    int dst_stride,
                    int width,
                    int height);

/**
 * 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)180, 并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
 */
void RotateNV_UV180_X(const uint8_t* src,
                      int src_stride,
                      uint8_t* dst,
                      int dst_stride,
                      int width,
                      int height);

/**
 * NV21的數(shù)據(jù)拷貝
 */
void NV21Copy(const uint8_t* src_y,
              int src_stride_y,
              const uint8_t* src_vu,
              int src_stride_vu,
              uint8_t* dst_y,
              int dst_stride_y,
              uint8_t* dst_vu,
              int dst_stride_vu,
              int width,
              int height);

/**
 * NV21旋轉(zhuǎn)
 */
void NV21Rotate(const uint8_t* src_y,
                int src_stride_y,
                const uint8_t* src_vu,
                int src_stride_vu,
                uint8_t* dst_y,
                int dst_stride_y,
                uint8_t* dst_vu,
                int dst_stride_vu,
                int width,
                int height,
                enum libyuv::RotationMode mode);

#endif //CAMERAMEDIACODEC_ROTATENV_UV_H
ConvertNV.h 這里是NV12同時旋轉(zhuǎn),轉(zhuǎn)換與裁剪的相關(guān)操作

/**
 * Created by you on 2018-09-06. NV21,NV12之間的UV剪切并旋轉(zhuǎn)操作
 * NV21與NV12區(qū)別只是UV與VU交差的區(qū)別,因此以下方法NV21,NV12可通用
 */
#include "RotateNV_UV.h"

#ifndef CAMERAMEDIACODEC_CONVERTNV_H
#define CAMERAMEDIACODEC_CONVERTNV_H

/**
 * 同時剪切NV21數(shù)據(jù)并旋轉(zhuǎn)
 */
void ConvertNV21(const uint8_t* sample,
                 uint8_t* dst_y,
                 int dst_stride_y,
                 uint8_t* dst_vu,
                 int dst_stride_vu,
                 int crop_x,
                 int crop_y,
                 int src_width,
                 int src_height,
                 int crop_width,
                 int crop_height,
                 libyuv::RotationMode mode);

/**
 * 同時NV21轉(zhuǎn)NV12并旋轉(zhuǎn)
 */
void NV21ToNV12Rotate(const uint8_t* src_y,
                      int src_stride_y,
                      const uint8_t* src_uv,
                      int src_stride_uv,
                      uint8_t* dst_y,
                      int dst_stride_y,
                      uint8_t* dst_uv,
                      int dst_stride_uv,
                      int width,
                      int height,
                      libyuv::RotationMode mode);

/**
 * 同時裁剪NV21轉(zhuǎn)NV12并旋轉(zhuǎn)
 */
void ConvertNV21ToNV12(const uint8_t* sample,
                       uint8_t* dst_y,
                       int dst_stride_y,
                       uint8_t* dst_uv,
                       int dst_stride_uv,
                       int crop_x,
                       int crop_y,
                       int src_width,
                       int src_height,
                       int crop_width,
                       int crop_height,
                       libyuv::RotationMode mode);

#endif //CAMERAMEDIACODEC_CONVERTNV_H
最后是JAVA層調(diào)用的代碼
/**
 * Created by you on 2018-03-12.
 * 圖像數(shù)據(jù)nv21,i420, nv12, yv12的一些轉(zhuǎn)換裁剪旋轉(zhuǎn)的相關(guān)操作
 */
public final class YuvUtils {

    private YuvUtils() {}

    static {
        System.loadLibrary("yuv-utils");
    }

    /**
     * 同時將NV21數(shù)據(jù)轉(zhuǎn)換成I420并旋轉(zhuǎn), 不剪切
     * @param nv21 camera datas
     * @param i420 out datas
     * @param w
     * @param h
     * @param orientation 旋轉(zhuǎn)角度
     */
    public static native void nv21ToI420Rotate(byte[] nv21,
                                               byte[] i420,
                                               int w, int h,
                                               @Orientation.OrientationMode int orientation);

    /**
     * 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成I420并旋轉(zhuǎn)
     * @param nv21
     * @param i420
     * @param w 相機(jī)原尺寸
     * @param h
     * @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
     * @param ch 同上  <= h
     * @param left
     * @param top
     * @param orientation 旋轉(zhuǎn)角度
     */
    public static native void clipNv21ToI420Rotate(byte[] nv21,
                                                   byte[] i420,
                                                   int w, int h,
                                                   int cw, int ch,
                                                   int left, int top,
                                                   @Orientation.OrientationMode int orientation);

    /**
     * 同時將NV21數(shù)據(jù)轉(zhuǎn)換成NV12并旋轉(zhuǎn), 不剪切
     * @param nv21 camera datas
     * @param nv12 out datas
     * @param w
     * @param h
     * @param orientation 旋轉(zhuǎn)角度
     */
    public static native void nv21ToNV12Rotate(byte[] nv21,
                                               byte[] nv12,
                                               int w, int h,
                                               @Orientation.OrientationMode int orientation);

    /**
     * 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成NV12并旋轉(zhuǎn)
     * @param nv21
     * @param nv12
     * @param w 相機(jī)原尺寸
     * @param h
     * @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w, h
     * @param ch 同上
     * @param left
     * @param top
     * @param orientation  旋轉(zhuǎn)角度
     */
    public static native void clipNv21ToNV12Rotate(byte[] nv21,
                                                   byte[] nv12,
                                                   int w, int h,
                                                   int cw, int ch,
                                                   int left, int top,
                                                   @Orientation.OrientationMode int orientation);

    /**
     * 同時將NV21數(shù)據(jù)轉(zhuǎn)換成YV12并旋轉(zhuǎn), 不剪切
     * @param nv21 camera datas
     * @param yv12 out datas
     * @param w
     * @param h
     * @param orientation  旋轉(zhuǎn)角度
     */
    public static native void nv21ToYV12Rotate(byte[] nv21,
                                               byte[] yv12,
                                               int w, int h,
                                               @Orientation.OrientationMode int orientation);

    /**
     * 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成YV12并旋轉(zhuǎn)
     * @param nv21
     * @param yv12
     * @param w 相機(jī)原尺寸
     * @param h
     * @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
     * @param ch 同上  <= h
     * @param left
     * @param top
     * @param orientation  旋轉(zhuǎn)角度
     */
    public static native void clipNv21ToYV12Rotate(byte[] nv21,
                                                   byte[] yv12,
                                                   int w, int h,
                                                   int cw, int ch,
                                                   int left, int top,
                                                   @Orientation.OrientationMode int orientation);

    /**
     * 將NV21數(shù)據(jù)旋轉(zhuǎn), 不剪切
     * @param nv21 camera datas
     * @param outs out datas
     * @param w
     * @param h
     * @param orientation  旋轉(zhuǎn)角度
     */
    public static native void nv21Rotate(byte[] nv21,
                                         byte[] outs,
                                         int w, int h,
                                         @Orientation.OrientationMode int orientation);

    /**
     * 同時剪切NV21數(shù)據(jù)并旋轉(zhuǎn)
     * @param nv21
     * @param outs
     * @param w 相機(jī)原尺寸
     * @param h
     * @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
     * @param ch 同上  <= h
     * @param left
     * @param top
     * @param orientation  旋轉(zhuǎn)角度
     */
    public static native void clipNv21Rotate(byte[] nv21,
                                             byte[] outs,
                                             int w, int h,
                                             int cw, int ch,
                                             int left, int top,
                                             @Orientation.OrientationMode int orientation);

}

C++實(shí)現(xiàn)和使用方法示例代碼中都有,這里不再過多介紹
附上源碼https://github.com/youxiaochen/CameraMedia
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帆锋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子禽额,更是在濱河造成了極大的恐慌锯厢,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脯倒,死亡現(xiàn)場離奇詭異实辑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)藻丢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門剪撬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悠反,你說我怎么就攤上這事残黑。” “怎么了斋否?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵梨水,是天一觀的道長。 經(jīng)常有香客問我茵臭,道長疫诽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮奇徒,結(jié)果婚禮上雏亚,老公的妹妹穿的比我還像新娘。我一直安慰自己摩钙,他們只是感情好罢低,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胖笛,像睡著了一般奕短。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匀钧,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音谬返,去河邊找鬼之斯。 笑死,一個胖子當(dāng)著我的面吹牛遣铝,可吹牛的內(nèi)容都是我干的佑刷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼酿炸,長吁一口氣:“原來是場噩夢啊……” “哼瘫絮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起填硕,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤麦萤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扁眯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壮莹,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年姻檀,在試婚紗的時候發(fā)現(xiàn)自己被綠了命满。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡绣版,死狀恐怖胶台,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杂抽,我是刑警寧澤诈唬,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站默怨,受9級特大地震影響讯榕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一愚屁、第九天 我趴在偏房一處隱蔽的房頂上張望济竹。 院中可真熱鬧,春花似錦霎槐、人聲如沸送浊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袭景。三九已至,卻和暖如春闭树,著一層夾襖步出監(jiān)牢的瞬間耸棒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工报辱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留与殃,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓碍现,卻偏偏與公主長得像幅疼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子昼接,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

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