压力测试如下:循环播放N次,无内存泄露
源码如下:
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <alsa/asoundlib.h>
#include <unistd.h>
#include <string.h>
#define max_buffer_time 500000
snd_pcm_t *mPCMc;
snd_pcm_format_t format; //pcm 数据的格式
unsigned int channels =1; //channel
unsigned int rate; //采样率
snd_pcm_uframes_t chunk_size; //简单理解为一个(channel 1秒xxx)period产生的frame个数
size_t bits_per_sample; //一个sample包含的bit 数
size_t chunk_bytes; //简单理解为(1秒产生xxx)1 period的pcm数据大小
size_t frame_size;
int init(void){
if(snd_pcm_open(&mPCMc, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
printf("snd_pcm_open fail\n");
goto err;
}
//2、录音相关参数设置
snd_pcm_hw_params_t *hwparams;
//构建参数结构体
snd_pcm_hw_params_alloca(&hwparams);
//3、获取参数
if(snd_pcm_hw_params_any(mPCMc, hwparams) < 0) {
printf("snd_pcm_hw_params_any fail\n");
goto err;
}
//4、设置相关参数SND_PCM_ACCESS_MMAP_INTERLEAVED, SND_PCM_ACCESS_RW_INTERLEAVED
int r=snd_pcm_hw_params_set_access(mPCMc, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if( r< 0) {
printf("snd_pcm_hw_params_set_access fail:%s\n",snd_strerror(r));
goto err;
}
format = SND_PCM_FORMAT_S16_LE;
//设置pcm数据格式
if(snd_pcm_hw_params_set_format(mPCMc, hwparams, format) < 0) {
printf("snd_pcm_hw_params_set_format fail\n");
goto err;
}
//设置channel
r = snd_pcm_hw_params_set_channels(mPCMc, hwparams, channels);
if( r< 0) {
printf("snd_pcm_hw_params_set_channels fail:%s\n",snd_strerror(r));
goto err;
}
rate = 44100;
//设置采样率
if(snd_pcm_hw_params_set_rate_near(mPCMc, hwparams,&rate, 0) < 0) {
printf("snd_pcm_hw_params_set_channels fail\n");
goto err;
}
if(rate!=44100){
printf("The rate %d Hz is not supported, Using %d Hz instead.\n",44100,rate);
}
unsigned int buffer_time;
unsigned int period_time ;
if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) {
printf("snd_pcm_hw_params_get_buffer_time_max fail\n");
goto err;
}
printf("buffer time is %d\n",buffer_time);
if (buffer_time > max_buffer_time){
buffer_time = max_buffer_time;
}
period_time = buffer_time / 4;
if (snd_pcm_hw_params_set_buffer_time_near(mPCMc, hwparams, &buffer_time, 0) < 0) {
printf("snd_pcm_hw_params_set_buffer_time_near fail\n");
goto err;
}
if (snd_pcm_hw_params_set_period_time_near(mPCMc, hwparams, &period_time, 0) < 0) {
printf("snd_pcm_hw_params_set_period_time_near fail\n");
goto err;
}
//设置参数给alsa
r =snd_pcm_hw_params(mPCMc, hwparams);
if ( r< 0) {
printf("snd_pcm_hw_params fail:%s\n",snd_strerror(r));
goto err;
}
//获取相关参数
snd_pcm_uframes_t buffer_size;
snd_pcm_hw_params_get_period_size(hwparams, &(chunk_size), 0);
snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
if (chunk_size == buffer_size) {
printf("Can't use period equal to buffer size (%lu == %lu)\n",chunk_size,buffer_size);
goto err;
}
//根据设置计算出audio 的chunk 大小
bits_per_sample = snd_pcm_format_physical_width(format);
frame_size = channels*2;
chunk_bytes = (chunk_size*bits_per_sample*channels)/8;
return 0;
err:
if (mPCMc) {
snd_pcm_close(mPCMc);
mPCMc = NULL;
}
return -1;
}
#define SPACE 1024*300
char g_ring[SPACE];
int g_rpos;
int g_wpos;
int left_space(void){
int leftSpace=0;
if(g_wpos>=g_rpos)
leftSpace=SPACE-g_wpos+g_rpos;
else
leftSpace=g_rpos-g_wpos;
return leftSpace;
}
void put_data(unsigned char* data,int len){
while(left_space()<len)
usleep(1000);
int tmp = g_wpos;
tmp+=len;
if(tmp>SPACE-1) {
// System.arraycopy(data,0, mRingBuff,mWpos,BUF_LEN-mWpos);
memcpy(g_ring+g_wpos,data,SPACE-g_wpos);
// System.arraycopy(data,BUF_LEN-mWpos, mRingBuff,0,tmp - BUF_LEN);
memcpy(g_ring,data+SPACE-g_wpos,tmp -SPACE);
g_wpos = tmp - SPACE ;
}
else {
// System.arraycopy(data, 0, mRingBuff, mWpos, len);
memcpy(g_ring+g_wpos,data,len);
g_wpos+=len;
}
}
void addmRpos(int val){
int tmp=g_rpos+val;
if(tmp>=SPACE)
g_rpos = tmp-SPACE;
else
g_rpos=tmp;
}
static int finish=0;
void get_period(unsigned char* period,int* getted){
while(!finish&&SPACE-left_space()<chunk_bytes)
usleep(1000);
int tmp=g_rpos;
int shouldGet=SPACE-left_space()>chunk_bytes?chunk_bytes:SPACE-left_space();
tmp+=shouldGet;
if(tmp>=SPACE){
//System.arraycopy(bufRing, ur.getmRpos(), fram, 0, UartReader.BUF_LEN-ur.getmRpos());
//System.arraycopy(bufRing, 0, fram, UartReader.BUF_LEN-ur.getmRpos(), 6-(UartReader.BUF_LEN-ur.getmRpos()));
memcpy(period,g_ring+g_rpos,SPACE-g_rpos);
memcpy(period+SPACE-g_rpos,g_ring,shouldGet-(SPACE-g_rpos));
}else {
//System.arraycopy(bufRing, ur.getmRpos(), fram, 0, 6);
memcpy(period,g_ring+g_rpos,shouldGet);
}
addmRpos(shouldGet);
*getted=shouldGet;
}
static int xrun_recovery(snd_pcm_t *handle, int err)
{
if (err == -EPIPE) { /* under-run */
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
return 0;
} else if (err == -ESTRPIPE) {
while ((err = snd_pcm_resume(handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (err < 0) {
err = snd_pcm_prepare(handle);
if (err < 0)
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
}
return 0;
}
return err;
}
void* play(void* arg){
init();
// set_swparams(mPCMc);
printf("channels=%d,formate=%d,rate=%d,bits_per_sample=%d,chunk_bytes=%d,chunk_size=%d\n",channels,16,rate,bits_per_sample,chunk_bytes,chunk_size);
int r = 0;
int first=1;
snd_pcm_uframes_t offset, frames;
//r =snd_pcm_prepare(mPCMc);
if (r < 0) {
printf("p error!(%s)\n", snd_strerror(r));
}
//r =snd_pcm_start(mPCMc);
if (r < 0) {
printf("s error!(%s)\n", snd_strerror(r));
}
snd_pcm_sframes_t avail, commits;
unsigned char data[chunk_bytes];
int len=chunk_bytes;
snd_pcm_state_t state;
snd_pcm_state_t state0=-1;
while(len==chunk_bytes){
#if 0
get_period(data);
int ret = snd_pcm_writei(mPCMc, data, chunk_bytes/2);
if (ret == -EPIPE) {
printf("underrun occured\n");
break;
}
else if (ret < 0) {
printf("error from writei: %s\n", snd_strerror(ret));
break;
}
#endif
#if 1
snd_pcm_state_t state = snd_pcm_state(mPCMc);
if(state0!=state){
printf("state=%d\n",state);
state0=state;
}
switch(state){
case SND_PCM_STATE_PAUSED:
case SND_PCM_STATE_XRUN:
{
printf("xrun happen\n");
r = xrun_recovery(mPCMc, -EPIPE);
if (r < 0) {
printf("XRUN recovery failed: %s\n", snd_strerror(r));
goto end;
}
first = 1;
break;
}
case SND_PCM_STATE_SUSPENDED:
{
while ((r = snd_pcm_resume(mPCMc)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (r < 0) {
r = snd_pcm_prepare(mPCMc);
if (r < 0){
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(r));
goto end;
}
}
break;
}
default:
break;
}
avail=snd_pcm_avail(mPCMc);
//printf("111==111 %d\n",avail);
if (avail < chunk_size) {
if (first) {
first = 0;
r = snd_pcm_start(mPCMc);
if (r < 0) {
printf("Start error: %s\n", snd_strerror(r));
// exit(EXIT_FAILURE);
}
} else {
r = snd_pcm_wait(mPCMc, -1);
if (r < 0) {
if ((r = xrun_recovery(mPCMc, r)) < 0) {
printf("snd_pcm_wait error: %s\n", snd_strerror(r));
goto end;
}
first = 1;
}
}
continue;
}
frames = chunk_size;
while (avail >= chunk_size){
get_period(data,&len);
const snd_pcm_channel_area_t *areas;
r = snd_pcm_mmap_begin(mPCMc, &areas, &offset, &frames);
//printf("frames=%d %d\n",frames,len);
if (r < 0){
printf("snd_pcm_mmap_begin err=%d\n",r);
snd_pcm_recover(mPCMc, r, 1);
if (r < 0) {
printf("recover error!(%s)\n", snd_strerror(r));
goto end;
}
}
memcpy(areas[0].addr + (offset * frame_size),data, len);
// memcpy(areas[1].addr + (offset * frame_size),data, len);
commits = snd_pcm_mmap_commit(mPCMc, offset, frames);
if (commits < 0 || (commits != frames &&len==chunk_bytes)) {
printf("=commit issue\n");
if ((r = xrun_recovery(mPCMc, commits >= 0 ? -EPIPE : commits)) < 0) {
printf("MMAP commit error: %s\n", snd_strerror(r));
goto end;
}
first = 1;
}
else if(len<chunk_bytes){
printf("frames=%d %d\n",frames,len);
break;
}
avail -= frames;
}
#endif
}
end:
printf("play end\n");
snd_pcm_drain(mPCMc);
snd_pcm_close(mPCMc);
return ;
}
static int flag=0;
static int cnt=0;
static int interrupt_cb(void *ctx){
if(flag==0)
return 0;
cnt++;
printf("timeout %d\n",cnt);
return cnt>10?1:0;
}
int main(int argc, char *argv[]){
pthread_t tid;
AVFormatContext *fmt_ctx;
av_register_all();
start:
g_rpos= g_wpos=0;
finish=0;
pthread_create(&tid, NULL, play, NULL);
fmt_ctx = avformat_alloc_context();
fmt_ctx->interrupt_callback.callback=interrupt_cb;
AVDictionary *dic=NULL;
av_dict_set(&dic,"rtbufsize","32",0);
char *rul="http://mp3cdn.hifiok.com/0E/BC/wKgB4VK5wvKAbAzSAJ5ge0R6u2M126.mp3?sign=a1a6819177a9f456496ba712683d6f27&t=1611874126";
if (avformat_open_input(&fmt_ctx,strlen(argv[1])>100?rul:argv[1], NULL, &dic) < 0) {
printf("open file error %s\n",argv[1]);
return -1;
}
printf("%d %d %d\n",fmt_ctx->max_picture_buffer,fmt_ctx->format_probesize,fmt_ctx->pb->buffer_size);
printf("%d \n",fmt_ctx->pb->max_packet_size);
av_dict_free(&dic);
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
printf("find stream info error\n");
goto err0;
}
//获取音频索引
int audio_stream_index = -1;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
printf("find audio stream index\n");
break;
}
}
if(audio_stream_index==-1){
goto err0;
}
//获取解码器
AVCodecContext *codec_ctx = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[audio_stream_index]->codecpar);
AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
//打开解码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
printf("could not open codec\n");
return;
}
printf("r=%d c=%d f=%d\n",codec_ctx->sample_rate,codec_ctx->channels,codec_ctx->sample_fmt);
//分配AVPacket和AVFrame内存,用于接收音频数据,解码数据
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
int got_frame;//接收解码结果
int index = 0;
//pcm输出文件
// FILE *out_file = fopen("./test.pcm", "wb");
while (1) {
//将音频数据读入packet
flag=1;
if(av_read_frame(fmt_ctx, packet) != 0){
flag=0;
cnt=0;
break;
}
flag=0;
cnt=0;
if (packet->stream_index == audio_stream_index) {//取音频索引packet
if (avcodec_decode_audio4(codec_ctx, frame, &got_frame, packet) <0) {//将packet解码成AVFrame
printf("decode error:%d\n", index);
break;
}
if (got_frame > 0) {
//printf("decode frame:%d\n", index++);
//fwrite(frame->data[0], 1, (size_t)(frame->linesize[0]),out_file); //想将单个声道pcm数据写入文件
put_data(frame->data[0],frame->linesize[0]);
}
}
av_packet_unref(packet);
av_frame_unref(frame);
}
finish=1;
printf("decode finish,sleep\n");
sleep(15);
printf("decode finish...\n");
end:
//释放资源
pthread_join(tid, NULL);
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_close(codec_ctx);
avcodec_free_context(&codec_ctx);
err0:
avformat_close_input(&fmt_ctx);
sleep(10);
goto start;
return 0;
}