使用gpac封裝mp4代碼解析(一)

        首先非常感謝先行者LiaoJunXiong (參考網頁:https://blog.csdn.net/weixin_43549602/article/details/84571906)提供的使用gpac封裝mp4的代碼,大大簡化了gpac使用的難度。

        不過很遺憾,原作者沒有對代碼做很多註釋,或者說基本上沒做註釋,因此在這裏我針對於各個函數加上詳細註釋,這樣可以使各函數、代碼的功能清晰明瞭,使人對這個工程有更爲深入的瞭解。由於代碼不算短,因此分系列,每一篇介紹一個函數。

        先來說第一個函數ParseNalu,源碼如下:

int AF_MP4Writer::ParseNalu(unsigned char *pData, int nSize, int *pStart, int *pEnd)
{
    int i = 0;
    *pStart = 0;
    *pEnd = 0;

    while ((pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
		&& (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x00 || pData[i + 3] != 0x01))
    {
        i++;
        if (i + 4 >= nSize)
	    return 0;
    }

    if (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
        i++;

    if (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
	return 0;
	
    i += 3;
    *pStart = i;

    while ((pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x00)
		&& (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01))
    {
        i++;
        if (i + 3 >= nSize)
	{ 
		*pEnd = nSize; 
		return (*pEnd - *pStart);
	}
    }

    *pEnd = i;
    return (*pEnd - *pStart);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////   

   while ((pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
        && (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x00 || pData[i + 3] != 0x01))
    {
        i++;
        if (i + 4 >= nSize)
            return 0;
    }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        這一段代碼的意思是:

        從輸入數組pData[0](i初始值爲0)開始,依次查找0x00 0x00 0x01或0x00 0x00 0x00 0x01的序列(起始碼)。如果沒有找到,也就是說pData[0] pData[1] pData[2]不是0x00 0x00 0x01,並且pData[0] pData[1] pData[2] pData[3]也不是0x00 0x00 0x00 0x01,則使i自增1,也就是說從pData[1]開始,再繼續觀察,看是否滿足pData[1] pData[2] pData[3]爲0x00 0x00 0x01或pData[0] pData[1] pData[2] pData[3]爲0x00 0x00 0x00 0x01,如果還不滿足,則i再自增1,依次類推……

        如果一直不滿足要求,直到i+4達到了傳入的最大長度,則認爲沒有找到起始碼,返回0,即數據長度爲0。

        如果查找的過程中滿足條件了,不管是滿足0x00 0x00 0x01還是0x00 0x00 0x00 0x01,都跳出循環,無需再查找了,記錄下此時i的位置(此時pData[i]爲第一個0x00)。

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

if (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
        i++;

if (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01)
        return 0;
    
i += 3;
*pStart = i;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        這段代碼的意思是:

        經過上一段程序運行到這裏,說明無非就是2種情況,一種是pData[i] pData[i+1] pData[i+2]爲0x00 0x00 0x01,另一種是pData[i] pData[i+1] pData[i+2] pData[i+3]爲0x00 0x00 0x00 0x01。這段代碼就是要區分這2種情況。

        如果不是0x00 0x00 0x01,則把i自增1,如果是就不用自增了。

        如果是0x00 0x00 0x01的情況,上邊i沒有自增,還是維持pData[i] pData[i+1] pData[i+2]爲0x00 0x00 0x01,則繼續往下進行;如果是0x00 0x00 0x00 0x01的情況,上邊i自增了1,實際上是跳過了pData[i],即第一個0x00,而檢查後邊3個數據是否爲0x00 0x00 0x01,如果是,則繼續往下進行。注意,實際上此時對於2種情況,i的值是不同的,差了1。

        如果上邊2個條件都不滿足,則說明出錯了,返回數據長度爲0。當然,實際上這種情況正常情況下不可能出現,屬於增強代碼健壯性。

        i += 3這句話代碼的意思是不管是0x00 0x00 0x01還是0x00 0x00 0x00 0x01的哪一種情況,都跳過這個起始碼,而將i指向起始碼之後緊跟着的第一個字節數據(nal頭),並使*pStart保存i值。

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

while ((pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x00)
        && (pData[i] != 0x00 || pData[i + 1] != 0x00 || pData[i + 2] != 0x01))
{
        i++;
        if (i + 3 >= nSize)
        { 
                *pEnd = nSize; 
                return (*pEnd - *pStart);
         }
    }

    *pEnd = i;
    return (*pEnd - *pStart);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        這段代碼的意思是:

        繼續按上邊的方法查找下一個起始碼(因爲i此時已經跳過了第一個起始碼)。每個NAL前有一個起始碼 0x00 0x00 0x01(或者0x00 0x00 0x00 0x01),每個起始碼作爲一個NAL的起始標識,當檢測到下一個起始碼時,當前NAL結束。因此實際上是在找這一個NAL的結束位置,從而獲得本NAL的大小。如果沒有再找到新的起始碼,則將結尾處值設置爲nSize(說明到結尾都是屬於這一個NAL的),否則設置爲下一個起始碼的第1個0x00處的位置值(這不難理解,1-5共5個,一定是6-1)。返回數據長度爲結尾處位置值減去起始處位置值,不用再加1了,因爲已經指向了後一個字節。

 

        至此,ParseNalu函數就解析完了,經過詳細解釋,應該覺得這個函數好理解多了吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章