使用FFMPEG实现音频播放器
导言
因为公司项目的原因,要学习如何使用FFMPEG进行音频播放,折腾一圈发现,使用FFMPEG还真不是一件简单的事,更为可惜的是,当年在这方面的杰出人物—雷霄骅的英逝,这方面的文档愈发稀少,而FFMPEG相关代码不断更新,整理此文,算是对他的一种缅怀吧。
FFMPEG简介
Q:FFMPEG是什么?
A:FFMPEG是一个多媒体框架
Q:FFMPEG作为一个多媒体框架,有什么功能?
A:FFMEPG作为一个多媒体框架,是由多个模块组成的,每个模块都有对应的功能:
- libavutil是一个包含简化编程功能的库,其中包括随机数生成器,数据结构,数学代码,核心多媒体工具等更多东西。
- libavcodec是一个包含音频/视频解码器和编码器的库。
- libavformat是一个包含了多媒体格式的分离器和混流器的库。
- libavdevice是一个包含输入输出设备的库,用于捕捉和渲染很多来自常用的多媒体输入/输出软件框架的数据,包括Video4Linux,Video4Linux2,VfW和ALSA。
- libavfilter是一个包含媒体过滤器的库。
- libswscale是一个用于执行高度优化的图像缩放和颜色空间/像素格式转换操作的库。
- libswresample是一个用于执行高度优化的音频重采样,重新矩阵和取样格式转换操作的库。
FFMPEG入门资料
安装FFMPEG
对于如何安装FFMEPG,主要可以查看官方的安装指南,简要步骤如下:
- 安装gcc,yasm
- 下载源码
- 解压后运行./configure
- 执行make & make install
- 安装完成
编写代码
写在编码之前
在编写代码之前,请务必搞懂以下文章的内容:
可能在观看代码时有些不懂得地方,可以看这里:
或者查看手册中的具体说明:
代码
/*********************************
Author: Toudsour
Created Time: 一 3/20 15:15:26 2017
File Name:player.c
*********************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<avcodec.h>
#include<avformat.h>
#include<swresample.h>
#define MAX_AUDIO_FRAME_SIZE 192000
#define LOG(str) printf("Line %d: %s \n", __LINE__, str)
const char *url = "WavinFlag.aac";
const char *output_path = "WavingFlag.pcm";
int main()
{
//注册所有的工具
av_register_all();
//初始化网络相关工具
avformat_network_init();
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *cod_ctx = NULL;
AVCodec *cod = NULL;
//分配一个avformat
fmt_ctx = avformat_alloc_context();
if (fmt_ctx == NULL)
LOG("alloc fail");
//打开文件,解封装
if (avformat_open_input(&fmt_ctx, url, NULL, NULL) != 0)
LOG("open fail");
//查找文件的相关流信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
LOG("find stream fail");
//输出格式信息
av_dump_format(fmt_ctx, 0, url, 0);
//查找解码信息
int stream_index = -1;
for (int i = 0; i < fmt_ctx->nb_streams; i++)
if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
stream_index = i;
break;
}
if (stream_index == -1)
LOG("find stream fail");
//保存解码器
cod_ctx = fmt_ctx->streams[stream_index]->codec;
cod = avcodec_find_decoder(cod_ctx->codec_id);
if (cod == NULL)
LOG("find codec fail");
if (avcodec_open2(cod_ctx, cod, NULL) < 0)
LOG("can't open codec");
FILE *file = NULL;
file = fopen(output_path, "wb");
//创建packet,用于存储解码前的数据
AVPacket *packet = malloc(sizeof(AVPacket));
av_init_packet(packet);
//设置转码后输出相关参数
//采样的布局方式
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
//采样个数
int out_nb_samples = 1024;
//采样格式
enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16;
//采样率
int out_sample_rate = 44100;
//通道数
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
//创建buffer
int buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, sample_fmt, 1);
//注意要用av_malloc
uint8_t *buffer = av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
//创建Frame,用于存储解码后的数据
AVFrame *frame = av_frame_alloc();
printf("Bitrate :\t %3lld\n", fmt_ctx->bit_rate);
printf("Decoder Name :\t %s\n", cod_ctx->codec->long_name);
printf("Cannels:\t %d\n", cod_ctx->channels);
printf("Sample per Second:\t %d\n", cod_ctx->sample_rate);
uint32_t ret,len = 0;
int got_picture;
int64_t in_channel_layout = av_get_default_channel_layout(cod_ctx->channels);
//打开转码器
struct SwrContext *convert_ctx = swr_alloc();
//设置转码参数
convert_ctx = swr_alloc_set_opts(convert_ctx, out_channel_layout, sample_fmt, out_sample_rate, \
in_channel_layout, cod_ctx->sample_fmt, cod_ctx->sample_rate, 0, NULL);
//初始化转码器
swr_init(convert_ctx);
int index = 0;
//while循环,每次读取一帧,并转码
while (av_read_frame(fmt_ctx, packet) >= 0) {
printf("index : %d\n", index);
if (packet->stream_index == stream_index) {
//解码声音
if (avcodec_decode_audio4(cod_ctx, frame, &got_picture, packet) < 0) {
LOG("decode error");
return -1;
}
if (got_picture > 0) {
//转码
swr_convert(convert_ctx, &buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)frame->data, frame->nb_samples);
printf("index: %5d\t pts:%10lld\t packet size:%d\n", index, packet->pts, packet->size);
fwrite(buffer, 1, buffer_size, file);
}
}
index ++;
av_free_packet(packet);
}
swr_free(&convert_ctx);
fclose(file);
LOG("safe exit");
return 0;
}
编译
FFMPEG编译也不是一件容易的事情,在这里我建议使用pkg-config
来给出相关编译信息。
废话不多说,这是下边是MakeFile的内容:
player : player.c
gcc -g -Wall player.c -o player -std=c99 \
-I/usr/local/include/libavformat \
-I/usr/local/include/libavcodec \
-I/usr/local/include/libswresample \
`pkg-config --libs libavformat`
运行
从代码中可以看出,我们需要一个*.acc
音频文件作为我们播放源,这需要大家自己去各大音频网站上自己去获取,最后生成的*.pcm
如何打开呢?
这里我们需要下载工具Audition
,下载安装后,在打开是选择双声道,44100HZ采样然后点击播放就可以播放啦。
祝大家编码顺利。