9.CameraX采集數(shù)據(jù)h264和faac推流

CameraX采集數(shù)據(jù)生成 YUV_420_888格式

通過分析接口得到ImageProxy 然后得到planes數(shù)組

 @Override
public void analyze(ImageProxy image, int rotationDegrees) { 
  int width = image.getWidth();
  int height = image.getHeight();
  //格式 YUV/RGB...
  int format = image.getFormat();
  //圖像數(shù)據(jù)
  ImageProxy.PlaneProxy[] planes = image.getPlanes();
  //數(shù)組3個元素  Y U V
}

I420的排列 YYYY YYYY YYYY YYYY UUUU VVVV

對于U和V數(shù)據(jù) 排列方式可能有兩種

1. planes[1] = {uuuu...}  planes[2] = {vvvv...}  

2. planes[1] = {uvuv...}  planes[2] = {vuvu...}

通過 int pixelstride = plane.getPixelStride()獲取返回值 0 表示上述第一種情況 返回值1 表示上述第二種情況

YUV數(shù)據(jù)獲取 需要考慮RowStride 步長問題(字節(jié)對齊)

Y數(shù)據(jù)

1) 若RowStride = width  直接通過planes[0].getBuffer獲取Y數(shù)據(jù)
2) 若RowStride > width  如4*4的I420每行以8字節(jié)對齊  還需要考慮最后一行無占位數(shù)據(jù)
//用于保存獲取的I420數(shù)據(jù)贴妻。大小為:y+u+v, width*height + width/2*height/2 + width/2*height/2 ByteBuffer yuv420 = 
ByteBuffer.allocate(image.getWidth() * image.getHeight() * 3 / 2);

int rowStride = plane.getRowStride();
ByteBuffer buffer = plane.getBuffer();
byte[] row = new byte[image.getWidth()];

// 每行要排除的無效數(shù)據(jù),但是需要注意:實際測試中 最后一行沒有這個補(bǔ)位數(shù)據(jù) 
// 因為Y數(shù)據(jù) RowStride 為大于等于Width烛谊,所以不會出現(xiàn)負(fù)數(shù)導(dǎo)致錯誤
// RowStride 等于Width,則得到空數(shù)組勘畔,不丟棄數(shù)據(jù)
byte[] skipRow = new byte[rowStride - image.getWidth()]; 
for (int i = 0; i < image.getHeight(); i++) {
    buffer.get(row); 
    yuv420.put(row);
    // 不是最后一行,則丟棄此數(shù)據(jù)
    if (i < image.getHeight() - 1) {
        buffer.get(skipRow); 
    }
}
= width
> width

U與V數(shù)據(jù)

1) = width
2) > width
3) = width/2
4) > width/2

1)planes[1]中不僅包含U數(shù)據(jù)褐着,還會包含V的數(shù)據(jù)脾歧,此時pixelStride==2

planes[1]


= width

planes[2]

= width

2)Y數(shù)據(jù)一樣,可能由于字節(jié)對齊充甚,出現(xiàn)RowStride大于Width的情況以政,與等于Width一樣, planes[1]中不僅包含U 數(shù)據(jù),還會包含V的數(shù)據(jù)伴找,此時pixelStride==2

planes[1]

> width

planes[2]

> width

3)獲取的U數(shù)據(jù)對應(yīng)的RowStride等于Width/2盈蛮,表示我們得到的planes[1]只包含U數(shù)據(jù)。此時pixelStride==1

= width/2

4)planes[1]只包含U數(shù)據(jù)技矮,但是與Y數(shù)據(jù)一樣眉反,可能存在占位數(shù)據(jù)。此時pixelStride==1

> width/2

獲得了攝像頭采集的數(shù)據(jù)之后穆役,我們需要獲取對應(yīng)的YUV數(shù)據(jù)寸五,需要根據(jù)pixelStride判斷格式,同時還需要通過 rowStride來確定是否存在無效數(shù)據(jù)耿币,那么最終我們獲取YUV數(shù)據(jù)的完整實現(xiàn)為

 //圖像格式
        int format = image.getFormat();
        if (format != ImageFormat.YUV_420_888) {
            //拋出異常
        }

        ByteBuffer i420 = ByteBuffer.allocate(image.getWidth() * image.getHeight() * 3 / 2);
        // 3個元素 0:Y梳杏,1:U海洼,2:V
        ImageProxy.PlaneProxy[] planes = image.getPlanes();
        // byte[]

        /**
         * Y數(shù)據(jù)
         */
        //y數(shù)據(jù)的這個值只能是:1
        int pixelStride = planes[0].getPixelStride();
        ByteBuffer yBuffer = planes[0].getBuffer();
        int rowStride = planes[0].getRowStride();

        //1拍鲤、rowStride 等于Width ,那么就是一個空數(shù)組
        //2葵诈、rowStride 大于Width 塑悼,那么就是每行多出來的數(shù)據(jù)大小個byte
        byte[] skipRow = new byte[rowStride - image.getWidth()];
        byte[] row = new byte[image.getWidth()];
        for (int i = 0; i < image.getHeight(); i++) {
            yBuffer.get(row);
            i420.put(row);
            // 不是最后一行才有無效占位數(shù)據(jù)劲适,最后一行因為后面跟著U 數(shù)據(jù),沒有無效占位數(shù)據(jù)厢蒜,不需要丟棄
            if (i < image.getHeight() - 1) {
                yBuffer.get(skipRow);
            }
        }

        /**
         * U霞势、V
         */
        for (int i = 1; i < 3; i++) {
            ImageProxy.PlaneProxy plane = planes[i];
            pixelStride = plane.getPixelStride();
            rowStride = plane.getRowStride();
            ByteBuffer buffer = plane.getBuffer();

            //每次處理一行數(shù)據(jù)
            int uvWidth = image.getWidth() / 2;
            int uvHeight = image.getHeight() / 2;

            // 一次處理一個字節(jié)
            for (int j = 0; j < uvHeight; j++) {
                for (int k = 0; k < rowStride; k++) {
                    //最后一行
                    if (j == uvHeight - 1) {
                        //uv沒混合在一起
                        if (pixelStride == 1) {
                            //rowStride :大于等于Width/2
                            // 結(jié)合外面的if:
                            //  如果是最后一行烹植,我們就不管結(jié)尾的占位數(shù)據(jù)了
                            if (k >= uvWidth) {
                                break;
                            }
                        } else if (pixelStride == 2) {
                            //uv混在了一起
                            // rowStride:大于等于 Width
                            if (k >= image.getWidth()) {
                                break;
                            }
                        }
                    }


                    byte b = buffer.get();
                    // uv沒有混合在一起
                    if (pixelStride == 1) {
                        if (k < uvWidth) {
                            i420.put(b);
                        }
                    } else if (pixelStride == 2) {
                        // uv混合在一起了
                        //1、偶數(shù)位下標(biāo)的數(shù)據(jù)是我們本次要獲得的U/V數(shù)據(jù)
                        //2愕贡、占位無效數(shù)據(jù)要丟棄草雕,不保存
                        if (k < image.getWidth() && k % 2 == 0) {
                            i420.put(b);
                        }
                    }
                }
            }
        }


        //I420
        byte[] result = i420.array();

旋轉(zhuǎn)和縮放 可以使用openCV 或者 libyuv實現(xiàn)

image.png

視頻編碼和推流 使用h264

x264_encoder_encode(...);//編碼的i_pts每次需要增長

H.264碼流在網(wǎng)絡(luò)中傳輸時實際是以NALU的形式進(jìn)行傳輸?shù)? 每個NAL之間由00 00 00 01 或者 00 00 01 進(jìn)行分割

在分割符之后的第一個字節(jié),就是表示這個nal的類型

  • 0x67:sps
  • 0x68: pps
  • 0x65: IDR
for (int i = 0; i < pi_nal; ++i) {
    int type = pp_nal[i].i_type;
    uint8_t *p_payload = pp_nal[i].p_payload;//數(shù)據(jù)
    int i_payload = pp_nal[i].i_payload;//數(shù)據(jù)長度
    if(type == NAL_SPS){
        spslen = i_payload - 4;//去掉間隔 00 00 00 01
        sps=(uint8_t)alloca(spslen);//在棧中申請內(nèi)存使用完自動釋放
        memcpy(sps,p_payload+4,spslen);
    }else if(type == NAL_PPS){
        ppslen = i_payload - 4;
        pps=(uint8_t)alloca(ppslen);
        memcpy(pps,p_payload+4,ppslen);
        //發(fā)送數(shù)據(jù) sendSPSPPS(sps, spslen, pps, ppslen);
    }else{
        //發(fā)送h264 sendH264(type, p_payload, i_payload);
    }
}

音頻編碼和推流 faac

//輸入樣本  要給編碼器編碼的樣本數(shù)
unsigned long inputSample;
unsigned long maxOutputBytes;
codec = faacEncOpen(sampleRate,channels,&inputSample,&maxOutputBytes);
inputByteNum = inputSample*2; //樣本是16位  一個樣本是2字節(jié)
//得到編碼的各種參數(shù)配置
faacEncConfigurationPtr configurationPtr = faacEncGetCurrentConfiguration(codec);
configurationPtr->mpegVersion = MPEG4;
configurationPtr->aacObjectType=LOW;
configurationPtr->outputFormat=0;//傳入0表示編碼出aac裸數(shù)據(jù) 傳1表示每一幀音頻編碼結(jié)果數(shù)據(jù)都會攜帶ADTS(包含采樣率 聲道等信息的數(shù)據(jù)頭)
configurationPtr->inputFormat=FAAC_INPUT_16BIT;
faacEncSetConfiguration(codec,configurationPtr);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末固以,一起剝皮案震驚了整個濱河市墩虹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憨琳,老刑警劉巖诫钓,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異篙螟,居然都是意外死亡尖坤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門闲擦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慢味,“玉大人,你說我怎么就攤上這事墅冷〈柯罚” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵寞忿,是天一觀的道長驰唬。 經(jīng)常有香客問我,道長腔彰,這世上最難降的妖魔是什么叫编? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮霹抛,結(jié)果婚禮上搓逾,老公的妹妹穿的比我還像新娘。我一直安慰自己杯拐,他們只是感情好霞篡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著端逼,像睡著了一般朗兵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顶滩,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天余掖,我揣著相機(jī)與錄音,去河邊找鬼礁鲁。 笑死盐欺,一個胖子當(dāng)著我的面吹牛赁豆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播找田,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼歌憨,長吁一口氣:“原來是場噩夢啊……” “哼着憨!你這毒婦竟也來了墩衙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甲抖,失蹤者是張志新(化名)和其女友劉穎漆改,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體准谚,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挫剑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了柱衔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片樊破。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖唆铐,靈堂內(nèi)的尸體忽然破棺而出哲戚,到底是詐尸還是另有隱情,我是刑警寧澤艾岂,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布顺少,位于F島的核電站,受9級特大地震影響王浴,放射性物質(zhì)發(fā)生泄漏脆炎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一氓辣、第九天 我趴在偏房一處隱蔽的房頂上張望秒裕。 院中可真熱鬧,春花似錦钞啸、人聲如沸簇爆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽入蛆。三九已至,卻和暖如春硕勿,著一層夾襖步出監(jiān)牢的瞬間哨毁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工源武, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扼褪,地道東北人想幻。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像话浇,于是被迫代替她去往敵國和親脏毯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

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