signed

QiShunwang

“诚信为本、客户至上”

ffmpeg 解码acc,mp3音频并使用alsa lib播放例子

2021/3/21 11:49:07   来源:

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;
}