ffmpeg拉音频流并用alsa播放

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

Ubuntu上ffmpeg拉音频流RTMP用alsa播放

1.环境需求

ffmpeg和alsa

csdn教程一大把。

2.实现方法

①通过FFmpeg框架,拉取音频流,提取出音频数据并处理。

②通过Alsa框架进行播放;

3.直接上源码

#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>


#define SAMPLE_RATE 48000
#define CHANNELS 2
#define FSIZE 2*CHANNELS

int main(int argc, char *argv[])
{
	int rc;
	int size;
	
	int ret;
	
	unsigned int val;

	int dir;
	snd_pcm_uframes_t frames;
	char *buffer;
	char *inFile;

	int fd;
	int err;

	inFile = "output.raw";

	fd = open(inFile,O_RDONLY);
	
	snd_pcm_t *handle;
    //以播放模式打开设备
	rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_PLAYBACK, 0);
	if (rc < 0) 
	{
		fprintf(stderr,"unable to open pcm device: %s\n",snd_strerror(rc));
		exit(1);
	}
    //配置硬件参数结构体
	snd_pcm_hw_params_t *params;
	
	//params申请内存
    snd_pcm_hw_params_malloc(&params);

	 //使用pcm设备初始化hwparams
	err=snd_pcm_hw_params_any(handle, params);
	if (err < 0)
	{
		fprintf(stderr, "Can not configure this PCM device: %s\n",snd_strerror(err));
		exit(1);
	}
	
	//设置多路数据在buffer中的存储方式
	//SND_PCM_ACCESS_RW_INTERLEAVED每个周期(period)左右声道的数据交叉存放
	err=snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) 
	{
		fprintf(stderr,"Failed to set PCM device to interleaved: %s\n",snd_strerror(err));
		exit(1);
	}
	
	//设置16位采样格式,S16为有符号16位,LE是小端模式
	err=snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_S16_LE);
	if (err < 0)
	{
		fprintf(stderr,"Failed to set PCM device to 16-bit signed PCM: %s\n",snd_strerror(err));
		exit(1);
	}
	
	//设置声道数,双声道
	err=snd_pcm_hw_params_set_channels(handle, params, CHANNELS);
	if (err < 0)
	{
		fprintf(stderr, "Failed to set PCM device to mono: %s\n",snd_strerror(err));
		exit(1);
	}
	
	//采样率48000
	val = SAMPLE_RATE;
	//设置采样率,如果采样率不支持,会用硬件支持最接近的采样率
	err=snd_pcm_hw_params_set_rate_near(handle, params,&val, &dir);
	if (err < 0) 
	{
		fprintf(stderr, "Failed to set PCM device to sample rate =%d: %s\n",val,snd_strerror(err));
		exit(1);
	}
	
	unsigned int buffer_time,period_time;
	//获取最大的缓冲时间,buffer_time单位为us,500000us=0.5s
	snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0);
	if ( buffer_time >500000)
		buffer_time = 500000;
	
    //设置缓冲时间
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0);
	if (err < 0) 
	{
		fprintf(stderr, "Failed to set PCM device to buffer time =%d: %s\n",buffer_time,snd_strerror(err));
		exit(1);
	}
	
    period_time = 26315;
    //设置周期时间
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0);
	if (err < 0) 
	{
		fprintf(stderr, "Failed to set PCM device to period time =%d: %s\n",period_time,snd_strerror(err));
		exit(1);
	}

    //让这些参数作用于PCM设备
	rc = snd_pcm_hw_params(handle, params);
	if (rc < 0) 
	{
		fprintf(stderr,"unable to set hw parameters: %s\n",snd_strerror(rc));
		exit(1);
	}

	snd_pcm_hw_params_get_period_size(params, &frames,&dir);
	// 1 frame = channels * sample_size.
	size = frames * FSIZE; /* 2 bytes/sample, 1 channels */
	buffer = (char *) malloc(size);
    

    //服务器地址
    char *in_name="rtmp://127.0.0.1/live/stream";
   
    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 = 48000;
    pframePCM->nb_samples = frames;
    pframePCM->channels = CHANNELS;
    av_frame_get_buffer(pframePCM, 0);
    
    //拉流获得的音频帧
	AVFrame *pframeAAC;
	pframeAAC = av_frame_alloc();

	pframeAAC->format = AV_SAMPLE_FMT_FLTP;
    pframeAAC->channel_layout = AV_CH_LAYOUT_STEREO;
    pframeAAC->sample_rate = 44100;
    pframeAAC->nb_samples = 1024;
    pframeAAC->channels = CHANNELS;
    av_frame_get_buffer(pframeAAC, 0);

    
    //作用:拉流获得的音频帧-->用于播放的音频帧
    //音频帧转换器
    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, 
	                   48000, 
					   AV_CH_LAYOUT_STEREO,     //初始音频帧
	                   AV_SAMPLE_FMT_FLTP,
	                   44100, 
	                   0, 
	                   NULL);
    
	//音频帧转换器初始化
	if ((ret = swr_init(pcm_convert_ctx)) < 0) 
	{
        fprintf(stderr, "Failed to initialize the resampling context\n");
        return -1;
    }
    

	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, pframeAAC, &got_picture, dec_pkt);
        if(!got_picture)
        {
            printf("获取初始音频帧失败\n");
            continue;
        }
        
		//aac-->pcm格式转换
        ret=swr_convert(pcm_convert_ctx,pframePCM->data, pframePCM->nb_samples,(const uint8_t **)pframeAAC->data, pframeAAC->nb_samples);
        if (ret <= 0)
		{
		    printf("音频帧转化失败\n");
			continue;
		}

		memcpy(buffer,pframePCM->data[0],size);
        
        //播放音频
		rc = snd_pcm_writei(handle, buffer, frames);
		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)frames) 
		{
			fprintf(stderr,"short write, write %d frames\n", rc);
		}
		
        av_free_packet(dec_pkt);
	}
	avformat_close_input(&infmt_ctx);
	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	free(buffer);
	close(fd);
	return 0;
}

4.编译指令

gcc alsa_ffmpeg_decode.c -o alsa_ffmpeg_decode -lasound -lavformat -lavutil -lavcodec -lavdevice -lswresample -lswscale

5.存在的问题
拉流下来的音频播放延时有10秒

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值