zoukankan      html  css  js  c++  java
  • ffmpeg处理rtmp/文件/rtsp的推流和拉流

    ffmpeg处理rtmp/文件/rtsp的推流和拉流

     
    本demo演示了利用ffmpeg从服务器拉流或本地文件读取流,更改流url或文件类型名称发送回服务器或存到本地的作用。
    由于本程序只写了3个小时,还要忙别的,所以会有一些bug和优化的地方。不过demo的意义已经达到了。
     
    [cpp] view plain copy
     
    1. //info.h  
    2. #ifndef __INFO_H__  
    3. #define __INFO_H__  
    4.   
    5. #include <string.h>   
    6. #include <stdio.h>  
    7.   
    8. #endif  

    [cpp] view plain copy
     
    1. //ffmpeg.h  
    2. #ifndef __FFMPEG_H__  
    3. #define __FFMPEG_H__  
    4.   
    5. #include "info.h"  
    6.   
    7. extern "C"  
    8. {  
    9. #include "libavformat/avformat.h"  
    10. #include "libavformat/avio.h"  
    11. #include "libavcodec/avcodec.h"  
    12. #include "libswscale/swscale.h"  
    13. #include "libavutil/avutil.h"  
    14. #include "libavutil/mathematics.h"  
    15. #include "libswresample/swresample.h"  
    16. #include "libavutil/opt.h"  
    17. #include "libavutil/channel_layout.h"  
    18. #include "libavutil/samplefmt.h"  
    19. #include "libavdevice/avdevice.h"  //摄像头所用  
    20. #include "libavfilter/avfilter.h"  
    21. #include "libavutil/error.h"  
    22. #include "libavutil/mathematics.h"    
    23. #include "libavutil/time.h"    
    24. #include "inttypes.h"  
    25. #include "stdint.h"  
    26. };  
    27.   
    28. #pragma comment(lib,"avformat.lib")  
    29. #pragma comment(lib,"avcodec.lib")  
    30. #pragma comment(lib,"avdevice.lib")  
    31. #pragma comment(lib,"avfilter.lib")  
    32. #pragma comment(lib,"avutil.lib")  
    33. #pragma comment(lib,"postproc.lib")  
    34. #pragma comment(lib,"swresample.lib")  
    35. #pragma comment(lib,"swscale.lib")  
    36.   
    37.   
    38. //#define INPUTURL   "test.flv"   
    39. #define INPUTURL     "rtmp://test1.com:1935/myapp/teststream1"   
    40. //#define OUTPUTURL  "testnew.flv";   
    41. #define OUTPUTURL    "rtmp://test1.com:1935/myapp/teststream1new"  
    42.   
    43. //video param  
    44. extern int m_dwWidth;  
    45. extern int m_dwHeight;  
    46. extern double m_dbFrameRate;  //帧率                                                    
    47. extern AVCodecID video_codecID;  
    48. extern AVPixelFormat video_pixelfromat;  
    49. extern char spspps[100];  
    50. extern int spspps_size;  
    51.   
    52. //audio param  
    53. extern int m_dwChannelCount; //声道  
    54. extern int m_dwBitsPerSample; //样本  
    55. extern int m_dwFrequency;     //采样率  
    56. extern AVCodecID audio_codecID;  
    57. extern int audio_frame_size;  
    58.   
    59. #define AUDIO_ID            0                                                 //packet 中的ID ,如果先加入音频 pocket 则音频是 0  视频是1,否则相反  
    60. #define VEDIO_ID            1  
    61.   
    62. extern int nRet;                                                              //状态标志  
    63. extern AVFormatContext* icodec;   
    64. extern AVInputFormat* ifmt;  
    65. extern char szError[256];                                                     //错误字符串  
    66. extern AVFormatContext* oc ;                                                  //输出流context  
    67. extern AVOutputFormat* ofmt;  
    68. extern AVStream* video_st;  
    69. extern AVStream* audio_st;  
    70. extern AVCodec *audio_codec;  
    71. extern AVCodec *video_codec;  
    72. extern int video_stream_idx;  
    73. extern int audio_stream_idx;  
    74. extern AVPacket pkt;     
    75. extern AVBitStreamFilterContext * vbsf_aac_adtstoasc;                         //aac->adts to asc过滤器  
    76. static int  m_nVideoTimeStamp = 0;  
    77. static int  m_nAudioTimeStamp = 0;  
    78.   
    79. int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe);  
    80. int InitOutput();  
    81. AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec);   
    82. int OpenCodec(AVStream * istream, AVCodec * icodec); //打开编解码器  
    83. void read_frame(AVFormatContext *oc);    //从文件读取一帧数据根据ID读取不同的帧  
    84. void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t); //这个是根据传过来的buf 和size 写入文件  
    85. int Es2Mux_2(); //通过时间戳控制写入音视频帧顺序  
    86. int UintInput();  
    87. int UintOutput();  
    88.   
    89. #endif  
     
    [cpp] view plain copy
     
    1. //ffmpeg.cpp  
    2. #include "ffmpeg.h"  
    3.   
    4. int nRet = 0;  
    5. AVFormatContext* icodec = NULL;   
    6. AVInputFormat* ifmt = NULL;  
    7. char szError[256];   
    8. AVFormatContext* oc = NULL;  
    9. AVOutputFormat* ofmt = NULL;  
    10. AVStream * video_st = NULL;  
    11. AVStream * audio_st = NULL;  
    12. AVCodec *audio_codec;  
    13. AVCodec *video_codec;  
    14. double audio_pts = 0.0;  
    15. double video_pts = 0.0;  
    16. int video_stream_idx = -1;  
    17. int audio_stream_idx = -1;  
    18. AVPacket pkt;  
    19. AVBitStreamFilterContext * vbsf_aac_adtstoasc = NULL;  
    20.   
    21. //video param  
    22. int m_dwWidth = 0;  
    23. int m_dwHeight = 0;  
    24. double m_dbFrameRate = 25.0;  //帧率                                                    
    25. AVCodecID video_codecID = AV_CODEC_ID_H264;  
    26. AVPixelFormat video_pixelfromat = AV_PIX_FMT_YUV420P;  
    27. char spspps[100];  
    28. int spspps_size = 0;  
    29.   
    30. //audio param  
    31. int m_dwChannelCount = 2;      //声道  
    32. int m_dwBitsPerSample = 16;    //样本  
    33. int m_dwFrequency = 44100;     //采样率  
    34. AVCodecID audio_codecID = AV_CODEC_ID_AAC;  
    35. int audio_frame_size  = 1024;  
    36.   
    37.   
    38. int InitInput(char * Filename,AVFormatContext ** iframe_c,AVInputFormat** iinputframe)  
    39. {  
    40.     int i = 0;  
    41.     nRet = avformat_open_input(iframe_c, Filename,(*iinputframe), NULL);  
    42.     if (nRet != 0)  
    43.     {  
    44.         av_strerror(nRet, szError, 256);  
    45.         printf(szError);  
    46.         printf(" ");  
    47.         printf("Call avformat_open_input function failed! ");  
    48.         return 0;  
    49.     }  
    50.     if (av_find_stream_info(*iframe_c) < 0)  
    51.     {  
    52.         printf("Call av_find_stream_info function failed! ");  
    53.         return 0;  
    54.     }  
    55.     //输出视频信息  
    56.     av_dump_format(*iframe_c, -1, Filename, 0);  
    57.   
    58.     //添加音频信息到输出context  
    59.     for (i = 0; i < (*iframe_c)->nb_streams; i++)  
    60.     {  
    61.         if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)  
    62.         {  
    63.             video_stream_idx = i;  
    64.             m_dwWidth = (*iframe_c)->streams[i]->codec->width;  
    65.             m_dwHeight = (*iframe_c)->streams[i]->codec->height;  
    66.             m_dbFrameRate = av_q2d((*iframe_c)->streams[i]->r_frame_rate);  
    67.             video_codecID = (*iframe_c)->streams[i]->codec->codec_id;  
    68.             video_pixelfromat = (*iframe_c)->streams[i]->codec->pix_fmt;  
    69.             spspps_size = (*iframe_c)->streams[i]->codec->extradata_size;  
    70.             memcpy(spspps,(*iframe_c)->streams[i]->codec->extradata,spspps_size);  
    71.         }  
    72.         else if ((*iframe_c)->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)  
    73.         {  
    74.             audio_stream_idx = i;  
    75.             m_dwChannelCount = (*iframe_c)->streams[i]->codec->channels;  
    76.             switch ((*iframe_c)->streams[i]->codec->sample_fmt)  
    77.             {  
    78.             case AV_SAMPLE_FMT_U8:  
    79.                 m_dwBitsPerSample  = 8;  
    80.                 break;  
    81.             case AV_SAMPLE_FMT_S16:  
    82.                 m_dwBitsPerSample  = 16;  
    83.                 break;  
    84.             case AV_SAMPLE_FMT_S32:  
    85.                 m_dwBitsPerSample  = 32;  
    86.                 break;  
    87.             default:  
    88.                 break;  
    89.             }  
    90.             m_dwFrequency = (*iframe_c)->streams[i]->codec->sample_rate;  
    91.             audio_codecID = (*iframe_c)->streams[i]->codec->codec_id;  
    92.             audio_frame_size = (*iframe_c)->streams[i]->codec->frame_size;  
    93.         }  
    94.     }  
    95.     return 1;  
    96. }  
    97.   
    98. int InitOutput()  
    99. {  
    100.     int i = 0;  
    101.     /* allocate the output media context */  
    102.     avformat_alloc_output_context2(&oc, NULL, "flv", OUTPUTURL);  
    103.     if (!oc)   
    104.     {  
    105.         return getchar();  
    106.     }  
    107.     ofmt = oc->oformat;  
    108.   
    109.     /* open the output file, if needed */  
    110.     if (!(ofmt->flags & AVFMT_NOFILE))  
    111.     {  
    112.         if (avio_open(&oc->pb, OUTPUTURL, AVIO_FLAG_WRITE) < 0)  
    113.         {  
    114.             printf("Could not open '%s' ", OUTPUTURL);  
    115.             return getchar();  
    116.         }  
    117.     }  
    118.   
    119.     //添加音频信息到输出context  
    120.     if(audio_stream_idx != -1)  
    121.     {  
    122.         ofmt->audio_codec = audio_codecID;  
    123.         audio_st = Add_output_stream_2(oc, AVMEDIA_TYPE_AUDIO,audio_codecID,&audio_codec);  
    124.     }  
    125.   
    126.     //添加视频信息到输出context  
    127.     ofmt->video_codec = video_codecID;  
    128.     video_st = Add_output_stream_2(oc, AVMEDIA_TYPE_VIDEO,video_codecID,&video_codec);  
    129.   
    130.     if (OpenCodec(video_st,video_codec) < 0)   //打开视频编码器  
    131.     {  
    132.         printf("can not open video codec ");  
    133.         return getchar();  
    134.     }  
    135.   
    136.     if(audio_stream_idx != -1)  
    137.     {  
    138.         if (OpenCodec(audio_st,audio_codec) < 0)   //打开音频编码器  
    139.         {  
    140.             printf("can not open audio codec ");  
    141.             return getchar();  
    142.         }  
    143.     }  
    144.   
    145.     av_dump_format(oc, 0, OUTPUTURL, 1);  
    146.   
    147.     if (avformat_write_header(oc, NULL))  
    148.     {  
    149.         printf("Call avformat_write_header function failed. ");  
    150.         return 0;  
    151.     }  
    152.   
    153.     if(audio_stream_idx != -1)  
    154.     {  
    155.         if ((strstr(oc->oformat->name, "flv") != NULL) ||   
    156.             (strstr(oc->oformat->name, "mp4") != NULL) ||   
    157.             (strstr(oc->oformat->name, "mov") != NULL) ||  
    158.             (strstr(oc->oformat->name, "3gp") != NULL))      
    159.         {  
    160.             if (audio_st->codec->codec_id == AV_CODEC_ID_AAC) //AV_CODEC_ID_AAC  
    161.             {  
    162.                 vbsf_aac_adtstoasc =  av_bitstream_filter_init("aac_adtstoasc");    
    163.             }  
    164.         }   
    165.     }  
    166.     if(vbsf_aac_adtstoasc == NULL)    
    167.     {    
    168.         return -1;    
    169.     }   
    170.     return 1;  
    171. }  
    172.   
    173. AVStream * Add_output_stream_2(AVFormatContext* output_format_context,AVMediaType codec_type_t, AVCodecID codecID,AVCodec **codec)  
    174. {  
    175.     AVCodecContext* output_codec_context = NULL;  
    176.     AVStream * output_stream = NULL;  
    177.   
    178.     /* find the encoder */  
    179.     *codec = avcodec_find_encoder(codecID);  
    180.     if (!(*codec))   
    181.     {  
    182.         return NULL;  
    183.     }  
    184.     output_stream = avformat_new_stream(output_format_context, *codec);  
    185.     if (!output_stream)  
    186.     {  
    187.         return NULL;  
    188.     }  
    189.   
    190.     output_stream->id = output_format_context->nb_streams - 1;  
    191.     output_codec_context = output_stream->codec;  
    192.     output_codec_context->codec_id = codecID;  
    193.     output_codec_context->codec_type = codec_type_t;  
    194.   
    195.     switch (codec_type_t)  
    196.     {  
    197.     case AVMEDIA_TYPE_AUDIO:  
    198.         AVRational CodecContext_time_base;  
    199.         CodecContext_time_base.num = 1;  
    200.         CodecContext_time_base.den = m_dwFrequency;  
    201.         output_stream->time_base = CodecContext_time_base;  
    202.         output_codec_context->time_base = CodecContext_time_base;  
    203.         output_stream->start_time = 0;  
    204.         output_codec_context->sample_rate = m_dwFrequency;  
    205.         output_codec_context->channels = m_dwChannelCount;  
    206.         output_codec_context->frame_size = audio_frame_size;  
    207.         switch (m_dwBitsPerSample)  
    208.         {  
    209.         case 8:  
    210.             output_codec_context->sample_fmt  = AV_SAMPLE_FMT_U8;  
    211.             break;  
    212.         case 16:  
    213.             output_codec_context->sample_fmt  = AV_SAMPLE_FMT_S16;  
    214.             break;  
    215.         case 32:  
    216.             output_codec_context->sample_fmt  = AV_SAMPLE_FMT_S32;  
    217.             break;  
    218.         default:  
    219.             break;  
    220.         }  
    221.         output_codec_context->block_align = 0;  
    222.         if(! strcmp( output_format_context-> oformat-> name,  "mp4" ) ||  
    223.             !strcmp (output_format_context ->oformat ->name , "mov" ) ||  
    224.             !strcmp (output_format_context ->oformat ->name , "3gp" ) ||  
    225.             !strcmp (output_format_context ->oformat ->name , "flv" ))  
    226.         {  
    227.             output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;  
    228.         }  
    229.         break;  
    230.     case AVMEDIA_TYPE_VIDEO:  
    231.         AVRational r_frame_rate_t;  
    232.         r_frame_rate_t.num = 1000;  
    233.         r_frame_rate_t.den = (int)(m_dbFrameRate * 1000);  
    234.         output_stream->time_base = r_frame_rate_t;  
    235.         output_codec_context->time_base = r_frame_rate_t;  
    236.   
    237.         AVRational r_frame_rate_s;  
    238.         r_frame_rate_s.num = (int)(m_dbFrameRate * 1000);  
    239.         r_frame_rate_s.den = 1000;  
    240.         output_stream->r_frame_rate = r_frame_rate_s;  
    241.   
    242.         output_stream->start_time = 0;  
    243.         output_codec_context->pix_fmt = video_pixelfromat;  
    244.         output_codec_context->width = m_dwWidth;  
    245.         output_codec_context->height = m_dwHeight;  
    246.         output_codec_context->extradata = (uint8_t *)spspps;  
    247.         output_codec_context->extradata_size = spspps_size;  
    248.         //这里注意不要加头,demux的时候 h264filter过滤器会改变文件本身信息  
    249.         //这里用output_codec_context->extradata 来显示缩略图  
    250.         //if(! strcmp( output_format_context-> oformat-> name,  "mp4" ) ||  
    251.         //  !strcmp (output_format_context ->oformat ->name , "mov" ) ||  
    252.         //  !strcmp (output_format_context ->oformat ->name , "3gp" ) ||  
    253.         //  !strcmp (output_format_context ->oformat ->name , "flv" ))  
    254.         //{  
    255.         //  output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;  
    256.         //}  
    257.         break;  
    258.     default:  
    259.         break;  
    260.     }  
    261.     return output_stream;  
    262. }  
    263.   
    264. int OpenCodec(AVStream * istream, AVCodec * icodec)  
    265. {  
    266.     AVCodecContext *c = istream->codec;  
    267.     nRet = avcodec_open2(c, icodec, NULL);   
    268.     return nRet;  
    269. }  
    270.   
    271. int UintInput()  
    272. {  
    273.     /* free the stream */  
    274.     av_free(icodec);  
    275.     return 1;  
    276. }  
    277.   
    278. int UintOutput()  
    279. {  
    280.     int i = 0;  
    281.     nRet = av_write_trailer(oc);  
    282.     if (nRet < 0)  
    283.     {  
    284.         av_strerror(nRet, szError, 256);  
    285.         printf(szError);  
    286.         printf(" ");  
    287.         printf("Call av_write_trailer function failed ");  
    288.     }  
    289.     if (vbsf_aac_adtstoasc !=NULL)  
    290.     {  
    291.         av_bitstream_filter_close(vbsf_aac_adtstoasc);   
    292.         vbsf_aac_adtstoasc = NULL;  
    293.     }  
    294.     av_dump_format(oc, -1, OUTPUTURL, 1);   
    295.     /* Free the streams. */  
    296.     for (i = 0; i < oc->nb_streams; i++)   
    297.     {  
    298.         av_freep(&oc->streams[i]->codec);  
    299.         av_freep(&oc->streams[i]);  
    300.     }  
    301.     if (!(ofmt->flags & AVFMT_NOFILE))  
    302.     {  
    303.         /* Close the output file. */  
    304.         avio_close(oc->pb);  
    305.     }  
    306.     av_free(oc);  
    307.     return 1;  
    308. }  
    309.   
    310. void read_frame(AVFormatContext *oc)  
    311. {  
    312.     int ret = 0;  
    313.   
    314.     ret = av_read_frame(icodec, &pkt);  
    315.   
    316.     if (pkt.stream_index == video_stream_idx)  
    317.     {  
    318.         write_frame_3(oc,VEDIO_ID,pkt);  
    319.     }  
    320.     else if (pkt.stream_index == audio_stream_idx)  
    321.     {  
    322.         write_frame_3(oc,AUDIO_ID,pkt);  
    323.     }  
    324. }  
    325.   
    326. void write_frame_3(AVFormatContext *oc,int ID,AVPacket pkt_t)  
    327. {  
    328.     int64_t pts = 0, dts = 0;  
    329.     int nRet = -1;  
    330.     AVRational time_base_t;  
    331.     time_base_t.num = 1;  
    332.     time_base_t.den = 1000;  
    333.   
    334.     if(ID == VEDIO_ID)  
    335.     {  
    336.         AVPacket videopacket_t;  
    337.         av_init_packet(&videopacket_t);  
    338.   
    339.         if (av_dup_packet(&videopacket_t) < 0)  
    340.         {  
    341.             av_free_packet(&videopacket_t);  
    342.         }  
    343.   
    344.         videopacket_t.pts = pkt_t.pts;  
    345.         videopacket_t.dts = pkt_t.dts;  
    346.         videopacket_t.pos = 0;  
    347.         videopacket_t.priv = 0;  
    348.         videopacket_t.flags = 1;  
    349.         videopacket_t.convergence_duration = 0;  
    350.         videopacket_t.side_data_elems = 0;  
    351.         videopacket_t.stream_index = VEDIO_ID;  
    352.         videopacket_t.duration = 0;  
    353.         videopacket_t.data = pkt_t.data;  
    354.         videopacket_t.size = pkt_t.size;  
    355.         nRet = av_interleaved_write_frame(oc, &videopacket_t);  
    356.         if (nRet != 0)  
    357.         {  
    358.             printf("error av_interleaved_write_frame _ video ");  
    359.         }  
    360.         av_free_packet(&videopacket_t);  
    361.     }  
    362.     else if(ID == AUDIO_ID)  
    363.     {  
    364.         AVPacket audiopacket_t;  
    365.         av_init_packet(&audiopacket_t);  
    366.   
    367.         if (av_dup_packet(&audiopacket_t) < 0)  
    368.         {  
    369.             av_free_packet(&audiopacket_t);  
    370.         }  
    371.   
    372.         audiopacket_t.pts = pkt_t.pts;  
    373.         audiopacket_t.dts = pkt_t.dts;  
    374.         audiopacket_t.pos = 0;  
    375.         audiopacket_t.priv = 0;  
    376.         audiopacket_t.flags = 1;  
    377.         audiopacket_t.duration = 0;  
    378.         audiopacket_t.convergence_duration = 0;  
    379.         audiopacket_t.side_data_elems = 0;  
    380.         audiopacket_t.stream_index = AUDIO_ID;  
    381.         audiopacket_t.duration = 0;  
    382.         audiopacket_t.data = pkt_t.data;  
    383.         audiopacket_t.size = pkt_t.size;  
    384.   
    385.         //添加过滤器  
    386.         if(! strcmp( oc-> oformat-> name,  "mp4" ) ||  
    387.             !strcmp (oc ->oformat ->name , "mov" ) ||  
    388.             !strcmp (oc ->oformat ->name , "3gp" ) ||  
    389.             !strcmp (oc ->oformat ->name , "flv" ))  
    390.         {  
    391.             if (audio_st->codec->codec_id == AV_CODEC_ID_AAC)  
    392.             {  
    393.                 if (vbsf_aac_adtstoasc != NULL)  
    394.                 {  
    395.                     AVPacket filteredPacket = audiopacket_t;   
    396.                     int a = av_bitstream_filter_filter(vbsf_aac_adtstoasc,                                             
    397.                         audio_st->codec, NULL,&filteredPacket.data, &filteredPacket.size,  
    398.                         audiopacket_t.data, audiopacket_t.size, audiopacket_t.flags & AV_PKT_FLAG_KEY);   
    399.                     if (a >  0)               
    400.                     {                  
    401.                         av_free_packet(&audiopacket_t);   
    402.                         filteredPacket.destruct = av_destruct_packet;    
    403.                         audiopacket_t = filteredPacket;               
    404.                     }     
    405.                     else if (a == 0)  
    406.                     {  
    407.                         audiopacket_t = filteredPacket;     
    408.                     }  
    409.                     else if (a < 0)              
    410.                     {                  
    411.                         fprintf(stderr, "%s failed for stream %d, codec %s",  
    412.                             vbsf_aac_adtstoasc->filter->name,audiopacket_t.stream_index,audio_st->codec->codec ?  audio_st->codec->codec->name : "copy");  
    413.                         av_free_packet(&audiopacket_t);     
    414.   
    415.                     }  
    416.                 }  
    417.             }  
    418.         }  
    419.         nRet = av_interleaved_write_frame(oc, &audiopacket_t);  
    420.         if (nRet != 0)  
    421.         {  
    422.             printf("error av_interleaved_write_frame _ audio ");  
    423.         }  
    424.         av_free_packet(&audiopacket_t);  
    425.     }  
    426. }  
    427.   
    428. int Es2Mux_2()  
    429. {  
    430.     for (;;)  
    431.     {  
    432.         read_frame(oc);  
    433.     }  
    434.     return 1;  
    435. }  
    [cpp] view plain copy
     
    1. //main.cpp  
    2. #include "ffmpeg.h"  
    3.   
    4. int main(int argc ,char ** argv)  
    5. {  
    6.     av_register_all();  
    7.     avformat_network_init();  
    8.   
    9.     InitInput(INPUTURL,&icodec,&ifmt);  
    10.     InitOutput();  
    11.     printf("--------程序运行开始---------- ");  
    12.     //////////////////////////////////////////////////////////////////////////  
    13.     Es2Mux_2();  
    14.     //////////////////////////////////////////////////////////////////////////  
    15.     UintOutput();  
    16.     UintInput();  
    17.     printf("--------程序运行结束---------- ");  
    18.     printf("-------请按任意键退出--------- ");  
    19.     return getchar();  
    20. }  
  • 相关阅读:
    Megcup 2017 决赛第一题 规则
    折正方体-------------给你出道题
    论公平
    Vue 将一个组件嵌入到另一个组件中
    Vue下的index.html
    Vue下的package.json
    Vue 一些用法
    chkconfig 系统服务管理
    Windows和Linux下的 telnet命令 不是内部或外部命令的解决方案
    JSONPlaceholder
  • 原文地址:https://www.cnblogs.com/lidabo/p/6932134.html
Copyright © 2011-2022 走看看