ffmpeg解码出音频,采用SDL2播放的两种方式

下面介绍ffmpeg音频解码并采用SDL2播放的两种方式。
mp4压缩文件数据首先读出avPacket压缩数据包,然后通过解码器,解码出AVframe数据包,AVframe里面就包含了非压缩的视频和音频数据。我们这里只是设计音频的播放。avframe里面的音频数据多数是FLTP格式,而SDL2播放数据是AUDIO_S16SYS数据格式。两种数据完全不一样的组织,FLTP是浮点数据,而SDL要求的是整型数据,还有就是数据在缓存里面左右声道的分布不一样。下面是我的播放器采用两种方式:

1、自己变换FLTP数据

直接看代码,就算把AVframe里面的数据,将浮点变成整形,然后重新排列数据。

    //下面这段主要是直接把FLTP格式语音数据变换为适合SDL播放的数据。
    //这段代码部分来源于,stackoverflow
        /*   short *sample_buffer = (short*)malloc(aFrame->nb_samples * 2 * 2); //
           memset(sample_buffer, 0, aFrame->nb_samples * 4);  //
           printf("in_samples = %d\n", aFrame->nb_samples);

                          int i=0;
                          float *inputChannel0 = (float*)(aFrame->extended_data[0]); 
                           //extended_data【0】左通道; extended_data【1】为右通道

                          // 单声道
                          if( aFrame->channels == 1 ) {
                              for( i=0; inb_samples; i++ ) {
                                  float sample = *inputChannel0++;
                                  if( sample < -1.0f ) {
                                      sample = -1.0f;
                                  } else if( sample > 1.0f ) {
                                      sample = 1.0f;
                                  }

                                  sample_buffer[i] = (int16_t)(sample * 32767.0f);
                              }
                          } else { // 立体声
                              float* inputChannel1 = (float*)(aFrame->extended_data[1]);
                              for( i=0; inb_samples; i++) {
                                  sample_buffer[i*2] = (int16_t)((*inputChannel0++) * 32767.0f);
                                  sample_buffer[i*2+1] = (int16_t)((*inputChannel1++) * 32767.0f);
                              }
                          }

                          audio_buf=(unsigned char *)sample_buffer;
                          audio_len=aFrame->nb_samples*4;
                          while(audio_len>0)  //等待SDL读取数据
                          {
                              SDL_Delay(1);
                          }  */

2、采用重采样技术把FLTP数据变换成适合SDL2d数据

首先、初始化SDL

int VideoPlayer::openSDL()
{
    ///打开SDL,并设置播放的格式为:AUDIO_S16SYS 双声道,44100hz
    ///后期使用ffmpeg解码完音频后,需要重采样成和这个一样的格式,否则播放会有杂音
    SDL_AudioSpec *wanted_spec, *spec;
    int wanted_nb_channels = 2;
     int samplerate = 44100;
 //   int samplerate = out_sample_rate;
    wanted_spec = (SDL_AudioSpec*)SDL_malloc(sizeof(struct SDL_AudioSpec));
    wanted_spec->channels = wanted_nb_channels;
    wanted_spec->samples = 1024;
    wanted_spec->freq = samplerate;
    wanted_spec->format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
    wanted_spec->silence = 0;            // 0指示静音
//    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  // 自定义SDL缓冲区大小
    wanted_spec->callback = sdlAudioCallBackFunc;  // 回调函数
    wanted_spec->userdata = this;                  // 传给上面回调函数的外带数据
    //打开音频设备,第二个参数为播放音频的设备,NULL表示默认设备
    if (SDL_OpenAudio(wanted_spec, NULL) < 0) {
        printf("打开SDL Audio 失败\n");
        return -1;
    }
     return 0;
}

要记住打开SDL设备。
第二、初始化重采样部分。

    swrCtx = swr_alloc();  //分配上下文
    if (!swrCtx)
    {
        printf("swr_alloc error \n");
        return -1;
    }
    //下面设置重新采样输入和输出参数
    swrCtx = swr_alloc_set_opts(NULL,
                    av_get_default_channel_layout(audio_tgt_channels),//输出
                    out_sample_fmt, //编码前你希望的格式
                    out_sample_rate,//输出
                    av_get_default_channel_layout(aCodecCtx->channels), //in_channel_layout, //输入
                    aCodecCtx->sample_fmt,//PCM源文件的采样格式
                    aCodecCtx->sample_rate, //输入
                    0, 0);
    swr_init(swrCtx);
//中间是解码数据
//1、解码数据输入解码器:avcodec_send_packet(pCodecCtx, packet) 
//2、获取解码数据:avcodec_receive_frame(pCodecCtx, pFrame)
         int dst_nb_samples =av_rescale_rnd(aFrame->nb_samples,out_sample_rate,//得到变换后采用点数
              aFrame->sample_rate, AVRounding(0));
        int len=	swr_convert(swrCtx, audio_out_buffer, dst_nb_samples, (const uint8_t**)aFrame->data, aFrame->nb_samples);   //变换器启动
          audio_buf = *audio_out_buffer;  //获得输出数据
          audio_len = len * av_get_bytes_per_sample(out_sample_fmt)*2;	//获取输出音频数据大小

第三、SDL2输出语音

void VideoPlayer::sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len)
{
    VideoPlayer *sd = (VideoPlayer*)userdata;
         SDL_memset(stream, 0, len);
           //缓存数据已播放完毕
        if(sd->audio_len <= 0)
        {
            return;
        }
        //缓存中能播放最大的长度
        len = len >sd->audio_len ?sd->audio_len : len;
        //将数据混合至声卡设备
        SDL_MixAudio(stream, sd->audio_buf, len, SDL_MIX_MAXVOLUME);
        sd->audio_buf += len;//当前播放位置更新
        sd->audio_len -= len;//缓存剩余长度更新
}

语音解码全部流程介绍完毕!!

你可能感兴趣的:(分享,笔记,音视频,语音识别,人工智能)