1 Write before you start
Some time ago, I shared a stream of H264 encapsulated ps to related articles. This time, I share with you the stream of H264 encapsulated as TS to related implementations. In fact, it is also a work need. As in the previous section, the encapsulation of each data header is explained in sections. Of course, rtp headers are added to facilitate future development. If development is not needed, it can be shielded by itself. Of course, the movement of the main buffer pointer is needed.
2 Encapsulation from Head to Rule Points
The whole encapsulation process is similar to ps, but the biggest difference is that the length of TS stream to data is fixed 188 size, while PS stream is variable packet structure. Because of the difference in structure, they have different resistance to transmission error. TS stream uses fixed length data packet, when transmission error destroys a TS packet. In synchronization information, the receiver can detect the next TS packet to synchronization information at a fixed location, thus restoring synchronization and avoiding data loss. The length of PS stream can be changed to the data packet. When the synchronization information of a PS packet is lost, the receiver can not synchronize the information and can not confirm the next synchronization information, which results in serious loss of information. Therefore, in the harsh environment, the loss of transmission code is serious. TS stream is generally used to avoid it. When the network environment is stable and the probability of transmission error is small, PS stream is used to transmit.
For TS streams, you need to know the following program mapping table (PAT: Program Associate Table) and program mapping table (PMT: Program Map Table). When sending data to video data key frames, you need to add PAT and PMT in the header of the package.
The specific structure is as follows
Packaging composition: (PAT +PMT) + TS + PES + H264 + (TS + H264 + TS + H264...)
Data Length: PES Packet Length = 188 Bytes - TS Packet Header Length (4 Bytes) - Adaptation Domain Length (PES Length or 0)
Be careful:
a. Each time 188 bytes of fixed-length data packet are filled with 1 if they are not enough. memset is the best choice when filling in every bit of data.
b. Because of my personal habit, when encapsulating the key frame, I lost PAM+PMT+TS+PES and filled it with 188 bytes, which reminded you that it was wrong, totally wrong, and the PES had to follow the H264 data.
c. The data length that PES can represent is only short and two bytes, so when the data length exceeds, multiple PES headers need to be considered.
3 Realization of Pseudo-Code from Part to Part
- /*
- *@remark: Abstract Logic Processing Function Interface for Overall Data Delivery
- */
- int rtsp_RTPPackage( RTP_SESSION_S *pRtpSender, int nFrameLen, StreamType_E enStreamType)
- {
- int nRet = 0;
- int bVideo = 1 ;
- int nSendDataOff = 0;
- int nSendSize = 0;
- int nPayLoadSize = 0;
- int nHasSend = 0;
- int IFrameFlag = 0;
- char TSFrameHdr[1024];
- int nHead = 0;
- memset(TSFrameHdr, 0, 1024);
- memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
- bVideo = ((enStreamType == VIDEO_STREAM ) ? 1 : 0);
- //@ remark: Determine whether the data is an I frame. If it is an I frame, add PAT and PMT.
- if( (bVideo == 1) && pRtpSender->stAvData.u8IFrame == 1)
- {
- if((nRet = mk_ts_pat_packet(TSFrameHdr +nSendDataOff,
- pRtpSender->hHdlTs)) <= 0)
- {
- DBG_INFO(" mk_ts_pat_packet failed!\n");
- return -1;
- }
- //@ remark: Every time you add a header, you must pay attention to the pointer to the offset.
- nSendDataOff += nRet;
- if((nRet = mk_ts_pmt_packet(TSFrameHdr + nSendDataOff,
- pRtpSender->hHdlTs)) <= 0)
- {
- DBG_INFO(" mk_ts_pmt_packet failed!\n");
- return -1;
- }
- nSendDataOff += nRet;
- }
- //@ remark: To add PS header, it is important to note that there is also a counting field in PS.
- if((nRet = mk_ts_packet(TSFrameHdr + nSendDataOff, pRtpSender->hHdlTs,
- 1, bVideo, pRtpSender->stAvData.u8IFrame, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
- {
- DBG_INFO(" mk_ts_packet failed!\n");
- return -1;
- }
- nSendDataOff += nRet;
- //This field is used to calculate the ts length because the ts packet is fixed 188 bytes.
- nHead = nRet;
- //@ remark: If you add PES header, you must receive H264 data later. You can't fill it with 1.
- if((nRet = mk_pes_packet(TSFrameHdr + nSendDataOff, bVideo, nFrameLen, 1,
- pRtpSender->stAvData.u64TimeStamp, pRtpSender->stAvData.u64TimeStamp)) <= 0 )
- {
- DBG_INFO(" mk_pes_packet failed!\n");
- return -1;
- }
- nSendDataOff += nRet;
- nHead += nRet;
- //@ remark: If the length of the first data sent is longer than the remaining length, the remaining length of the ts packet will be sent first.
- if( nFrameLen > (TS_LOAD_LEN - nHead))
- {
- memcpy(TSFrameHdr + nSendDataOff, pRtpSender->stAvData.data, TS_LOAD_LEN - nHead);
- nSendDataOff += (TS_LOAD_LEN - nHead);
- nHasSend = (TS_LOAD_LEN - nHead);
- if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 0, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
- {
- DBG_INFO(" rtsp_send_pack failed!\n");
- return -1;
- }
- }
- //@ remark: If the length of the first transmitted data is less than the remaining length of the ts header, then the length of the transmitted data frame, with the remaining 188 length filled with 1.
- else
- {
- memcpy(TSFrameHdr + nSendDataOff, pRtpSender->stAvData.data, nFrameLen);
- nSendDataOff += nFrameLen;
- nHasSend = nFrameLen;
- memset(TSFrameHdr +nSendDataOff, 0xFF, (TS_LOAD_LEN-nHead - nFrameLen));
- nSendDataOff += (TS_LOAD_LEN -nHead- nFrameLen);
- if( rtsp_send_rtppack(TSFrameHdr, &nSendDataOff, pRtpSender->stAvData.u64TimeStamp, 1, (pRtpSender->stAvData.u8IFrame?1:0), bVideo, 1, pRtpSender) != 0 )
- {
- DBG_INFO(" rtsp_send_rtppack failed!\n");
- return -1;
- }
- }
- //The corresponding length of data is cheaper, because I fix 1460 to rtp packets to send data when processing, so offset is handled here to add rtp headers easily.
- nPayLoadSize = RTP_MAX_PACKET_BUFF - 4 - RTP_HDR_LEN - (4+6) * 7; //Subtract the rtp header, ts header, a rtp package up to seven ts packages.
- nFrameLen -= (TS_LOAD_LEN - nHead);
- //@ remark: The second time you send data, when you send data, you need to add another ps header.
- while(nFrameLen > 0 )
- {
- nSendSize = (nFrameLen > nPayLoadSize) ? nPayLoadSize : nFrameLen;
- if( rtsp_send_rtppack(pRtpSender->stAvData.data + nHasSend, &nSendSize, pRtpSender->stAvData.u64TimeStamp,
- ((nSendSize == nFrameLen) ? 1 : 0), IFrameFlag, bVideo, 0, pRtpSender) != 0 )
- {
- DBG_INFO(" rtsp_send_rtppack failed!\n");
- return -1;
- }
- nFrameLen -= nSendSize;
- nHasSend += nSendSize;
- memset(pRtpSender->stRtpPack, 0, RTP_MAX_PACKET_BUFF);
- IFrameFlag = 0;
- }
- return 0;
- }
- /*
- *@remark : Add the pat header.
- */
- int mk_ts_pat_packet(char *buf, int handle)
- {
- int nOffset = 0;
- int nRet = 0;
- if (!buf)
- {
- return 0;
- }
- if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PAT, 1)))
- {
- return 0;
- }
- nOffset += nRet;
- if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
- {
- return 0;
- }
- nOffset += nRet;
- if (0 >= (nRet = ts_pat_header(buf + nOffset)))
- {
- return 0;
- }
- nOffset += nRet;
- //Each pat will be treated as a ts package, so each remaining part will be filled with 1.
- memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
- return TS_PACKET_SIZE;
- }
- int ts_pat_header(char *buf)
- {
- BITS_BUFFER_S bits;
- if (!buf)
- {
- return 0;
- }
- bits_initwrite(&bits, 32, (unsigned char *)buf);
- bits_write(&bits, 8, 0x00); //table id, fixed to 0x00
- bits_write(&bits, 1, 1); //section syntax indicator, fixed to 1
- bits_write(&bits, 1, 0); // zero, 0
- bits_write(&bits, 2, 0x03); //reserved1, fixed to 0x03
- bits_write(&bits, 12, 0x0D); //section length, which represents the number of useful bytes after this byte, including CRC32
- bits_write(&bits, 16, 0x0001); //transport stream id, used to distinguish other TS streams
- bits_write(&bits, 2, 0x03); //reserved2, fixed to 0x03
- bits_write(&bits, 5, 0x00); //version number, range 0-31
- bits_write(&bits, 1, 1); //Current next indicator, 0. The next table is valid. 1 The current PAT table can be used.
- bits_write(&bits, 8, 0x00); //section number, PAT may be divided into multi-segment transmission, the first segment is 00.
- bits_write(&bits, 8, 0x00); // last section number
- bits_write(&bits, 16, 0x0001); // program number
- bits_write(&bits, 3, 0x07); //reserved3 and pmt_pid are a group of channels indicated by program number.
- bits_write(&bits, 13, TS_PID_PMT); // pmt of pid in ts head
- bits_write(&bits, 8, 0x9F); //CRC_32
- bits_write(&bits, 8, 0xC7);
- bits_write(&bits, 8, 0x62);
- bits_write(&bits, 8, 0x58);
- bits_align(&bits);
- return bits.i_data;
- }
- /*
- *@remaark: Add PMT header
- */
- int mk_ts_pmt_packet(char *buf, int handle)
- {
- int nOffset = 0;
- int nRet = 0;
- if (!buf)
- {
- return 0;
- }
- if (0 >= (nRet = ts_header(buf, handle, TS_TYPE_PMT, 1)))
- {
- return 0;
- }
- nOffset += nRet;
- if (0 >= (nRet = ts_pointer_field(buf + nOffset)))
- {
- return 0;
- }
- nOffset += nRet;
- if (0 >= (nRet = ts_pmt_header(buf + nOffset)))
- {
- return 0;
- }
- nOffset += nRet;
- //Each pmt is treated as a ts package, so the rest of each pmt is filled with 1.
- memset(buf + nOffset, 0xFF, TS_PACKET_SIZE - nOffset);
- return TS_PACKET_SIZE;
- }
- int ts_pmt_header(char *buf)
- {
- BITS_BUFFER_S bits;
- if (!buf)
- {
- return 0;
- }
- bits_initwrite(&bits, 32, (unsigned char *)buf);
- bits_write(&bits, 8, 0x02); //table id, fixed to 0x02
- bits_write(&bits, 1, 1); //section syntax indicator, fixed to 1
- bits_write(&bits, 1, 0); // zero, 0
- bits_write(&bits, 2, 0x03); //reserved1, fixed to 0x03
- bits_write(&bits, 12, 0x1C); //section length, which represents the number of useful bytes after this byte, including CRC32
- bits_write(&bits, 16, 0x0001); //program number, which indicates the channel number associated with the current PMT.
- bits_write(&bits, 2, 0x03); //reserved2, fixed to 0x03
- bits_write(&bits, 5, 0x00); //version number, range 0-31
- bits_write(&bits, 1, 1); //Current next indicator, 0. The next table is valid. 1 The current PAT table can be used.
- bits_write(&bits, 8, 0x00); //section number, PAT may be divided into multi-segment transmission, the first segment is 00.
- bits_write(&bits, 8, 0x00); // last section number
- bits_write(&bits, 3, 0x07); //reserved3, fixed to 0x07
- bits_write(&bits, 13, TS_PID_VIDEO); //PCR of pid in ts head, if the program definition for private data stream is not related to PCR, the value of this field will be 0x1FFF.
- bits_write(&bits, 4, 0x0F); //Reserved 4, fixed to 0x0F
- bits_write(&bits, 12, 0x00); //program info length, the first two bit s are 00
- bits_write(&bits, 8, TS_PMT_STREAMTYPE_H264_VIDEO); //stream type, marked Video or Audio or other data
- bits_write(&bits, 3, 0x07); //reserved, fixed to 0x07
- bits_write(&bits, 13, TS_PID_VIDEO); // elementary of pid in ts head
- bits_write(&bits, 4, 0x0F); //reserved, fixed to 0x0F
- bits_write(&bits, 12, 0x00); //elementary stream info length, the first two bit s are 00
- bits_write(&bits, 8, TS_PMT_STREAMTYPE_11172_AUDIO); //stream type, marked Video or Audio or other data
- bits_write(&bits, 3, 0x07); //reserved, fixed to 0x07
- bits_write(&bits, 13, TS_PID_AUDIO); // elementary of pid in ts head
- bits_write(&bits, 4, 0x0F); //reserved, fixed to 0x0F
- bits_write(&bits, 12, 0x00); //elementary stream info length, the first two bit s are 00
- bits_write(&bits, 8, 0xA4); //stream type, marked Video or Audio or other data
- bits_write(&bits, 3, 0x07); //reserved, fixed to 0x07
- bits_write(&bits, 13, 0x00A4); // elementary of pid in ts head
- bits_write(&bits, 4, 0x0F); //reserved, fixed to 0x0F
- bits_write(&bits, 12, 0x00); //elementary stream info length, the first two bit s are 00
- bits_write(&bits, 8, 0x34); //CRC_32
- bits_write(&bits, 8, 0x12);
- bits_write(&bits, 8, 0xA3);
- bits_write(&bits, 8, 0x72);
- bits_align(&bits);
- return bits.i_data;
- }
- /*
- *@remark: ts Head encapsulation
- */
- int mk_ts_packet(char *buf, int handle, int bStart, int bVideo, int bIFrame, unsigned long long timestamp)
- {
- int nOffset = 0;
- int nRet = 0;
- if (!buf)
- {
- return 0;
- }
- if (0 >= (nRet = ts_header(buf, handle, bVideo ? TS_TYPE_VIDEO : TS_TYPE_AUDIO, bStart)))
- {
- return 0;
- }
- nOffset += nRet;
- if (0 >= (nRet = ts_adaptation_field(buf + nOffset, bStart, bVideo && (bIFrame), timestamp)))
- {
- return 0;
- }
- nOffset += nRet;
- return nOffset;
- }
- /* *@remark: ts Head-related packaging
- * PSI Including PAT, PMT, NIT, CAT
- * PSI--Program Specific Information, PAT--program association table, PMT--program map table
- * NIT--network information table, CAT--Conditional Access Table
- * There can be multiple TS streams in a network (distinguished by ts_id in PAT)
- * There can be multiple channels in a TS stream (distinguished by pnumber and pmt_pid in PAT)
- * There can be multiple PES streams in a channel (distinguished by mpt_stream in PMT)
- */
- int ts_header(char *buf, int handle, TS_TYPE_E type, int bStart)
- {
- BITS_BUFFER_S bits;
- TS_MNG_S *pMng = (TS_MNG_S *)handle;
- if (!buf || !handle || TS_TYPE_BEGIN >= type || TS_TYPE_END <= type)
- {
- return 0;
- }
- bits_initwrite(&bits, 32, (unsigned char *)buf);
- bits_write(&bits, 8, 0x47); //sync_byte, fixed to 0x47, means that a TS group follows.
- //payload unit start indicator sets different values based on whether TS package contains PES package or PSI data.
- //1. If it contains a PES packet header, set it to 1, and if it contains the rest of the PES package, set it to 0.
- //2. If PSI data is included, set to 1, the first byte of payload will be point_field, and 0 means that there is no point_field in payload.
- //3. If the TS package is null, the flag is set to 0.
- bits_write(&bits, 1, 0); // transport error indicator
- bits_write(&bits, 1, bStart); // payload unit start indicator
- bits_write(&bits, 1, 0); //transport priority, 1 denotes high priority.
- if (TS_TYPE_PAT == type)
- {
- bits_write(&bits, 13, 0x00); // pid, 0x00 PAT, 0x01 CAT
- }
- else if (TS_TYPE_PMT == type)
- {
- bits_write(&bits, 13, TS_PID_PMT);
- }
- else if (TS_TYPE_VIDEO == type)
- {
- bits_write(&bits, 13, TS_PID_VIDEO);
- }
- else if (TS_TYPE_AUDIO == type)
- {
- bits_write(&bits, 13, TS_PID_AUDIO);
- }
- bits_write(&bits, 2, 0); //transport scrambling control, transmission scrambling control
- if (TS_TYPE_PAT == type || TS_TYPE_PMT == type)
- {
- //continuity counter is a continuous count value between TS packets with the same PID value.
- //When the adaption_field_control field of the packet is 0:10, the field does not increase.
- bits_write(&bits, 2, 0x01); // adaptation field control, 00 forbid, 01 have payload, 10 have adaptation, 11 have payload and adaptation
- bits_write(&bits, 4, pMng->nPatCounter); // continuity counter, 0~15
- if (TS_TYPE_PAT != type)
- {
- pMng->nPatCounter++;
- pMng->nPatCounter &= 0x0F;
- }
- }
- else
- {
- bits_write(&bits, 2, 0x03); //The first bit indicates whether there is an adjustment field or not, and the second bit indicates whether there is a payload.
- bits_write(&bits, 4, pMng->nContinuityCounter);
- pMng->nContinuityCounter++;
- pMng->nContinuityCounter &= 0x0F;
- }
- bits_align(&bits);
- return bits.i_data;
- }
- /*
- *remark:Add pes header
- */
- int mk_pes_packet(char *buf, int bVideo, int length, int bDtsEn, unsigned long long pts, unsigned long long dts)
- {
- PES_HEAD_S pesHead;
- PES_OPTION_S pesOption;
- PES_PTS_S pesPts;
- PES_PTS_S pesDts;
- if (!buf)
- {
- return 0;
- }
- if( bVideo == 1)
- {
- //If the sampling frequency of video is 90 kHZ, the increment is 3600.
- pts = pts * 9 / 100; // 90000Hz
- dts = dts * 9 / 100; // 90000Hz
- }
- else
- {
- //For audio, you need to calculate the increment according to 8000HZ [if necessary].
- pts = pts * 8 / 1000; // 8000Hz
- dts = dts * 8 / 1000; // 8000Hz
- }
- memset(&pesHead, 0, sizeof(pesHead));
- memset(&pesOption, 0, sizeof(pesOption));
- memset(&pesPts, 0, sizeof(pesPts));
- memset(&pesDts, 0, sizeof(pesDts));
- pesHead.startcode = htonl(0x000001) >> 8;
- pesHead.stream_id = bVideo ? 0xE0 : 0xC0;
- if (PES_MAX_SIZE < length)
- {
- pesHead.pack_len = 0;
- }
- else
- {
- pesHead.pack_len = htons(length + sizeof(pesOption) + sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0));
- }
- pesOption.fixed = 0x02;
- pesOption.pts_dts = bDtsEn ? 0x03 : 0x02;
- pesOption.head_len = sizeof(pesPts) + (bDtsEn ? sizeof(pesDts) : 0);
- pesPts.fixed2 = pesPts.fixed3 = pesPts.fixed4 = 0x01;
- pesPts.fixed1 = bDtsEn ? 0x03 : 0x02;
- pesPts.ts1 = (pts >> 30) & 0x07;
- pesPts.ts2 = (pts >> 22) & 0xFF;
- pesPts.ts3 = (pts >> 15) & 0x7F;
- pesPts.ts4 = (pts >> 7) & 0xFF;
- pesPts.ts5 = pts & 0x7F;
- pesDts.fixed1 = pesDts.fixed2 = pesDts.fixed3 = pesDts.fixed4 = 0x01;
- pesDts.ts1 = (dts >> 30) & 0x07;
- pesDts.ts2 = (dts >> 22) & 0xFF;
- pesDts.ts3 = (dts >> 15) & 0x7F;
- pesDts.ts4 = (dts >> 7) & 0xFF;
- pesDts.ts5 = dts & 0x7F;
- char *head = buf;
- memcpy(head, &pesHead, sizeof(pesHead));
- head += sizeof(pesHead);
- memcpy(head, &pesOption, sizeof(pesOption));
- head += sizeof(pesOption);
- memcpy(head, &pesPts, sizeof(pesPts));
- head += sizeof(pesPts);
- if (bDtsEn)
- {
- memcpy(head, &pesDts, sizeof(pesDts));
- head += sizeof(pesPts);
- }
- return (head - buf);
- }
- /*
- *@remark: Finally, encapsulate the rtp header and send the final encapsulated data package.
- */
- int rtsp_send_rtppack(char *Databuf, int *datalen, unsigned long curtimestamp, int mark_flag, int IFrameFlag, int bVideo, int nFrameStart, RTP_SESSION_S *pRtpSender)
- {
- int nHasSend = 0;
- int nRet = 0;
- int nTsHeadNum = 0;
- int nHadDataLen = 0;
- int nTcpSendLen = 0;
- static unsigned short cSeqnum;
- //@ remark: Represents the first transmission of data, so no additional ts header is required.
- if( nFrameStart == 1 )
- {
- nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100));
- nHasSend += nRet;
- memcpy(pRtpSender->stRtpPack + nHasSend, Databuf, *datalen);
- nHasSend += *datalen;
- }
- else //If this frame data is not sent for the first time, a new ts package and a ts header need to be added.
- {
- // rtp+ rtp_ext + ts +data
- nRet = mk_rtp_packet(pRtpSender->stRtpPack + nHasSend, mark_flag, IFrameFlag, bVideo, ++cSeqnum, (curtimestamp * 9/100));
- nHasSend += nRet;
- while(*datalen > 0 && nTsHeadNum < 7)
- {
- nRet = mk_ts_packet(pRtpSender->stRtpPack + nHasSend , pRtpSender->hHdlTs, 0, bVideo, (IFrameFlag > 0 ? 1:0), curtimestamp);
- nHasSend += nRet;
- if(*datalen < (TS_LOAD_LEN- nRet))
- {
- memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, *datalen);
- nHasSend += *datalen;
- nHadDataLen += *datalen;
- //Not enough Ts188 to supplement with 1.
- memset(pRtpSender->stRtpPack + nHasSend, 0xFF, TS_LOAD_LEN- nRet - (*datalen));
- nHasSend += (TS_LOAD_LEN - nRet - *datalen);
- }
- else
- {
- memcpy(pRtpSender->stRtpPack + nHasSend, Databuf + nHadDataLen, TS_LOAD_LEN - nRet);
- nHasSend += (TS_LOAD_LEN - nRet);
- *datalen -= (TS_LOAD_LEN - nRet);
- nHadDataLen += (TS_LOAD_LEN - nRet);
- }
- nTsHeadNum ++;
- }
- *datalen = nHadDataLen; //Actually send bare data to length
- }
- if(pRtpSender->RtspsockFd <= 0 )
- {
- DBG_INFO("send rtp packet socket error\n");
- return -1;
- }
- nTcpSendLen = hi_tcp_noblock_send(pRtpSender->RtspsockFd, pRtpSender->stRtpPack, nHasSend, NULL,1500);
- if(nTcpSendLen != nHasSend )
- {
- DBG_INFO("send rtp packet failed:%s\n",strerror(errno));
- return -1;
- }
- return 0;
- }
- /*
- *remark: Some macro definitions and functions about byte manipulation used above can be seen in many libraries from open source to video processing.
- For convenience, they will also be posted to share, of course, you can also refer to the source code in vlc.
- */
- /*@remark: Constant Definition*/
- #define TS_PID_PMT (0x62)
- #define TS_PID_VIDEO (0x65)
- #define TS_PID_AUDIO (0x84)
- #define TS_PMT_STREAMTYPE_11172_AUDIO (0x03)
- #define TS_PMT_STREAMTYPE_13818_AUDIO (0x04)
- #define TS_PMT_STREAMTYPE_AAC_AUDIO (0x0F)
- #define TS_PMT_STREAMTYPE_H264_VIDEO (0x1B)
- /* @remark: Structural Definition*/
- typedef struct
- {
- int i_size; //Number of p_data bytes
- int i_data; //The position of the current operation byte
- unsigned char i_mask; //The mask of the current operating bit
- unsigned char *p_data; // bits buffer
- } BITS_BUFFER_S;
- typedef struct
- {
- unsigned int startcode : 24; //Fixed at 00 00 01.
- unsigned int stream_id : 8; // 0xC0-0xDF audio stream, 0xE0-0xEF video stream, 0xBD Private stream 1, 0xBE Padding stream, 0xBF Private stream 2
- unsigned short pack_len; // PES packet length
- } __attribute__ ((packed)) PES_HEAD_S;
- typedef struct
- {
- #if (BYTE_ORDER == LITTLE_ENDIAN)
- unsigned char original : 1; //Original or copy, original or copy
- unsigned char copyright : 1; // copyright flag
- unsigned char align : 1; //Data alignment indicator, data location indicator
- unsigned char priority : 1; // PES priority
- unsigned char scramb : 2; //PES Scrambling control, Scrambling control
- unsigned char fixed : 2; //Fixed to 10.
- unsigned char exten : 1; // PES extension flag
- unsigned char crc : 1; // PES CRC flag
- unsigned char acopy : 1; // additional copy info flag
- unsigned char trick : 1; // DSM(Digital Storage Media) trick mode flag
- unsigned char rate : 1; //ES rate flag, ES flow rate marker
- unsigned char escr : 1; //ESCR(Elementary Stream Clock Reference) flag, ES stream clock reference mark
- unsigned char pts_dts : 2; // PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
- #elif (BYTE_ORDER == BIG_ENDIAN)
- unsigned char fixed : 2; //Fixed to 10.
- unsigned char scramb : 2; //PES Scrambling control, Scrambling control
- unsigned char priority : 1; // PES priority
- unsigned char align : 1; //Data alignment indicator, data location indicator
- unsigned char copyright : 1; // copyright flag
- unsigned char original : 1; //Original or copy, original or copy
- unsigned char pts_dts : 2; // PTS DTS flags, 00 no PTS and DTS, 01 forbid, 10 have PTS, 11 have PTS and DTS
- unsigned char escr : 1; //ESCR(Elementary Stream Clock Reference) flag, ES stream clock reference mark
- unsigned char rate : 1; //ES rate flag, ES flow rate marker
- unsigned char trick : 1; // DSM(Digital Storage Media) trick mode flag
- unsigned char acopy : 1; // additional copy info flag
- unsigned char crc : 1; // PES CRC flag
- unsigned char exten : 1; // PES extension flag
- #endif
- unsigned char head_len; // PES header data length
- } __attribute__ ((packed)) PES_OPTION_S;
- typedef struct
- {// ts total 33 bits
- #if (BYTE_ORDER == LITTLE_ENDIAN)
- unsigned char fixed2 : 1; //Fixed to 1
- unsigned char ts1 : 3; // bit30-32
- unsigned char fixed1 : 4; //DTS is 0x01, PTS is 0x02, PTS+DTS is 0x03.
- unsigned char ts2; // bit22-29
- unsigned char fixed3 : 1; //Fixed to 1
- unsigned char ts3 : 7; // bit15-21
- unsigned char ts4; // bit7-14
- unsigned char fixed4 : 1; //Fixed to 1
- unsigned char ts5 : 7; // bit0-6
- #elif (BYTE_ORDER == BIG_ENDIAN)
- unsigned char fixed1 : 4; //DTS is 0x01, PTS is 0x02, PTS+DTS is 0x03.
- unsigned char ts1 : 3; // bit30-32
- unsigned char fixed2 : 1; //Fixed to 1
- unsigned char ts2; // bit22-29
- unsigned char ts3 : 7; // bit15-21
- unsigned char fixed3 : 1; //Fixed to 1
- unsigned char ts4; // bit7-14
- unsigned char ts5 : 7; // bit0-6
- unsigned char fixed4 : 1; //Fixed to 1
- #endif
- } __attribute__ ((packed)) PES_PTS_S;
- /* remark:Interface function definition*/
- int bits_initwrite(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
- {
- if (!p_data)
- {
- return -1;
- }
- p_buffer->i_size = i_size;
- p_buffer->i_data = 0;
- p_buffer->i_mask = 0x80;
- p_buffer->p_data = p_data;
- p_buffer->p_data[0] = 0;
- return 0;
- }
- void bits_align(BITS_BUFFER_S *p_buffer)
- {
- if (p_buffer->i_mask != 0x80 && p_buffer->i_data < p_buffer->i_size)
- {
- p_buffer->i_mask = 0x80;
- p_buffer->i_data++;
- p_buffer->p_data[p_buffer->i_data] = 0x00;
- }
- }
- inline void bits_write(BITS_BUFFER_S *p_buffer, int i_count, unsigned long i_bits)
- {
- while (i_count > 0)
- {
- i_count--;
- if ((i_bits >> i_count ) & 0x01)
- {
- p_buffer->p_data[p_buffer->i_data] |= p_buffer->i_mask;
- }
- else
- {
- p_buffer->p_data[p_buffer->i_data] &= ~p_buffer->i_mask;
- }
- p_buffer->i_mask >>= 1;
- if (p_buffer->i_mask == 0)
- {
- p_buffer->i_data++;
- p_buffer->i_mask = 0x80;
- }
- }
- }
- int bits_initread(BITS_BUFFER_S *p_buffer, int i_size, unsigned char *p_data)
- {
- if (!p_data)
- {
- return -1;
- }
- p_buffer->i_size = i_size;
- p_buffer->i_data = 0;
- p_buffer->i_mask = 0x80;
- p_buffer->p_data = p_data;
- return 0;
- }
- inline int bits_read(BITS_BUFFER_S *p_buffer, int i_count, unsigned long *i_bits)
- {
- if (!i_bits)
- {
- return -1;
- }
- *i_bits = 0;
- while (i_count > 0)
- {
- i_count--;
- if (p_buffer->p_data[p_buffer->i_data] & p_buffer->i_mask)
- {
- *i_bits |= 0x01;
- }
- if (i_count)
- {
- *i_bits = *i_bits << 1;
- }
- p_buffer->i_mask >>= 1;
- if(p_buffer->i_mask == 0)
- {
- p_buffer->i_data++;
- p_buffer->i_mask = 0x80;
- }
- }
- return 0;
- }
5 Write at the end
Looking at my last post about ps packaging, I'll notice that there are some differences between the two blog posts about byte pressing. I would like to make two simple points about me.
The first time is that the encapsulation in the ts processing is implemented by another colleague. I used it all because I used it, but the last call encapsulation was done by myself. The second is
ps and ts are handled differently. One fixed length, one variable length. So it's a good way to deal with it. I'm a little lazy, so I haven't changed it.