借鉴雷霄骅的PCM编码为AAC。
url: http://blog.csdn.net/leixiaohua1020/article/details/25430449
编码模块是编码存放在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 ;
}