天天看點

播放器接收緩沖區的設計思路

編者:李國帥

背景原因:

舊時文檔,當時編寫播放器時的設計思路

設計思路:

1、使用大塊的記憶體,然後使用環形存儲,以前使用過

                   申請8M記憶體

                   添加不同大小的資料幀,比如1K,5K等等,每次寫入一幀讀取一幀。

                   每一幀的頭部若幹個位元組是一個結構體,說明目前幀的序号,長度,在大記憶體中的位置等等。

                   也可以建立一個資料幀的索引,每個索引包含一個結構體,結構體裡面對資料進行索引和定位。

                   這個設計最關鍵的部分在于讀寫到記憶體結尾處,怎麼傳回頭部進行讀寫。讀取速度大于寫入速度該如何處理等等。

                   優點是占用記憶體少,沒有記憶體碎片。

                   缺點是:邏輯複雜,容易出錯。

2、目前方法:申請獨立的記憶體

                   申請一個固定的索引結構體數組

m_pFrameQueue = new FrameNode[m_nMaxQueueLen];
                            typedef struct  _tagFrameNode
                            {
                                     LONG                          nFrameType;             //BYTE
                                     LONG                          nIndex;                       //
                                     REFERENCE_TIME   lTimestamp;              // frame's timestamp
                                     _tagFrameNode*    prev;                            // point to next node
                                     _tagFrameNode*    next;                            // point to next node
                                     LONG                          lSize;                            // frame's size of pDataBuffer
                                     PBYTE                         pDataBuffer;   // frame's data
                            }FrameNode;//描述了用戶端在接收到資料組成的幀格式,緩存隊列以此結點存儲。
                   把結構體首尾串聯起來
                            m_pReadPos = m_pWritePos = m_pFrameQueue;
                            FrameNode *p = m_pFrameQueue;
                            p->prev = m_pFrameQueue+(m_nMaxQueueLen-1);
                            p->nIndex = 0;
                            for(UINT i = 1; i < m_nMaxQueueLen; i++)
                            {
                                     p->nIndex = i;
                                     p->pDataBuffer= NULL;
                                     p->lSize = 0;
 
                                     p->next    = m_pFrameQueue + i;
                                     p = p->next;
                                     p->prev = m_pFrameQueue + i-1;
                            }
                            p->next = m_pFrameQueue;
                   為所有的結構體資料指針配置設定記憶體
                            p = m_pFrameQueue;
                            for(UINT i = 0; i < m_nMaxQueueLen; i++)
                            {
                                     p->pDataBuffer= (BYTE*)malloc(m_nMaxFrameSize);
                                     if(p->pDataBuffer!=NULL)m_nAllocCount++;
                                     p = p->next;
                            }
                   添加資料
                            m_pWritePos->lTimestamp = pFrameNode->lTimestamp;
                            m_pWritePos->nFrameType = pFrameNode->nFrameType;
                            m_pWritePos->lSize = pFrameNode->lSize;
                            memcpy(m_pWritePos->pDataBuffer, pFrameNode->pDataBuffer, m_pWritePos->lSize);
 
                            m_nCounts++;
                            m_pWritePos = m_pWritePos->next;
                   讀取資料
                            pFrameNode = m_pReadPos;
                            m_pReadPos = m_pReadPos->next;
                            m_nCounts--;      

                   優點是:邏輯簡單,速度快。

                   缺點是:無效記憶體太多。如果播放器較多,記憶體耗費量大。每幀資料大小不一,比如cif和高清資料幀大小不一,這時候會存在相容性浪費。

3、可以使用共享記憶體(綜合以上兩種方法)

                   但是沒有必要使用真正作業系統意義上的共享記憶體,而隻是整個應用程式能夠使用的共享記憶體。

                   隻要把應用程式做成一個單件,那麼就可能讓所有的子產品或者執行個體來使用它。

                   需要很多時間,并且需要很多的調試,暫時不使用這樣的改造。

                   如果大家都從一個地方擷取資源,那麼怎麼處理隻用不還的問題?這就變成了吃大鍋飯。

                   每個人自己管理自己的資源才能夠更加有效的實作整個資源的優化管理。

                   發現把記憶體集中起來更不好控制。

                   嘗試不能成功的關鍵還是沒有做好幀的控制,導緻解碼出現問題,directshow中pin的deliver函數阻塞。

                            如果緩沖區滿,後面所有的幀都不能添加。如果緩沖區中騰出了空間,應該找到一個關鍵幀放進去,而不是遇到一個放一個。

                            如果緩沖區滿,可以把非關鍵幀丢掉,直到遇到一個關鍵幀再播放。

                            不能因為這個幀過大而把它丢掉,因為這個可能是個關鍵幀,丢掉之後後面的p幀很難進行解碼。

附加