基于FFMPEG的音频编码器

编码模块

借鉴雷霄骅的PCM编码为AAC。
url: http://blog.csdn.net/leixiaohua1020/article/details/25430449web

编码模块是编码存放在FIFO中的数据,而后udp输出,具体的数据流向图以下:
这里写图片描述
编码模块数据流向图
编码前,为32位双声道48KHz的PCM数据,由于ffmpeg MP2编码器所支持的PCM数据为16位,因此须要PCM重采样。

编码单独为一个线程,具体的程序流程图以下。图中蓝色背景为实际输出数据函数,浅绿色为编码函数,棕色为PCM重采样函数。
这里写图片描述
这里写图片描述网络

简单介绍下流程中各函数意义:
av_register_all():注册FFmpeg全部编解码器。
avformat_ network_init():注册FFmpeg全部网络协议。(若是没有,不能udp、rtmp等输出)
avformat_alloc_output_context2():初始化输出码流的AVFormatContext。
avio_open():打开输出文件。
av_new_stream():建立输出码流的AVStream。
avcodec_find_encoder():查找编码器。
avcodec_open2():打开编码器。
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不须要此函数。好比说MPEG2TS)。
swr_convert ():PCM重采样。即将frame_buf的原始数据PCM重采样后,写到AVFrame.data中去。
avcodec_encode_audio2():编码音频。即将AVFrame(存储PCM采样数据)编码为AVPacket(存储AAC,MP3等格式的码流数据)。
av_write_frame():将编码后的视频码流写入文件。
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不须要此函数。好比说MPEG2TS)。svg

代码实现以下:函数

struct SwrContext *swr_config_coder(struct SwrContext *s)
{
    if(NULL == s)
    {
        s = swr_alloc();
    }
    s = swr_alloc_set_opts(s,         // we're allocating a new context
            AV_CH_LAYOUT_STEREO,    // out_ch_layout
            AV_SAMPLE_FMT_S16,      // out_sample_fmt
            SWR_SAMPLE_RATE,        // out_sample_rate
            AV_CH_LAYOUT_STEREO,    // in_ch_layout
            AV_SAMPLE_FMT_S32,      // in_sample_fmt
            SWR_SAMPLE_RATE,        // in_sample_rate
            0,                      // log_offset
            NULL);                  // log_ctx
    swr_init(s);

    return s;
}


int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)
{
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
        CODEC_CAP_DELAY))
        return 0;
    while (1)
    {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
            NULL, &got_frame);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame){
            ret=0;
            break;
        }
        printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    }
    return ret;
}

void * Coder_Thread(void * arg)
{
    UDP_Lock();
    //define use struct
    AVFormatContext* pFormatCtx = NULL;
    AVOutputFormat* fmt = NULL;
    AVStream* audio_st = NULL;
    AVCodecContext* pCodecCtx = NULL;
    AVCodec* pCodec = NULL;
    SwrContext* swr_ctx = NULL;                 //pcm转换结构体

    uint8_t* frame_buf[SWR_CH_MAX];
    AVPacket pkt;
    AVFrame* pFrame = NULL;

    int got_frame=0;
    int dst_nb_samples;
    int ret=0;
    int size=0;

    //int64_t now_time = 0;
    //int64_t last_time = 0;

#ifdef DEBUG_CODER_FIFO
    int pipe_fd;
    int open_mode = O_RDONLY;

    if (access(CODER_FIFO_NAME, F_OK) == -1)
    {
        ret = mkfifo(CODER_FIFO_NAME, 0777);
        if (ret != 0)
        {
            DEBUG_LOG("Could not create fifo %s\n", CODER_FIFO_NAME);
        }
    }

    pipe_fd = open(CODER_FIFO_NAME, open_mode);
    if(pipe_fd == -1)
    {
        DEBUG_LOG("Cannot open FIFO");
        return NULL;
    }
#endif

    char out_file[32];
    ST_PEBROCASTOUTPUT stPE_BrocastOutput;
    PARAMS_GetBroadCastOutput(&stPE_BrocastOutput);
    snprintf(out_file, 32, "udp://%d.%d.%d.%d:%d",
            stPE_BrocastOutput.u8BroadCastIpAddr[0],
            stPE_BrocastOutput.u8BroadCastIpAddr[1],
            stPE_BrocastOutput.u8BroadCastIpAddr[2],
            stPE_BrocastOutput.u8BroadCastIpAddr[3],
            stPE_BrocastOutput.u8BroadCasDesttPort);
    DEBUG_LOG("udp_url : %s", out_file);

#ifdef DEBUG_CODER_PCM
    //fixme tset swrpcmout
    FILE *pout_file = NULL;
    int data_size;
    pout_file = fopen("S16.pcm", "wb");
#endif

    //ser config
    swr_ctx = swr_config_coder(swr_ctx);
    if(swr_ctx == NULL)
    {
        DEBUG_LOG("swr_ctx config fail!");
        return NULL;
    }

    av_register_all();
    avformat_network_init();

    //Method 1.
    //pFormatCtx = avformat_alloc_context();
    //fmt = av_guess_format(NULL, out_file, NULL);
    //pFormatCtx->oformat = fmt;


    //Method 2.
    avformat_alloc_output_context2(&pFormatCtx, NULL, "mpegts", out_file);
    if(!pFormatCtx)
    {
        DEBUG_LOG("Could not create output context\n");
        return NULL ;
    }
    fmt = pFormatCtx->oformat;

    //Open output URL
    if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_WRITE) < 0)
    {
        DEBUG_LOG("Failed to open output file!\n");
        return NULL;
    }

    audio_st = avformat_new_stream(pFormatCtx, NULL);
    if (audio_st == NULL)
    {
        return NULL ;
    }

    //set codec param
    pCodecCtx = audio_st->codec;
    pCodecCtx->codec_id = fmt->audio_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
    pCodecCtx->sample_rate= 48000;
    pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
    pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
    //fixme
    //pCodecCtx->bit_rate = 64000;

    //Show some information
    //av_dump_format(pFormatCtx, 0, out_file, 1);

    //open encoder
    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec){
        DEBUG_LOG("Can not find encoder!\n");
        return NULL ;
    }
    if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
        DEBUG_LOG("Failed to open encoder!\n");
        return NULL ;
    }

    //new frame
    pFrame = av_frame_alloc();
    pFrame->nb_samples = pCodecCtx->frame_size;
    pFrame->format = pCodecCtx->sample_fmt;

    //size maybe nums bytes of 1 fram
    size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,AV_SAMPLE_FMT_S32, 1);
    // 初始化buffer
    frame_buf[0] = av_malloc(size);
    if(frame_buf[0] == NULL)
    {
        DEBUG_LOG("frame_buf[0] malloc fail.");
        return NULL;
    }

    //Write Header
    ret = avformat_write_header(pFormatCtx, NULL);
    if (ret < 0)
    {
        DEBUG_LOG( "Error occurred when opening output file\n");
        return NULL;
    }
    av_new_packet(&pkt, size);
    while(1)
    {
        memset(frame_buf[0], 0, size);
        read(pipe_fd, frame_buf[0], size);

#ifdef DEBUG_CODER_PCM
        //fixme test out32pcm
        fwrite(frame_buf[0], 1, size, pout_file);
#endif

        //alloc fram->data
        av_freep(&pFrame->data[0]);
        ret = av_samples_alloc(pFrame->data, &pFrame->linesize[0],
            av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO), pFrame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
        if(ret < 0)
        {
            DEBUG_LOG("alloc samples error!");
            break;
        }
        //convert pcm
        dst_nb_samples = swr_convert(swr_ctx, pFrame->data, pFrame->nb_samples,
            (const uint8_t **)frame_buf, pFrame->nb_samples);
        if( dst_nb_samples < 0)
        {
            DEBUG_LOG("Convert pcm error!");
            break;
        }

#ifdef DEBUG_CODER_PCM
        //DEBUG_LOG("#####size of pcm :%x", sizeof(pFrame->data[0]));
        //fixme test outpcm
        //data_size = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO) * dst_nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
        //fwrite(pFrame->data[0], 1, data_size, pout_file);
#endif

        //Encode
        got_frame = 0;
        ret = avcodec_encode_audio2(pCodecCtx, &pkt, pFrame, &got_frame);
        if(ret < 0)
        {
            DEBUG_LOG("Failed to encode!\n");
            return NULL ;
        }

        if (got_frame==1)
        {
            pkt.stream_index = audio_st->index;

            ret = av_interleaved_write_frame(pFormatCtx, &pkt);
            if(ret < 0)
            {
                DEBUG_LOG("write pkt error");
                break;
            }
            av_free_packet(&pkt);
        }

        if(g_coderEnd == true)
        {
            DEBUG_LOG("End coder thread");
            g_coderEnd = false;
            break;
        }

    }

    //Flush Encoder
    ret = flush_encoder(pFormatCtx,0);
    if (ret < 0) {
        DEBUG_LOG("Flushing encoder failed\n");
        return NULL ;
    }


    //Write Trailer
    av_write_trailer(pFormatCtx);

    //Clean
    close(pipe_fd);
    if (audio_st)
    {
        avcodec_close(audio_st->codec);
        av_free(pFrame);
        av_free(frame_buf[0]);
    }
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);

#ifdef DEBUG_CODER_PCM
    fclose(pout_file);
#endif

    DEBUG_LOG("Exit coder thread");
    g_coder_thread = 0;
    UDP_UnLock();
    return NULL ;
}