使用libavcodec将.m4a转换为PCM

3
我正在尝试将.m4a文件转换为原始PCM文件,以便在Audacity中播放它。根据AVCodecContext,这是一个44100 Hz的轨道,使用样本格式AV_SAMPLE_FMT_FLTP,我的理解是,当使用avcodec_decode_audio4进行解码时,我应该得到两个浮点值数组(每个通道一个)。我不确定AVCodecContext的bits_per_coded_sample = 16的意义。不幸的是,Audacity播放的结果就像原始音轨和一些白噪声混合在一起。以下是我所做的一些示例代码。请注意,我还添加了一个用于使用有符号16位非交错数据(sample_format = AC_SAMPLE_FMT_S16P)的音轨的情况,Audacity可以正常播放。
int AudioDecoder::decode(std::string path)
{
  const char* input_filename=path.c_str();

  av_register_all();

  AVFormatContext* container=avformat_alloc_context();
  if(avformat_open_input(&container,input_filename,NULL,NULL)<0){
    printf("Could not open file");
  }

  if(avformat_find_stream_info(container, NULL)<0){
      printf("Could not find file info");
  }
  av_dump_format(container,0,input_filename,false);

  int stream_id=-1;
  int i;
  for(i=0;i<container->nb_streams;i++){
    if(container->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
        stream_id=i;
        break;
    }
  }
  if(stream_id==-1){
    printf("Could not find Audio Stream");
  }

  AVDictionary *metadata=container->metadata;
  AVCodecContext *ctx=container->streams[stream_id]->codec;
  AVCodec *codec=avcodec_find_decoder(ctx->codec_id);

  if(codec==NULL){
    printf("cannot find codec!");
  }

  if(avcodec_open2(ctx,codec,NULL)<0){
     printf("Codec cannot be found");
  }

  AVSampleFormat sfmt = ctx->sample_fmt;

  AVPacket packet;
  av_init_packet(&packet);
  AVFrame *frame = avcodec_alloc_frame();

  int buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE+ FF_INPUT_BUFFER_PADDING_SIZE;;
  uint8_t buffer[buffer_size];
  packet.data=buffer;
  packet.size =buffer_size;

  FILE *outfile = fopen("test.raw", "wb");

  int len;
  int frameFinished=0;

  while(av_read_frame(container,&packet) >= 0)
  {
      if(packet.stream_index==stream_id)
      {
        //printf("Audio Frame read \n");
        int len=avcodec_decode_audio4(ctx, frame, &frameFinished, &packet);

        if(frameFinished)
        {       
          if (sfmt==AV_SAMPLE_FMT_S16P)
          { // Audacity: 16bit PCM little endian stereo
            int16_t* ptr_l = (int16_t*)frame->extended_data[0];
            int16_t* ptr_r = (int16_t*)frame->extended_data[1];
            for (int i=0; i<frame->nb_samples; i++)
            {
              fwrite(ptr_l++, sizeof(int16_t), 1, outfile);
              fwrite(ptr_r++, sizeof(int16_t), 1, outfile);
            }
          }
          else if (sfmt==AV_SAMPLE_FMT_FLTP)
          { //Audacity: big endian 32bit stereo start offset 7 (but has noise)
            float* ptr_l = (float*)frame->extended_data[0];
            float* ptr_r = (float*)frame->extended_data[1];
            for (int i=0; i<frame->nb_samples; i++)
            {
                fwrite(ptr_l++, sizeof(float), 1, outfile);
                fwrite(ptr_r++, sizeof(float), 1, outfile);
             }
           }            
        }
    }
}
fclose(outfile);
av_close_input_file(container);
return 0;   

我希望这只是一个天真的转换(最/最不显著的位问题),但目前我还没有能够解决它。请注意,如果是32位或64位浮点数(大端或小端),Audacity只能导入原始浮点数据。

感谢您的任何见解。

2个回答

0

在AC_SAMPLE_FMT_S16P中,您必须使用AV_SAMPLE_FMT_FLTP的转换器

如何将采样率从AV_SAMPLE_FMT_FLTP转换为AV_SAMPLE_FMT_S16?

这是一个工作示例(在pAudioBuffer中,您有白噪声中的pcm数据):

SwrContext *swr;
swr=swr_alloc();
av_opt_set_int(swr,"in_channel_layout",2,0);
av_opt_set_int(swr, "out_channel_layout", 2,  0);
av_opt_set_int(swr, "in_sample_rate",     codecContext->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate",    codecContext->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt",  AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16P,  0);
swr_init(swr);
int16_t * pAudioBuffer = (int16_t *) av_malloc (AUDIO_INBUF_SIZE * 2);
while(av_read_frame(fmt_cntx,&readingPacket)==0){
   if(readingPacket.stream_index==audioSteam->index){
    AVPacket decodingPacket=readingPacket;
        while(decodingPacket.size>0){
     int gotFrame=0;
         int result=avcodec_decode_audio4(codecContext,frame,&gotFrame,&decodingPacket);
     if(result<0){
           av_frame_free(&frame);
       avformat_close_input(&fmt_cntx);
       return null;
        }
        if(result>=0 && gotFrame){
          int data_size=frame->nb_samples*frame->channels;
          swr_convert(swr,&pAudioBuffer,frame->nb_samples,frame->extended_data,frame->nb_samples);
          jshort *outShortArray=(*pEnv)->NewShortArray(pEnv,data_size);
                                (*pEnv)->SetShortArrayRegion(pEnv,outShortArray,0,data_size,pAudioBuffer);
          (*pEnv)->CallVoidMethod(pEnv,pObj,callBackShortBuffer,outShortArray,data_size);
          (*pEnv)->DeleteLocalRef(pEnv,outShortArray);
          decodingPacket.size -= result;
          decodingPacket.data += result;
        }else{
          decodingPacket.size=0;
          decodingPacket.data=NULL;
        }}
    av_free_packet(&decodingPacket);
    }

0

我认为问题出在“nb_samples”上。它并不完全是你需要的。最好尝试使用“linesize [0]”。

例如:

char* ptr_l = (char*)frame->extended_data[0];
char* ptr_r = (char*)frame->extended_data[1];
size_t size = sizeof(float);
for (int i=0; i<frame->linesize[0]; i+=size)
{
   fwrite(ptr_l, size, 1, outfile);
   fwrite(ptr_r, size, 1, outfile);
   ptr_l += size;
   ptr_r += size;    
}

这是针对“float”的,对于“int16_t”也要重复相同的操作。但是“size”将会是“sizeof(int16_t)”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接