Linux 下实现RTP实时打包发送H.264视频文件

2024-06-08 00:08

本文主要是介绍Linux 下实现RTP实时打包发送H.264视频文件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在实现H264实时RTP打包和发送之前,我们需要先熟悉H264的编码原理及语法结构,然后是熟悉RTP协议以及RTP协议传输H264数据的相关准则。下面是与此相关的几篇博客。

    H264语法结构及编码原理

    RTP Payload H264

    Linux 下实现RTP实时打包发送H.264码流

    下面是rtp.c的代码

[objc]  view plain  copy
  1. /*=============================================================================  
  2.  *     FileName: rtp.c  
  3.  *         Desc: rtp payload h.264 data  
  4.  *       Author: licaibiao  
  5.  *   LastChange: 2017-04-07   
  6.  * =============================================================================*/  
  7. #include <stdio.h>    
  8. #include <stdlib.h>    
  9. #include <string.h>    
  10. #include <memory.h>      
  11. #include <sys/types.h>    
  12. #include <sys/socket.h>    
  13. #include <arpa/inet.h>  
  14. #include <netinet/in.h>    
  15. #include <netdb.h>   
  16. #include <unistd.h>   
  17. #include "rtp.h"    
  18.   
  19. //#define DEBUG_LOG  
  20.   
  21. typedef struct    
  22. {    
  23.     int startcodeprefix_len;      //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)    
  24.     unsigned len;                 //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)    
  25.     unsigned max_size;            //! Nal Unit Buffer size    
  26.     int forbidden_bit;            //! should be always FALSE    
  27.     int nal_reference_idc;        //! NALU_PRIORITY_xxxx    
  28.     int nal_unit_type;            //! NALU_TYPE_xxxx        
  29.     charchar *buf;                    //! contains the first byte followed by the EBSP    
  30.     unsigned short lost_packets;  //! true, if packet loss is detected    
  31. } NALU_t;    
  32.   
  33. FILEFILE *bits = NULL;              //!< the bit stream file    
  34. static int FindStartCode2 (unsigned charchar *Buf);//查找开始字符0x000001    
  35. static int FindStartCode3 (unsigned charchar *Buf);//查找开始字符0x00000001    
  36. static int info2=0;  
  37. static int info3=0;   
  38. RTP_FIXED_HEADER *rtp_hdr;    
  39. NALU_HEADER      *nalu_hdr;    
  40. FU_INDICATOR     *fu_ind;    
  41. FU_HEADER        *fu_hdr;    
  42.   
  43. NALU_t *AllocNALU(int buffersize)    
  44. {    
  45.     NALU_t *n;    
  46.     if ((n = (NALU_t*)calloc (1sizeof (NALU_t))) == NULL)    
  47.     {    
  48.         printf("AllocNALU: n");    
  49.         exit(0);    
  50.     }    
  51.     n->max_size = buffersize;    
  52.     if((n->buf = (char*)calloc (buffersize, sizeof (char))) == NULL)    
  53.     {    
  54.         free (n);    
  55.         printf ("AllocNALU: n->buf");    
  56.         exit(0);    
  57.     }    
  58.     return n;    
  59. }    
  60.   
  61. void FreeNALU(NALU_t *n)    
  62. {    
  63.     if (n)    
  64.     {    
  65.         if (n->buf)    
  66.         {    
  67.             free(n->buf);    
  68.             n->buf=NULL;    
  69.         }    
  70.         free (n);    
  71.     }    
  72. }    
  73.   
  74. void OpenBitstreamFile (charchar *fn)    
  75. {    
  76.     if (NULL == (bits = fopen(fn, "rb")))    
  77.     {    
  78.         printf("open file error\n");    
  79.         exit(0);    
  80.     }    
  81. }    
  82.   
  83. //这个函数输入为一个NAL结构体,主要功能为得到一个完整的NALU并保存在NALU_t的buf中,获取他的长度,填充F,IDC,TYPE位。    
  84. //并且返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度  
  85. //前缀之后的第一个字节为 NALU_HEADER     
  86. int GetAnnexbNALU (NALU_t *nalu)    
  87. {    
  88.     int pos = 0;    
  89.     int rewind;   
  90.     int StartCodeFound;  
  91.     unsigned charchar *Buf;    
  92.       
  93.     if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)  
  94.         printf ("GetAnnexbNALU: Could not allocate Buf memory\n");    
  95.       
  96.     printf("nalu->max_size=%d\n",(int)nalu->max_size);  
  97.     memset(Buf,0,nalu->max_size);  
  98.     nalu->startcodeprefix_len = 3;//初始化码流序列的开始字符为3个字节    
  99.     if (3 != fread (Buf, 13, bits))//从码流中读3个字节    
  100.     {    
  101.         free(Buf);    
  102.         return 0;    
  103.     }    
  104.     info2 = FindStartCode2 (Buf);//判断是否为0x000001     
  105.     if(info2 != 1)     
  106.     {    
  107.         //如果不是,再读一个字节    
  108.         if(1 != fread(Buf+311, bits))//读一个字节    
  109.         {    
  110.             free(Buf);    
  111.             return 0;    
  112.         }    
  113.         info3 = FindStartCode3 (Buf);//判断是否为0x00000001    
  114.         if (info3 != 1)//如果不是,返回-1    
  115.         {     
  116.             free(Buf);    
  117.             return -1;    
  118.         }    
  119.         else     
  120.         {    
  121.             //如果是0x00000001,得到开始前缀为4个字节    
  122.             pos = 4;    
  123.             nalu->startcodeprefix_len = 4;    
  124.         }    
  125.     }     
  126.     else    
  127.     {    
  128.         //如果是0x000001,得到开始前缀为3个字节    
  129.         nalu->startcodeprefix_len = 3;    
  130.         pos = 3;    
  131.     }    
  132.     //查找下一个开始字符的标志位    
  133.     StartCodeFound = 0;    
  134.     info2 = 0;    
  135.     info3 = 0;        
  136.       
  137.     while (!StartCodeFound)    
  138.     {    
  139.         if (feof (bits))//判断是否到了文件尾    
  140.         {    
  141.             nalu->len = (pos-1) - nalu->startcodeprefix_len;    
  142.             printf("nalu->len1=%d\n",nalu->len );  
  143.             memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); //拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001        
  144.             nalu->forbidden_bit  = nalu->buf[0] & 0x80;     // 1 bit  
  145.             nalu->nal_reference_idc = nalu->buf[0] & 0x60;     // 2 bit    
  146.             nalu->nal_unit_type      = nalu->buf[0] & 0x1f;     // 5 bit    
  147.             free(Buf);    
  148.             return pos - 1;    
  149.         }    
  150.         Buf[pos++] = fgetc (bits);//读一个字节到BUF中    
  151.         info3 = FindStartCode3(&Buf[pos-4]);//判断是否为0x00000001    
  152.         if(info3 != 1)   
  153.         {  
  154.             info2 = FindStartCode2(&Buf[pos-3]);//判断是否为0x000001       
  155.         }     
  156.         StartCodeFound = (info2 == 1 || info3 == 1);    
  157.     }    
  158.       
  159.     // Here, we have found another start code (and read length of startcode bytes more than we should    
  160.     // have.  Hence, go back in the file    
  161.     rewind = (info3 == 1) ? -4 : -3;    
  162.       
  163.     if (0 != fseek (bits, rewind, SEEK_CUR))//把文件指针指向前一个NALU的末尾    
  164.     {    
  165.         free(Buf);    
  166.         printf("GetAnnexbNALU: Cannot fseek in the bit stream file");    
  167.     }    
  168.       
  169.     // Here the Start code, the complete NALU, and the next start code is in the Buf.      
  170.     // The size of Buf is pos, pos+rewind are the number of bytes excluding the next    
  171.     // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code    
  172.       
  173.     nalu->len = (pos+rewind) - nalu->startcodeprefix_len;    
  174.     printf("nalu->len2=%d\n",nalu->len );  
  175.     memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001    
  176.     nalu->forbidden_bit       = nalu->buf[0] & 0x80;  // 1  bit  
  177.     nalu->nal_reference_idc  = nalu->buf[0] & 0x60;     // 2  bit    
  178.     nalu->nal_unit_type       = nalu->buf[0] & 0x1f;     // 5  bit    
  179.     free(Buf);    
  180.     return (pos+rewind);//返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度    
  181. }    
  182.   
  183. //输出NALU长度和TYPE    
  184. void dump(NALU_t *n)    
  185. {    
  186.     if (!n)return;    
  187.     //printf("a new nal:");    
  188.     printf(" len: %d  ", n->len);    
  189.     printf("nal_unit_type: %x\n", n->nal_unit_type);    
  190. }    
  191.   
  192. int main(int argc, char* argv[])    
  193. {    
  194.     //FILE *stream;    
  195.     //stream=fopen("Test.264", "wb");    
  196.     NALU_t *n;  
  197.     SOCKET socket1;      
  198.     char  *nalu_payload;      
  199.     char  sendbuf[1500];  
  200.     int   len ;   
  201.     int   bytes = 0;    
  202.     float framerate = 25;   
  203.     unsigned short seq_num = 0;    
  204.     unsigned int timestamp_increase = 0;  
  205.     unsigned int ts_current = 0;    
  206.     struct sockaddr_in server;    
  207.   
  208.     len = sizeof(server);  
  209.     OpenBitstreamFile("./h264/test.h264");  
  210.     timestamp_increase = (unsigned int)(90000.0 / framerate); //+0.5);    
  211.     server.sin_family = AF_INET;    
  212.     server.sin_port = htons(DEST_PORT);              
  213.     server.sin_addr.s_addr = inet_addr(DEST_IP);     
  214.     socket1 = socket(AF_INET, SOCK_DGRAM, 0);    
  215.   
  216.     n = AllocNALU(8000000);//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针    
  217.   
  218.     while(!feof(bits))     
  219.     {    
  220.         GetAnnexbNALU(n);//每执行一次,文件的指针指向本次找到的NALU的末尾,下一个位置即为下个NALU的起始码0x000001    
  221.         dump(n);//输出NALU长度和TYPE    
  222.         //(1)一个NALU就是一个RTP包的情况: RTP_FIXED_HEADER(12字节)  + NALU_HEADER(1字节) + EBPS    
  223.         //(2)一个NALU分成多个RTP包的情况: RTP_FIXED_HEADER (12字节) + FU_INDICATOR (1字节)+  FU_HEADER(1字节) + EBPS(1400字节)    
  224.         memset(sendbuf, 01500);//清空sendbuf;此时会将上次的时间戳清空,因此需要ts_current来保存上次的时间戳值    
  225.         //rtp固定包头,为12字节,该句将sendbuf[0]的地址赋给rtp_hdr,以后对rtp_hdr的写入操作将直接写入sendbuf。    
  226.         rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0];     
  227.         //设置RTP HEADER,    
  228.         rtp_hdr->csrc_len     = 0;  
  229.         //rtp_hdr->extension=0;  
  230.         //rtp_hdr->padding=0;  
  231.         rtp_hdr->payload     = H264;         //负载类型号,    
  232.         rtp_hdr->version     = 2;            //版本号,此版本固定为2    
  233.         rtp_hdr->marker      = 0;        //标志位,由具体协议规定其值。    
  234.         rtp_hdr->ssrc        = htonl(10);    //随机指定为10,并且在本RTP会话中全局唯一  bytes 8-11  
  235.         //  当一个NALU小于1400字节的时候,采用一个单RTP包发送    
  236.         if(n->len<=1400)    
  237.         {       
  238.             //设置rtp M 位;    
  239.             rtp_hdr->marker = 1;    
  240.             rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1  bytes 2, 3  
  241.             //设置NALU HEADER,并将这个HEADER填入sendbuf[12]    
  242.             nalu_hdr        = (NALU_HEADER*)&sendbuf[12]; //将sendbuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中;    
  243.             nalu_hdr->F  = n->forbidden_bit >> 7;    
  244.             nalu_hdr->NRI    = n->nal_reference_idc >> 5;//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。    
  245.             nalu_hdr->TYPE   = n->nal_unit_type;    
  246.   
  247.             nalu_payload    = &sendbuf[13];//同理将sendbuf[13]赋给nalu_payload    
  248.             memcpy(nalu_payload, n->buf + 1, n->len - 1);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。  
  249.   
  250.             ts_current = ts_current + timestamp_increase;    
  251.             printf("ts_current=%d\n",ts_current);  
  252.             rtp_hdr->timestamp = htonl(ts_current);  
  253.             printf("ts_current1=%x\n",rtp_hdr->timestamp);  
  254.             bytes = n->len + 12+1;                      //获得sendbuf的长度,为nalu的长度(包含NALU头但除去起始前缀)加上rtp_header的固定长度12字节    
  255. #ifdef DEBUG_LOG  
  256.             {  
  257.                 int ii = 0;  
  258.                 printf("-----------------------------------\n");  
  259.                 for(ii=0; ii<22; ii++)  
  260.                 {  
  261.                     printf("buf%d=%x  ", ii, (unsigned char)sendbuf[ii]);  
  262.                 }  
  263.                 printf("\n");  
  264.                 printf("------------------------------------\n");  
  265.             }  
  266. #endif            
  267.             sendto(socket1, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));  
  268.             usleep(40000);    
  269.             //fwrite(sendbuf,bytes, 1, stream);     
  270.         }    
  271.         else if(n->len>1400)    
  272.         {    
  273.             //得到该nalu需要用多少长度为1400字节的RTP包来发送    
  274.             int k = 0;  
  275.             int l = 0;   
  276.             int t = 0;          //用于指示当前发送的是第几个分片RTP包             
  277.             k = n->len / 1400;   //需要k个1400字节的RTP包    
  278.             l = n->len % 1400;   //最后一个RTP包的需要装载的字节数    
  279.              
  280.             ts_current = ts_current+timestamp_increase;    
  281.             printf("ts_current=%d\n",ts_current);  
  282.             rtp_hdr->timestamp = htonl(ts_current);    
  283.             while(t <= k)    
  284.             {    
  285.                 rtp_hdr->seq_no = htons(seq_num++); //序列号,每发送一个RTP包增1    
  286.                 if(!t)                              //发送一个需要分片的NALU的第一个分片,置FU HEADER的S位    
  287.                 {    
  288.                     //设置rtp M 位;    
  289.                     rtp_hdr->marker = 0;    
  290.                     //设置FU INDICATOR,并将这个HEADER填入sendbuf[12]    
  291.                     fu_ind       = (FU_INDICATOR*)&sendbuf[12]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;    
  292.                     fu_ind->F     = n->forbidden_bit >> 7;    
  293.                     fu_ind->NRI   = n->nal_reference_idc >> 5;    
  294.                     fu_ind->TYPE = 28;                       
  295.                     //设置FU HEADER,并将这个HEADER填入sendbuf[13]    
  296.                     fu_hdr       = (FU_HEADER*)&sendbuf[13];    
  297.                     fu_hdr->E     = 0;    
  298.                     fu_hdr->R     = 0;    
  299.                     fu_hdr->S     = 1;    
  300.                     fu_hdr->TYPE = n->nal_unit_type;    
  301.                     nalu_payload = &sendbuf[14];            //同理将sendbuf[14]赋给nalu_payload    
  302.                     memcpy(nalu_payload, n->buf + 11400);//去掉NALU头    
  303.                     bytes = 1400 + 12 + 2;                 //获得sendbuf的长度,为nalu的长度(除去起始前缀和NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节    
  304. #ifdef DEBUG_LOG  
  305.                     {  
  306.                         int ii=0;  
  307.                         printf("-----------------------------------\n");  
  308.                         for(ii=0;ii<22;ii++)  
  309.                         {  
  310.                             printf("buf%d=%x  ",ii,(unsigned char)sendbuf[ii]);  
  311.                         }  
  312.                         printf("\n");  
  313.                         printf("------------------------------------\n");  
  314.                     }     
  315. #endif  
  316.                     sendto(socket1, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));  
  317.                     //fwrite(sendbuf,bytes, 1, stream);    
  318.                     //usleep(20000);    
  319.                     t++;                        
  320.                 }    
  321.                 //发送一个需要分片的NALU的非第一个分片,清零FU HEADER的S位,如果该分片是该NALU的最后一个分片,置FU HEADER的E位    
  322.                 else if(k == t)//发送的是最后一个分片,注意最后一个分片的长度可能超过1400字节(当l>1386时)。    
  323.                 {    
  324.                     //设置rtp M 位;当前传输的是最后一个分片时该位置1    
  325.                     rtp_hdr->marker = 1;    
  326.                       
  327.                     //设置FU INDICATOR,并将这个HEADER填入sendbuf[12]    
  328.                     fu_ind          = (FU_INDICATOR*)&sendbuf[12]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;    
  329.                     fu_ind->F        = n->forbidden_bit >> 7;    
  330.                     fu_ind->NRI      = n->nal_reference_idc >> 5;    
  331.                     fu_ind->TYPE = 28;    
  332.                       
  333.                     //设置FU HEADER,并将这个HEADER填入sendbuf[13]    
  334.                     fu_hdr          = (FU_HEADER*)&sendbuf[13];    
  335.                     fu_hdr->R       = 0;    
  336.                     fu_hdr->S        = 0;    
  337.                     fu_hdr->TYPE = n->nal_unit_type;   
  338.                     fu_hdr->E        = 1;     
  339.                     nalu_payload    = &sendbuf[14];//同理将sendbuf[14]的地址赋给nalu_payload    
  340.                       
  341.                     if((n != NULL) && (n->buf != NULL) && (l > 1))  
  342.                     {  
  343.                         memcpy(nalu_payload, n->buf + t * 1400 + 1, l - 1);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入sendbuf[14]开始的字符串。    
  344.                         bytes = l - 1 + 12 + 2;       //获得sendbuf的长度,为剩余nalu的长度l-1加上rtp_header,FU_INDICATOR,FU_HEADER三个包头共14字节    
  345. #ifdef DEBUG_LOG  
  346.                         {  
  347.                             int ii=0;  
  348.                             printf("-----------------------------------\n");  
  349.                             for(ii=0;ii<22;ii++)  
  350.                             {  
  351.                                 printf("buf%d=%x  ",ii,(unsigned char)sendbuf[ii]);  
  352.                             }  
  353.                             printf("\n");  
  354.                             printf("------------------------------------\n");  
  355.                         }  
  356. #endif    
  357.                         sendto(socket1, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));                       
  358.                     }  
  359.                     else  
  360.                     {  
  361.                         printf("n->buf == NULL !\n");  
  362.                     }                    
  363.                     //fwrite(sendbuf,bytes, 1, stream);    
  364.                     //usleep(20000);  
  365.                     t++;                       
  366.                 }    
  367.                 else if(t < k && 0 != t)    
  368.                 {    
  369.                     //设置rtp M 位;    
  370.                     rtp_hdr->marker = 0;    
  371.                     //设置FU INDICATOR,并将这个HEADER填入sendbuf[12]    
  372.                     fu_ind          = (FU_INDICATOR*)&sendbuf[12]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;    
  373.                     fu_ind->F        = n->forbidden_bit>>7;    
  374.                     fu_ind->NRI  = n->nal_reference_idc>>5;    
  375.                     fu_ind->TYPE     = 28;                             
  376.                     //设置FU HEADER,并将这个HEADER填入sendbuf[13]    
  377.                     fu_hdr          = (FU_HEADER*)&sendbuf[13];    
  378.                     //fu_hdr->E=0;    
  379.                     fu_hdr->R        = 0;    
  380.                     fu_hdr->S        = 0;    
  381.                     fu_hdr->E        = 0;    
  382.                     fu_hdr->TYPE     = n->nal_unit_type;                      
  383.                     nalu_payload    = &sendbuf[14];               //同理将sendbuf[14]的地址赋给nalu_payload    
  384.                     memcpy(nalu_payload, n->buf + t * 1400 + 11400);//去掉起始前缀的nalu剩余内容写入sendbuf[14]开始的字符串。    
  385.                     bytes = 1400 + 12 + 2;                      //获得sendbuf的长度,为nalu的长度(除去原NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节    
  386. #ifdef DEBUG_LOG  
  387.                     {  
  388.                         int ii = 0;  
  389.                         printf("-----------------------------------\n");  
  390.                         for(ii=0;ii<22;ii++)  
  391.                         {  
  392.                             printf("buf%d=%x  ",ii,(unsigned char)sendbuf[ii]);  
  393.                         }  
  394.                         printf("\n");  
  395.                         printf("------------------------------------\n");  
  396.                     }     
  397. #endif  
  398.                     sendto(socket1, sendbuf, bytes, 0, (struct sockaddr *)&server, sizeof(server));               
  399.                     //fwrite(sendbuf, bytes, 1, stream);    
  400.                     //usleep(20000);    
  401.                     t++;    
  402.                 }    
  403.             }    
  404.             usleep(40000);    
  405.         }       
  406.     }    
  407.     FreeNALU(n);    
  408.     fclose(bits);  
  409.     bits = NULL;  
  410.     //fclose(stream);  
  411.     return 0;    
  412. }    
  413.   
  414. static int FindStartCode2 (unsigned charchar *Buf)    
  415. {    
  416.  if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1return 0//判断是否为0x000001,如果是返回1    
  417.  else return 1;    
  418. }    
  419.   
  420. static int FindStartCode3 (unsigned charchar *Buf)    
  421. {    
  422.  if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1return 0;//判断是否为0x00000001,如果是返回1    
  423.  else return 1;    
  424. }   

    上面的代码是实现h.264数据的实时打包和发送,接收和显示端,我们可以使用VLC或是Mplayer播放器来实现。完整的工程目录为:   

[python]  view plain  copy
  1. rtp_h264  
  2. ├── h264  
  3. │   └── test.h264  
  4. ├── Makefile  
  5. ├── rtp.c  
  6. ├── rtp.h  
  7. └── sdp  
  8.     ├── mplayer.sdp  
  9.     └── vlc.sdp  

    编译工程,在运行工程之前,先将sdp 文件拖到播放器中,然后运行程序就可以直接看到视频画面。


这里需要注意几点

    (1)使用VLC播放器会出现有点卡顿,且播放时间显示异常,但是用MPlayer播放器不会存在这些问题

    (2)程序中的usleep() 延时是必须的,如果发送太快,播放器会处理不过来

    (3)H.264 的SPS和PPS数据是h.264文件的开始两帧,需要客户端先运行才能获取到。


完整的工程(包括sdp文件)可以在这里下载:Linux 下RTP实时打包发送H.264码流


这篇关于Linux 下实现RTP实时打包发送H.264视频文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040689

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

hevc和H.264格式的区别

HEVC(High Efficiency Video Coding)和H.264(也称为Advanced Video Coding,AVC)都是视频压缩标准,但它们之间存在一些显著的区别,主要集中在压缩效率、资源需求和兼容性方面。 压缩效率 HEVC,也被称为H.265,提供了比H.264更高的压缩效率。这意味着在相同的视频质量下,HEVC能够以大约一半的比特率进行编码,从而减少存储空间需求和

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主