RTMP攝像頭直播-CameraX數(shù)據(jù)采集處理

距離上一次寫東西,如果不翻記錄孽水,是真想不起來是什么時(shí)候了票腰,在記憶中,應(yīng)該是三月的時(shí)候女气,或者更早了杏慰,因?yàn)槟菚r(shí)候還沒有換工作。寫到這里還是忍不住去翻了一下以往的記錄炼鞠,發(fā)現(xiàn)后來也有寫過兩篇缘滥,已經(jīng)沒什么印象了。從3月到8月五個(gè)多月的時(shí)間谒主,回憶起來仿佛就在昨天朝扼。這半年來,對(duì)于我來說變化實(shí)在太大霎肯,首先是離開了自己工作了三年多的公司擎颖,很多種原因,在離開的時(shí)候并沒有一絲絲不舍观游,此刻突然想到過往的一切搂捧,好多人好多事浮現(xiàn)在眼前,就在昨天看到前同事發(fā)的狀態(tài)懂缕,好多熟悉的面孔允跑,那一刻又映入眼簾。是的搪柑,畢竟是自己曾經(jīng)為之努力奮斗的地方聋丝,尤其在19年的時(shí)候,那時(shí)候真的用過心并努力過工碾,也包括20年初期的時(shí)候弱睦。當(dāng)然,隨之而來的就是擺爛了倚喂。剛出來那段時(shí)間有太多的不適應(yīng)每篷,首先是自己租了房子,吃飯都需要掏錢買端圈,以至于覺得錢花的好快焦读,在之前每收到工資我就直接轉(zhuǎn)到天天基金賬戶了,而現(xiàn)在得考慮房租和飯錢和其它日常開銷舱权,在加上每天上班都需要擠地鐵矗晃,以至于前期那段時(shí)間整個(gè)人被整的一團(tuán)糟。

我以為在我換了工作宴倍,激情會(huì)再一次被點(diǎn)燃张症,記得前段時(shí)間和一個(gè)玩的很好的初中同學(xué)聊天仓技,談到現(xiàn)在的狀態(tài),我談到如果再讓我選擇一次俗他,我也堅(jiān)決不會(huì)做這個(gè)行業(yè)了脖捻,我想愛惜好自己的眼睛,去當(dāng)兵兆衅,然而再也沒有第二次選擇的機(jī)會(huì)了地沮。有時(shí)候挺想做一條咸魚,但又無法做到最咸的那一條羡亩,既然當(dāng)下選擇了摩疑,我覺得還是努力做好當(dāng)下的事情,既然做了一天和尚畏铆,就應(yīng)該努力把這個(gè)鐘敲好雷袋。當(dāng)然經(jīng)過歲月的洗禮,也有值得慶幸的地方辞居,自己的心態(tài)也逐漸走向成熟楷怒,不再浮躁,而是能靜下心來速侈,思考一些事情率寡。

關(guān)于之前開源的項(xiàng)目迫卢,https://github.com/zhuhuitao/printer,前前后后一共迭代了10個(gè)版本左右倚搬,當(dāng)時(shí)在做這件事情的時(shí)候,沒有想到會(huì)有這么多同學(xué)去看和使用乾蛤,說實(shí)在的每界,很開心。也看到好幾個(gè)同學(xué)提了issues家卖,和留言眨层,由于我目前沒有從事相關(guān)工作,身邊也沒有打印機(jī)上荡,所以已接近大半年沒有維護(hù)了趴樱,在此想跟大家說一聲抱歉,后續(xù)如果有機(jī)會(huì)我還是會(huì)把問題整理出來酪捡,統(tǒng)一解決叁征,深感抱歉。

前言

在很久以前一直想轉(zhuǎn)音視頻方向逛薇,一直沒有機(jī)會(huì)捺疼,畢竟想跨入這個(gè)方向,確實(shí)有一些難度永罚。雖然現(xiàn)在項(xiàng)目中也有音視頻相關(guān)的東西啤呼,無奈都不是我負(fù)責(zé)卧秘。人生嘛總會(huì)遇到容易的事情和困難的事情,如果總是逃避困難的事情官扣,想想也沒有什么意義翅敌,當(dāng)然適當(dāng)強(qiáng)迫一下自己,或許會(huì)收到不一樣的結(jié)果惕蹄。在學(xué)習(xí)的過程中哼御,學(xué)會(huì)總結(jié)和輸出真的太重要了,如果別人看到后有收獲焊唬,當(dāng)然是值得開心的了恋昼,更多的是自己在總結(jié)和輸出的時(shí)候,往往有更多的收獲和對(duì)某個(gè)知識(shí)的理解赶促。

android設(shè)備直播流程

在使用Android設(shè)備進(jìn)行攝像頭直播時(shí)液肌,其過程應(yīng)該是這樣的:


流程

就圖像而言,首先需要獲得攝像頭采集的數(shù)據(jù)鸥滨,然后得到這個(gè)byte[]進(jìn)行編碼嗦哆,再進(jìn)行后續(xù)的封包與發(fā)送。我們通過CameraX圖像分析接口得到的數(shù)據(jù)為ImageProxy(Image的代理類)婿滓。那么怎么從ImageProxy/Image中獲取我們需要的數(shù)據(jù)呢老速,這個(gè)數(shù)據(jù)格式是什么?

ImageProxy/Image

Image是android SDK提供的一個(gè)完整的圖像緩沖區(qū)凸主,圖像數(shù)據(jù)為:YUV或者RGB等格式橘券。在編碼時(shí),一般編碼器接收的待編碼數(shù)據(jù)格式為I420卿吐。而ImageProxy則是CameraX中定義的一個(gè)接口旁舰,Image的所有方法,也都能夠從ImageProxy調(diào)用嗡官〖埽可以通過image的getPlanes方法得到PlaneProxy數(shù)組,關(guān)于CameraX的詳細(xì)資料我們都可以在android官方文檔查看到衍腥。https://developer.android.google.cn/training/camerax?hl=zh_cn磺樱。當(dāng)然CameraX給到我們的數(shù)據(jù)格式在官網(wǎng)中有提到,為YUV_420_888格式的圖片。

YUV420

YUV模型是根據(jù)一個(gè)亮度(Y分量)和兩個(gè)色度(UV分量)來定義顏色空間婆咸,常見的YUV格式有YUY2竹捉、YUYV、YVYU擅耽、UYVY活孩、AYUV、Y41P、Y411憾儒、Y211民珍、IF09捞附、IYUV、YV12、YVU9恩溅、YUV411嘶炭、YUV420等瞻颂,其中比較常見的YUV420分為兩種:YUV420P和YUV420SP腥放。其中Y表示亮度,U和V表示色度边琉。( 如果UV數(shù)據(jù)都為0属百,那么我們將得到一個(gè)黑白的圖像。)RGB中每個(gè)像素點(diǎn)都有獨(dú)立的R变姨、G和B三個(gè)顏色分量值族扰,YUV根據(jù)U和V采樣數(shù)目的不同,分為如YUV444定欧、YUV422和YUV420等渔呵,而YUV420表示的就是每個(gè)像素點(diǎn)有一個(gè)獨(dú)立的亮度表示,即Y分量砍鸠;而色度扩氢,即U和V分量則由每4個(gè)像素點(diǎn)共享一個(gè)。舉例來說爷辱,對(duì)于4x4的圖片录豺,在YUV420下,有16個(gè)Y值托嚣,4個(gè)U值和4個(gè)V值巩检。YUV420根據(jù)顏色數(shù)據(jù)的存儲(chǔ)順序不同厚骗,又分為了多種不同的格式示启,這些格式實(shí)際存儲(chǔ)的信息還是完全一致的。舉例來說领舰,對(duì)于4x4的圖片夫嗓,在YUV420下,任何格式都有16個(gè)Y值冲秽,4個(gè)U值和4個(gè)V值舍咖,不同格式只是Y、U和V的排列順序變化锉桑。I420YYYYYYYYYYYYYYYYUUUUVVVV 排霉,NV21 則為 YYYYYYYYYYYYYYYYUVUVUVUV 。也就是說民轴,YUV420
是一類格式的集合攻柠,YUV420并不能完全確定顏色數(shù)據(jù)的存儲(chǔ)順序球订。
更詳細(xì)的介紹可以參考這篇文章https://zhuanlan.zhihu.com/p/495400095

PlaneProxy/Plane

Y、U和V三個(gè)分量的數(shù)據(jù)分別保存在三個(gè) Plane 類中瑰钮,即通過 getPlanes() 得到的數(shù)組冒滩。 Plane 實(shí)際是對(duì)ByteBuffer 的封裝。Image保證了planes[0]一定是Y浪谴,planes[1]一定是U开睡,planes[2]一定是V。且對(duì)于plane [0]苟耻,Y分量數(shù)據(jù)一定是連
續(xù)存儲(chǔ)的篇恒,中間不會(huì)有U或V數(shù)據(jù)穿插,也就是說我們一定能夠一次性得到所有Y分量的值凶杖。
但是對(duì)于UV數(shù)據(jù)婚度,可能存在以下兩種情況:

  1. planes[1] = {UUUU...},planes[2] = {VVVV...};
  2. planes[1] = {UVUV...}官卡,planes[2] = {VUVU...}蝗茁。
    所以在我么取數(shù)據(jù)時(shí)需要在根據(jù)Plane中的另一個(gè)信息來確定如何取對(duì)應(yīng)的U或者V數(shù)據(jù)。
//行內(nèi)數(shù)據(jù)值間隔
//1寻咒,表示無間隔取值哮翘,即為上面的第一種情況
//2,表示需要間隔一個(gè)數(shù)值取值毛秘,即為上面第二種情況
int pixelStride = plan.getPixelStride();

根據(jù)這個(gè)屬性饭寺,我們將確定數(shù)據(jù)如何存儲(chǔ),因此如果需要取出代表I420格式的byte[]叫挟,則為:

YUV420中艰匙,y數(shù)據(jù)的長(zhǎng)度為:width*height,而u,v都為width/2*height/2.
int pixelStride = plans[0].getPixelStride();
planes[0].getBuffer()
byte [] = new byte[image.getWidth()/2*image.getHeight()/2];
int pixelStride = planes[1].getPixelStride();
if(pixelStride == 1){
  planes[1].getBuffer();//u數(shù)據(jù)
}else if(pixelStride == 2){
    ByteBuffer uBuffer = planes[1].getBuffer();
    for(int i = 0;i<uBuffer.remaining;i++){
    u[i] = uBuffer.get();//丟棄一個(gè)數(shù)據(jù)抹恳,這里其實(shí)是v數(shù)據(jù)
    uBuffer.get():
}
}

//v數(shù)據(jù)與u數(shù)據(jù)同樣獲取

但是如果使用上面的代碼去獲取I420數(shù)據(jù)员凝,可能會(huì)驚奇的發(fā)現(xiàn),并不是在所有設(shè)置的Width與Height(分辨率)下都能夠正常運(yùn)行奋献。我們忽略了什么健霹,為什么會(huì)出現(xiàn)問題呢?在Plane中我們已經(jīng)使用了 getBuffer 與 getPixelStride 兩個(gè)方法瓶蚂,但是還有一個(gè) getRowStride 是干嘛的呢糖埋?

RowStride

RowStride表示行步長(zhǎng),Y數(shù)據(jù)對(duì)應(yīng)的行步長(zhǎng)可能為:

  1. 等于Width窃这;
  2. 大于Width瞳别;
    以4x4的I420為例,其數(shù)據(jù)可以看為:
      Y   Y   Y   Y
      Y   Y   Y   Y
      Y   Y   Y   Y
      Y   Y   Y   Y
      U   U
      U   U
      V   V
      V   V

如果RowStride等于Width,那么我們直接通過 planes[0].getBuffer() 獲得Y數(shù)據(jù)沒有問題祟敛。
但是如果RowStride大于Width倍奢,比如對(duì)于4x4的I420,如果每行需要以8字節(jié)對(duì)齊垒棋,那么可能得到的RowStride不
等于4(Width)卒煞,而是得到8。那么此時(shí)會(huì)在每行數(shù)據(jù)末尾補(bǔ)充占位的無效數(shù)據(jù):

        Y   Y   Y   Y    0    0    0    0
        Y   Y   Y   Y    0    0    0    0
        Y   Y   Y   Y    0    0    0    0
        Y    Y  Y   Y    最后一行沒有占位

對(duì)于這種情況叼架,我們獲取Y數(shù)據(jù)畔裕,則為:

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

        //1、rowStride 等于Width 乖订,那么就是一個(gè)空數(shù)組
        //2扮饶、rowStride 大于Width ,那么就是每行多出來的數(shù)據(jù)大小個(gè)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ù)乍构,最后一行因?yàn)楹竺娓鳸 數(shù)據(jù)甜无,沒有無效占位數(shù)據(jù),不需要丟棄
            if (i < image.getHeight() - 1) {
                yBuffer.get(skipRow);
            }
        }

而對(duì)于U與V數(shù)據(jù)哥遮,對(duì)應(yīng)的行步長(zhǎng)可能為:

  1. 等于Width岂丘;
  2. 大于Width;
  3. 等于Width/2;
  4. 大于Width/2

等于width

這表示眠饮,我們獲得planes[1]中不僅包含U數(shù)據(jù)奥帘,還會(huì)包含V的數(shù)據(jù),此時(shí)pixelStride==2

    U    V    U    V
    U    V    U    V

那么V數(shù)據(jù):planes[2]仪召,則為:

    V    U    V    U
    V    U    V    U

這種情況下寨蹋,我們上面的代碼也已經(jīng)處理了。

大于width

與Y數(shù)據(jù)一樣扔茅,可能由于字節(jié)對(duì)齊已旧,出現(xiàn)RowStride大于Width的情況,與等于Width一樣召娜,planes[1]中不僅包含U數(shù)據(jù)运褪,還會(huì)包含V的數(shù)據(jù),此pixelStride==2萤晴。

      U    V    U    V    0    0    0    0
      U    V    U    V    最后一行沒有占位

planes[2]吐句,則為:

    V    U    V    U    0    0    0    0
    V    U    V    U    最后一行沒有占位

等于width/2

當(dāng)獲取的U數(shù)據(jù)對(duì)應(yīng)的RowStride等于Width/2,表示我們得到的planes[1]只包含U數(shù)據(jù)店读。此時(shí)pixelStride==1。那么planes[1]+planes[2]為:

    U    U
    U    U
    V    V
    V    V

這種情況攀芯,所有的U數(shù)據(jù)是連在一起的屯断,即 planes[1].getBuffer 可以直接獲得完整的U數(shù)據(jù)。

大于width/2

同樣我們得到的planes[1]只包含U數(shù)據(jù),但是與Y數(shù)據(jù)一樣殖演,可能存在占位數(shù)據(jù)氧秘。此時(shí)pixelStride==1。planes[1]+planes[2]為:

    U    U    0    0    0    0    0    0
    U    U          最后一行沒有占位
    V    V    0    0    0    0    0    0
    V    V           最后一行沒有占位

總結(jié)

在獲得了攝像頭采集的數(shù)據(jù)之后趴久,我們需要獲取對(duì)應(yīng)的YUV數(shù)據(jù)丸相,需要根據(jù)pixelStride判斷格式,同時(shí)還需要通過rowStride來確定是否存在無效數(shù)據(jù)彼棍,那么最終我們獲取YUV數(shù)據(jù)的完整實(shí)現(xiàn)為:

    public static byte[] getBytes(ImageProxy image, int rotationDegrees, int width, int height) {
        //圖像格式
        int format = image.getFormat();
        if (format != ImageFormat.YUV_420_888) {
            //拋出異常
        }

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

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

        //1座硕、rowStride 等于Width 弛作,那么就是一個(gè)空數(shù)組
        //2、rowStride 大于Width 华匾,那么就是每行多出來的數(shù)據(jù)大小個(gè)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ù)映琳,最后一行因?yàn)楹竺娓鳸 數(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;

            // 一次處理一個(gè)字節(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();

        if (rotationDegrees == 90 || rotationDegrees == 270) {
            //旋轉(zhuǎn)之后 您机,圖像寬高交換
            result = rotation(result, image.getWidth(), image.getHeight(), rotationDegrees);
        }

        return result;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末穿肄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子际看,更是在濱河造成了極大的恐慌咸产,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仲闽,死亡現(xiàn)場(chǎng)離奇詭異脑溢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赖欣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門屑彻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顶吮,你說我怎么就攤上這事社牲。” “怎么了悴了?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵搏恤,是天一觀的道長(zhǎng)违寿。 經(jīng)常有香客問我,道長(zhǎng)熟空,這世上最難降的妖魔是什么藤巢? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮息罗,結(jié)果婚禮上掂咒,老公的妹妹穿的比我還像新娘。我一直安慰自己迈喉,他們只是感情好绍刮,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弊添,像睡著了一般录淡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上油坝,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天嫉戚,我揣著相機(jī)與錄音,去河邊找鬼澈圈。 笑死彬檀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞬女。 我是一名探鬼主播窍帝,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诽偷!你這毒婦竟也來了坤学?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤报慕,失蹤者是張志新(化名)和其女友劉穎深浮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眠冈,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡飞苇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜗顽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片布卡。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖雇盖,靈堂內(nèi)的尸體忽然破棺而出忿等,到底是詐尸還是另有隱情,我是刑警寧澤刊懈,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布这弧,位于F島的核電站娃闲,受9級(jí)特大地震影響虚汛,放射性物質(zhì)發(fā)生泄漏匾浪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一卷哩、第九天 我趴在偏房一處隱蔽的房頂上張望蛋辈。 院中可真熱鬧,春花似錦将谊、人聲如沸冷溶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逞频。三九已至,卻和暖如春栋齿,著一層夾襖步出監(jiān)牢的瞬間苗胀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工瓦堵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留基协,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓菇用,卻偏偏與公主長(zhǎng)得像澜驮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惋鸥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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