ffmpeg以流的方式读取acc mp3音频文件解码并重采样后使用alsa lib 声卡card0播放的例子
代码如下:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <alsa/asoundlib.h>
#ifndef WORD
#define WORD unsigned short
#endif
#ifndef DWORD
#define DWORD unsigned int
#endif
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
};
//打開pcm并获取播放句柄
int init_pcm_play(snd_pcm_t **playback_handle,snd_pcm_uframes_t chunk_size,unsigned int rate,int bits_per_sample,int channels)
{
snd_pcm_hw_params_t *hw_params;//硬件信息和PCM流配置
snd_pcm_format_t format;
//初始化声卡
//1. 打开PCM,最后一个参数为0意味着标准配置
if (0 > snd_pcm_open(playback_handle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0))
{
printf("snd_pcm_open err\n");
return -1;
}
//2. 分配snd_pcm_hw_params_t结构体
if(0 > snd_pcm_hw_params_malloc (&hw_params))
{
printf("snd_pcm_hw_params_malloc err\n");
return -1;
}
//3. 初始化hw_params
if(0 > snd_pcm_hw_params_any (*playback_handle, hw_params))
{
printf("snd_pcm_hw_params_any err\n");
return -1;
}
//4. 初始化访问权限
if (0 > snd_pcm_hw_params_set_access (*playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED))
{
printf("snd_pcm_hw_params_any err\n");
return -1;
}
//5. 初始化采样格式SND_PCM_FORMAT_U8,8位
if(8 == bits_per_sample)
{
if (0 > snd_pcm_hw_params_set_format (*playback_handle, hw_params, SND_PCM_FORMAT_U8))
{
printf("snd_pcm_hw_params_set_format err\n");
return -1;
}
format = SND_PCM_FORMAT_U8;
}
if(16 == bits_per_sample)
{
if (0 > snd_pcm_hw_params_set_format (*playback_handle, hw_params, SND_PCM_FORMAT_S16_LE))
{
printf("snd_pcm_hw_params_set_format err\n");
return -1;
}
format = SND_PCM_FORMAT_S16_LE;
}
//6. 设置采样率
if (0 > snd_pcm_hw_params_set_rate_near (*playback_handle, hw_params, &rate, 0))
{
printf("snd_pcm_hw_params_set_rate_near err\n");
return -1;
}
//7. 设置通道数量
if (0 > snd_pcm_hw_params_set_channels(*playback_handle, hw_params, 2))
{
printf("snd_pcm_hw_params_set_channels err\n");
return -1;
}
//8. 设置hw_params
if (0 > snd_pcm_hw_params (*playback_handle, hw_params))
{
printf("snd_pcm_hw_params err\n");
return -1;
}
snd_pcm_hw_params_get_period_size(hw_params, &chunk_size, 0);
snd_pcm_hw_params_free (hw_params);
return 0;
}
static int fill_iobuffer(void *opaque, uint8_t *buf, int buf_size)
{
struct memPbuf *pStreamFrame = NULL;
FILE* fp_open = (FILE*)opaque;
int true_size = 0;
if(!feof(fp_open)){
//从内部构建的缓存中去读
int true_size=fread(buf,1,buf_size,fp_open);
if(true_size < buf_size)
{
//这里是循环播放。如果想只播放一次则不用将读指针偏移到文件头
fseek(fp_open,0,SEEK_SET);
printf("read true_size %d < buf_size %d +++++++++++++++++++++++++++\n",true_size,buf_size);
}
return true_size;
}
else
{
return -1;
}
}
int main(int argc, char ** argv)
{
/******alsa ******/
snd_pcm_t *playback_handle;
int bits_per_sample = 0;
int readSize = 1024;
unsigned char * iobuffer = NULL;
AVIOContext *avio = NULL;
FILE* fp_open = NULL;
printf("image_process main \n");
if(2 != argc)
{
printf("usage:%s audio file\n", argv[0]);
return -1;
}
//注册解码器
av_register_all();
avformat_network_init();
fp_open=fopen(argv[1],"r");
AVFormatContext * pFormatCtx = avformat_alloc_context();
iobuffer=(unsigned char *)av_malloc(readSize);
avio =avio_alloc_context(iobuffer, readSize,0,(void *)fp_open,fill_iobuffer,NULL,NULL);
pFormatCtx->pb = avio;
pFormatCtx->flags=AVFMT_FLAG_CUSTOM_IO;
//打开封装格式
if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0)
{
return -1;
}
//
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
printf("封装格式查找流失败.\n");
return -1;
}
// Find the first audio stream
int audioStream = -1;
for(int i=0; i < pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
audioStream=i;
break;
}
}
if(audioStream==-1)
{
printf("Didn't find a audio stream.\n");
return -1;
}
// Dump valid information onto standard error
av_dump_format(pFormatCtx, audioStream, NULL, false);
// Get a pointer to the codec context for the audio stream
AVCodecContext *pCodecCtx=pFormatCtx->streams[audioStream]->codec;
// Find the decoder for the audio stream
AVCodec *pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
printf("Codec not found.\n");
return -1;
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
printf("Could not open codec.\n");
return -1;
}
//对AAC编码文件来说,编码根据音频参数编码,解码根据音频参数重新构建声波,FFMPEG构建的
//音频存储方式不一定支持播放,所以需要重采样样本 如AAC解码的样本格式是AV_SAMPLE_FMT_FLTP
uint64_t iInputLayout = av_get_default_channel_layout(pCodecCtx->channels);
int iInputChans = pCodecCtx->channels;
AVSampleFormat eInputSampleFormat = pCodecCtx->sample_fmt;
int iInputSampleRate = pCodecCtx->sample_rate;
uint64_t iOutputLayout = av_get_default_channel_layout(pCodecCtx->channels);
int iOutputChans = pCodecCtx->channels;
AVSampleFormat eOutputSampleFormat = AV_SAMPLE_FMT_S16;
int iOutputSampleRate = pCodecCtx->sample_rate;
SwrContext *pSwrCtx = swr_alloc_set_opts(NULL,iOutputLayout, eOutputSampleFormat, iOutputSampleRate,
iInputLayout,eInputSampleFormat , iInputSampleRate,0, NULL);
swr_init(pSwrCtx);
if(eOutputSampleFormat == AV_SAMPLE_FMT_S16 )
{
bits_per_sample = 16;
}
/*** alsa 获取声卡播放句柄 ***/
init_pcm_play(&playback_handle,256,iOutputSampleRate,bits_per_sample,2);
//AVPacket读取原始解码前的数据
AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
av_init_packet(packet);
//1帧数据样本数
int iFrameSamples = pCodecCtx->frame_size;
// 存储原始数据
int iRawLineSize = 0;
int iRawBuffSize = av_samples_get_buffer_size(&iRawLineSize, iInputChans, iFrameSamples, eInputSampleFormat, 0);
uint8_t *pRawBuff = (uint8_t *)av_malloc(iRawBuffSize);
//原始数据保存在AVFrame结构体中
AVFrame* pRawframe = av_frame_alloc();
pRawframe->nb_samples = iFrameSamples;
pRawframe->format = eInputSampleFormat;
pRawframe->channels = iInputChans;
int iReturn = avcodec_fill_audio_frame(pRawframe, iInputChans, eInputSampleFormat, (const uint8_t*)pRawBuff, iRawBuffSize, 0);
if(iReturn<0)
{
return -1;
}
// 存储转换后数据
int iConvertLineSize = 0;
int iConvertBuffSize = av_samples_get_buffer_size(&iConvertLineSize, iOutputChans, iFrameSamples, eOutputSampleFormat, 0);
uint8_t *pConvertBuff = (uint8_t *)av_malloc(iConvertBuffSize);
//转换后数据保存在AVFrame结构体中
AVFrame* pConvertframe = av_frame_alloc();
pConvertframe->nb_samples = iFrameSamples;
pConvertframe->format = eOutputSampleFormat;
pConvertframe->channels = iOutputChans;
iReturn = avcodec_fill_audio_frame(pConvertframe, iOutputChans, eOutputSampleFormat, (const uint8_t*)pConvertBuff, iConvertBuffSize, 0);
if(iReturn<0)
{
return -1;
}
int iGetPicture;
int iDecodeRet;
int iFrameNo = 0;
if (0 > snd_pcm_prepare (playback_handle))
{
printf("snd_pcm_prepare err\n");
return -1;
}
while(av_read_frame(pFormatCtx, packet)>=0)
{
if(packet->stream_index==audioStream)
{
iDecodeRet = avcodec_decode_audio4( pCodecCtx, pRawframe,&iGetPicture, packet);
if ( iDecodeRet < 0 )
{
av_frame_unref(pRawframe);
av_packet_unref(packet);
printf("Error in decoding audio frame.\n");
return -1;
}
if ( iGetPicture > 0 )
{
swr_convert(pSwrCtx, (uint8_t**)pConvertframe->data, iFrameSamples ,(const uint8_t**)pRawframe->data, iFrameSamples );
//往声卡上发送音频pcm数据。
snd_pcm_writei(playback_handle, pConvertframe->data[0], pConvertframe->linesize[0]/4);
//printf("FrameNo:%5d frame size: %d \n",iFrameNo,pConvertframe->linesize[0]);
//fwrite(pConvertframe->data[0],pConvertframe->linesize[0],1,pOutFile);
//usleep(5000);
iFrameNo++;
}
}
av_free_packet(packet);
}
//释放声卡设备
snd_pcm_close(playback_handle);
av_free(pRawBuff);
av_free(pConvertBuff);
swr_free(&pSwrCtx);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
printf("Aac encode Success!!\n");
getchar();
return 0;
}