系列索引:FFmpeg入门系列索引
上一篇:FFmpeg入门教程10.13:Linux下摄像头捕获并编码为h264(无音频)
本系列的之前文章介绍了视频的编解码相关,接下来介绍音频的编解码,本文将mp3音频文件解码为pcm。
使用的mp3音频文件为从网易云音乐上下载的排骨教主的牵丝戏,文件大小为9.6MB。
先看一下文件信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ffprobe test.mp3 Input 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 Metadata: encoder : Lavc57.24 Stream Metadata: comment : Cover (front)
注意音频流Stream #0:0
这行信息,格式为mp3,采样率为44.1kHz,stereo立体声(即双声道),fltp表示数据格式为浮点型(float)。
解码流程图为:
flowchart TB
F --Yes--> I
K --下一帧--> F
I --No--> F
subgraph init
direction TB
A(开始) --> B[打开文件]
B --> C[查找流信息]
C --> D[查找对应解码器]
D --> E[打开解码器]
E --> F{读取帧}
F --No--> G(结束)
end
subgraph decode
direction TB
I{是视频帧?} --Yes--> J[发送帧给解码器]
J --> K[从解码器获取结果]
end
flowchart TB
F --Yes--> I
K --Next Frame--> F
I --No--> F
subgraph init
direction TB
A(Start) --> B[avformat_open_input]
B --> C[av_find_stream_info]
C --> D[avcodec_find_decoder]
D --> E[avcodec_open]
E --> F{av_read_frame}
F --No--> G(End)
end
subgraph decode
direction TB
I{Video Packet?} --Yes--> J[avcodec_send_packet]
J --> K[avcodec_receive_frame]
end
可以看到和FFmpeg入门教程10.04:解码视频流过程 的基本流程是一样的。
解码代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #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 ){ if (av_sample_fmt_is_planar (codecCtx->sample_fmt)){ int numBytes =av_get_bytes_per_sample (codecCtx->sample_fmt); 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播放一下看看效果:
1 ffplay -ar 44100 -ac 2 -f f32le -i test.pcm
ar为audio rate,ac为audio channel ,f32le为float 32位小端数据格式。
显示为:
没发现什么大问题。
完整代码在ffmpeg_beginner 中的10.14.ffmpeg_audio_decode_mp32pcm
。
下一篇:FFmpeg入门教程10.15:音频重采样解码为pcm