前言

本文只包含了代码,没有附上关于PS流的讲解。

文本的代码也是搜集了网上的代码,本文的创新点在于自己修改调试了代码,并且可以教大家如何修改代码可以轻松应用于自己的项目。

本文行文脉络为 -> 给出全文代码 -> 重点讲解代码如何修改 -> 应用于自己的项目

注: 本文大部分代码非原创,但是我一直找不到原创的链接,只有找到大家转载的链接,所以如果大家找到源代码请联系我,我修改文章附上链接。

全文代码

将全文代码复制到自己的项目后,先别急着运行,拉到下面看修改部分,修改后再编译运行。

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>#define PS_HDR_LEN  14
#define SYS_HDR_LEN 18
#define PSM_HDR_LEN 24
#define PES_HDR_LEN 19
#define RTP_HDR_LEN 12
#define RTP_VERSION 2
#define RTP_MAX_PACKET_BUFF 1460
#define PS_PES_PAYLOAD_SIZE 65522union LESize
{unsigned short int  length;unsigned char   byte[2];
};typedef struct bits_buffer_s {unsigned char* p_data;unsigned char  i_mask;int i_size;int i_data;
}bits_buffer_s;struct Data_Info_s {uint64_t s64CurPts;int      IFrame;uint16_t u16CSeq;uint32_t u32Ssrc;char szBuff[RTP_MAX_PACKET_BUFF];
};int _socketFd;/****@remark:  讲传入的数据按地位一个一个的压入数据*@param :  buffer   [in]  压入数据的buffer*          count    [in]  需要压入数据占的位数*          bits     [in]  压入的数值*/
void bits_write(bits_buffer_s *buffer, count, bits)
{bits_buffer_s *p_buffer = buffer;int i_count = (count);uint64_t i_bits = (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 )     /*循环完一个字节的8位后,重新开始下一位*/\{p_buffer->i_data++;p_buffer->i_mask = 0x80;}}
}int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc);
int gb28181_send_rtp_pack(char *databuff, int nDataLen, int mark_flag, struct Data_Info_s* pPacker);
int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts);
int gb28181_make_psm_header(char *pData);
int gb28181_make_sys_header(char *pData);
int gb28181_make_ps_header(char *pData, unsigned long long s64Scr);
int SendDataBuff(char* buff, int size);int findStartCode(unsigned char *buf, int zeros_in_startcode)
{int info;int i;info = 1;for (i = 0; i < zeros_in_startcode; i++)if (buf[i] != 0)info = 0;if (buf[i] != 1)info = 0;return info;
}int getNextNalu(FILE* inpf, unsigned char* buf)
{int pos = 0;int startCodeFound = 0;int info2 = 0;int info3 = 0;while (!feof(inpf) && (buf[pos++] = fgetc(inpf)) == 0);while (!startCodeFound){if (feof(inpf)){return pos - 1;}buf[pos++] = fgetc(inpf);info3 = findStartCode(&buf[pos - 4], 3);startCodeFound = (info3 == 1);if (info3 != 1)info2 = findStartCode(&buf[pos - 3], 2);startCodeFound = (info2 == 1 || info3 == 1);}if (info2){fseek(inpf, -3, SEEK_CUR);return pos - 3;}if (info3){fseek(inpf, -4, SEEK_CUR);return pos - 4;}
}/****@remark:  音视频数据的打包成ps流,并封装成rtp*@param :  pData      [in] 需要发送的音视频数据*          nFrameLen  [in] 发送数据的长度*          pPacker    [in] 数据包的一些信息,包括时间戳,rtp数据buff,发送的socket相关信息*          stream_type[in] 数据类型 0 视频 1 音频*@return:  0 success others failed
*/int gb28181_streampackageForH264(char *pData, int nFrameLen, struct Data_Info_s* pPacker, int stream_type)
{char szTempPacketHead[256];int  nSizePos = 0;int  nSize = 0;char *pBuff = NULL;memset(szTempPacketHead, 0, 256);// 1 package for ps headergb28181_make_ps_header(szTempPacketHead + nSizePos, pPacker->s64CurPts);nSizePos += PS_HDR_LEN;//2 system headerif( pPacker->IFrame == 1 ){// 如果是I帧的话,则添加系统头gb28181_make_sys_header(szTempPacketHead + nSizePos);nSizePos += SYS_HDR_LEN;//这个地方我是不管是I帧还是p帧都加上了map的,貌似只是I帧加也没有问题
//    gb28181_make_psm_header(szTempPacketHead + nSizePos);
//    nSizePos += PSM_HDR_LEN;}// psm头 (也是map)gb28181_make_psm_header(szTempPacketHead + nSizePos);nSizePos += PSM_HDR_LEN;//加上rtp发送出去,这样的话,后面的数据就只要分片分包就只有加上pes头和rtp头了if(gb28181_send_rtp_pack(szTempPacketHead, nSizePos, 0, pPacker) != 0 )return -1;// 这里向后移动是为了方便拷贝pes头//这里是为了减少后面音视频裸数据的大量拷贝浪费空间,所以这里就向后移动,在实际处理的时候,要注意地址是否越界以及覆盖等问题pBuff = pData - PES_HDR_LEN;while(nFrameLen > 0){//每次帧的长度不要超过short类型,过了就得分片进循环行发送nSize = (nFrameLen > PS_PES_PAYLOAD_SIZE) ? PS_PES_PAYLOAD_SIZE : nFrameLen;// 添加pes头gb28181_make_pes_header(pBuff, stream_type ? 0xC0:0xE0, nSize, pPacker->s64CurPts, pPacker->s64CurPts);//最后在添加rtp头并发送数据if( gb28181_send_rtp_pack(pBuff, nSize + PES_HDR_LEN, ((nSize == nFrameLen)?1:0), pPacker) != 0 ){printf("gb28181_send_pack failed!\n");return -1;}//分片后每次发送的数据移动指针操作nFrameLen -= nSize;//这里也只移动nSize,因为在while向后移动的pes头长度,正好重新填充pes头数据pBuff     += nSize;}return 0;
}/****@remark:   ps头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData  [in] 填充ps头数据的地址*           s64Src [in] 时间戳*@return:   0 success, others failed
*/
int gb28181_make_ps_header(char *pData, unsigned long long s64Scr)
{unsigned long long lScrExt = (s64Scr) % 100;//s64Scr = s64Scr / 100;// 这里除以100是由于sdp协议返回的video的频率是90000,帧率是25帧/s,所以每次递增的量是3600,// 所以实际你应该根据你自己编码里的时间戳来处理以保证时间戳的增量为3600即可,//如果这里不对的话,就可能导致卡顿现象了bits_buffer_s   bitsBuffer;bitsBuffer.i_size = PS_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80; // 二进制:10000000 这里是为了后面对一个字节的每一位进行操作,避免大小端夸字节字序错乱bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, PS_HDR_LEN);bits_write(&bitsBuffer, 32, 0x000001BA);      /*start codes*/bits_write(&bitsBuffer, 2,  1);           /*marker bits '01b'*/bits_write(&bitsBuffer, 3,  (s64Scr>>30)&0x07);     /*System clock [32..30]*/bits_write(&bitsBuffer, 1,  1);           /*marker bit*/bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF);   /*System clock [29..15]*/bits_write(&bitsBuffer, 1,  1);           /*marker bit*/bits_write(&bitsBuffer, 15, s64Scr&0x7fff);         /*System clock [14..0]*/bits_write(&bitsBuffer, 1,  1);           /*marker bit*/bits_write(&bitsBuffer, 9,  lScrExt&0x01ff);    /*System clock ext*/bits_write(&bitsBuffer, 1,  1);           /*marker bit*/bits_write(&bitsBuffer, 22, (255)&0x3fffff);    /*bit rate(n units of 50 bytes per second.)*/bits_write(&bitsBuffer, 2,  3);           /*marker bits '11'*/bits_write(&bitsBuffer, 5,  0x1f);          /*reserved(reserved for future use)*/bits_write(&bitsBuffer, 3,  0);           /*stuffing length*/return 0;
}/****@remark:   sys头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData  [in] 填充ps头数据的地址*@return:   0 success, others failed
*/
int gb28181_make_sys_header(char *pData)
{bits_buffer_s   bitsBuffer;bitsBuffer.i_size = SYS_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);/*system header*/bits_write( &bitsBuffer, 32, 0x000001BB); /*start code*/bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6);/*header_length 表示次字节后面的长度,后面的相关头也是次意思*/bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/bits_write( &bitsBuffer, 22, 50000);    /*rate_bound*/bits_write( &bitsBuffer, 1,  1);            /*marker_bit*/bits_write( &bitsBuffer, 6,  1);            /*audio_bound*/bits_write( &bitsBuffer, 1,  0);            /*fixed_flag */bits_write( &bitsBuffer, 1,  1);          /*CSPS_flag */bits_write( &bitsBuffer, 1,  1);          /*system_audio_lock_flag*/bits_write( &bitsBuffer, 1,  1);          /*system_video_lock_flag*/bits_write( &bitsBuffer, 1,  1);          /*marker_bit*/bits_write( &bitsBuffer, 5,  1);          /*video_bound*/bits_write( &bitsBuffer, 1,  0);          /*dif from mpeg1*/bits_write( &bitsBuffer, 7,  0x7F);       /*reserver*//*audio stream bound*/bits_write( &bitsBuffer, 8,  0xC0);         /*stream_id*/bits_write( &bitsBuffer, 2,  3);          /*marker_bit */bits_write( &bitsBuffer, 1,  0);            /*PSTD_buffer_bound_scale*/bits_write( &bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*//*video stream bound*/bits_write( &bitsBuffer, 8,  0xE0);         /*stream_id*/bits_write( &bitsBuffer, 2,  3);          /*marker_bit */bits_write( &bitsBuffer, 1,  1);          /*PSTD_buffer_bound_scale*/bits_write( &bitsBuffer, 13, 2048);       /*PSTD_buffer_size_bound*/return 0;
}/****@remark:   psm头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData  [in] 填充ps头数据的地址*@return:   0 success, others failed
*/
int gb28181_make_psm_header(char *pData)
{bits_buffer_s   bitsBuffer;bitsBuffer.i_size = PSM_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, PSM_HDR_LEN);bits_write(&bitsBuffer, 24,0x000001); /*start code*/bits_write(&bitsBuffer, 8, 0xBC);   /*map stream id*/bits_write(&bitsBuffer, 16,18);     /*program stream map length*/bits_write(&bitsBuffer, 1, 1);      /*current next indicator */bits_write(&bitsBuffer, 2, 3);      /*reserved*/bits_write(&bitsBuffer, 5, 0);      /*program stream map version*/bits_write(&bitsBuffer, 7, 0x7F);   /*reserved */bits_write(&bitsBuffer, 1, 1);      /*marker bit */bits_write(&bitsBuffer, 16,0);      /*programe stream info length*/bits_write(&bitsBuffer, 16, 8);     /*elementary stream map length  is*//*audio*/bits_write(&bitsBuffer, 8, 0x90);       /*stream_type*/bits_write(&bitsBuffer, 8, 0xC0);   /*elementary_stream_id*/bits_write(&bitsBuffer, 16, 0);     /*elementary_stream_info_length is*//*video*/bits_write(&bitsBuffer, 8, 0x1B);       /*stream_type*/bits_write(&bitsBuffer, 8, 0xE0);   /*elementary_stream_id*/bits_write(&bitsBuffer, 16, 0);     /*elementary_stream_info_length *//*crc (2e b9 0f 3d)*/bits_write(&bitsBuffer, 8, 0x45);   /*crc (24~31) bits*/bits_write(&bitsBuffer, 8, 0xBD);   /*crc (16~23) bits*/bits_write(&bitsBuffer, 8, 0xDC);   /*crc (8~15) bits*/bits_write(&bitsBuffer, 8, 0xF4);   /*crc (0~7) bits*/return 0;
}/****@remark:   pes头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData      [in] 填充ps头数据的地址*           stream_id  [in] 码流类型*           paylaod_len[in] 负载长度*           pts        [in] 时间戳*           dts        [in]*@return:   0 success, others failed
*/
int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts)
{bits_buffer_s   bitsBuffer;bitsBuffer.i_size = PES_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, PES_HDR_LEN);/*system header*/bits_write( &bitsBuffer, 24,0x000001);  /*start code*/bits_write( &bitsBuffer, 8, (stream_id)); /*streamID*/bits_write( &bitsBuffer, 16,(payload_len)+13);  /*packet_len*/ //指出pes分组中数据长度和该字节后的长度和bits_write( &bitsBuffer, 2, 2 );    /*'10'*/bits_write( &bitsBuffer, 2, 0 );    /*scrambling_control*/bits_write( &bitsBuffer, 1, 0 );    /*priority*/bits_write( &bitsBuffer, 1, 0 );    /*data_alignment_indicator*/bits_write( &bitsBuffer, 1, 0 );    /*copyright*/bits_write( &bitsBuffer, 1, 0 );    /*original_or_copy*/bits_write( &bitsBuffer, 1, 1 );    /*PTS_flag*/bits_write( &bitsBuffer, 1, 1 );    /*DTS_flag*/bits_write( &bitsBuffer, 1, 0 );    /*ESCR_flag*/bits_write( &bitsBuffer, 1, 0 );    /*ES_rate_flag*/bits_write( &bitsBuffer, 1, 0 );    /*DSM_trick_mode_flag*/bits_write( &bitsBuffer, 1, 0 );    /*additional_copy_info_flag*/bits_write( &bitsBuffer, 1, 0 );    /*PES_CRC_flag*/bits_write( &bitsBuffer, 1, 0 );    /*PES_extension_flag*/bits_write( &bitsBuffer, 8, 10);    /*header_data_length*/// 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前//的字节指出了有无可选字段。/*PTS,DTS*/bits_write( &bitsBuffer, 4, 3 );                    /*'0011'*/bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 );     /*PTS[32..30]*/bits_write( &bitsBuffer, 1, 1 );bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF);    /*PTS[29..15]*/bits_write( &bitsBuffer, 1, 1 );bits_write( &bitsBuffer, 15,(pts)&0x7FFF);          /*PTS[14..0]*/bits_write( &bitsBuffer, 1, 1 );bits_write( &bitsBuffer, 4, 1 );                    /*'0001'*/bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 );     /*DTS[32..30]*/bits_write( &bitsBuffer, 1, 1 );bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF);    /*DTS[29..15]*/bits_write( &bitsBuffer, 1, 1 );bits_write( &bitsBuffer, 15,(dts)&0x7FFF);          /*DTS[14..0]*/bits_write( &bitsBuffer, 1, 1 );return 0;
}/****@remark:   rtp头的打包,并循环发送数据*@param :   pData      [in] 发送的数据地址*           nDatalen   [in] 发送数据的长度*           mark_flag  [in] mark标志位*           curpts     [in] 时间戳*           pPacker    [in] 数据包的基本信息*@return:   0 success, others failed
*/int gb28181_send_rtp_pack(char *databuff, int nDataLen, int mark_flag, struct Data_Info_s* pPacker)
{int nRes = 0;int nPlayLoadLen = 0;int nSendSize    = 0;char szRtpHdr[RTP_HDR_LEN];memset(szRtpHdr, 0, RTP_HDR_LEN);if(nDataLen + RTP_HDR_LEN <= RTP_MAX_PACKET_BUFF)// 1460 pPacker指针本来有一个1460大小的buffer数据缓存{// 一帧数据发送完后,给mark标志位置1gb28181_make_rtp_header(szRtpHdr, ((mark_flag == 1 )? 1 : 0 ), ++pPacker->u16CSeq, pPacker->s64CurPts, pPacker->u32Ssrc);memcpy(pPacker->szBuff, szRtpHdr, RTP_HDR_LEN);memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nDataLen);nRes = SendDataBuff(pPacker->szBuff, nDataLen + RTP_HDR_LEN);if (nRes != (RTP_HDR_LEN + nDataLen)){printf(" udp send error !\n");return -1;}}else{nPlayLoadLen = RTP_MAX_PACKET_BUFF - RTP_HDR_LEN; // 每次只能发送的数据长度 除去rtp头gb28181_make_rtp_header(pPacker->szBuff, 0, ++pPacker->u16CSeq, pPacker->s64CurPts, pPacker->u32Ssrc);memcpy(pPacker->szBuff + RTP_HDR_LEN, databuff, nPlayLoadLen);nRes = SendDataBuff(pPacker->szBuff, RTP_HDR_LEN + nPlayLoadLen);if (nRes != (RTP_HDR_LEN + nPlayLoadLen)){printf(" udp send error !\n");return -1;}nDataLen -= nPlayLoadLen;// databuff += (nPlayLoadLen - RTP_HDR_LEN);databuff += nPlayLoadLen; // 表明前面到数据已经发送出去databuff -= RTP_HDR_LEN; // 用来存放rtp头while(nDataLen > 0){if(nDataLen <= nPlayLoadLen){//一帧数据发送完,置mark标志位gb28181_make_rtp_header(databuff, mark_flag, ++pPacker->u16CSeq, pPacker->s64CurPts, pPacker->u32Ssrc);nSendSize = nDataLen;}else{gb28181_make_rtp_header(databuff, 0, ++pPacker->u16CSeq, pPacker->s64CurPts, pPacker->u32Ssrc);nSendSize = nPlayLoadLen;}nRes = SendDataBuff(databuff, RTP_HDR_LEN + nSendSize);if (nRes != (RTP_HDR_LEN + nSendSize)){printf(" udp send error !\n");return -1;}nDataLen -= nSendSize;databuff += nSendSize;//因为buffer指针已经向后移动一次rtp头长度后,//所以每次循环发送rtp包时,只要向前移动裸数据到长度即可,这是buffer指针实际指向到位置是//databuff向后重复的rtp长度的裸数据到位置上}}return 0;
}int SendDataBuff(char* buff, int size) {/* 设置address */struct sockaddr_in addr_serv;int len;memset(&addr_serv, 0, sizeof(addr_serv));addr_serv.sin_family = AF_INET;addr_serv.sin_addr.s_addr = inet_addr("192.168.30.159");addr_serv.sin_port = htons(20002);len = sizeof(addr_serv);int res = sendto(_socketFd, buff, size, 0, (struct sockaddr *)&addr_serv, len);if (res != 0) {printf("res is %d\n", res);}return res;
}int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc)
{bits_buffer_s   bitsBuffer;if (pData == NULL)return -1;bitsBuffer.i_size = RTP_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, RTP_HDR_LEN);bits_write(&bitsBuffer, 2, RTP_VERSION);  /* rtp version  */bits_write(&bitsBuffer, 1, 0);        /* rtp padding  */bits_write(&bitsBuffer, 1, 0);        /* rtp extension  */bits_write(&bitsBuffer, 4, 0);        /* rtp CSRC count */bits_write(&bitsBuffer, 1, (marker_flag));      /* rtp marker   */bits_write(&bitsBuffer, 7, 96);     /* rtp payload type*/bits_write(&bitsBuffer, 16, (cseq));      /* rtp sequence    */bits_write(&bitsBuffer, 32, (curpts));    /* rtp timestamp   */bits_write(&bitsBuffer, 32, (ssrc));    /* rtp SSRC    */return 0;
}int main(int argc, char** argv)
{if ((_socketFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {printf("创建套接字失败:");return -1;}int ul = 1;int ret = ioctl(_socketFd, FIONBIO, &ul); //设置为非阻塞模式if (ret == -1) {printf("设置非阻塞失败!");}struct Data_Info_s pPacker;pPacker.IFrame = 1;pPacker.u32Ssrc = 1234567890123;pPacker.s64CurPts = 0;FILE* fp = fopen(argv[1], "rb");char* buf = (char*)malloc(1024 * 1024);while(1) {int size = getNextNalu(fp, (unsigned char *)(buf + PES_HDR_LEN));if (size <= 0) {break;}gb28181_streampackageForH264(buf + PES_HDR_LEN, size, &pPacker, 0);pPacker.s64CurPts += 3600;usleep(40*1000);}fclose(fp);return 0;
}

修改处1 主函数套接字的绑定

创建好套接字后,如果想要设置规定的ip端口发送,可以调用bind函数,将自己的ip和端口绑定,这样就可以规范发送ip和端口。

修改处2 修改接收端口与ip

大家找到SendDataBuff函数,将这里的ip和端口修改一下,这样就规定了指定的ip和端口

addr_serv.sin_addr.s_addr = inet_addr("192.168.30.159");
addr_serv.sin_port = htons(20002);

修改处3 传入文件方式修改

大家可以看到,这个代码获取文件名的方式是直接在终端输入的方式

FILE* fp = fopen(argv[1], "rb");

我们可以讲argv[1]改为自己的文件名或者文件路径+文件名即可。这样在调试的时候会比较方便,如果需要灵活的变动播放视频那就不需要再改动了。

好了,本文讲解结束,若还有补充我会加更,若有什么错误请在评论区批评指正。感谢大家~

觉得有用就点个赞再走吧~

RTP 将h264流封装成ps流并发送(C语言代码)相关推荐

  1. 最简单的h264/h265/svac和g711封装成ps流符合gb28181过检码流要求

    最近做国标级联,鼓捣了个简单的ps流封装,做分享做笔记. #include <stdint.h> #include <string> #include <memory.h ...

  2. H264和音频流打包成PS流 (MPEG2-PS)

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 原文:https://blog.csdn.net/hiwubihe/article/details/80736848 [本系列相关文章] H26 ...

  3. H264编码 封装成MP4格式 视频流 RTP封包

    From:http://www.cnblogs.com/ghw-NO1/archive/2012/08/28/2660848.html 一.概述 本文讲述的是对H264编码且封装成MP4格式的视频流进 ...

  4. Java stream 将两个具有相同属性的流合并成一个流

    场景 第一个流中的某个属性 需要第二个流来附上 如 流 1 含有 id 和 status 以及其他属性 status 属性值为空 需要 流 2 根据 id 相等 来附上 流 2 只含有 id 和 st ...

  5. ffmpeg实现将H264裸流封装成.mp4或.avi文件

    ffmpeg学习历程 由于我是移植到arm-linux环境(海思HI3521A),H264裸流直接从海思的编码模块VENC获取. H264数据流序列:    SPS, PPS, SEI, I, P, ...

  6. python可以封装成独立程序吗_windows环境下把Python代码打包成独立执行的exe

    有时候因为出差,突然急需处理一批数据.虽然写好的脚本存储在云端随用随取,然而编译的环境还需要重新搭建,模块也需要重新装载,从头到尾这么一遍下来,也是要花费可观的时间成本的. 有没有什么办法,可以让.p ...

  7. ES流、PES流、PS流和TS流介绍

    流媒体系统结构 整个传输过程为:音视频数据分别通过图像声音编码器打包成ES(elemental stream 基本数据流),在通过PES打包器打包成PES(packet elemental strea ...

  8. 国标PS流打包和RTP发送代码

    这个国标PS流打包/封包的代码来自于互联网,QuickGBLink在原先代码基础上做了一些改进.因为代码来自于别人的成果,基于互联网知识分享的精神,我们打算将其开源,希望能对更多学习和开发国标的开发者 ...

  9. H264和音频流打包成TS流 (MPEG2-TS)

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 原文:https://blog.csdn.net/hiwubihe/article/details/80865920 [本系列相关文章] H26 ...

  10. PES,TS,PS,RTP等流的打包格式解析之PS流

    本篇描述PS流的封装格式 1.PS头封装格式 PS流是对PES的进一步封装,是将具有共同时间基准的一个或多个PES包组合而成的单一的数据流:其基本单位是PS包,PS流由很多个PS包组成,PS包主要由固 ...

最新文章

  1. 在Markdown中用mermaid画流程图和ER图
  2. dnf服务器哪个人最多,DNF:人最多跨区竟然不是跨一?网友:这个跨区人多,土豪也多!...
  3. 【Android 逆向】代码调试器开发 ( ptrace 函数 | 读取进程内存数据 )
  4. 找找 Spring Event 源码中各种设计模式的使用
  5. 【爬虫】Scrapy爬取腾讯社招信息
  6. 【Linux】Linux中常用操作命令
  7. java 反射类 实例化_java-如何在Android上通过反射实例化成员类
  8. UIView的一些基本方法 init、loadView、viewDidLoad、viewDidUnload、dealloc
  9. python asyncio_Python 中的异步编程:Asyncio
  10. 项目Beta冲刺Day3
  11. centos7.2下查看的java版本号
  12. lisp 多边形象限_AutoLISP图程序设计.ppt
  13. Intel 旗下 Habana Labs 遭勒索软件 Pay2Key 攻击,数据被盗
  14. 我们一起来DIY一个电子秤吧
  15. 3.9 JS制作登录验证码
  16. 【系统分析师之路】第十一章 系统分析师之期中测试(章节重点)
  17. 硬件工程师成长之路(4)——元件焊接
  18. 记:应聘浙江农信,软件开发工程师
  19. 4G5G相关缩略词中文及功能简述
  20. 16春季计算机应用基础,西交16春季《计算机应用基础》在线作业及答案

热门文章

  1. Arctime——可视化字幕编辑器,解放你的双手
  2. Wifi文件传输项目总结
  3. 软件构造心得(5)spec、RI、AF、A的概念辨析之spec
  4. 【子网,超网和掩码】
  5. 学游戏设计有前途吗?
  6. Verilog设计流水灯(一)
  7. maxima学习笔记(一)
  8. oracle把一列拆成多列,Oracle将一列分成多列
  9. A fastandrobust convolutionalneuralnetwork-based defect detection model inproductqualitycontrol-阅读笔记
  10. 记录下如何判断错误:no such file or directory