FFMPEG 音频解码和绘制波形

Posted

技术标签:

【中文标题】FFMPEG 音频解码和绘制波形【英文标题】:FFMPEG Audio decode and draw waveform 【发布时间】:2016-04-05 11:41:01 【问题描述】:

我正在尝试使用ffmpeg解码音频并绘制波形,输入音频数据是AV_SAMPLE_FMT_S16P,基本上我是按照教程here进行的,使用libao播放音频很好。现在我需要使用解码数据绘制波形,目前我正在编写左右通道以分离 csv 文件并在 excel 上绘图。但波形与 Audacity 中使用相同音频剪辑显示的波形有所不同。当我分析写在 csv 上的值时,大多数值都接近 uint16_t(65535) 的最大值,但还有一些其他较低的值,但大多数是高峰值。

这里是源代码,

    const char* input_filename="/home/user/Music/Clip.mp3";
    av_register_all();
    AVFormatContext* container=avformat_alloc_context();
    if(avformat_open_input(&container,input_filename,NULL,NULL)<0)
        endApp("Could not open file");
    

    if(avformat_find_stream_info(container, NULL)<0)
        endApp("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)
        endApp("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)
        endApp("cannot find codec!");
    

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



    AVPacket packet;
    av_init_packet(&packet);

    //AVFrame *frame=avcodec_alloc_frame();
    AVFrame *frame=av_frame_alloc();

    int buffer_size=AVCODEC_MAX_AUDIO_FRAME_SIZE+ FF_INPUT_BUFFER_PADDING_SIZE;

    // MSVC can't do variable size allocations on stack, ohgodwhy
    uint8_t *buffer = new uint8_t[buffer_size];
    packet.data=buffer;
    packet.size =buffer_size;

    int frameFinished=0;

    int plane_size;

    ofstream fileCh1,fileCh2;
    fileCh1.open ("ch1.csv");
    fileCh2.open ("ch2.csv");

    AVSampleFormat sfmt=ctx->sample_fmt;

    while(av_read_frame(container,&packet)>=0)
    

        if(packet.stream_index==stream_id)
            int len=avcodec_decode_audio4(ctx,frame,&frameFinished,&packet);
            int data_size = av_samples_get_buffer_size(&plane_size, ctx->channels,
                                                frame->nb_samples,
                                                ctx->sample_fmt, 1);


            if(frameFinished)
                int write_p=0;
                // QTime t;
                switch (sfmt)

                    case AV_SAMPLE_FMT_S16P:

                        for (int nb=0;nb<plane_size/sizeof(uint16_t);nb++)
                            for (int ch = 0; ch < ctx->channels; ch++) 
                                if(ch==0)
                                    fileCh1 <<((uint16_t *) frame->extended_data[ch])[nb]<<"\n";
                                else if(ch==1)
                                    fileCh2 <<((uint16_t *) frame->extended_data[ch])[nb]<<"\n";
                            
                        

                        break;

                
             else 
                DBG("frame failed");
            
        


        av_free_packet(&packet);
    
    fileCh1.close();
    fileCh2.close();
    avcodec_close(ctx);
    avformat_close_input(&container);
    delete buffer;
    return 0;

编辑:

我已经附加了使用opencv绘制的波形图像,这里我将样本值缩放到0-255范围,并将值127作为0(Y轴)。现在为每个样本绘制从 (x,127) 到 (x,sample value) 的线,其中 x=1,2,3,...

【问题讨论】:

样本值的转换似乎失去了精度。使用printf 格式化样本值时会得到什么输出? printf("%u ", (unsigned short)out[write_p]);cout&lt;&lt;(unsigned short)out[write_p];cout&lt;&lt;(int)out[write_p]; 等三种方法打印值给我相同的值。但这些值与 csv 文件输出不同。 很好奇。当格式为 16 位签名时,为什么要转换为 uint16_t? 我没有注意到,其实我是按照0xdeafc0de.wordpress.com/2013/12/19/…这里的代码没有修改,可能是这个问题,我会检查它并告诉你结果。跨度> 如果您想轻松处理几乎任何格式,请考虑使用本教程中描述的功能:rodic.fr/blog/… 【参考方案1】:

问题是当样本格式被签名时转换为uint16_t(AV_SAMPLE_FMT_S16P,其中 S 表示签名)。因此,-1 将作为 2147483648 写入文件,依此类推。

要修复它,请更改行:

fileCh1 <<((uint16_t *) frame->extended_data[ch])[nb]<<"\n";

到:

fileCh1 <<((int16_t *) frame->extended_data[ch])[nb]<<"\n";

【讨论】:

以上是关于FFMPEG 音频解码和绘制波形的主要内容,如果未能解决你的问题,请参考以下文章

ffmpeg 波形音频和 avi 视频格式合并

FFmpeg进行音频的解码和播放

在iOS中绘制录音音频波形图

iOS利用FFmpeg解码音频数据并播放

FFMPEG:设置音频波形颜色的不透明度

绘制音频波形和频谱图重叠