FFmpeg5入门教程15:mp3音频解码为pcm

本系列的之前文章介绍了视频的编解码相关,接下来介绍音频的编解码,本文将mp3音频文件解码为pcm。

使用的mp3音频文件为从网易云音乐上下载的排骨教主的牵丝戏,文件大小为9.6MB。

先看一下文件信息:

$ffprobe test.mp3
Input #0, mp3, from 'test.mp3':
  Metadata:
    encoder         : Lavf57.25.100
    album           : 排骨翻唱合集
    artist          : 排骨教主
    title           : 牵丝戏
    comment         : 163 key(Don't modify):L64FU3W4YxX3ZFTmbZ+8/VtyyFZpYaEZwBWeBYUoIhTD9n+9XApvnDI33SQMX5/hovsUgti9hVA1nVRCnV2p/JFk/KogWWpzJQE7UDv7jwhDvQEpzKhh5cV1ribc34A73au+8wyCBqJDRJP2g7PSBHwGuxUsoS2O64gLvAdVfhfjd9p5aDHjskwNs6ZjhoQ45q6BlPJRf+bTmH0STg/SHgWMyYdN6EeHUVkFQTY06
    track           : 12
  Duration: 00:03:59.44, start: 0.025056, bitrate: 321 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 320 kb/s
    Metadata:
      encoder         : Lavc57.24
    Stream #0:1: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 640x640 [SAR 256:256 DAR 1:1], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      comment         : Cover (front)

注意音频流Stream #0:0这行信息,格式为mp3,采样率为44.1kHz,stereo立体声(即双声道),fltp表示数据格式为浮点型(float)。

解码流程图为:

flow

可以看到和FFmpeg5入门教程05:解码视频流过程的基本流程是一样的。

解码代码为:

#include <stdio.h>

#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/ffversion.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include "libpostproc/postprocess.h"

int main()
{
    
    
    const char inFileName[] = "/home/jackey/Music/test.mp3";
    const char outFileName[] = "test.pcm";
    FILE *file=fopen(outFileName,"w+b");
    if(!file){
    
    
        printf("Cannot open output file.\n");
        return -1;
    }

    AVFormatContext *fmtCtx =avformat_alloc_context();
    AVCodecContext *codecCtx = NULL;
    AVPacket *pkt=av_packet_alloc();
    AVFrame *frame = av_frame_alloc();

    int aStreamIndex = -1;

    do{
    
    

        if(avformat_open_input(&fmtCtx,inFileName,NULL,NULL)<0){
    
    
            printf("Cannot open input file.\n");
            return -1;
        }
        if(avformat_find_stream_info(fmtCtx,NULL)<0){
    
    
            printf("Cannot find any stream in file.\n");
            return -1;
        }

        av_dump_format(fmtCtx,0,inFileName,0);

        for(size_t i=0;i<fmtCtx->nb_streams;i++){
    
    
            if(fmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){
    
    
                aStreamIndex=(int)i;
                break;
            }
        }
        if(aStreamIndex==-1){
    
    
            printf("Cannot find audio stream.\n");
            return -1;
        }

        AVCodecParameters *aCodecPara = fmtCtx->streams[aStreamIndex]->codecpar;
        AVCodec *codec = avcodec_find_decoder(aCodecPara->codec_id);
        if(!codec){
    
    
            printf("Cannot find any codec for audio.\n");
            return -1;
        }
        codecCtx = avcodec_alloc_context3(codec);
        if(avcodec_parameters_to_context(codecCtx,aCodecPara)<0){
    
    
            printf("Cannot alloc codec context.\n");
            return -1;
        }
        codecCtx->pkt_timebase = fmtCtx->streams[aStreamIndex]->time_base;

        if(avcodec_open2(codecCtx,codec,NULL)<0){
    
    
            printf("Cannot open audio codec.\n");
            return -1;
        }

        while(av_read_frame(fmtCtx,pkt)>=0){
    
    
            if(pkt->stream_index==aStreamIndex){
    
    
                if(avcodec_send_packet(codecCtx,pkt)>=0){
    
    
                    while(avcodec_receive_frame(codecCtx,frame)>=0){
    
    
                        /*
                          Planar(平面),其数据格式排列方式为 (特别记住,该处是以点nb_samples采样点来交错,不是以字节交错):
                          LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)
                          而不带P的数据格式(即交错排列)排列方式为:
                          LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)
                        */
                        if(av_sample_fmt_is_planar(codecCtx->sample_fmt)){
    
    
                            int numBytes =av_get_bytes_per_sample(codecCtx->sample_fmt);
                            //pcm播放时是LRLRLR格式,所以要交错保存数据
                            for(int i=0;i<frame->nb_samples;i++){
    
    
                                for(int ch=0;ch<codecCtx->channels;ch++){
    
    
                                    fwrite((char*)frame->data[ch]+numBytes*i,1,numBytes,file);
                                }
                            }
                        }
                    }
                }
            }
            av_packet_unref(pkt);
        }
    }while(0);

    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_close(codecCtx);
    avcodec_free_context(&codecCtx);
    avformat_free_context(fmtCtx);

    fclose(file);

    return 0;
}

和解码视频的部分类似。解码结果为84.5MB。

我们使用ffplay播放一下看看效果:

ffplay -ar 44100 -ac 2 -f f32le -i test.pcm 

ar为audio rate,ac为audio channel ,f32le为float 32位小端数据格式。

显示为:

result

没发现什么大问题。

完整代码在github中的15.ffmpeg_audio_decode_mp32pcm

猜你喜欢

转载自blog.csdn.net/qq_26056015/article/details/125881590