Linux平台的C语言实现ffmpeg框架+alsa框架的音乐播放器(wav、MP3格式转pcm)

6 篇文章 1 订阅
5 篇文章 0 订阅

Linux平台的C语言实现ffmpeg框架+alsa框架的音乐播放器(wav、MP3格式转pcm)

1.ffpeg和alsa环境
(csdn教程一堆)

2.程序源码

#include <alsa/asoundlib.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>

static char *device = "hw:1,0";			/* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16;	/* sample format */
static unsigned int rate = 44100;			/* stream rate */
static unsigned int channels = 2;			/* count of channels */
static unsigned int buffer_time = 500000;		/* ring buffer length in us */
static unsigned int period_time = 100000;		/* period time in us */
static int resample = 1;				/* enable alsa-lib resampling */
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
snd_pcm_access_t mode = SND_PCM_ACCESS_RW_INTERLEAVED;
static snd_output_t *output = NULL;

/*配置参数*/
static int set_hwparams(snd_pcm_t *handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access)
{
	unsigned int rrate;
	snd_pcm_uframes_t size;
	int err, dir;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}
	/* set hardware resampling */
	err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
	if (err < 0) {
		printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the interleaved read/write format */
	/*访问格式*/
	err = snd_pcm_hw_params_set_access(handle, params, mode);
	if (err < 0) {
		printf("Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	/*采样格式*/
	err = snd_pcm_hw_params_set_format(handle, params, format);
	if (err < 0) {
		printf("Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the count of channels */
	/*音频声道*/
	err = snd_pcm_hw_params_set_channels(handle, params, channels);
	if (err < 0) {
		printf("Channels count (%u) not available for playbacks: %s\n", channels, snd_strerror(err));
		return err;
	}

	/* set the stream rate */
	/*采样率*/
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		printf("Rate %uHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		printf("Rate doesn't match (requested %uHz, get %iHz)\n", rate, err);
		return -EINVAL;
	}

	/* set the buffer time */
	/*底层buffer区间,以时间为单位,500000=0.5s*/
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		printf("Unable to set buffer time %u for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size(params, &size);
	if (err < 0) {
		printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}

	buffer_size = size;
	printf("buffer_size=%ld\n",buffer_size);
	/* set the period time */
	/*底层period区间,以时间为单位,100000=0.1s*/
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		printf("Unable to set period time %u for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}
	/*底层period区间,以字节为单位,44100*0.1=4410*/
	err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
	if (err < 0) {
		printf("Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}
	period_size = size;
	printf("period_size=%ld\n",period_size);
	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int rc;
	int size;
	
	snd_pcm_t *handle;
	snd_pcm_hw_params_t *hwparams;

	snd_pcm_hw_params_alloca(&hwparams);

	printf("Playback device is %s\n", device);
	printf("Stream parameters are %uHz, %s, %u channels\n", rate, snd_pcm_format_name(format), channels);
    int err;

    err = snd_output_stdio_attach(&output, stdout, 0);
	if (err < 0) {
		printf("Output failed: %s\n", snd_strerror(err));
		return 0;
	}
    /*设置播放模式*/
	err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
	if (err < 0) 
	{
		printf("Playback open error: %s\n", snd_strerror(err));
		return 0;
	}
    /*设置参数*/
    err = set_hwparams(handle, hwparams, mode);
	if (err < 0) {
		printf("Setting of hwparams failed: %s\n", snd_strerror(err));
		return 0;
	}
     
	//period_size大概是采样点数/帧——4410点/帧
	//s16位代表两个字节,再加上双声道
	//size公式=period_size*channels*16/8
	size = (period_size * channels * snd_pcm_format_physical_width(format)) / 8; /* 2 bytes/sample, 1 channels */
	printf("size:%d\n",size);

	char *buffer;
	buffer = (char *) malloc(size);
    memset(buffer,0,size);

	char *in_name="鄧紫棋 - 睡公主.wav";

    int ret;
    AVFormatContext* infmt_ctx = NULL;
    //创建输入封装器
    ret=avformat_open_input(&infmt_ctx, in_name, NULL, NULL);
    if (ret != 0) 
    {
        printf("failed alloc output context\n");
        return -1;
    }
	
	//读取一部分视音频流并且获得一些相关的信息
    avformat_find_stream_info(infmt_ctx, NULL);
    
	//视频流和音频流的标志
	int audioindex=-1;
	//查找视频||音频流
	for(int i=0; i<infmt_ctx->nb_streams; i++) 
	{
		if(infmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
		{
			audioindex=i;
			break;
	    }

	}
    
	if (audioindex == -1) 
	{
        printf("input video stream not exist\n");
        return -1;
    }
    
	AVCodec* decodec = NULL;

	AVCodecContext* decodec_ctx = NULL;

	decodec_ctx=infmt_ctx->streams[audioindex]->codec;
	//找到解码器
	decodec = avcodec_find_decoder(decodec_ctx->codec_id);
	if (!decodec) 
	{
		printf("not find decoder\n");
		avformat_close_input(&infmt_ctx);
		return -1;
	}

	//打开解码器
	ret = avcodec_open2(decodec_ctx, decodec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
        return -1;
    }
	
	//查看输入封装内容
	av_dump_format(infmt_ctx, 0, in_name,0);
    
    AVPacket *dec_pkt;
	dec_pkt = (AVPacket *)av_malloc(sizeof(AVPacket)); 
    
	AVFrame *pframePCM;
	pframePCM = av_frame_alloc();

	pframePCM->format = AV_SAMPLE_FMT_S16;
    pframePCM->channel_layout = AV_CH_LAYOUT_STEREO;
    pframePCM->sample_rate = rate;
    pframePCM->nb_samples = period_size;
    pframePCM->channels = channels;
    av_frame_get_buffer(pframePCM, 0);

	AVFrame *pframeSRC;
	pframeSRC = av_frame_alloc();

	struct SwrContext *pcm_convert_ctx  = swr_alloc();
    if (!pcm_convert_ctx) 
    {
        fprintf(stderr, "Could not allocate resampler context\n");
        return -1;
    }
    
	swr_alloc_set_opts(pcm_convert_ctx, 
	                   AV_CH_LAYOUT_STEREO, 
	                   AV_SAMPLE_FMT_S16, 
	                   pframePCM->sample_rate, 
					   av_get_default_channel_layout(decodec_ctx->channels), 
	                   decodec_ctx->sample_fmt,
	                   decodec_ctx->sample_rate, 
	                   0, 
	                   NULL);
    
	ret = swr_init(pcm_convert_ctx);
	if (ret<0) 
	{
        fprintf(stderr, "Failed to initialize the resampling context\n");
        return -1;
    }

    int got_picture;
	int nb_data;

	while (1) 
	{
		ret=av_read_frame(infmt_ctx,dec_pkt);
		if (ret != 0)
        {    
            printf("fail to read_frame\n");
            break;
        }
        
		//解码获取初始音频
        ret = avcodec_decode_audio4(decodec_ctx, pframeSRC, &got_picture, dec_pkt);
        if(!got_picture)
        {
            printf("456\n");
            continue;
        }
        
		//MP3->PCM,
        ret=swr_convert(pcm_convert_ctx,pframePCM->data, pframePCM->nb_samples,(const uint8_t **)pframeSRC->data, pframeSRC->nb_samples);
        if (ret <= 0)
		{
		    printf("123\n");
			continue;
		}

        nb_data=ret;
		//向硬件写入音频数据
		rc = snd_pcm_writei(handle, pframePCM->data[0], nb_data);
		if (rc == -EPIPE) {
			fprintf(stderr, "underrun occurred\n");
			err=snd_pcm_prepare(handle);
			if(err<0)
			{
				fprintf(stderr, "can not recover from underrun: %s\n",snd_strerror(err));
			}
		
		} 
		else if (rc < 0) {
			fprintf(stderr,"error from writei: %s\n",snd_strerror(rc));
		}  
		else if (rc != (int)nb_data) {
			fprintf(stderr,"short write, write %d frames\n", rc);
		}
	}
	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	free(buffer);
	
	return 0;
}

3.编译

gcc alsa_ffmpeg_play.c -o alsa_ffmpeg_play -lavformat -lavcodec -lswscale -lswresample -lavutil -lasound

4.总结
大家可以尝试其他音频格式。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1、Java实现wav音频文件换为pcm音频文件(AudioUtils.java) 2、Java实现播放pcm音频文件(PCMPlay.java) WAVwav是一种无损的音频文件格式WAV符合 PIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。 PCM:PCM(Pulse Code Modulation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]、[0]等符号构成的数字信号,而未经过任何编码和压缩处理。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。 简单来说:wav是一种无损的音频文件格式pcm是没有压缩的编码方式。 WAVPCM的关系 WAV可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被PCM编码处理的WAV,但这不表示WAV只能使用PCM编码,MP3编码同样也可以运用在WAV中,和AVI一样,只要安装好了相应的Decode,就可以欣赏这些WAV了。在Windows平台下,基于PCM编码的WAV是被支持得最好的音频格式,所有音频软件都能完美支持,由于本身可以达到较高的音质的要求,因此,WAV也是音乐编辑创作的首选格式,适合保存音乐素材。因此,基于PCM编码的WAV被作为了一种中介的格式,常常使用在其他编码的相互换之中,例如MP3换成WMA。 简单来说:pcm是无损wav文件中音频数据的一种编码方式,但wav还可以用其它方式编码。
ffmpeg SDL Qt播放器框架图主要包括三个核心组件:FFmpeg、SDL和Qt。 在框架图中,FFmpeg作为主要的多媒体处理库,负责解码、编码、码和处理音视频数据。它支持多种音视频格式,并提供了丰富的API供开发者调用。FFmpeg可以从本地文件、网络流媒体或实时摄像头中获取音视频数据,并将其解码成原始音视频流。 SDL(Simple DirectMedia Layer)是一个跨平台的音视频输出和输入库,它提供了对音视频设备和图形处理的底层访问接口。SDL可以与各种操作系统、硬件和图形API相结合使用,并且具有卓越的性能和兼容性。在播放器框架中,SDL用于接收FFmpeg解码后的音视频数据,并将其输出到屏幕上进行显示。 Qt是一个功能强大的跨平台应用程序开发框架,提供了丰富的图形界面和多媒体功能。在播放器框架中,Qt用于创建播放器应用程序的用户界面,包括播放控制按钮、进度条、音量调节等交互元素。通过Qt,用户可以方便地操作播放器,选择不同的音视频文件进行播放,并进行暂停、停止、快进、快退等操作。 整个框架的工作流程如下:首先,通过Qt创建播放器应用程序的界面,并将其与SDL相关联。当用户通过界面选择要播放的音视频文件时,Qt利用FFmpeg从文件中读取音视频数据并进行解码。解码后的数据经过SDL输出到屏幕上进行实时显示。同时,Qt还负责管理播放器的状态,包括播放、暂停、停止和音量等。用户通过界面上的交互元素进行操作后,Qt会响应相应的事件,修改播放器状态,并与FFmpeg和SDL进行交互,实现音视频数据的读取、解码和输出。 通过整合FFmpeg、SDL和Qt,可以构建一个功能完善、易于使用的音视频播放器框架实现强大的多媒体处理和播放功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值