1. 程式人生 > >基於FFMPEG的音訊編碼器

基於FFMPEG的音訊編碼器

編碼模組

編碼模組是編碼存放在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)。

程式碼實現如下:

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 ; }