ffmpeg+alsa 实现的mp3播放器

压力测试如下:循环播放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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无v邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值