zoukankan      html  css  js  c++  java
  • 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——解码篇:(二)用ffmpeg解码音频

    其实这篇的内容和(一)用ffmpeg解码视频基本是一样的,重点还是给ffmpeg指定callback函数,而这个函数是从RTSP服务端那里获取音频数据的。

    这里,解码音频的示例代码量之所以比解码视频的略微复杂,主要是因为ffmpeg解码音频时要比解码视频要复杂一点,具体可以参见ffmpeg解码音频示例以及官网示例代码

    具体内容将不再赘述,源码如下:

      1 extern "C"
      2 {
      3 #include <libavcodec/avcodec.h>
      4 #include <libavformat/avformat.h>
      5 #include <libavformat/avio.h>
      6 #include <libswscale/swscale.h>
      7 #include <libswresample/swresample.h>
      8 }
      9 
     10 #include <SDL.h>
     11 #include <SDL_thread.h>
     12 
     13 #ifdef __MINGW32__
     14 #undef main /* Prevents SDL from overriding main() */
     15 #endif
     16 
     17 #include <stdio.h>
     18 #include <assert.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <fcntl.h>
     22 
     23 
     24 // compatibility with newer API
     25 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
     26 #define av_frame_alloc avcodec_alloc_frame
     27 #define av_frame_free avcodec_free_frame
     28 #endif
     29 
     30 #define SDL_AUDIO_BUFFER_SIZE 1024
     31 #define MAX_AUDIO_FRAME_SIZE 192000
     32 
     33 #include <signal.h>
     34 #include "rtspClient.h"
     35 #include <iostream>
     36 #include <string>
     37 
     38 using std::cout;
     39 using std::endl;
     40 
     41 int rtspClientRequest(RtspClient * Client, string url);
     42 int fill_iobuffer(void * opaque, uint8_t * buf, int bufsize);
     43 
     44 typedef struct AudioParams {
     45     int freq;
     46     int channels;
     47     int64_t channel_layout;
     48     enum AVSampleFormat fmt;
     49     int frame_size;
     50     int bytes_per_sec;
     51 } AudioParams;
     52 int sample_rate, nb_channels;
     53 int64_t channel_layout;
     54 AudioParams audio_hw_params_tgt;
     55 AudioParams audio_hw_params_src;
     56 
     57 int resample(AVFrame * af, uint8_t * audio_buf, int * audio_buf_size);
     58 
     59 struct SwrContext * swr_ctx = NULL;
     60 
     61 int resample(AVFrame * af, uint8_t * audio_buf, int * audio_buf_size)
     62 {
     63     int data_size = 0;
     64     int resampled_data_size = 0;
     65     int64_t dec_channel_layout;
     66     data_size = av_samples_get_buffer_size(NULL, 
     67             av_frame_get_channels(af),
     68             af->nb_samples,
     69             (AVSampleFormat)af->format,
     70             1);
     71 
     72     dec_channel_layout =
     73         (af->channel_layout && av_frame_get_channels(af) == av_get_channel_layout_nb_channels(af->channel_layout)) ?
     74         af->channel_layout : av_get_default_channel_layout(av_frame_get_channels(af));
     75     if(     af->format              != audio_hw_params_src.fmt                 ||
     76             af->sample_rate     != audio_hw_params_src.freq              ||
     77             dec_channel_layout     != audio_hw_params_src.channel_layout     ||
     78             !swr_ctx) {
     79         swr_free(&swr_ctx);
     80         swr_ctx = swr_alloc_set_opts(NULL, 
     81                                         audio_hw_params_tgt.channel_layout, (AVSampleFormat)audio_hw_params_tgt.fmt, audio_hw_params_tgt.freq, 
     82                                         dec_channel_layout, (AVSampleFormat)af->format, af->sample_rate, 
     83                                         0, NULL);
     84         if (!swr_ctx || swr_init(swr_ctx) < 0) {
     85             av_log(NULL, AV_LOG_ERROR,
     86                    "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!
    ",
     87                     af->sample_rate, av_get_sample_fmt_name((AVSampleFormat)af->format), av_frame_get_channels(af),
     88                     audio_hw_params_tgt.freq, av_get_sample_fmt_name(audio_hw_params_tgt.fmt), audio_hw_params_tgt.channels);
     89             swr_free(&swr_ctx);
     90             return -1;
     91         }
     92         printf("swr_init
    ");
     93         audio_hw_params_src.channels = av_frame_get_channels(af);
     94         audio_hw_params_src.fmt = (AVSampleFormat)af->format;
     95         audio_hw_params_src.freq = af->sample_rate;
     96     }
     97 
     98     if (swr_ctx) {
     99         const uint8_t **in = (const uint8_t **)af->extended_data;
    100         uint8_t **out = &audio_buf;
    101         int out_count = (int64_t)af->nb_samples * audio_hw_params_tgt.freq / af->sample_rate + 256;
    102         int out_size  = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, out_count, audio_hw_params_tgt.fmt, 0);
    103         int len2;
    104         if (out_size < 0) {
    105             av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed
    ");
    106             return -1;
    107         }
    108         av_fast_malloc(&audio_buf, (unsigned int*)audio_buf_size, out_size);
    109         if (!audio_buf)
    110             return AVERROR(ENOMEM);
    111         len2 = swr_convert(swr_ctx, out, out_count, in, af->nb_samples);
    112         if (len2 < 0) {
    113             av_log(NULL, AV_LOG_ERROR, "swr_convert() failed
    ");
    114             return -1;
    115         }
    116         if (len2 == out_count) {
    117             av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small
    ");
    118             if (swr_init(swr_ctx) < 0)
    119                 swr_free(&swr_ctx);
    120         }
    121         resampled_data_size = len2 * audio_hw_params_tgt.channels * av_get_bytes_per_sample(audio_hw_params_tgt.fmt);
    122     } else {
    123         audio_buf = af->data[0];
    124         resampled_data_size = data_size;
    125     }
    126 
    127     return resampled_data_size;
    128 }
    129 
    130 static void sigterm_handler(int sig)
    131 {
    132     exit(123);
    133 }
    134 
    135 typedef struct PacketQueue {
    136   AVPacketList *first_pkt, *last_pkt;
    137   int nb_packets;
    138   int size;
    139   SDL_mutex *mutex;
    140   SDL_cond *cond;
    141 } PacketQueue;
    142 
    143 PacketQueue audioq;
    144 
    145 int quit = 0;
    146 
    147 void packet_queue_init(PacketQueue *q) {
    148   memset(q, 0, sizeof(PacketQueue));
    149   q->mutex = SDL_CreateMutex();
    150   q->cond = SDL_CreateCond();
    151 }
    152 
    153 int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
    154 
    155   AVPacketList *pkt1;
    156   if(av_dup_packet(pkt) < 0) {
    157     return -1;
    158   }
    159   pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
    160   if (!pkt1)
    161     return -1;
    162   pkt1->pkt = *pkt;
    163   pkt1->next = NULL;
    164   
    165   
    166   SDL_LockMutex(q->mutex);
    167   
    168   if (!q->last_pkt)
    169     q->first_pkt = pkt1;
    170   else
    171     q->last_pkt->next = pkt1;
    172   q->last_pkt = pkt1;
    173   q->nb_packets++;
    174   q->size += pkt1->pkt.size;
    175   SDL_CondSignal(q->cond);
    176   
    177   SDL_UnlockMutex(q->mutex);
    178   return 0;
    179 }
    180 
    181 int packet_queue_put_nullpacket(PacketQueue *q, int stream_index)
    182 {
    183     AVPacket pkt1, *pkt = &pkt1;
    184     av_init_packet(pkt);
    185     pkt->data = NULL;
    186     pkt->size = 0; 
    187     pkt->stream_index = stream_index;
    188     return packet_queue_put(q, pkt);
    189 }
    190 
    191 static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
    192 {
    193   AVPacketList *pkt1;
    194   int ret;
    195   
    196   SDL_LockMutex(q->mutex);
    197   
    198   for(;;) {
    199     
    200     if(quit) {
    201       ret = -1;
    202       break;
    203     }
    204 
    205     pkt1 = q->first_pkt;
    206     if (pkt1) {
    207       q->first_pkt = pkt1->next;
    208       if (!q->first_pkt)
    209     q->last_pkt = NULL;
    210       q->nb_packets--;
    211       q->size -= pkt1->pkt.size;
    212       *pkt = pkt1->pkt;
    213       av_free(pkt1);
    214       ret = 1;
    215       break;
    216     } else if (!block) {
    217       ret = 0;
    218       break;
    219     } else {
    220       SDL_CondWait(q->cond, q->mutex);
    221     }
    222   }
    223   SDL_UnlockMutex(q->mutex);
    224   return ret;
    225 }
    226 
    227 AVFrame frame;
    228 int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {
    229 
    230     static AVPacket pkt;
    231     static uint8_t *audio_pkt_data = NULL;
    232     static int audio_pkt_size = 0;
    233 
    234     int len1, data_size = 0;
    235 
    236     for(;;) {
    237         while(audio_pkt_size > 0) {
    238             int got_frame = 0;
    239             len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
    240             if(len1 < 0) {
    241                 /* if error, skip frame */
    242                 audio_pkt_size = 0;
    243                 break;
    244             }
    245             audio_pkt_data += len1;
    246             audio_pkt_size -= len1;
    247             data_size = 0;
    248             if(got_frame) {
    249                 data_size = resample(&frame, audio_buf, &buf_size);
    250                 // data_size = av_samples_get_buffer_size(NULL, 
    251                 //         aCodecCtx->channels,
    252                 //         frame.nb_samples,
    253                 //         aCodecCtx->sample_fmt,
    254                 //         1);
    255                 assert(data_size <= buf_size);
    256                 // memcpy(audio_buf, frame.data[0], data_size);
    257             }
    258             if(data_size <= 0) {
    259                 /* No data yet, get more frames */
    260                 continue;
    261             }
    262             // memcpy(audio_buf, frame.data[0], data_size);
    263 
    264             /* We have data, return it and come back for more later */
    265             return data_size;
    266         }
    267         if(pkt.data)
    268             av_free_packet(&pkt);
    269 
    270         if(quit) {
    271             return -1;
    272         }
    273 
    274         if(packet_queue_get(&audioq, &pkt, 1) < 0) {
    275             return -1;
    276         }
    277         audio_pkt_data = pkt.data;
    278         audio_pkt_size = pkt.size;
    279     }
    280 }
    281 
    282 void audio_callback(void *userdata, Uint8 *stream, int len) {
    283 
    284   AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
    285   int len1, audio_size;
    286 
    287   static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
    288   static unsigned int audio_buf_size = 0;
    289   static unsigned int audio_buf_index = 0;
    290 
    291   while(len > 0) {
    292       if(audio_buf_index >= audio_buf_size) {
    293           /* We have already sent all our data; get more */
    294           audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
    295           if(audio_size < 0) {
    296               /* If error, output silence */
    297               audio_buf_size = 1024; // arbitrary?
    298               memset(audio_buf, 0, audio_buf_size);
    299           } else {
    300               audio_buf_size = audio_size;
    301           }
    302           audio_buf_index = 0;
    303       }
    304       len1 = audio_buf_size - audio_buf_index;
    305       if(len1 > len)
    306           len1 = len;
    307       memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
    308       len -= len1;
    309       stream += len1;
    310       audio_buf_index += len1;
    311   }
    312 }
    313 
    314 int main(int argc, char *argv[]) {
    315 
    316   AVFormatContext *pFormatCtx = NULL;
    317   int             i, audioStream;
    318   AVPacket        packet;
    319   
    320   AVCodecContext  *aCodecCtxOrig = NULL;
    321   AVCodecContext  *aCodecCtx = NULL;
    322   AVCodec         *aCodec = NULL;
    323 
    324   SDL_Event       event;
    325   SDL_AudioSpec   wanted_spec, spec;
    326 
    327   AVInputFormat *piFmt = NULL;
    328   RtspClient Client;
    329 
    330   signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
    331   signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */
    332 
    333   if(argc != 2) {
    334       cout << "Usage: " << argv[0] << " <URL>" << endl;
    335       cout << "For example: " << endl;
    336       cout << argv[0] << " rtsp://127.0.0.1/ansersion" << endl;
    337       return 1;
    338   }
    339   rtspClientRequest(&Client, argv[1]);
    340   // Register all formats and codecs
    341   av_register_all();
    342   
    343   if(SDL_Init(SDL_INIT_AUDIO)) {
    344     fprintf(stderr, "Could not initialize SDL - %s
    ", SDL_GetError());
    345     exit(1);
    346   }
    347 
    348   // // Open video file
    349   // if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
    350   //   return -1; // Couldn't open file
    351   
    352   pFormatCtx = NULL;
    353   pFormatCtx = avformat_alloc_context();
    354   unsigned char * iobuffer = (unsigned char *)av_malloc(32768);
    355   AVIOContext * avio = avio_alloc_context(iobuffer, 32768, 0, &Client, fill_iobuffer, NULL, NULL);
    356   pFormatCtx->pb = avio;
    357 
    358   if(!avio) {
    359       printf("avio_alloc_context error!!!
    ");
    360       return -1;
    361   }
    362 
    363   if(av_probe_input_buffer(avio, &piFmt, "", NULL, 0, 0) < 0) {
    364       printf("av_probe_input_buffer error!
    ");
    365       return -1;
    366   } else {
    367       printf("probe success
    ");
    368       printf("format: %s[%s]
    ", piFmt->name, piFmt->long_name);
    369   }
    370 
    371   int err = avformat_open_input(&pFormatCtx, "nothing", NULL, NULL);
    372   if(err) {
    373       printf("avformat_open_input error: %d
    ", err);
    374       return -1;
    375   }
    376   // Retrieve stream information
    377   if(avformat_find_stream_info(pFormatCtx, NULL)<0)
    378     return -1; // Couldn't find stream information
    379   
    380   // Dump information about file onto standard error
    381   // av_dump_format(pFormatCtx, 0, argv[1], 0);
    382   av_dump_format(pFormatCtx, 0, "", 0);
    383     
    384   // Find the first video stream
    385   audioStream=-1;
    386   for(i=0; i<pFormatCtx->nb_streams; i++) {
    387     if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
    388        audioStream < 0) {
    389       audioStream=i;
    390     }
    391   }
    392   // if(videoStream==-1)
    393   //   return -1; // Didn't find a video stream
    394   if(audioStream==-1)
    395     return -1;
    396    
    397   aCodecCtxOrig=pFormatCtx->streams[audioStream]->codec;
    398   aCodec = avcodec_find_decoder(aCodecCtxOrig->codec_id);
    399   if(!aCodec) {
    400     fprintf(stderr, "Unsupported codec!
    ");
    401     return -1;
    402   }
    403 
    404   // Copy context
    405   aCodecCtx = avcodec_alloc_context3(aCodec);
    406   if(avcodec_copy_context(aCodecCtx, aCodecCtxOrig) != 0) {
    407     fprintf(stderr, "Couldn't copy codec context");
    408     return -1; // Error copying codec context
    409   }
    410 
    411   avcodec_open2(aCodecCtx, aCodec, NULL);
    412 
    413   sample_rate = aCodecCtx->sample_rate;
    414   nb_channels = aCodecCtx->channels;
    415   channel_layout = aCodecCtx->channel_layout;
    416 
    417   // printf("channel_layout=%" PRId64 "
    ", channel_layout);
    418   printf("channel_layout=%lld
    ", channel_layout);
    419   printf("nb_channels=%d
    ", nb_channels);
    420   printf("freq=%d
    ", sample_rate);
    421 
    422   if (!channel_layout || nb_channels != av_get_channel_layout_nb_channels(channel_layout)) {
    423       channel_layout = av_get_default_channel_layout(nb_channels);
    424       channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    425       printf("correction
    ");
    426   }
    427 
    428   // Set audio settings from codec info
    429   wanted_spec.freq = sample_rate;
    430   wanted_spec.format = AUDIO_S16SYS;
    431   wanted_spec.channels = nb_channels;
    432   wanted_spec.silence = 0;
    433   wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    434   wanted_spec.callback = audio_callback;
    435   wanted_spec.userdata = aCodecCtx;
    436   
    437   if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {
    438     fprintf(stderr, "SDL_OpenAudio: %s
    ", SDL_GetError());
    439     return -1;
    440   }
    441   printf("freq: %d	channels: %d
    ", spec.freq, spec.channels);
    442 
    443   audio_hw_params_tgt.fmt = AV_SAMPLE_FMT_S16;
    444   audio_hw_params_tgt.freq = spec.freq;
    445   audio_hw_params_tgt.channel_layout = channel_layout;
    446   audio_hw_params_tgt.channels =  spec.channels;
    447   audio_hw_params_tgt.frame_size = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, 1, audio_hw_params_tgt.fmt, 1);
    448   audio_hw_params_tgt.bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params_tgt.channels, audio_hw_params_tgt.freq, audio_hw_params_tgt.fmt, 1);
    449   if (audio_hw_params_tgt.bytes_per_sec <= 0 || audio_hw_params_tgt.frame_size <= 0) {
    450       printf("size error
    ");
    451       return -1;
    452   }
    453   audio_hw_params_src = audio_hw_params_tgt;
    454 
    455   // audio_st = pFormatCtx->streams[index]
    456   packet_queue_init(&audioq);
    457   SDL_PauseAudio(0);
    458 
    459   // Read frames and save first five frames to disk
    460   i=0;
    461   int ret = 1;
    462   // while(av_read_frame(pFormatCtx, &packet)>=0) {
    463   while(ret >= 0) {
    464       ret = av_read_frame(pFormatCtx, &packet);
    465 
    466       if(ret < 0) {
    467           /* av_read_frame may get error when RTP data are blocked due to the network busy */
    468           if(ret == AVERROR_EOF || avio_feof(pFormatCtx->pb)) {
    469               packet_queue_put_nullpacket(&audioq, audioStream);
    470               printf("continue ret=%d
    ", ret);
    471               ret = 0;
    472               continue;
    473           }
    474           printf("ret=%d
    ", ret);
    475           break;
    476       }
    477       printf("av_read_frame
    ");
    478       if(packet.stream_index==audioStream) {
    479           packet_queue_put(&audioq, &packet);
    480       } else {
    481           av_free_packet(&packet);
    482       }
    483       // Free the packet that was allocated by av_read_frame
    484       SDL_PollEvent(&event);
    485       switch(event.type) {
    486           case SDL_QUIT:
    487               printf("SDL_QUIT
    ");
    488               quit = 1;
    489               SDL_Quit();
    490               exit(0);
    491               break;
    492           default:
    493               printf("SDL_Default
    ");
    494               break;
    495       }
    496 
    497   }
    498 
    499   while(1) SDL_Delay(1000);
    500   
    501   // Close the codecs
    502   avcodec_close(aCodecCtxOrig);
    503   avcodec_close(aCodecCtx);
    504   
    505   // Close the video file
    506   avformat_close_input(&pFormatCtx);
    507   
    508   return 0;
    509 }
    510 
    511 int rtspClientRequest(RtspClient * Client, string url)
    512 {
    513     if(!Client) return -1;
    514 
    515     // cout << "Start play " << url << endl;
    516     string RtspUri(url);
    517     // string RtspUri("rtsp://192.168.81.145/ansersion");
    518 
    519     /* Set up rtsp server resource URI */
    520     Client->SetURI(RtspUri);
    521     
    522     /* Send DESCRIBE command to server */
    523     Client->DoDESCRIBE();
    524 
    525     /* Parse SDP message after sending DESCRIBE command */
    526     Client->ParseSDP();
    527 
    528     /* Send SETUP command to set up all 'audio' and 'video' 
    529      * sessions which SDP refers. */
    530     Client->DoSETUP();
    531 
    532     /* Send PLAY command to play only 'video' sessions.*/
    533     Client->DoPLAY("audio");
    534 
    535     return 0;
    536 }
    537 
    538 int fill_iobuffer(void * opaque, uint8_t * buf, int bufsize) {
    539     size_t size = 0;
    540     if(!opaque) return -1;
    541     RtspClient * Client = (RtspClient *)opaque;
    542     if(!Client->GetMediaData("audio", buf, &size, bufsize)) size = 0;
    543     printf("fill_iobuffer size: %u
    ", size);
    544     return size;
    545 }

    注:

    1, 兼容myRtspClient-1.2.1及以上版本,且仅支持接收mp2,mp3音频;

    2, 音频解码原理可参见:http://www.cnblogs.com/ansersion/p/5265033.html;

    3, 示例源码编译需要SDL和ffmpeg,具体可参见解码视频的附录二;

    4, 博主编译环境为 x86_64位ubuntu 16.04,以供参考。

    myRtspClient-1.2.3

    ffmpeg-2.8.5

    下载源码以及Makefile

    编译、配置和运行同上一篇:用ffmpeg解码视频

    上一篇               回目录            下一篇

  • 相关阅读:
    [AngularJS] Html ngSanitize, $sce
    [AngularJS] ngModelController render function
    [MODX] 3. Placeholder +
    [MODX] 2. Chunks $
    HTTP-Runoob:HTPP Content-Type
    HTTP-Runoob:HTTP状态码
    HTTP-Runoob:HTTP请求头信息
    HTTP-Runoob:HTTP请求方法
    HTTP-Runoob:HTPP消息结构
    HTTP-Runoob:HTTP简介
  • 原文地址:https://www.cnblogs.com/ansersion/p/5285397.html
Copyright © 2011-2022 走看看