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-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo