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("\n");  
  47.         printf("Call avformat_open_input function failed!\n");  
  48.         return 0;  
  49.     }  
  50.     if (av_find_stream_info(*iframe_c) < 0)  
  51.     {  
  52.         printf("Call av_find_stream_info function failed!\n");  
  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'\n", 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\n");  
  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\n");  
  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.\n");  
  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("\n");  
  287.         printf("Call av_write_trailer function failed\n");  
  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\n");  
  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\n");  
  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("--------程序运行开始----------\n");  
  12.     //////////////////////////////////////////////////////////////////////////  
  13.     Es2Mux_2();  
  14.     //////////////////////////////////////////////////////////////////////////  
  15.     UintOutput();  
  16.     UintInput();  
  17.     printf("--------程序运行结束----------\n");  
  18.     printf("-------请按任意键退出---------\n");  
  19.     return getchar();  
  20. }  
上一篇:SQL 绕圈算法???


下一篇:ASP.NET Web API身份验证和授权